0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-02 18:18:56 +02:00

ircd: HTTP interface function-objects et al.

This commit is contained in:
Jason Volk 2017-03-13 14:07:58 -07:00
parent 03528ebf67
commit 472ee2c648
12 changed files with 739 additions and 523 deletions

View file

@ -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

View file

@ -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}
{}
};

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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(&ltime));
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}

View file

@ -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)
{

View file

@ -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(&ltime));
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..
*/

View file

@ -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,

View file

@ -96,7 +96,7 @@ resource::method getter
{ body, bodysz },
};
client.sock->write(iov);
write(*client.sock, iov);
return {};
}};

View file

@ -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 };
}
};

View file

@ -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();
}