0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-27 01:02:46 +01:00
construct/include/ircd/resource.h
2018-11-06 21:36:36 -08:00

241 lines
6.5 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_RESOURCE_H
namespace ircd
{
struct client;
struct resource;
}
/// The target of an HTTP request specified by clients with a path.
///
struct ircd::resource
{
IRCD_EXCEPTION(ircd::error, error)
enum flag :uint;
struct opts;
struct method;
struct request;
struct response;
static log::log log;
static std::map<string_view, resource *, iless> resources;
string_view path;
std::unique_ptr<const struct opts> opts;
std::map<string_view, method *> methods;
unique_const_iterator<decltype(resources)> resources_it;
string_view allow_methods_list(const mutable_buffer &buf) const;
public:
method &operator[](const string_view &name) const;
resource(const string_view &path, struct opts);
resource(const string_view &path);
resource() = default;
~resource() noexcept;
static resource &find(const string_view &path);
};
enum ircd::resource::flag
:uint
{
DIRECTORY = 0x01,
};
struct ircd::resource::opts
{
/// developer's literal description of the resource
string_view description
{
"no description"
};
/// flags for the resource
flag flags
{
flag(0)
};
/// parameter count limits (DIRECTORY only)
std::pair<short, short> parc
{
0, // minimum params
15 // maximum params
};
};
struct ircd::resource::method
{
enum flag :uint;
struct opts;
struct stats;
using handler = std::function<response (client &, request &)>;
struct resource *resource;
string_view name;
handler function;
std::unique_ptr<const struct opts> opts;
std::unique_ptr<struct stats> stats;
unique_const_iterator<decltype(resource::methods)> methods_it;
string_view verify_origin(client &, request &) const;
string_view authenticate(client &, request &) const;
void handle_timeout(client &) const;
void call_handler(client &, request &);
public:
void operator()(client &, const http::request::head &, const string_view &content_partial);
method(struct resource &, const string_view &name, handler, struct opts);
method(struct resource &, const string_view &name, handler);
~method() noexcept;
};
enum ircd::resource::method::flag
:uint
{
REQUIRES_AUTH = 0x01,
RATE_LIMITED = 0x02,
VERIFY_ORIGIN = 0x04,
CONTENT_DISCRETION = 0x08,
};
struct ircd::resource::method::opts
{
flag flags {(flag)0};
/// Timeout specific to this resource.
seconds timeout {30s};
/// The maximum size of the Content-Length for this method. Anything
/// larger will be summarily rejected with a 413.
size_t payload_max {128_KiB};
/// MIME type; first part is the Registry (i.e application) and second
/// part is the format (i.e json). Empty value means nothing rejected.
std::pair<string_view, string_view> mime;
};
struct ircd::resource::method::stats
{
uint64_t requests {0}; // The method was found and called.
uint64_t timeouts {0}; // The method's timeout was exceeded.
uint64_t completions {0}; // The handler returned without throwing.
uint64_t internal_errors {0}; // The handler threw a very bad exception.
};
struct ircd::resource::request
:json::object
{
template<class> struct object;
http::request::head head;
string_view content;
http::query::string query;
string_view origin;
string_view access_token;
vector_view<string_view> parv;
string_view param[8];
m::user::id::buf user_id;
m::node::id::buf node_id;
request(const http::request::head &head,
const string_view &content)
:json::object{content}
,head{head}
,content{content}
,query{this->head.query}
{}
request() = default;
};
template<class tuple>
struct ircd::resource::request::object
:tuple
{
resource::request &r;
const http::request::head &head;
const string_view &content;
const http::query::string &query;
const decltype(r.origin) &origin;
const decltype(r.user_id) &user_id;
const decltype(r.node_id) &node_id;
const decltype(r.access_token) &access_token;
const vector_view<string_view> &parv;
const json::object &body;
object(resource::request &r)
:tuple{r}
,r{r}
,head{r.head}
,content{r.content}
,query{r.query}
,origin{r.origin}
,user_id{r.user_id}
,node_id{r.node_id}
,access_token{r.access_token}
,parv{r.parv}
,body{r}
{}
};
struct ircd::resource::response
{
struct chunked;
static const size_t HEAD_BUF_SZ;
static conf::item<std::string> access_control_allow_origin;
response(client &, const http::code &, const string_view &content_type, const size_t &content_length, const string_view &headers = {});
response(client &, const string_view &str, const string_view &content_type, const http::code &, const vector_view<const http::header> &);
response(client &, const string_view &str, const string_view &content_type, const http::code & = http::OK, const string_view &headers = {});
response(client &, const json::object &str, const http::code & = http::OK);
response(client &, const json::array &str, const http::code & = http::OK);
response(client &, const json::members & = {}, const http::code & = http::OK);
response(client &, const json::value &, const http::code & = http::OK);
response(client &, const json::iov &, const http::code & = http::OK);
response(client &, const http::code &, const json::members &);
response(client &, const http::code &, const json::value &);
response(client &, const http::code &, const json::iov &);
response(client &, const http::code &);
response() = default;
};
struct ircd::resource::response::chunked
:resource::response
{
static conf::item<size_t> default_buffer_size;
client *c {nullptr};
unique_buffer<mutable_buffer> buf;
size_t write(const const_buffer &chunk);
const_buffer flush(const const_buffer &);
bool finish();
std::function<const_buffer (const const_buffer &)> flusher();
chunked(client &, const http::code &, const string_view &content_type, const string_view &headers = {});
chunked(client &, const http::code &, const string_view &content_type, const vector_view<const http::header> &);
chunked(client &, const http::code &, const vector_view<const http::header> &);
chunked(client &, const http::code &);
chunked(const chunked &) = delete;
chunked(chunked &&) noexcept;
chunked() = default;
~chunked() noexcept;
};