2018-02-04 03:22:01 +01:00
|
|
|
// 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.
|
2016-11-29 16:23:38 +01:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
#define HAVE_IRCD_RESOURCE_H
|
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
namespace ircd
|
|
|
|
{
|
2018-02-18 00:44:53 +01:00
|
|
|
struct client;
|
2017-08-28 23:51:22 +02:00
|
|
|
struct resource;
|
|
|
|
}
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2018-05-21 12:01:40 +02:00
|
|
|
/// The target of an HTTP request specified by clients with a path.
|
|
|
|
///
|
2017-08-28 23:51:22 +02:00
|
|
|
struct ircd::resource
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
IRCD_EXCEPTION(ircd::error, error)
|
|
|
|
|
2017-12-12 21:26:39 +01:00
|
|
|
enum flag :uint;
|
|
|
|
struct opts;
|
2016-11-29 16:23:38 +01:00
|
|
|
struct method;
|
|
|
|
struct request;
|
|
|
|
struct response;
|
|
|
|
|
2018-10-24 21:44:13 +02:00
|
|
|
static log::log log;
|
2017-08-23 23:06:14 +02:00
|
|
|
static std::map<string_view, resource *, iless> resources;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-23 23:06:14 +02:00
|
|
|
string_view path;
|
2018-11-06 04:14:53 +01:00
|
|
|
std::unique_ptr<const struct opts> opts;
|
2017-08-23 23:06:14 +02:00
|
|
|
std::map<string_view, method *> methods;
|
|
|
|
unique_const_iterator<decltype(resources)> resources_it;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2018-11-06 04:47:35 +01:00
|
|
|
string_view allow_methods_list(const mutable_buffer &buf) const;
|
2017-03-21 03:30:07 +01:00
|
|
|
|
2016-11-29 16:23:38 +01:00
|
|
|
public:
|
2018-11-07 06:35:30 +01:00
|
|
|
method &operator[](const string_view &name) const;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2018-11-06 04:14:53 +01:00
|
|
|
resource(const string_view &path, struct opts);
|
2017-12-12 21:26:39 +01:00
|
|
|
resource(const string_view &path);
|
2017-08-23 23:06:14 +02:00
|
|
|
resource() = default;
|
2018-11-06 04:47:35 +01:00
|
|
|
~resource() noexcept;
|
2017-08-23 23:06:14 +02:00
|
|
|
|
2018-04-24 04:11:11 +02:00
|
|
|
static resource &find(const string_view &path);
|
2016-11-29 16:23:38 +01:00
|
|
|
};
|
|
|
|
|
2017-12-12 21:26:39 +01:00
|
|
|
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
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2018-11-06 04:14:53 +01:00
|
|
|
struct ircd::resource::method
|
|
|
|
{
|
|
|
|
enum flag :uint;
|
|
|
|
struct opts;
|
2018-11-06 04:47:35 +01:00
|
|
|
struct stats;
|
2018-11-06 04:14:53 +01:00
|
|
|
using handler = std::function<response (client &, request &)>;
|
|
|
|
|
2019-02-05 09:41:15 +01:00
|
|
|
static conf::item<bool> x_matrix_verify_origin;
|
|
|
|
static conf::item<bool> x_matrix_verify_destination;
|
|
|
|
|
2018-11-06 04:14:53 +01:00
|
|
|
struct resource *resource;
|
|
|
|
string_view name;
|
|
|
|
handler function;
|
|
|
|
std::unique_ptr<const struct opts> opts;
|
2018-11-06 04:47:35 +01:00
|
|
|
std::unique_ptr<struct stats> stats;
|
2018-11-06 04:14:53 +01:00
|
|
|
unique_const_iterator<decltype(resource::methods)> methods_it;
|
|
|
|
|
2018-11-06 04:47:35 +01:00
|
|
|
string_view verify_origin(client &, request &) const;
|
|
|
|
string_view authenticate(client &, request &) const;
|
|
|
|
void handle_timeout(client &) const;
|
|
|
|
void call_handler(client &, request &);
|
|
|
|
|
2018-11-06 04:14:53 +01:00
|
|
|
public:
|
2018-11-06 04:47:35 +01:00
|
|
|
void operator()(client &, const http::request::head &, const string_view &content_partial);
|
2018-11-06 04:14:53 +01:00
|
|
|
|
|
|
|
method(struct resource &, const string_view &name, handler, struct opts);
|
|
|
|
method(struct resource &, const string_view &name, handler);
|
2018-11-06 04:16:11 +01:00
|
|
|
~method() noexcept;
|
2018-11-06 04:14:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2018-11-06 05:27:11 +01:00
|
|
|
struct ircd::resource::method::stats
|
|
|
|
{
|
2018-11-07 06:36:36 +01:00
|
|
|
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.
|
2018-11-06 05:27:11 +01:00
|
|
|
};
|
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
struct ircd::resource::request
|
2017-08-23 23:47:21 +02:00
|
|
|
:json::object
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
2017-09-21 11:11:07 +02:00
|
|
|
template<class> struct object;
|
2017-08-28 23:51:22 +02:00
|
|
|
|
2018-02-18 00:44:53 +01:00
|
|
|
http::request::head head;
|
2018-01-12 03:45:25 +01:00
|
|
|
string_view content;
|
2017-08-23 23:06:14 +02:00
|
|
|
http::query::string query;
|
2018-04-03 07:58:12 +02:00
|
|
|
string_view origin;
|
2018-02-11 07:07:06 +01:00
|
|
|
string_view access_token;
|
2017-10-12 05:52:33 +02:00
|
|
|
vector_view<string_view> parv;
|
2018-02-22 02:41:28 +01:00
|
|
|
string_view param[8];
|
2018-04-23 01:22:26 +02:00
|
|
|
m::user::id::buf user_id;
|
2018-05-19 04:31:44 +02:00
|
|
|
m::node::id::buf node_id;
|
2017-03-21 03:30:07 +01:00
|
|
|
|
2018-01-12 03:45:25 +01:00
|
|
|
request(const http::request::head &head,
|
2018-02-18 00:44:53 +01:00
|
|
|
const string_view &content)
|
|
|
|
:json::object{content}
|
|
|
|
,head{head}
|
|
|
|
,content{content}
|
2018-02-22 02:41:28 +01:00
|
|
|
,query{this->head.query}
|
2018-02-18 00:44:53 +01:00
|
|
|
{}
|
|
|
|
|
|
|
|
request() = default;
|
2017-03-21 03:30:07 +01:00
|
|
|
};
|
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
template<class tuple>
|
2017-09-21 11:11:07 +02:00
|
|
|
struct ircd::resource::request::object
|
2017-08-28 23:51:22 +02:00
|
|
|
:tuple
|
|
|
|
{
|
2017-09-21 11:11:07 +02:00
|
|
|
resource::request &r;
|
|
|
|
const http::request::head &head;
|
2018-01-12 03:45:25 +01:00
|
|
|
const string_view &content;
|
2017-09-21 11:11:07 +02:00
|
|
|
const http::query::string &query;
|
2018-04-03 07:58:12 +02:00
|
|
|
const decltype(r.origin) &origin;
|
2017-12-12 21:26:39 +01:00
|
|
|
const decltype(r.user_id) &user_id;
|
2018-05-19 04:31:44 +02:00
|
|
|
const decltype(r.node_id) &node_id;
|
2018-02-11 07:07:06 +01:00
|
|
|
const decltype(r.access_token) &access_token;
|
2017-10-12 05:52:33 +02:00
|
|
|
const vector_view<string_view> &parv;
|
2017-09-21 11:11:07 +02:00
|
|
|
const json::object &body;
|
|
|
|
|
|
|
|
object(resource::request &r)
|
|
|
|
:tuple{r}
|
|
|
|
,r{r}
|
|
|
|
,head{r.head}
|
|
|
|
,content{r.content}
|
|
|
|
,query{r.query}
|
2018-04-03 07:58:12 +02:00
|
|
|
,origin{r.origin}
|
2017-12-12 21:26:39 +01:00
|
|
|
,user_id{r.user_id}
|
2018-05-19 04:31:44 +02:00
|
|
|
,node_id{r.node_id}
|
2018-02-11 07:07:06 +01:00
|
|
|
,access_token{r.access_token}
|
2017-10-12 05:52:33 +02:00
|
|
|
,parv{r.parv}
|
2017-09-21 11:11:07 +02:00
|
|
|
,body{r}
|
|
|
|
{}
|
2017-08-28 23:51:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ircd::resource::response
|
2017-03-21 03:30:07 +01:00
|
|
|
{
|
2018-04-14 08:19:43 +02:00
|
|
|
struct chunked;
|
|
|
|
|
2018-08-25 22:58:19 +02:00
|
|
|
static const size_t HEAD_BUF_SZ;
|
2018-10-03 00:47:43 +02:00
|
|
|
static conf::item<std::string> access_control_allow_origin;
|
2018-08-25 22:58:19 +02:00
|
|
|
|
2018-03-09 22:15:32 +01:00
|
|
|
response(client &, const http::code &, const string_view &content_type, const size_t &content_length, const string_view &headers = {});
|
2017-12-24 22:25:09 +01:00
|
|
|
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 = {});
|
2017-09-15 21:07:07 +02:00
|
|
|
response(client &, const json::object &str, const http::code & = http::OK);
|
2017-09-25 02:00:05 +02:00
|
|
|
response(client &, const json::array &str, const http::code & = http::OK);
|
2017-09-15 21:07:07 +02:00
|
|
|
response(client &, const json::members & = {}, const http::code & = http::OK);
|
2017-09-25 02:00:05 +02:00
|
|
|
response(client &, const json::value &, const http::code & = http::OK);
|
2017-09-09 21:20:00 +02:00
|
|
|
response(client &, const json::iov &, const http::code & = http::OK);
|
2017-09-12 22:34:21 +02:00
|
|
|
response(client &, const http::code &, const json::members &);
|
2017-09-25 02:00:05 +02:00
|
|
|
response(client &, const http::code &, const json::value &);
|
2017-09-09 21:20:00 +02:00
|
|
|
response(client &, const http::code &, const json::iov &);
|
|
|
|
response(client &, const http::code &);
|
2017-03-21 03:30:07 +01:00
|
|
|
response() = default;
|
2016-11-29 16:23:38 +01:00
|
|
|
};
|
|
|
|
|
2018-04-14 08:19:43 +02:00
|
|
|
struct ircd::resource::response::chunked
|
|
|
|
:resource::response
|
|
|
|
{
|
2018-09-05 07:03:12 +02:00
|
|
|
static conf::item<size_t> default_buffer_size;
|
|
|
|
|
2018-04-14 08:19:43 +02:00
|
|
|
client *c {nullptr};
|
2018-09-05 07:03:12 +02:00
|
|
|
unique_buffer<mutable_buffer> buf;
|
2018-04-14 08:19:43 +02:00
|
|
|
|
|
|
|
size_t write(const const_buffer &chunk);
|
2018-09-05 07:03:12 +02:00
|
|
|
const_buffer flush(const const_buffer &);
|
2018-04-14 08:19:43 +02:00
|
|
|
bool finish();
|
|
|
|
|
2018-09-05 07:03:12 +02:00
|
|
|
std::function<const_buffer (const const_buffer &)> flusher();
|
|
|
|
|
2018-04-14 08:19:43 +02:00
|
|
|
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;
|
|
|
|
};
|