mirror of
https://github.com/matrix-construct/construct
synced 2025-02-18 01:30:12 +01:00
ircd: HTTP interface function-objects et al.
This commit is contained in:
parent
03528ebf67
commit
472ee2c648
12 changed files with 739 additions and 523 deletions
|
@ -49,7 +49,7 @@ struct client
|
|||
list::const_iterator clit;
|
||||
std::shared_ptr<socket> sock;
|
||||
|
||||
virtual bool handle();
|
||||
virtual bool serve();
|
||||
bool main() noexcept;
|
||||
|
||||
public:
|
||||
|
@ -63,34 +63,22 @@ struct client
|
|||
virtual ~client() noexcept;
|
||||
};
|
||||
|
||||
host_port remote_address(const client &);
|
||||
host_port local_address(const client &);
|
||||
|
||||
// Creates a client.
|
||||
std::shared_ptr<client> add_client(std::shared_ptr<socket>);
|
||||
|
||||
} // namespace ircd
|
||||
|
||||
namespace ircd {
|
||||
namespace http {
|
||||
|
||||
struct client
|
||||
:ircd::client
|
||||
{
|
||||
virtual bool handle() override;
|
||||
|
||||
using ircd::client::client;
|
||||
};
|
||||
|
||||
} // namespace ircd
|
||||
} // namespace http
|
||||
|
||||
namespace ircd {
|
||||
|
||||
struct client::init
|
||||
{
|
||||
init();
|
||||
~init() noexcept;
|
||||
};
|
||||
|
||||
host_port remote_addr(const client &);
|
||||
host_port local_addr(const client &);
|
||||
|
||||
const char *write(client &, const char *&start, const char *const &stop);
|
||||
char *read(client &, char *&start, char *const &stop);
|
||||
string_view readline(client &, char *&start, char *const &stop);
|
||||
|
||||
http::response::write_closure write_closure(client &);
|
||||
parse::read_closure read_closure(client &);
|
||||
|
||||
std::shared_ptr<client> add_client(std::shared_ptr<socket>); // Creates a client.
|
||||
|
||||
} // namespace ircd
|
||||
|
|
|
@ -110,67 +110,104 @@ struct line::header
|
|||
|
||||
struct headers
|
||||
{
|
||||
using closure = std::function<void (const std::pair<string_view, string_view> &)>;
|
||||
|
||||
string_view host;
|
||||
string_view expect;
|
||||
string_view te;
|
||||
size_t content_length {0};
|
||||
using closure = std::function<void (const line::header &)>;
|
||||
|
||||
headers(parse::context &, const closure & = {});
|
||||
};
|
||||
|
||||
struct content
|
||||
:std::string
|
||||
:string_view
|
||||
{
|
||||
content(parse::context &, const headers &);
|
||||
};
|
||||
IRCD_OVERLOAD(discard)
|
||||
|
||||
struct request
|
||||
{
|
||||
struct head;
|
||||
struct body;
|
||||
content(parse::context &, const size_t &length, discard_t);
|
||||
content(parse::context &, const size_t &length);
|
||||
content() = default;
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
struct head;
|
||||
struct body;
|
||||
};
|
||||
struct content;
|
||||
|
||||
struct request::head
|
||||
:line::request
|
||||
,headers
|
||||
{
|
||||
head(parse::context &pc, const headers::closure &c = {})
|
||||
:line::request{pc}
|
||||
,headers{pc, c}
|
||||
{}
|
||||
};
|
||||
using write_closure = std::function<void (const_buffers &)>;
|
||||
using proffer = std::function<void (const head &)>;
|
||||
|
||||
struct request::body
|
||||
:content
|
||||
{
|
||||
body(parse::context &pc, const head &h)
|
||||
:content{pc, h}
|
||||
{}
|
||||
response(const code &,
|
||||
const string_view &content,
|
||||
const write_closure &,
|
||||
const std::initializer_list<line::header> & = {});
|
||||
|
||||
response(parse::context &,
|
||||
content *const & = nullptr,
|
||||
const proffer & = nullptr,
|
||||
const headers::closure & = {});
|
||||
};
|
||||
|
||||
struct response::head
|
||||
:line::response
|
||||
,headers
|
||||
{
|
||||
head(parse::context &pc, const headers::closure &c = {})
|
||||
:line::response{pc}
|
||||
,headers{pc, c}
|
||||
{}
|
||||
size_t content_length {0};
|
||||
|
||||
head(parse::context &pc, const headers::closure &c = {});
|
||||
};
|
||||
|
||||
struct response::body
|
||||
:content
|
||||
struct response::content
|
||||
:http::content
|
||||
{
|
||||
body(parse::context &pc, const head &h)
|
||||
:content{pc, h}
|
||||
content(parse::context &pc, const head &h, discard_t)
|
||||
:http::content{pc, h.content_length, discard}
|
||||
{}
|
||||
|
||||
content(parse::context &pc, const head &h)
|
||||
:http::content{pc, h.content_length}
|
||||
{}
|
||||
|
||||
content() = default;
|
||||
};
|
||||
|
||||
struct request
|
||||
{
|
||||
struct head;
|
||||
struct content;
|
||||
|
||||
using write_closure = std::function<void (const_buffers &)>;
|
||||
using proffer = std::function<void (const head &)>;
|
||||
|
||||
request(const string_view &host = {},
|
||||
const string_view &method = "GET",
|
||||
const string_view &resource = "/",
|
||||
const string_view &content = {},
|
||||
const write_closure & = nullptr,
|
||||
const std::initializer_list<line::header> & = {});
|
||||
|
||||
request(parse::context &,
|
||||
content *const & = nullptr,
|
||||
const write_closure & = nullptr,
|
||||
const proffer & = nullptr,
|
||||
const headers::closure & = {});
|
||||
};
|
||||
|
||||
struct request::head
|
||||
:line::request
|
||||
{
|
||||
string_view host;
|
||||
string_view expect;
|
||||
string_view te;
|
||||
size_t content_length {0};
|
||||
|
||||
head(parse::context &pc, const headers::closure &c = {});
|
||||
};
|
||||
|
||||
struct request::content
|
||||
:http::content
|
||||
{
|
||||
content(parse::context &pc, const head &h, discard_t)
|
||||
:http::content{pc, h.content_length, discard}
|
||||
{}
|
||||
|
||||
content(parse::context &pc, const head &h)
|
||||
:http::content{pc, h.content_length}
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ struct resource::response
|
|||
struct resource::request
|
||||
{
|
||||
const http::request::head &head;
|
||||
http::request::body &content;
|
||||
http::request::content &content;
|
||||
};
|
||||
|
||||
struct resource::method
|
||||
|
|
|
@ -39,6 +39,11 @@ IRCD_EXCEPTION(error, nxdomain)
|
|||
|
||||
extern asio::ssl::context sslv23_client;
|
||||
|
||||
ip::address address(const ip::tcp::endpoint &);
|
||||
std::string hostaddr(const ip::address &);
|
||||
std::string hostaddr(const ip::tcp::endpoint &);
|
||||
uint16_t port(const ip::tcp::endpoint &);
|
||||
|
||||
} // namespace ircd
|
||||
|
||||
namespace ircd {
|
||||
|
@ -156,19 +161,53 @@ struct socket::init
|
|||
~init() noexcept;
|
||||
};
|
||||
|
||||
ip::address remote_address(const socket &);
|
||||
std::string remote_ip(const socket &);
|
||||
uint16_t remote_port(const socket &);
|
||||
ip::address local_address(const socket &);
|
||||
std::string local_ip(const socket &);
|
||||
uint16_t local_port(const socket &);
|
||||
|
||||
char *read(socket &, char *&start, char *const &stop);
|
||||
string_view readline(socket &, char *&start, char *const &stop);
|
||||
|
||||
size_t write(socket &, const char *const &buf, const size_t &max);
|
||||
template<class iov> size_t write(socket &, const iov &); // write_all
|
||||
template<class iov> size_t write(socket &, iov &); // write_some
|
||||
size_t write(socket &, const char *const &buf, const size_t &size);
|
||||
size_t write(socket &, const string_view &);
|
||||
|
||||
template<class iov> size_t read(socket &, const iov &); // read_all
|
||||
template<class iov> size_t read(socket &, iov &); // read_some
|
||||
size_t read(socket &, char *const &buf, const size_t &max);
|
||||
|
||||
|
||||
template<class iov>
|
||||
size_t
|
||||
read(socket &socket,
|
||||
iov &bufs)
|
||||
{
|
||||
const auto read(socket.read_some(bufs));
|
||||
const auto consumed(consume(bufs, read));
|
||||
assert(read == consumed);
|
||||
return read;
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
size_t
|
||||
read(socket &socket,
|
||||
const iov &bufs)
|
||||
{
|
||||
return socket.read(bufs);
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
size_t
|
||||
write(socket &socket,
|
||||
iov &bufs)
|
||||
{
|
||||
const auto wrote(socket.write_some(bufs));
|
||||
const auto consumed(consume(bufs, wrote));
|
||||
assert(wrote == consumed);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
size_t
|
||||
write(socket &socket,
|
||||
const iov &bufs)
|
||||
{
|
||||
return socket.write(bufs);
|
||||
}
|
||||
|
||||
inline
|
||||
socket::io::io(struct socket &sock,
|
||||
|
@ -193,8 +232,10 @@ template<class iov>
|
|||
auto
|
||||
socket::write(const iov &bufs)
|
||||
{
|
||||
const auto ret(async_write(ssl, bufs, yield(continuation())));
|
||||
return ret;
|
||||
return io(*this, out, [&]
|
||||
{
|
||||
return async_write(ssl, bufs, yield(continuation()));
|
||||
});
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
|
@ -202,16 +243,20 @@ auto
|
|||
socket::write(const iov &bufs,
|
||||
error_code &ec)
|
||||
{
|
||||
const auto ret(async_write(ssl, bufs, yield(continuation())[ec]));
|
||||
return ret;
|
||||
return io(*this, out, [&]
|
||||
{
|
||||
return async_write(ssl, bufs, yield(continuation())[ec]);
|
||||
});
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
auto
|
||||
socket::write_some(const iov &bufs)
|
||||
{
|
||||
const auto ret(ssl.async_write_some(bufs, yield(continuation())));
|
||||
return ret;
|
||||
return io(*this, out, [&]
|
||||
{
|
||||
return ssl.async_write_some(bufs, yield(continuation()));
|
||||
});
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
|
@ -219,8 +264,10 @@ auto
|
|||
socket::write_some(const iov &bufs,
|
||||
error_code &ec)
|
||||
{
|
||||
const auto ret(ssl.async_write_some(bufs, yield(continuation())[ec]));
|
||||
return ret;
|
||||
return io(*this, out, [&]
|
||||
{
|
||||
return ssl.async_write_some(bufs, yield(continuation())[ec]);
|
||||
});
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
|
@ -298,44 +345,32 @@ socket::set_timeout(const duration &t,
|
|||
timer.async_wait(std::move(h));
|
||||
}
|
||||
|
||||
inline uint16_t
|
||||
local_port(const socket &socket)
|
||||
{
|
||||
return socket.local().port();
|
||||
}
|
||||
|
||||
inline std::string
|
||||
local_ip(const socket &socket)
|
||||
{
|
||||
return local_address(socket).to_string();
|
||||
}
|
||||
|
||||
inline ip::address
|
||||
local_address(const socket &socket)
|
||||
{
|
||||
return socket.local().address();
|
||||
}
|
||||
|
||||
inline uint16_t
|
||||
remote_port(const socket &socket)
|
||||
{
|
||||
return socket.remote().port();
|
||||
}
|
||||
|
||||
inline std::string
|
||||
remote_ip(const socket &socket)
|
||||
{
|
||||
return remote_address(socket).to_string();
|
||||
}
|
||||
|
||||
inline ip::address
|
||||
remote_address(const socket &socket)
|
||||
{
|
||||
return socket.remote().address();
|
||||
}
|
||||
|
||||
} // namespace ircd
|
||||
|
||||
inline uint16_t
|
||||
ircd::port(const ip::tcp::endpoint &ep)
|
||||
{
|
||||
return ep.port();
|
||||
}
|
||||
|
||||
inline std::string
|
||||
ircd::hostaddr(const ip::tcp::endpoint &ep)
|
||||
{
|
||||
return hostaddr(address(ep));
|
||||
}
|
||||
|
||||
inline std::string
|
||||
ircd::hostaddr(const ip::address &addr)
|
||||
{
|
||||
return addr.to_string();
|
||||
}
|
||||
|
||||
inline boost::asio::ip::address
|
||||
ircd::address(const ip::tcp::endpoint &ep)
|
||||
{
|
||||
return ep.address();
|
||||
}
|
||||
|
||||
inline
|
||||
ircd::buffer::mutable_buffer::operator boost::asio::mutable_buffer()
|
||||
const
|
||||
|
|
197
ircd/client.cc
197
ircd/client.cc
|
@ -65,85 +65,101 @@ noexcept
|
|||
disconnect_all();
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::http::client::handle()
|
||||
try
|
||||
ircd::http::response::write_closure
|
||||
ircd::write_closure(client &client)
|
||||
{
|
||||
char buffer[2048];
|
||||
parse::buffer pb{buffer, buffer + sizeof(buffer)};
|
||||
parse::context pc{pb, [this](char *&read, char *const &stop)
|
||||
return [&client](const const_buffers &iov)
|
||||
{
|
||||
const mutable_buffers buffers{{read, stop}};
|
||||
read += sock->read_some(buffers);
|
||||
write(*client.sock, iov);
|
||||
};
|
||||
}
|
||||
|
||||
ircd::parse::read_closure
|
||||
ircd::read_closure(client &client)
|
||||
{
|
||||
return [&client](char *&start, char *const &stop)
|
||||
{
|
||||
read(client, start, stop);
|
||||
};
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::readline(client &client,
|
||||
char *&start,
|
||||
char *const &stop)
|
||||
{
|
||||
auto &sock(*client.sock);
|
||||
|
||||
size_t pos;
|
||||
string_view ret;
|
||||
char *const base(start); do
|
||||
{
|
||||
const std::array<mutable_buffer, 1> bufs
|
||||
{{
|
||||
{ start, stop }
|
||||
}};
|
||||
|
||||
start += sock.read_some(bufs);
|
||||
ret = {base, start};
|
||||
pos = ret.find("\r\n");
|
||||
}
|
||||
while(pos != std::string_view::npos);
|
||||
|
||||
return { begin(ret), std::next(begin(ret), pos + 2) };
|
||||
}
|
||||
|
||||
char *
|
||||
ircd::read(client &client,
|
||||
char *&start,
|
||||
char *const &stop)
|
||||
{
|
||||
auto &sock(*client.sock);
|
||||
const std::array<mutable_buffer, 1> bufs
|
||||
{{
|
||||
{ start, stop }
|
||||
}};
|
||||
|
||||
const http::request::head head{pc}; try
|
||||
{
|
||||
log::debug("client[%s] requesting resource \"%s\"",
|
||||
string(remote_address(*this)).c_str(),
|
||||
std::string(head.resource).c_str());
|
||||
|
||||
const auto &resource(*resource::resources.at(head.resource));
|
||||
resource(*this, pc, head);
|
||||
}
|
||||
catch(const std::out_of_range &e)
|
||||
{
|
||||
throw http::error(http::code::NOT_FOUND);
|
||||
}
|
||||
|
||||
return true;
|
||||
char *const base(start);
|
||||
start += sock.read_some(bufs);
|
||||
return base;
|
||||
}
|
||||
catch(const http::error &e)
|
||||
|
||||
const char *
|
||||
ircd::write(client &client,
|
||||
const char *&start,
|
||||
const char *const &stop)
|
||||
{
|
||||
char content_len[64]; const auto content_len_len
|
||||
{
|
||||
snprintf(content_len, sizeof(content_len), "Content-Length: %zu\r\n", e.content.size())
|
||||
};
|
||||
auto &sock(*client.sock);
|
||||
const std::array<const_buffer, 1> bufs
|
||||
{{
|
||||
{ start, stop }
|
||||
}};
|
||||
|
||||
const const_buffers iov
|
||||
{
|
||||
{ "HTTP/1.1 ", 9 },
|
||||
{ e.what(), strlen(e.what()) },
|
||||
{ "\r\n", 2 },
|
||||
{ string_view(content_len) },
|
||||
{ "\r\n", 2 },
|
||||
{ e.content.data(), e.content.size() }
|
||||
};
|
||||
|
||||
sock->write(iov);
|
||||
log::debug("client[%s] http::error(%d): %s",
|
||||
string(remote_address(*this)).c_str(),
|
||||
int(e.code),
|
||||
e.what());
|
||||
|
||||
switch(e.code)
|
||||
{
|
||||
case http::BAD_REQUEST: return false;
|
||||
case http::INTERNAL_SERVER_ERROR: return false;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
const char *const base(start);
|
||||
start += sock.write(bufs);
|
||||
return base;
|
||||
}
|
||||
|
||||
ircd::host_port
|
||||
ircd::local_address(const client &client)
|
||||
ircd::local_addr(const client &client)
|
||||
{
|
||||
if(!client.sock)
|
||||
return { "0.0.0.0"s, 0 };
|
||||
|
||||
const auto &sock(*client.sock);
|
||||
return { local_ip(sock), local_port(sock) };
|
||||
const auto &ep(sock.local());
|
||||
return { hostaddr(ep), port(ep) };
|
||||
}
|
||||
|
||||
ircd::host_port
|
||||
ircd::remote_address(const client &client)
|
||||
ircd::remote_addr(const client &client)
|
||||
{
|
||||
if(!client.sock)
|
||||
return { "0.0.0.0"s, 0 };
|
||||
|
||||
const auto &sock(*client.sock);
|
||||
return { remote_ip(sock), remote_port(sock) };
|
||||
const auto &ep(sock.remote());
|
||||
return { hostaddr(ep), port(ep) };
|
||||
}
|
||||
|
||||
ircd::client::client()
|
||||
|
@ -177,7 +193,7 @@ bool
|
|||
ircd::client::main()
|
||||
noexcept try
|
||||
{
|
||||
return handle();
|
||||
return serve();
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
|
@ -190,24 +206,71 @@ catch(const std::exception &e)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::client::serve()
|
||||
try
|
||||
{
|
||||
char buffer[4096];
|
||||
|
||||
parse::buffer pb{buffer, buffer + sizeof(buffer)};
|
||||
parse::context pc{pb, read_closure(*this)};
|
||||
http::request
|
||||
{
|
||||
pc, nullptr, write_closure(*this), [&]
|
||||
(const http::request::head &head)
|
||||
{
|
||||
log::debug("client[%s] requesting resource \"%s\"",
|
||||
string(remote_addr(*this)).c_str(),
|
||||
std::string(head.resource).c_str());
|
||||
try
|
||||
{
|
||||
const auto &resource(*resource::resources.at(head.resource));
|
||||
resource(*this, pc, head);
|
||||
}
|
||||
catch(const std::out_of_range &e)
|
||||
{
|
||||
throw http::error(http::code::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(const http::error &e)
|
||||
{
|
||||
log::debug("client[%s] http::error(%d): %s",
|
||||
string(remote_addr(*this)).c_str(),
|
||||
int(e.code),
|
||||
e.what());
|
||||
|
||||
switch(e.code)
|
||||
{
|
||||
case http::BAD_REQUEST: return false;
|
||||
case http::INTERNAL_SERVER_ERROR: return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error("client[%s] [500 Internal Error]: %s",
|
||||
string(remote_addr(*this)).c_str(),
|
||||
e.what());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<ircd::client>
|
||||
ircd::add_client(std::shared_ptr<socket> s)
|
||||
{
|
||||
const auto client(std::make_shared<http::client>(std::move(s)));
|
||||
const auto client(std::make_shared<ircd::client>(std::move(s)));
|
||||
log::info("New client[%s] on local[%s]",
|
||||
string(remote_address(*client)).c_str(),
|
||||
string(local_address(*client)).c_str());
|
||||
string(remote_addr(*client)).c_str(),
|
||||
string(local_addr(*client)).c_str());
|
||||
|
||||
async_recv_next(client, 30s);
|
||||
return client;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::client::handle()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class... args>
|
||||
std::shared_ptr<ircd::client>
|
||||
ircd::make_client(args&&... a)
|
||||
|
@ -294,7 +357,7 @@ ircd::handle_ec_timeout(client &client)
|
|||
{
|
||||
auto &sock(*client.sock);
|
||||
log::debug("client[%s]: disconnecting after inactivity timeout",
|
||||
string(remote_address(client)).c_str());
|
||||
string(remote_addr(client)).c_str());
|
||||
|
||||
sock.disconnect();
|
||||
return false;
|
||||
|
|
498
ircd/http.cc
498
ircd/http.cc
|
@ -47,21 +47,7 @@ BOOST_FUSION_ADAPT_STRUCT
|
|||
( decltype(ircd::http::line::header::first), first )
|
||||
( decltype(ircd::http::line::header::second), second )
|
||||
)
|
||||
/*
|
||||
BOOST_FUSION_ADAPT_STRUCT
|
||||
(
|
||||
ircd::http::request,
|
||||
( decltype(ircd::http::request::command), command )
|
||||
( decltype(ircd::http::request::headers), headers )
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT
|
||||
(
|
||||
ircd::http::response,
|
||||
( decltype(ircd::http::response::status), status )
|
||||
( decltype(ircd::http::response::headers), headers )
|
||||
)
|
||||
*/
|
||||
namespace ircd {
|
||||
namespace http {
|
||||
|
||||
|
@ -205,7 +191,7 @@ grammar<it, top>::grammar(qi::rule<it, top> &top_rule,
|
|||
}
|
||||
,version
|
||||
{
|
||||
raw[+(char_ - (NUL | CR | LF | SP))]
|
||||
raw[+(char_ - (NUL | CR | LF | WS))]
|
||||
,"version"
|
||||
}
|
||||
,status
|
||||
|
@ -268,119 +254,8 @@ struct parser
|
|||
}
|
||||
const parser;
|
||||
|
||||
} // namespace http
|
||||
} // namespace ircd
|
||||
|
||||
ircd::http::content::content(parse::context &pc,
|
||||
const headers &h)
|
||||
:std::string(h.content_length, char())
|
||||
{
|
||||
char *data(const_cast<char *>(this->data()));
|
||||
char *const stop(data + size());
|
||||
const size_t unparsed(pc.read - pc.parsed);
|
||||
const size_t have(std::min(unparsed, size()));
|
||||
memcpy(data, pc.parsed, have);
|
||||
data[have] = '\0';
|
||||
pc.parsed += have;
|
||||
data += have;
|
||||
if(data != stop)
|
||||
{
|
||||
pc.reader(data, stop);
|
||||
*data = '\0';
|
||||
}
|
||||
|
||||
assert(data == stop);
|
||||
}
|
||||
|
||||
ircd::http::headers::headers(parse::context &pc,
|
||||
const closure &c)
|
||||
{
|
||||
for(line::header h{pc}; !h.first.empty(); h = line::header{pc})
|
||||
{
|
||||
if(iequals(h.first, "host"))
|
||||
host = h.second;
|
||||
else if(iequals(h.first, "expect"))
|
||||
expect = h.second;
|
||||
else if(iequals(h.first, "te"))
|
||||
te = h.second;
|
||||
else if(iequals(h.first, "content-length"))
|
||||
content_length = lex_cast<size_t>(h.second);
|
||||
|
||||
if(c)
|
||||
c(h);
|
||||
}
|
||||
}
|
||||
|
||||
ircd::http::line::header::header(const line &line)
|
||||
try
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
eps > parser.header
|
||||
};
|
||||
|
||||
if(line.empty())
|
||||
return;
|
||||
|
||||
const char *start(line.data());
|
||||
const char *const stop(line.data() + line.size());
|
||||
qi::parse(start, stop, grammar, *this);
|
||||
}
|
||||
catch(const qi::expectation_failure<const char *> &e)
|
||||
{
|
||||
throw error(code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
ircd::http::line::response::response(const line &line)
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
parser.response_line
|
||||
};
|
||||
|
||||
const char *start(line.data());
|
||||
const char *const stop(line.data() + line.size());
|
||||
qi::parse(start, stop, grammar, *this);
|
||||
}
|
||||
|
||||
ircd::http::line::request::request(const line &line)
|
||||
try
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
eps > parser.request_line
|
||||
};
|
||||
|
||||
const char *start(line.data());
|
||||
const char *const stop(line.data() + line.size());
|
||||
qi::parse(start, stop, grammar, *this);
|
||||
}
|
||||
catch(const qi::expectation_failure<const char *> &e)
|
||||
{
|
||||
throw error(code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
ircd::http::line::line(parse::context &pc)
|
||||
:string_view{[&pc]
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
parser.line
|
||||
};
|
||||
|
||||
string_view ret;
|
||||
pc([&ret](const char *&start, const char *stop)
|
||||
{
|
||||
return qi::parse(start, stop, grammar, ret);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}()}
|
||||
{
|
||||
}
|
||||
|
||||
namespace ircd {
|
||||
namespace http {
|
||||
size_t printed_size(const std::initializer_list<line::header> &headers);
|
||||
size_t print(char *const &buf, const size_t &max, const std::initializer_list<line::header> &headers);
|
||||
|
||||
std::map<code, std::string> reason
|
||||
{
|
||||
|
@ -412,6 +287,373 @@ std::map<code, std::string> reason
|
|||
} // namespace http
|
||||
} // namespace ircd
|
||||
|
||||
size_t
|
||||
ircd::http::print(char *const &buf,
|
||||
const size_t &max,
|
||||
const std::initializer_list<line::header> &headers)
|
||||
{
|
||||
size_t ret(0);
|
||||
for(const auto &header : headers)
|
||||
ret += snprintf(buf + ret, max - ret, "%s: %s\r\n",
|
||||
header.first.data(),
|
||||
header.second.data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::http::printed_size(const std::initializer_list<line::header> &headers)
|
||||
{
|
||||
return std::accumulate(begin(headers), end(headers), size_t(0), []
|
||||
(auto &ret, const auto &pair)
|
||||
{
|
||||
// key : SP value CRLF
|
||||
return ret += pair.first.size() + 1 + 1 + pair.second.size() + 2;
|
||||
});
|
||||
}
|
||||
|
||||
ircd::http::request::request(parse::context &pc,
|
||||
content *const &c,
|
||||
const write_closure &write_closure,
|
||||
const proffer &proffer,
|
||||
const headers::closure &headers_closure)
|
||||
try
|
||||
{
|
||||
const head h{pc, headers_closure};
|
||||
const char *const content_mark(pc.parsed);
|
||||
const scope discard_unused_content{[&pc, &h, &content_mark]
|
||||
{
|
||||
const size_t consumed(pc.parsed - content_mark);
|
||||
const size_t remain(h.content_length - consumed);
|
||||
http::content{pc, remain, content::discard};
|
||||
}};
|
||||
|
||||
if(proffer)
|
||||
proffer(h);
|
||||
|
||||
if(c)
|
||||
*c = content{pc, h};
|
||||
}
|
||||
catch(const http::error &e)
|
||||
{
|
||||
if(write_closure)
|
||||
http::response{e.code, e.content, write_closure};
|
||||
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
if(write_closure)
|
||||
http::response{http::INTERNAL_SERVER_ERROR, e.what(), write_closure};
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
ircd::http::request::request(const string_view &host,
|
||||
const string_view &method,
|
||||
const string_view &resource,
|
||||
const string_view &content,
|
||||
const write_closure &closure,
|
||||
const std::initializer_list<line::header> &headers)
|
||||
{
|
||||
assert(!method.empty());
|
||||
assert(!resource.empty());
|
||||
|
||||
char host_line[128] {"Host: "}; const auto host_line_len
|
||||
{
|
||||
6 + snprintf(host_line + 6, std::min(sizeof(host_line) - 6, host.size() + 3), "%s\r\n",
|
||||
host.data())
|
||||
};
|
||||
|
||||
char content_len[64]; const auto content_len_len
|
||||
{
|
||||
snprintf(content_len, sizeof(content_len), "Content-Length: %zu\r\n",
|
||||
content.size())
|
||||
};
|
||||
|
||||
char user_headers[printed_size(headers) + 1]; const auto user_headers_len
|
||||
{
|
||||
print(user_headers, sizeof(user_headers), headers)
|
||||
};
|
||||
|
||||
const auto &space {" "s };
|
||||
const auto &version { "HTTP/1.1"s };
|
||||
const auto &terminator { "\r\n"s };
|
||||
const_buffers vector
|
||||
{
|
||||
{ method.data(), method.size() },
|
||||
{ space.data(), space.size(), },
|
||||
{ resource.data(), resource.size(), },
|
||||
{ space.data(), space.size(), },
|
||||
{ version.data(), version.size(), },
|
||||
{ terminator.data(), terminator.size() },
|
||||
{ host_line, size_t(host_line_len) },
|
||||
{ content_len, size_t(content_len_len) },
|
||||
{ user_headers, size_t(user_headers_len) },
|
||||
{ terminator.data(), terminator.size() },
|
||||
{ content.data(), content.size() },
|
||||
};
|
||||
|
||||
closure(vector);
|
||||
}
|
||||
|
||||
ircd::http::request::head::head(parse::context &pc,
|
||||
const headers::closure &c)
|
||||
:line::request{pc}
|
||||
{
|
||||
headers{pc, [this, &c](const auto &h)
|
||||
{
|
||||
if(iequals(h.first, "host"s))
|
||||
host = h.second;
|
||||
else if(iequals(h.first, "expect"s))
|
||||
expect = h.second;
|
||||
else if(iequals(h.first, "te"s))
|
||||
te = h.second;
|
||||
else if(iequals(h.first, "content-length"s))
|
||||
content_length = lex_cast<size_t>(h.second);
|
||||
|
||||
if(c)
|
||||
c(h);
|
||||
}};
|
||||
}
|
||||
|
||||
ircd::http::response::response(parse::context &pc,
|
||||
content *const &c,
|
||||
const proffer &proffer,
|
||||
const headers::closure &headers_closure)
|
||||
{
|
||||
const head h{pc, headers_closure};
|
||||
const char *const content_mark(pc.parsed);
|
||||
const scope discard_unused_content{[&pc, &h, &content_mark]
|
||||
{
|
||||
const size_t consumed(pc.parsed - content_mark);
|
||||
const size_t remain(h.content_length - consumed);
|
||||
http::content{pc, remain, content::discard};
|
||||
}};
|
||||
|
||||
if(proffer)
|
||||
proffer(h);
|
||||
|
||||
if(c)
|
||||
*c = content{pc, h};
|
||||
}
|
||||
|
||||
ircd::http::response::response(const code &code,
|
||||
const string_view &content,
|
||||
const write_closure &closure,
|
||||
const std::initializer_list<line::header> &headers)
|
||||
{
|
||||
char status_line[64]; const auto status_line_len
|
||||
{
|
||||
snprintf(status_line, sizeof(status_line), "HTTP/1.1 %u %s\r\n",
|
||||
uint(code),
|
||||
http::reason[code].data())
|
||||
};
|
||||
|
||||
char server_line[128]; const auto server_line_len
|
||||
{
|
||||
code >= 200 && code < 300?
|
||||
snprintf(server_line, sizeof(server_line), "Server: %s (IRCd) %s\r\n",
|
||||
BRANDING_NAME,
|
||||
BRANDING_VERSION):
|
||||
0
|
||||
};
|
||||
|
||||
const time_t ltime(time(nullptr));
|
||||
struct tm *const tm(localtime(<ime));
|
||||
char date_line[64]; const auto date_line_len
|
||||
{
|
||||
code < 400 || code >= 500?
|
||||
strftime(date_line, sizeof(date_line), "Date: %a, %d %b %Y %T %z\r\n", tm):
|
||||
0
|
||||
};
|
||||
|
||||
char content_len[64]; const auto content_len_len
|
||||
{
|
||||
code != NO_CONTENT?
|
||||
snprintf(content_len, sizeof(content_len), "Content-Length: %zu\r\n",
|
||||
content.size()):
|
||||
0
|
||||
};
|
||||
|
||||
const auto user_headers_bufsize
|
||||
{
|
||||
std::accumulate(begin(headers), end(headers), size_t(1), []
|
||||
(auto &ret, const auto &pair)
|
||||
{
|
||||
return ret += pair.first.size() + 1 + 1 + pair.second.size() + 2;
|
||||
})
|
||||
};
|
||||
|
||||
char user_headers[user_headers_bufsize];
|
||||
const auto user_headers_len(print(user_headers, sizeof(user_headers), headers));
|
||||
|
||||
const_buffers iov
|
||||
{
|
||||
{ status_line, size_t(status_line_len) },
|
||||
{ server_line, size_t(server_line_len) },
|
||||
{ date_line, size_t(date_line_len) },
|
||||
{ content_len, size_t(content_len_len) },
|
||||
{ user_headers, size_t(user_headers_len) },
|
||||
{ "\r\n", 2 },
|
||||
{ content.data(), content.size() },
|
||||
};
|
||||
|
||||
closure(iov);
|
||||
}
|
||||
|
||||
ircd::http::response::head::head(parse::context &pc,
|
||||
const headers::closure &c)
|
||||
:line::response{pc}
|
||||
{
|
||||
headers{pc, [this, &c](const auto &h)
|
||||
{
|
||||
if(iequals(h.first, "content-length"s))
|
||||
content_length = lex_cast<size_t>(h.second);
|
||||
|
||||
if(c)
|
||||
c(h);
|
||||
}};
|
||||
}
|
||||
|
||||
ircd::http::content::content(parse::context &pc,
|
||||
const size_t &length)
|
||||
:string_view{[&pc, &length]
|
||||
{
|
||||
const char *const base(pc.parsed);
|
||||
const size_t have(std::min(pc.unparsed(), length));
|
||||
size_t remain(length - have);
|
||||
pc.parsed += have;
|
||||
|
||||
while(remain && pc.remaining())
|
||||
{
|
||||
const auto read_max(std::min(remain, pc.remaining()));
|
||||
pc.reader(pc.read, pc.read + read_max);
|
||||
remain -= pc.unparsed();
|
||||
pc.parsed = pc.read;
|
||||
}
|
||||
|
||||
assert(pc.parsed == base + length);
|
||||
assert(pc.parsed == pc.read);
|
||||
|
||||
if(pc.remaining())
|
||||
*pc.read = '\0';
|
||||
|
||||
return string_view { base, pc.parsed };
|
||||
}()}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::http::content::content(parse::context &pc,
|
||||
const size_t &length,
|
||||
discard_t)
|
||||
:string_view{}
|
||||
{
|
||||
static char buf[512] alignas(16);
|
||||
|
||||
const size_t have(std::min(pc.unparsed(), length));
|
||||
size_t remain(length - have);
|
||||
pc.read -= have;
|
||||
|
||||
while(remain)
|
||||
{
|
||||
char *start(buf);
|
||||
__builtin_prefetch(start, 1, 0); // 1 = write, 0 = no cache
|
||||
pc.reader(start, start + std::min(remain, sizeof(buf)));
|
||||
remain -= std::distance(buf, start);
|
||||
}
|
||||
}
|
||||
|
||||
ircd::http::headers::headers(parse::context &pc,
|
||||
const closure &c)
|
||||
{
|
||||
for(line::header h{pc}; !h.first.empty(); h = line::header{pc})
|
||||
if(c)
|
||||
c(h);
|
||||
}
|
||||
|
||||
ircd::http::line::header::header(const line &line)
|
||||
try
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
eps > parser.header
|
||||
};
|
||||
|
||||
if(line.empty())
|
||||
return;
|
||||
|
||||
const char *start(line.data());
|
||||
const char *const stop(line.data() + line.size());
|
||||
qi::parse(start, stop, grammar, *this);
|
||||
}
|
||||
catch(const qi::expectation_failure<const char *> &e)
|
||||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "I require a valid %s starting at character %d.",
|
||||
ircd::string(e.what_).data(),
|
||||
int(e.last - e.first));
|
||||
|
||||
throw error(code::BAD_REQUEST, buf);
|
||||
}
|
||||
|
||||
ircd::http::line::response::response(const line &line)
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
parser.response_line
|
||||
};
|
||||
|
||||
const char *start(line.data());
|
||||
const char *const stop(line.data() + line.size());
|
||||
qi::parse(start, stop, grammar, *this);
|
||||
}
|
||||
|
||||
ircd::http::line::request::request(const line &line)
|
||||
try
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
eps > parser.request_line
|
||||
};
|
||||
|
||||
const char *start(line.data());
|
||||
const char *const stop(line.data() + line.size());
|
||||
qi::parse(start, stop, grammar, *this);
|
||||
}
|
||||
catch(const qi::expectation_failure<const char *> &e)
|
||||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "I require a valid %s starting at character %d.",
|
||||
ircd::string(e.what_).data(),
|
||||
int(e.last - e.first));
|
||||
|
||||
throw error(code::BAD_REQUEST, buf);
|
||||
}
|
||||
|
||||
ircd::http::line::line(parse::context &pc)
|
||||
:string_view{[&pc]
|
||||
{
|
||||
static const auto grammar
|
||||
{
|
||||
parser.line
|
||||
};
|
||||
|
||||
string_view ret;
|
||||
pc([&ret](const char *&start, const char *const &stop)
|
||||
{
|
||||
if(!qi::parse(start, stop, grammar, ret))
|
||||
{
|
||||
ret = {};
|
||||
return false;
|
||||
}
|
||||
else return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}()}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::http::error::error(const enum code &code,
|
||||
const string_view &text)
|
||||
:ircd::error{generate_skip}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
*/
|
||||
|
||||
#include <ircd/lex_cast.h>
|
||||
#include <ircd/socket.h>
|
||||
|
||||
namespace ircd {
|
||||
namespace m {
|
||||
|
@ -32,28 +31,42 @@ void
|
|||
ircd::test(const string_view &what)
|
||||
try
|
||||
{
|
||||
http::client client{host_port{"matrix.org", 8448}};
|
||||
const auto tok(tokens(what, " "));
|
||||
const std::string method(tok.at(2));
|
||||
const std::string rcontent(tok.size() >= 5? tok.at(4) : string_view{});
|
||||
|
||||
static const string_view request
|
||||
char url[256] {0};
|
||||
snprintf(url, sizeof(url), "/_matrix/client/%s", std::string(tok.at(3)).c_str());
|
||||
|
||||
const std::string host{tok.at(0)};
|
||||
const auto port(lex_cast<uint16_t>(tok.at(1)));
|
||||
ircd::client client
|
||||
{
|
||||
"GET /_matrix/client/versions HTTP/1.1\r\nHost: matrix.org\r\n\r\n"
|
||||
host_port{host, port}
|
||||
};
|
||||
|
||||
auto &sock(*client.sock);
|
||||
write(sock, request);
|
||||
http::request
|
||||
{
|
||||
host, method, url, rcontent, write_closure(client),
|
||||
{
|
||||
{ "Content-Type"s, "application/json"s }
|
||||
}
|
||||
};
|
||||
|
||||
char buf[4096];
|
||||
parse::buffer pb(buf);
|
||||
parse::context pc(pb, [&sock]
|
||||
(char *&read, char *const &stop)
|
||||
parse::buffer pb{buf};
|
||||
parse::context pc{pb, read_closure(client)};
|
||||
http::response
|
||||
{
|
||||
read += sock.read_some(mutable_buffers{{read, stop}});
|
||||
});
|
||||
pc, nullptr, [&pc](const auto &head)
|
||||
{
|
||||
http::response::content content{pc, head};
|
||||
const json::doc cdoc{content};
|
||||
|
||||
const http::response::head header{pc};
|
||||
const http::response::body content{pc, header};
|
||||
for(const auto &member : json::doc(content))
|
||||
std::cout << member.first << " --> " << member.second << std::endl;
|
||||
for(const auto &member : cdoc)
|
||||
std::cout << member.first << " --> " << member.second << std::endl;
|
||||
}
|
||||
};
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
|
|
121
ircd/resource.cc
121
ircd/resource.cc
|
@ -19,8 +19,6 @@
|
|||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ircd/socket.h>
|
||||
|
||||
namespace ircd {
|
||||
|
||||
IRCD_INIT_PRIORITY(STD_CONTAINER)
|
||||
|
@ -61,13 +59,13 @@ ircd::resource::operator()(client &client,
|
|||
const try
|
||||
{
|
||||
const auto &method(*methods.at(head.method));
|
||||
http::request::body content{pc, head};
|
||||
http::request::content content{pc, head};
|
||||
resource::request request
|
||||
{
|
||||
head, content
|
||||
};
|
||||
|
||||
response r(method(client, request));
|
||||
method(client, request);
|
||||
}
|
||||
catch(const std::out_of_range &e)
|
||||
{
|
||||
|
@ -105,52 +103,20 @@ ircd::resource::response::response(client &client,
|
|||
const http::code &code)
|
||||
{
|
||||
char cbuf[1024];
|
||||
mutable_buffer buf(cbuf, sizeof(cbuf));
|
||||
response(client, serialize(doc, data(buf), data(buf) + size(buf)), code);
|
||||
response(client, serialize(doc, cbuf, cbuf + sizeof(cbuf)), code);
|
||||
}
|
||||
|
||||
ircd::resource::response::response(client &client,
|
||||
const json::doc &doc,
|
||||
const http::code &code)
|
||||
{
|
||||
char status_line[64]; const auto status_line_len
|
||||
http::response
|
||||
{
|
||||
snprintf(status_line, sizeof(status_line), "HTTP/1.1 %u %s\r\n",
|
||||
uint(code),
|
||||
http::reason[code].data())
|
||||
code, doc, write_closure(client),
|
||||
{
|
||||
{ "Content-Type", "application/json" }
|
||||
}
|
||||
};
|
||||
|
||||
char server_line[128]; const auto server_line_len
|
||||
{
|
||||
snprintf(server_line, sizeof(server_line), "Server: %s (IRCd) %s\r\n",
|
||||
BRANDING_NAME,
|
||||
BRANDING_VERSION)
|
||||
};
|
||||
|
||||
const time_t ltime(time(nullptr));
|
||||
struct tm *const tm(localtime(<ime));
|
||||
char date_line[64]; const auto date_line_len
|
||||
{
|
||||
strftime(date_line, sizeof(date_line), "Date: %a, %d %b %Y %T %z\r\n", tm)
|
||||
};
|
||||
|
||||
char content_len[64]; const auto content_len_len
|
||||
{
|
||||
snprintf(content_len, sizeof(content_len), "Content-Length: %zu\r\n",
|
||||
doc.size())
|
||||
};
|
||||
|
||||
const const_buffers iov
|
||||
{
|
||||
{ status_line, size_t(status_line_len) },
|
||||
{ server_line, size_t(server_line_len) },
|
||||
{ date_line, size_t(date_line_len) },
|
||||
{ content_len, size_t(content_len_len) },
|
||||
{ "\r\n", 2 },
|
||||
{ doc.data(), doc.size() }
|
||||
};
|
||||
|
||||
client.sock->write(iov);
|
||||
}
|
||||
|
||||
ircd::resource::response::~response()
|
||||
|
@ -166,74 +132,3 @@ ircd::resource::member::member(const char *const &name,
|
|||
,valid{std::move(valid)}
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Museum of historical comments
|
||||
//
|
||||
// parse.c (1990 - 2016)
|
||||
//
|
||||
|
||||
/* ok, fake prefix happens naturally during a burst on a nick
|
||||
* collision with TS5, we cant kill them because one client has to
|
||||
* survive, so we just send an error.
|
||||
*/
|
||||
|
||||
/* meepfoo is a nickname (ignore)
|
||||
* #XXXXXXXX is a UID (KILL)
|
||||
* #XX is a SID (SQUIT)
|
||||
* meep.foo is a server (SQUIT)
|
||||
*/
|
||||
/*
|
||||
* *WARNING*
|
||||
* Numerics are mostly error reports. If there is something
|
||||
* wrong with the message, just *DROP* it! Don't even think of
|
||||
* sending back a neat error message -- big danger of creating
|
||||
* a ping pong error message...
|
||||
*/
|
||||
|
||||
/*
|
||||
* Prepare the parameter portion of the message into 'buffer'.
|
||||
* (Because the buffer is twice as large as the message buffer
|
||||
* for the socket, no overflow can occur here... ...on current
|
||||
* assumptions--bets are off, if these are changed --msa)
|
||||
* Note: if buffer is non-empty, it will begin with SPACE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We shouldn't get numerics sent to us,
|
||||
* any numerics we do get indicate a bug somewhere..
|
||||
*/
|
||||
|
||||
/* ugh. this is here because of nick collisions. when two servers
|
||||
* relink, they burst each other their nicks, then perform collides.
|
||||
* if there is a nick collision, BOTH servers will kill their own
|
||||
* nicks, and BOTH will kill the other servers nick, which wont exist,
|
||||
* because it will have been already killed by the local server.
|
||||
*
|
||||
* unfortunately, as we cant guarantee other servers will do the
|
||||
* "right thing" on a nick collision, we have to keep both kills.
|
||||
* ergo we need to ignore ERR_NOSUCHNICK. --fl_
|
||||
*/
|
||||
|
||||
/* quick comment. This _was_ tried. i.e. assume the other servers
|
||||
* will do the "right thing" and kill a nick that is colliding.
|
||||
* unfortunately, it did not work. --Dianora
|
||||
*/
|
||||
|
||||
/* note, now we send PING on server connect, we can
|
||||
* also get ERR_NOSUCHSERVER..
|
||||
*/
|
||||
|
||||
/* This message changed direction (nick collision?)
|
||||
* ignore it.
|
||||
*/
|
||||
|
||||
/* csircd will send out unknown umode flag for +a (admin), drop it here. */
|
||||
|
||||
/* Fake it for server hiding, if its our client */
|
||||
|
||||
/* bit of a hack.
|
||||
* I don't =really= want to waste a bit in a flag
|
||||
* number_of_nick_changes is only really valid after the client
|
||||
* is fully registered..
|
||||
*/
|
||||
|
|
|
@ -57,52 +57,27 @@ ircd::write(socket &socket,
|
|||
size_t
|
||||
ircd::write(socket &socket,
|
||||
const char *const &buf,
|
||||
const size_t &max)
|
||||
const size_t &size)
|
||||
{
|
||||
const std::array<const_buffer, 1> cbufs
|
||||
const std::array<const_buffer, 1> bufs
|
||||
{{
|
||||
{ buf, size }
|
||||
}};
|
||||
|
||||
return socket.write(bufs);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::read(socket &socket,
|
||||
char *const &buf,
|
||||
const size_t &max)
|
||||
{
|
||||
const std::array<mutable_buffer, 1> bufs
|
||||
{{
|
||||
{ buf, max }
|
||||
}};
|
||||
|
||||
return socket.write(cbufs);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::readline(socket &socket,
|
||||
char *&start,
|
||||
char *const &stop)
|
||||
{
|
||||
size_t pos;
|
||||
string_view ret;
|
||||
char *const base(start); do
|
||||
{
|
||||
const std::array<mutable_buffer, 1> buf
|
||||
{{
|
||||
{ start, stop }
|
||||
}};
|
||||
|
||||
start += socket.read_some(buf);
|
||||
ret = {base, start};
|
||||
pos = ret.find("\r\n");
|
||||
}
|
||||
while(pos != std::string_view::npos);
|
||||
|
||||
return { begin(ret), std::next(begin(ret), pos + 2) };
|
||||
}
|
||||
|
||||
char *
|
||||
ircd::read(socket &socket,
|
||||
char *&start,
|
||||
char *const &stop)
|
||||
{
|
||||
const std::array<mutable_buffer, 1> buf
|
||||
{{
|
||||
{ start, stop }
|
||||
}};
|
||||
|
||||
char *const base(start);
|
||||
start += socket.read_some(buf);
|
||||
return base;
|
||||
return socket.read_some(bufs);
|
||||
}
|
||||
|
||||
ircd::socket::scope_timeout::scope_timeout(socket &socket,
|
||||
|
|
|
@ -96,7 +96,7 @@ resource::method getter
|
|||
{ body, bodysz },
|
||||
};
|
||||
|
||||
client.sock->write(iov);
|
||||
write(*client.sock, iov);
|
||||
return {};
|
||||
}};
|
||||
|
||||
|
|
|
@ -19,39 +19,8 @@
|
|||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ircd/socket.h>
|
||||
|
||||
using namespace ircd;
|
||||
|
||||
const std::string body
|
||||
{[]{
|
||||
const json::doc object
|
||||
{R"(
|
||||
|
||||
{"versions":["r0.0.1"]}
|
||||
|
||||
)"};
|
||||
|
||||
std::array<char, 128> body {0};
|
||||
const size_t size(0);
|
||||
//const size_t size(print(body.data(), body.size(), object));
|
||||
return std::string(body.data(), size);
|
||||
}()};
|
||||
|
||||
const std::string header
|
||||
{[]{
|
||||
const auto head
|
||||
{
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"\r\n"
|
||||
};
|
||||
|
||||
std::array<char, 128> header;
|
||||
const size_t size(snprintf(header.data(), header.size(), head, body.size()));
|
||||
return std::string(header.data(), size);
|
||||
}()};
|
||||
|
||||
resource versions_resource
|
||||
{
|
||||
"/_matrix/client/r0/versions",
|
||||
|
@ -61,16 +30,15 @@ resource versions_resource
|
|||
resource::method getter
|
||||
{
|
||||
versions_resource, "GET", []
|
||||
(client &client, resource::request &request) -> resource::response
|
||||
(client &client, resource::request &request)
|
||||
-> resource::response
|
||||
{
|
||||
static const const_buffers iov
|
||||
static const json::doc object
|
||||
{
|
||||
{ header.data(), header.size() },
|
||||
{ body.data(), body.size() },
|
||||
R"({"versions":["r0.0.1"]})"
|
||||
};
|
||||
|
||||
client.sock->write(iov);
|
||||
return {};
|
||||
return resource::response { client, object };
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ extern "C" void gogo()
|
|||
{
|
||||
const auto iit(listeners.emplace("foo"s, "foo"s));
|
||||
auto &foo(iit.first->second);
|
||||
foo.host = ip::address::from_string("0.0.0.0");
|
||||
foo.host = ip::address::from_string("127.0.0.1");
|
||||
foo.ep = ip::tcp::endpoint(foo.host, 6667);
|
||||
foo.cond.notify_one();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue