mirror of
https://github.com/matrix-construct/construct
synced 2024-11-16 15:00:51 +01:00
260 lines
8.5 KiB
C++
260 lines
8.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_SERVER_REQUEST_H
|
|
|
|
namespace ircd::server
|
|
{
|
|
struct in;
|
|
struct out;
|
|
struct request;
|
|
|
|
size_t size(const in &);
|
|
size_t size(const out &);
|
|
size_t size_chunks(const in &);
|
|
|
|
// gets the unique tag number from request or 0 if no tag associated.
|
|
uint64_t id(const request &);
|
|
|
|
// convenience suite used to prefix log messages.
|
|
string_view loghead(const mutable_buffer &out, const request &);
|
|
string_view loghead(const request &);
|
|
string_view loghead(const mutable_buffer &out, const link &, const request &);
|
|
string_view loghead(const link &, const request &);
|
|
|
|
// control panel
|
|
void submit(const hostport &, request &);
|
|
bool cancel(request &);
|
|
}
|
|
|
|
/// Request data and options related to transmitting the request. This
|
|
/// is where buffers must be supplied to send data to the server.
|
|
///
|
|
struct ircd::server::out
|
|
{
|
|
const_buffer head;
|
|
const_buffer content;
|
|
|
|
/// The progress closure is an optional callback invoked every time more
|
|
/// content is written to the socket. The first argument is a view of the
|
|
/// data most recently written. The second argument is a view of all data
|
|
/// written so far. This is only invoked for content. At the first
|
|
/// invocation, the head has been fully written.
|
|
std::function<void (const_buffer, const_buffer)> progress;
|
|
|
|
/// Call server::out::gethead(request) to extract the details of the HTTP
|
|
/// request being sent by the request. This may not always be available,
|
|
/// like in some cases after the request was canceled.
|
|
static http::request::head gethead(const request &);
|
|
};
|
|
|
|
/// Request data and options related to the receive side of the request.
|
|
/// This is where buffers are supplied to receive data from the remote
|
|
/// server.
|
|
///
|
|
/// As a feature, when content == head, the head buffer is considered
|
|
/// as a contiguous buffer for both head and content; the content buffer
|
|
/// will be updated to point to any data after the head is received.
|
|
///
|
|
struct ircd::server::in
|
|
{
|
|
mutable_buffer head;
|
|
mutable_buffer content {head};
|
|
|
|
/// The progress closure is an optional callback invoked every time more
|
|
/// content is read from the socket. The first argument is a view of the
|
|
/// data most recently received. The second argument is a view of all data
|
|
/// received so far. This is only invoked for content, not for the head;
|
|
/// however the first time it is invoked it is safe to view the in.head
|
|
std::function<void (const_buffer, const_buffer)> progress;
|
|
|
|
/// The dynamic buffer is a convenience that allows for the content buffer
|
|
/// to be allocated on demand once the head is received and the length is
|
|
/// known. To use dynamic, set the content buffer to nothing (i.e default
|
|
/// constructed mutable_buffer). The allocated buffer will eventually be
|
|
/// placed here; any existing buffer will be discarded.
|
|
unique_buffer<mutable_buffer> dynamic;
|
|
|
|
/// Dynamic can also be used when receiving a chunked encoded message where
|
|
/// the length is not initially known. In that case, we create a buffer for
|
|
/// each chunk and append it to this vector. When the message is finished,
|
|
/// a final contiguous buffer is created in dynamic and the message is
|
|
/// copied there; this vector is cleared and content points there instead.
|
|
/// An option can be set in request::opts to skip the last step.
|
|
std::vector<unique_buffer<mutable_buffer>> chunks;
|
|
|
|
/// Call server::in::gethead(request) to extract the details of the HTTP
|
|
/// response being received by the request. This may not always be
|
|
/// available if it has not been received or was discarded etc.
|
|
static http::response::head gethead(const request &);
|
|
};
|
|
|
|
/// This is a handle for being a client to another server. This handle will
|
|
/// attempt to find an existing connection pool for the remote server otherwise
|
|
/// one will be created. Then it will multiplex your request and demultiplex
|
|
/// your response with all the other requests pending in the pipelines to
|
|
/// the remote.
|
|
///
|
|
struct ircd::server::request
|
|
:ctx::future<http::code>
|
|
{
|
|
struct opts;
|
|
using each_closure = std::function<bool (const peer &, const link &, const request &)>;
|
|
|
|
static const opts opts_default;
|
|
|
|
server::tag *tag {nullptr};
|
|
|
|
public:
|
|
/// Transmission data
|
|
server::out out;
|
|
|
|
/// Reception data
|
|
server::in in;
|
|
|
|
/// Options
|
|
const opts *opt { &opts_default };
|
|
|
|
request(const net::hostport &,
|
|
server::out &&,
|
|
server::in &&,
|
|
const opts *const & = nullptr);
|
|
|
|
request() = default;
|
|
request(request &&) noexcept;
|
|
request(const request &) = delete;
|
|
request &operator=(request &&) noexcept;
|
|
request &operator=(const request &) = delete;
|
|
~request() noexcept;
|
|
};
|
|
|
|
struct ircd::server::request::opts
|
|
{
|
|
/// When true, HTTP responses above the 200's are thrown as exceptions
|
|
/// from the future::get() on this object. Otherwise, if false any code
|
|
/// received is returned in the value and exceptions are thrown when no
|
|
/// code can be returned.
|
|
bool http_exceptions {true};
|
|
|
|
/// Only applies when using the dynamic content allocation feature; this
|
|
/// limits the size of that allocation in case the remote sends a larger
|
|
/// content-length value. If the remote sends more content, the behavior
|
|
/// is the same as if specifying an in.content buffer of this size.
|
|
size_t content_length_maxalloc {256_MiB};
|
|
|
|
/// Only applies when using dynamic content allocation when the message is
|
|
/// received with chunked encoding. By default, chunks are saved in
|
|
/// individual buffers and copied to a final contiguous buffer. We skip
|
|
/// that final step of allocating the contiguous buffer and the copy when
|
|
/// this is set to false; the chunk buffers will then remain in the chunks
|
|
/// vector as-is.
|
|
bool contiguous_content {true};
|
|
|
|
/// Priority indication is factored into the link selection algorithm for
|
|
/// making this request to the peer. It is not the only factor, and the
|
|
/// default is usually sufficient. Lower priority values are favored when
|
|
/// two requests are compared. When the priority is set to the lowest
|
|
/// possible value, a dedicated link may be opened to the peer even if the
|
|
/// maximum number of links are already open; other limits may be exceeded;
|
|
/// use that value with caution.
|
|
int16_t priority {0};
|
|
|
|
/// Only applies when using dynamic content allocation with a chunked
|
|
/// encoded response. This will hint the chunk vector. Ideally it can be
|
|
/// set to the number of chunks expected in a response to avoid growth of
|
|
/// that vector ... if you somehow know what that is going to be.
|
|
uint16_t chunks_reserve {4};
|
|
|
|
/// When true, if the buffer supplied to receive content is smaller than
|
|
/// the content-length, the overflowing portion of content is discarded
|
|
/// and the request completes without error. The user must check the
|
|
/// the content length to know if their content is incomplete. Otherwise
|
|
/// when false an overflow is an error and an exception is set so the
|
|
/// user does not process incomplete content.
|
|
bool truncate_content {false};
|
|
};
|
|
|
|
inline
|
|
ircd::server::request::request(const net::hostport &hostport,
|
|
server::out &&out,
|
|
server::in &&in,
|
|
const opts *const &opt)
|
|
:tag{nullptr}
|
|
,out{std::move(out)}
|
|
,in{std::move(in)}
|
|
,opt{opt?: &opts_default}
|
|
{
|
|
submit(hostport, *this);
|
|
}
|
|
|
|
inline
|
|
ircd::server::request::request(request &&o)
|
|
noexcept
|
|
:ctx::future<http::code>{std::move(o)}
|
|
,tag{std::move(o.tag)}
|
|
,out{std::move(o.out)}
|
|
,in{std::move(o.in)}
|
|
,opt{std::move(o.opt)}
|
|
{
|
|
if(tag)
|
|
associate(*this, *tag, std::move(o));
|
|
|
|
assert(!o.tag);
|
|
}
|
|
|
|
inline ircd::server::request &
|
|
ircd::server::request::operator=(request &&o)
|
|
noexcept
|
|
{
|
|
this->~request();
|
|
new (this) request
|
|
{
|
|
std::move(o)
|
|
};
|
|
|
|
assert(!o.tag);
|
|
return *this;
|
|
}
|
|
|
|
inline
|
|
ircd::server::request::~request()
|
|
noexcept
|
|
{
|
|
if(tag)
|
|
cancel(*this);
|
|
|
|
if(tag)
|
|
disassociate(*this, *tag);
|
|
|
|
assert(!tag);
|
|
}
|
|
|
|
inline size_t
|
|
ircd::server::size_chunks(const in &in)
|
|
{
|
|
return std::accumulate(begin(in.chunks), end(in.chunks), size_t(0), []
|
|
(auto ret, const auto &buffer)
|
|
{
|
|
return ret += size(buffer);
|
|
});
|
|
}
|
|
|
|
inline size_t
|
|
ircd::server::size(const in &in)
|
|
{
|
|
return size(in.head) + size(in.content);
|
|
}
|
|
|
|
inline size_t
|
|
ircd::server::size(const out &out)
|
|
{
|
|
return size(out.head) + size(out.content);
|
|
}
|