0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-10-31 19:08:59 +01:00
construct/include/ircd/m/fetch.h
2022-07-04 22:25:18 -07:00

238 lines
8.3 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2019 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_M_FETCH_H
/// Event Fetcher (remote).
///
/// This is a federation network interface to find and retrieve datas from
/// remote parties serially. It operates by querying servers in a room until
/// one server can provide a satisfying response. The exact method for
/// determining who to contact, when and how is encapsulated internally for
/// further development, but it is primarily stochastic. All viable servers
/// in a room are exhausted before an error is the result. A hint may be
/// provided in the options by the caller. If supplied, it will be attempted
/// first.
///
/// This is an asynchronous promise/future based interface. The result package
/// is delivered by a ctx::future with a result managing allocations that
/// originate internally. The caller of start() has no further responsibilities
/// to this interface.
///
/// Due to the composition of multiple operations performed internally, result
/// future has no real timeout control over the operation as a whole. While it
/// can always go out of scope for an effective cancelation, internal conf::item
/// are used to timeout failures after a deterministic `timeout * servers`.
/// This means the user is not required to wait_for() or wait_until() on the
/// future unless they want a stricter timeout; that may miss a valid response
/// for a rare piece of data held by a minority of servers.
///
/// Alternatively, m::feds is another federation network interface geared to
/// conducting a parallel request to every server in a room; this conducts a
/// serial request to every server in a room (and stopping when satisfied).
///
namespace ircd::m::fetch
{
struct init;
struct opts;
struct result;
struct request;
enum class op :uint8_t;
// Observers
string_view reflect(const op &);
bool for_each(const std::function<bool (request &)> &);
bool exists(const opts &);
size_t count();
// Primary operations
ctx::future<result> start(opts);
}
enum class ircd::m::fetch::op
:uint8_t
{
noop,
auth,
event,
backfill,
};
struct ircd::m::fetch::opts
{
/// Operation to perform.
fetch::op op {fetch::op::noop};
/// room::id apropos. Many federation requests require a room_id, but
/// nevertheless a room_id is still used by this unit as a pool of servers.
room::id room_id;
/// event::id apropos. For op::event operations this is being sought, but
/// for others it may be required as a reference point. If not supplied and
/// required, we'll try to use the top head from any room_id.
event::id event_id;
/// The principal allocation size. This is passed up the stack to m::fed,
/// server::request and ends up containing the request head and content,
/// and response head. The response content is usually dynamically
/// allocated and that buffer is the one which ends up in result. Note
/// that sufficiently large values here may allow for eliding the content
/// allocation based on the following formula: >= 16_KiB + (64_KiB * limit)
/// where 16_KiB is [current server default] for headers and 64_KiB is
/// m::event::MAX_SIZE.
size_t bufsz {0};
/// Name of a remote server which will be queried first; if failure,
/// the normal room_id based operation is the fallback. If the room
/// is not known to us, it would be best to set this.
string_view hint;
/// Limit the number of servers to be contacted for this operation. Zero
/// is automatic / unlimited. Note that setting this value to 1 in
/// conjunction with a hint is analogous to just making an m::fed request.
size_t attempt_limit {0};
//
// special options
//
/// If the op makes use of a spec limit parameter that can be controlled
/// by the user here. The default of 0 will be replaced by some internal
/// configured limit like 8 or 16 etc.
size_t backfill_limit {0};
/// Whether to hash the result for event_id (ignored for v1/v2); this is
/// important to ignore poisonous results and continuing.
bool check_event_id {true};
/// Whether to run the conforms checks on the result; this is important
/// to screen out poisonous results while continuing to try other servers.
bool check_conforms {true};
/// Whether to check if the content hash matches. This might not match if
/// the event is redacted (or junk), so other servers will then be tried.
/// Note the case of authoriative redactions below; and if true that may
/// allow a condition for forcing check_hashes=false.
bool check_hashes {true};
/// Whether to allow content hash mismatch iff the result was received from
/// the event's origin. If the origin of the event wants to redact the
/// event we accept; otherwise we continue to look for an unredacted copy.
bool authoritative_redaction {true};
/// Whether to verify signature of result before accepting; this is
/// important to ignore poisonous results and continuing.
bool check_signature {true};
};
struct ircd::m::fetch::result
{
/// Backing buffer for any data pointed to by this result.
shared_buffer<mutable_buffer> buf;
/// The backing buffer may contain other data ahead of the response
/// content; in any case this points to a view of the response content.
/// User access to response content should be via a json conversion rather
/// than this reference.
string_view content;
/// The name of the remote which supplied us with the result.
char origin[rfc3986::DOMAIN_BUFSIZE];
/// JSON result conversion. Note that developers should not let the result
/// instance go out of scope by making this conversion.
explicit operator json::object() const;
explicit operator json::array() const;
result() = default;
};
/// Fetch entity state. DO NOT CONSTRUCT. This is an internal structure but we
/// expose it here for examination, statistics and hacking since it has no
/// non-standard symbols; this is simpler than creating some accessor suite.
/// Instances of this object are created and managed internally by the m::fetch
/// unit after a fetch::start() is called. This definition is not required to
/// operate the m::fetch interface as a user.
struct ircd::m::fetch::request
{
using is_transparent = void;
/// Copy of the user's request options. Note that the backing of strings in
/// opts was changed to point at this structure; allowing safe access.
fetch::opts opts;
/// Time the first attempt was made; this value is not modified so it can
/// be used to measure the total time of all attempts.
system_point started;
/// Time the last attempt was started
system_point last;
/// Time the request entered the finished state. This being non-zero
/// indicates a finished state; may be difficult to observe.
system_point finished;
/// State for failed attempts; the names of servers which failed are
/// stored here. Failure here means the request succeeded but the server
/// did not provide a satisfying response. Appearing in this list prevents
/// a server from being selected for the next attempt.
std::set<std::string, std::less<>> attempted;
/// Reference to the current server being attempted. This string is placed
/// in the attempted set at the start of an attempt.
string_view origin;
/// HTTP heads and scratch buffer for server::request
unique_buffer<mutable_buffer> buf;
/// Our future for the server::request. Since we make
std::unique_ptr<server::request> future;
/// Promise for our user's future of this request.
ctx::promise<result> promise;
/// Error pointer state for an attempt. This is cleared each attempt.
std::exception_ptr eptr;
/// Buffer backing for opts
m::event::id::buf event_id;
m::room::id::buf room_id;
/// Internal
request(fetch::opts);
request(request &&) = delete;
request(const request &) = delete;
request &operator=(request &&) = delete;
request &operator=(const request &) = delete;
~request() noexcept;
};
/// Internally held
struct ircd::m::fetch::init
{
init(), ~init() noexcept;
};
inline
ircd::m::fetch::result::operator
json::array()
const
{
return content;
}
inline
ircd::m::fetch::result::operator
json::object()
const
{
return content;
}