mirror of
https://github.com/matrix-construct/construct
synced 2025-02-16 16:50:12 +01:00
ircd::rest: Add web request interface which isn't orchestrally complicated.
This commit is contained in:
parent
32d4d44662
commit
c02b3d845f
4 changed files with 337 additions and 0 deletions
|
@ -107,6 +107,7 @@
|
||||||
#include "mods/mods.h"
|
#include "mods/mods.h"
|
||||||
#include "net/net.h"
|
#include "net/net.h"
|
||||||
#include "server/server.h"
|
#include "server/server.h"
|
||||||
|
#include "rest.h"
|
||||||
#include "png.h"
|
#include "png.h"
|
||||||
#include "beep.h"
|
#include "beep.h"
|
||||||
#include "magick.h"
|
#include "magick.h"
|
||||||
|
|
197
include/ircd/rest.h
Normal file
197
include/ircd/rest.h
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
// The Construct
|
||||||
|
//
|
||||||
|
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2023 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_REST_H
|
||||||
|
|
||||||
|
/// Simple highlevel interface for web/http requests.
|
||||||
|
///
|
||||||
|
/// Prior to this it was too difficult to orchestrate all the objects and
|
||||||
|
/// buffers and low-level non-ergonomic procedures split between ircd::http
|
||||||
|
/// and ircd::server. This should instead have some familiarity to the
|
||||||
|
/// browser-js environment which developers can easily commit to their memory.
|
||||||
|
namespace ircd::rest
|
||||||
|
{
|
||||||
|
struct opts;
|
||||||
|
struct request;
|
||||||
|
|
||||||
|
struct get;
|
||||||
|
struct put;
|
||||||
|
struct post;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ircd::rest::request
|
||||||
|
:returns<string_view>
|
||||||
|
{
|
||||||
|
unique_const_buffer out;
|
||||||
|
|
||||||
|
request(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||||
|
request(const rfc3986::uri &, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::rest::get
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
get(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||||
|
get(const rfc3986::uri &, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::rest::put
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
put(const mutable_buffer &out, const rfc3986::uri &, const string_view &content, opts);
|
||||||
|
put(const rfc3986::uri &, const string_view &content, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::rest::post
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
post(const mutable_buffer &out, const rfc3986::uri &, const string_view &content, opts);
|
||||||
|
post(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||||
|
post(const rfc3986::uri &, const string_view &content, opts);
|
||||||
|
post(const rfc3986::uri &, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ircd::rest::opts
|
||||||
|
{
|
||||||
|
/// The HTTP method to use. This is overridden and should not be set unless
|
||||||
|
/// using the generic rest::request() call where it must be set.
|
||||||
|
string_view method;
|
||||||
|
|
||||||
|
/// The HTTP request body. This is overridden and should not be set unless
|
||||||
|
/// using the generic rest::request() call where it's set as needed.
|
||||||
|
string_view content;
|
||||||
|
|
||||||
|
/// The HTTP request body content-type. It is a good idea to set this when
|
||||||
|
/// there is request body content.
|
||||||
|
string_view content_type;
|
||||||
|
|
||||||
|
/// Additional request headers to send. These are pairs of string_views.
|
||||||
|
vector_view<const http::header> headers;
|
||||||
|
|
||||||
|
/// This is set automatically from the URI argument's domain and scheme
|
||||||
|
/// (service) by default. Setting it here will override.
|
||||||
|
net::hostport remote;
|
||||||
|
|
||||||
|
/// Managed internally by default and passed to server::request. Setting
|
||||||
|
/// things here will override.
|
||||||
|
server::out sout;
|
||||||
|
|
||||||
|
/// Managed internally by default and passed to server::request. Setting
|
||||||
|
/// things here will override.
|
||||||
|
server::in sin;
|
||||||
|
|
||||||
|
/// Passed to server::request. The http_exceptions option is useful here
|
||||||
|
/// to prevent this suite from throwing on non-2xx codes.
|
||||||
|
server::request::opts sopts;
|
||||||
|
|
||||||
|
/// Allows the HTTP response code to be returned to the caller. This may
|
||||||
|
/// not be initialized if the call throws any error first.
|
||||||
|
http::code *code {nullptr};
|
||||||
|
|
||||||
|
/// Allows the user to override the request::out with their own for
|
||||||
|
/// receiving dynamic content. Supply an empty unique_buffer instance.
|
||||||
|
unique_const_buffer *out {nullptr};
|
||||||
|
|
||||||
|
/// Timeout for the yielding/synchronous calls of this interface.
|
||||||
|
seconds timeout {20s};
|
||||||
|
|
||||||
|
/// Internal use
|
||||||
|
opts &&set(const string_view &method, const string_view &content = {});
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::post::post(const rfc3986::uri &uri,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
uri, opts.set("POST")
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::post::post(const rfc3986::uri &uri,
|
||||||
|
const string_view &content,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
uri, opts.set("POST", content)
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::post::post(const mutable_buffer &out,
|
||||||
|
const rfc3986::uri &uri,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
out, uri, opts.set("POST")
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::post::post(const mutable_buffer &out,
|
||||||
|
const rfc3986::uri &uri,
|
||||||
|
const string_view &content,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
out, uri, opts.set("POST", content)
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::put::put(const rfc3986::uri &uri,
|
||||||
|
const string_view &content,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
uri, opts.set("PUT", content)
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::put::put(const mutable_buffer &out,
|
||||||
|
const rfc3986::uri &uri,
|
||||||
|
const string_view &content,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
out, uri, opts.set("PUT", content)
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::get::get(const rfc3986::uri &uri,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
uri, opts.set("GET")
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::rest::get::get(const mutable_buffer &out,
|
||||||
|
const rfc3986::uri &uri,
|
||||||
|
opts opts)
|
||||||
|
:request
|
||||||
|
{
|
||||||
|
out, uri, opts.set("GET")
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline ircd::rest::opts &&
|
||||||
|
ircd::rest::opts::set(const string_view &method,
|
||||||
|
const string_view &content)
|
||||||
|
{
|
||||||
|
this->method = method;
|
||||||
|
this->content = content?: this->content;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
|
@ -237,6 +237,7 @@ endif
|
||||||
libircd_la_SOURCES += server.cc
|
libircd_la_SOURCES += server.cc
|
||||||
libircd_la_SOURCES += client.cc
|
libircd_la_SOURCES += client.cc
|
||||||
libircd_la_SOURCES += resource.cc
|
libircd_la_SOURCES += resource.cc
|
||||||
|
libircd_la_SOURCES += rest.cc
|
||||||
if JS
|
if JS
|
||||||
libircd_la_SOURCES += js.cc
|
libircd_la_SOURCES += js.cc
|
||||||
endif
|
endif
|
||||||
|
|
138
ircd/rest.cc
Normal file
138
ircd/rest.cc
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
// The Construct
|
||||||
|
//
|
||||||
|
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2023 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.
|
||||||
|
|
||||||
|
ircd::rest::request::request(const rfc3986::uri &uri,
|
||||||
|
opts opts)
|
||||||
|
{
|
||||||
|
if(!opts.remote)
|
||||||
|
opts.remote = net::hostport{uri};
|
||||||
|
|
||||||
|
const unique_mutable_buffer buf
|
||||||
|
{
|
||||||
|
empty(opts.sout.head) || empty(opts.sin.head)?
|
||||||
|
16_KiB: 0_KiB
|
||||||
|
};
|
||||||
|
|
||||||
|
window_buffer window{buf};
|
||||||
|
if(empty(opts.sout.head))
|
||||||
|
{
|
||||||
|
assert(opts.remote);
|
||||||
|
assert(opts.method);
|
||||||
|
http::request
|
||||||
|
{
|
||||||
|
window,
|
||||||
|
host(opts.remote),
|
||||||
|
opts.method,
|
||||||
|
uri.resource(),
|
||||||
|
size(opts.content),
|
||||||
|
opts.content_type,
|
||||||
|
opts.headers
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.sout.head = window.completed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty(opts.sout.content))
|
||||||
|
opts.sout.content = opts.content;
|
||||||
|
|
||||||
|
if(empty(opts.sin.head))
|
||||||
|
opts.sin.head = mutable_buffer{window};
|
||||||
|
|
||||||
|
// server::request will allocate dynamic content
|
||||||
|
opts.sin.content = mutable_buffer{};
|
||||||
|
|
||||||
|
assert(opts.remote);
|
||||||
|
server::request request
|
||||||
|
{
|
||||||
|
opts.remote,
|
||||||
|
std::move(opts.sout),
|
||||||
|
std::move(opts.sin),
|
||||||
|
&opts.sopts,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto code
|
||||||
|
{
|
||||||
|
request.get(opts.timeout)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(opts.code)
|
||||||
|
*opts.code = code;
|
||||||
|
|
||||||
|
if(opts.out)
|
||||||
|
*opts.out = std::move(request.in.dynamic);
|
||||||
|
else
|
||||||
|
this->out = std::move(request.in.dynamic);
|
||||||
|
|
||||||
|
ret = request.in.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::rest::request::request(const mutable_buffer &out,
|
||||||
|
const rfc3986::uri &uri,
|
||||||
|
opts opts)
|
||||||
|
{
|
||||||
|
if(!opts.remote)
|
||||||
|
opts.remote = net::hostport{uri};
|
||||||
|
|
||||||
|
const unique_mutable_buffer buf
|
||||||
|
{
|
||||||
|
empty(opts.sout.head) || empty(opts.sin.head)?
|
||||||
|
16_KiB: 0_KiB
|
||||||
|
};
|
||||||
|
|
||||||
|
window_buffer window{buf};
|
||||||
|
if(empty(opts.sout.head))
|
||||||
|
{
|
||||||
|
assert(opts.remote);
|
||||||
|
assert(opts.method);
|
||||||
|
http::request
|
||||||
|
{
|
||||||
|
window,
|
||||||
|
host(opts.remote),
|
||||||
|
opts.method,
|
||||||
|
uri.resource(),
|
||||||
|
size(opts.content),
|
||||||
|
opts.content_type,
|
||||||
|
opts.headers
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.sout.head = window.completed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty(opts.sout.content))
|
||||||
|
opts.sout.content = opts.content;
|
||||||
|
|
||||||
|
if(empty(opts.sin.head))
|
||||||
|
opts.sin.head = mutable_buffer{window};
|
||||||
|
|
||||||
|
if(empty(opts.sin.content))
|
||||||
|
opts.sin.content = out;
|
||||||
|
|
||||||
|
if(empty(opts.sin.content))
|
||||||
|
opts.sin.content = opts.sin.head;
|
||||||
|
|
||||||
|
assert(opts.remote);
|
||||||
|
server::request request
|
||||||
|
{
|
||||||
|
opts.remote,
|
||||||
|
std::move(opts.sout),
|
||||||
|
std::move(opts.sin),
|
||||||
|
&opts.sopts,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto code
|
||||||
|
{
|
||||||
|
request.get(opts.timeout)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(opts.code)
|
||||||
|
*opts.code = code;
|
||||||
|
|
||||||
|
ret = request.in.content;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue