mirror of
https://github.com/matrix-construct/construct
synced 2024-10-01 21:28:53 +02:00
ircd: Minor reorg client/resource relationship.
This commit is contained in:
parent
ae8a8e736a
commit
c20a2927d0
3 changed files with 170 additions and 168 deletions
|
@ -25,6 +25,8 @@
|
||||||
namespace ircd
|
namespace ircd
|
||||||
{
|
{
|
||||||
struct resource;
|
struct resource;
|
||||||
|
|
||||||
|
bool handle_request(client &client, parse::capstan &pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ircd::resource
|
struct ircd::resource
|
||||||
|
|
233
ircd/client.cc
233
ircd/client.cc
|
@ -201,6 +201,64 @@ ircd::write(client &client,
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::async_recv_next(std::shared_ptr<client> client)
|
||||||
|
{
|
||||||
|
async_recv_next(std::move(client), milliseconds(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// This function is the basis for the client's request loop. We still use
|
||||||
|
/// an asynchronous pattern until there is activity on the socket (a request)
|
||||||
|
/// in which case the switch to synchronous mode is made by jumping into an
|
||||||
|
/// ircd::context drawn from the request pool. When the request is finished,
|
||||||
|
/// the client exits back into asynchronous mode until the next request is
|
||||||
|
/// received and rinse and repeat.
|
||||||
|
//
|
||||||
|
/// This sequence exists to avoid any possible c10k-style limitation imposed by
|
||||||
|
/// dedicating a context and its stack space to the lifetime of a connection.
|
||||||
|
/// This is similar to the thread-per-request pattern before async was in vogue.
|
||||||
|
//
|
||||||
|
/// Developers: Pay close attention to the comments to know exactly where you
|
||||||
|
/// are and what you can do at any given point in this sequence.
|
||||||
|
///
|
||||||
|
void
|
||||||
|
ircd::async_recv_next(std::shared_ptr<client> client,
|
||||||
|
const milliseconds &timeout)
|
||||||
|
{
|
||||||
|
assert(bool(client));
|
||||||
|
assert(bool(client->sock));
|
||||||
|
|
||||||
|
// This call returns immediately so we no longer block the current context and
|
||||||
|
// its stack while waiting for activity on idle connections between requests.
|
||||||
|
|
||||||
|
auto &sock(*client->sock);
|
||||||
|
sock(timeout, [client(std::move(client)), timeout](const net::error_code &ec)
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
// Right here this handler is executing on the main stack (not in any
|
||||||
|
// ircd::context).
|
||||||
|
if(!handle_ec(*client, ec))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// This call returns immediately because we can never block the main stack outside
|
||||||
|
// of the ircd::context system. The context the closure ends up getting is the next
|
||||||
|
// available from the request pool, which may not be available immediately so this
|
||||||
|
// handler might be queued for some time after this call returns.
|
||||||
|
request([ec, client(std::move(client)), timeout]
|
||||||
|
{
|
||||||
|
// Right here this handler is executing on an ircd::context with its own
|
||||||
|
// stack dedicated to the lifetime of this request. If client::main()
|
||||||
|
// returns true, we bring the client back into async mode to wait for
|
||||||
|
// the next request.
|
||||||
|
if(client->main())
|
||||||
|
async_recv_next(std::move(client), timeout);
|
||||||
|
else
|
||||||
|
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// client
|
// client
|
||||||
//
|
//
|
||||||
|
@ -237,12 +295,6 @@ catch(const std::exception &e)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ircd
|
|
||||||
{
|
|
||||||
void handle_request(client &client, parse::capstan &pc, const http::request::head &head);
|
|
||||||
bool handle_request(client &client, parse::capstan &pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Main client loop.
|
/// Main client loop.
|
||||||
///
|
///
|
||||||
/// This function parses requests off the socket in a loop until there are no
|
/// This function parses requests off the socket in a loop until there are no
|
||||||
|
@ -276,6 +328,17 @@ noexcept try
|
||||||
parse::buffer pb{buffer};
|
parse::buffer pb{buffer};
|
||||||
parse::capstan pc{pb, read_closure(*this)}; do
|
parse::capstan pc{pb, read_closure(*this)}; do
|
||||||
{
|
{
|
||||||
|
request_timer = ircd::timer{};
|
||||||
|
const socket::scope_timeout timeout
|
||||||
|
{
|
||||||
|
*sock, request_timeout, [client(shared_from(*this))]
|
||||||
|
(const net::error_code &ec)
|
||||||
|
{
|
||||||
|
if(!ec)
|
||||||
|
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if(!handle_request(*this, pc))
|
if(!handle_request(*this, pc))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -358,106 +421,6 @@ catch(const std::exception &e)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a single request within the client main() loop.
|
|
||||||
///
|
|
||||||
/// This function returns false if the main() loop should exit
|
|
||||||
/// and thus disconnect the client. It should return true in most
|
|
||||||
/// cases even for lightly erroneous requests that won't affect
|
|
||||||
/// the next requests on the tape.
|
|
||||||
///
|
|
||||||
/// This function is timed. The timeout will prevent a client from
|
|
||||||
/// sending a partial request and leave us waiting for the rest.
|
|
||||||
/// As of right now this timeout extends to our handling of the
|
|
||||||
/// request too.
|
|
||||||
bool
|
|
||||||
ircd::handle_request(client &client,
|
|
||||||
parse::capstan &pc)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
client.request_timer = ircd::timer{};
|
|
||||||
const socket::scope_timeout timeout
|
|
||||||
{
|
|
||||||
*client.sock, request_timeout, [client(shared_from(client))]
|
|
||||||
(const net::error_code &ec)
|
|
||||||
{
|
|
||||||
if(!ec)
|
|
||||||
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ret{true};
|
|
||||||
http::request
|
|
||||||
{
|
|
||||||
pc, nullptr, [&client, &pc, &ret]
|
|
||||||
(const auto &head)
|
|
||||||
{
|
|
||||||
handle_request(client, pc, head);
|
|
||||||
ret = !iequals(head.connection, "close"s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch(const http::error &e)
|
|
||||||
{
|
|
||||||
log::debug("client[%s] HTTP %s in %ld$us %s",
|
|
||||||
string(remote(client)),
|
|
||||||
e.what(),
|
|
||||||
client.request_timer.at<microseconds>().count(),
|
|
||||||
e.content);
|
|
||||||
|
|
||||||
http::response
|
|
||||||
{
|
|
||||||
e.code, e.content, write_closure(client)
|
|
||||||
};
|
|
||||||
|
|
||||||
switch(e.code)
|
|
||||||
{
|
|
||||||
case http::BAD_REQUEST:
|
|
||||||
case http::REQUEST_TIMEOUT:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case http::INTERNAL_SERVER_ERROR:
|
|
||||||
throw;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
log::error("client[%s]: in %ld$us: %s",
|
|
||||||
string(remote(client)),
|
|
||||||
client.request_timer.at<microseconds>().count(),
|
|
||||||
e.what());
|
|
||||||
|
|
||||||
http::response
|
|
||||||
{
|
|
||||||
http::INTERNAL_SERVER_ERROR, e.what(), write_closure(client)
|
|
||||||
};
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ircd::handle_request(client &client,
|
|
||||||
parse::capstan &pc,
|
|
||||||
const http::request::head &head)
|
|
||||||
{
|
|
||||||
log::debug("client[%s] HTTP %s `%s' (content-length: %zu)",
|
|
||||||
string(remote(client)),
|
|
||||||
head.method,
|
|
||||||
head.path,
|
|
||||||
head.content_length);
|
|
||||||
|
|
||||||
auto &resource
|
|
||||||
{
|
|
||||||
ircd::resource::find(head.path)
|
|
||||||
};
|
|
||||||
|
|
||||||
resource(client, pc, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ircd::client>
|
std::shared_ptr<ircd::client>
|
||||||
ircd::add_client(std::shared_ptr<socket> s)
|
ircd::add_client(std::shared_ptr<socket> s)
|
||||||
{
|
{
|
||||||
|
@ -516,64 +479,6 @@ ircd::disconnect(client &client,
|
||||||
disconnect(*client.sock, type);
|
disconnect(*client.sock, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ircd::async_recv_next(std::shared_ptr<client> client)
|
|
||||||
{
|
|
||||||
async_recv_next(std::move(client), milliseconds(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// This function is the basis for the client's request loop. We still use
|
|
||||||
/// an asynchronous pattern until there is activity on the socket (a request)
|
|
||||||
/// in which case the switch to synchronous mode is made by jumping into an
|
|
||||||
/// ircd::context drawn from the request pool. When the request is finished,
|
|
||||||
/// the client exits back into asynchronous mode until the next request is
|
|
||||||
/// received and rinse and repeat.
|
|
||||||
//
|
|
||||||
/// This sequence exists to avoid any possible c10k-style limitation imposed by
|
|
||||||
/// dedicating a context and its stack space to the lifetime of a connection.
|
|
||||||
/// This is similar to the thread-per-request pattern before async was in vogue.
|
|
||||||
//
|
|
||||||
/// Developers: Pay close attention to the comments to know exactly where you
|
|
||||||
/// are and what you can do at any given point in this sequence.
|
|
||||||
///
|
|
||||||
void
|
|
||||||
ircd::async_recv_next(std::shared_ptr<client> client,
|
|
||||||
const milliseconds &timeout)
|
|
||||||
{
|
|
||||||
assert(bool(client));
|
|
||||||
assert(bool(client->sock));
|
|
||||||
|
|
||||||
// This call returns immediately so we no longer block the current context and
|
|
||||||
// its stack while waiting for activity on idle connections between requests.
|
|
||||||
|
|
||||||
auto &sock(*client->sock);
|
|
||||||
sock(timeout, [client(std::move(client)), timeout](const net::error_code &ec)
|
|
||||||
noexcept
|
|
||||||
{
|
|
||||||
// Right here this handler is executing on the main stack (not in any
|
|
||||||
// ircd::context).
|
|
||||||
if(!handle_ec(*client, ec))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// This call returns immediately because we can never block the main stack outside
|
|
||||||
// of the ircd::context system. The context the closure ends up getting is the next
|
|
||||||
// available from the request pool, which may not be available immediately so this
|
|
||||||
// handler might be queued for some time after this call returns.
|
|
||||||
request([ec, client(std::move(client)), timeout]
|
|
||||||
{
|
|
||||||
// Right here this handler is executing on an ircd::context with its own
|
|
||||||
// stack dedicated to the lifetime of this request. If client::main()
|
|
||||||
// returns true, we bring the client back into async mode to wait for
|
|
||||||
// the next request.
|
|
||||||
if(client->main())
|
|
||||||
async_recv_next(std::move(client), timeout);
|
|
||||||
else
|
|
||||||
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ircd
|
namespace ircd
|
||||||
{
|
{
|
||||||
static bool handle_ec_success(client &);
|
static bool handle_ec_success(client &);
|
||||||
|
|
103
ircd/resource.cc
103
ircd/resource.cc
|
@ -21,14 +21,104 @@
|
||||||
|
|
||||||
#include <ircd/m/m.h>
|
#include <ircd/m/m.h>
|
||||||
|
|
||||||
namespace ircd {
|
namespace ircd
|
||||||
|
{
|
||||||
|
void handle_request(client &client, parse::capstan &pc, const http::request::head &head);
|
||||||
|
}
|
||||||
|
|
||||||
IRCD_INIT_PRIORITY(STD_CONTAINER)
|
IRCD_INIT_PRIORITY(STD_CONTAINER)
|
||||||
decltype(resource::resources)
|
decltype(ircd::resource::resources)
|
||||||
resource::resources
|
ircd::resource::resources
|
||||||
{};
|
{};
|
||||||
|
|
||||||
} // namespace ircd
|
/// Handle a single request within the client main() loop.
|
||||||
|
///
|
||||||
|
/// This function returns false if the main() loop should exit
|
||||||
|
/// and thus disconnect the client. It should return true in most
|
||||||
|
/// cases even for lightly erroneous requests that won't affect
|
||||||
|
/// the next requests on the tape.
|
||||||
|
///
|
||||||
|
/// This function is timed. The timeout will prevent a client from
|
||||||
|
/// sending a partial request and leave us waiting for the rest.
|
||||||
|
/// As of right now this timeout extends to our handling of the
|
||||||
|
/// request too.
|
||||||
|
bool
|
||||||
|
ircd::handle_request(client &client,
|
||||||
|
parse::capstan &pc)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool ret{true};
|
||||||
|
http::request
|
||||||
|
{
|
||||||
|
pc, nullptr, [&client, &pc, &ret]
|
||||||
|
(const auto &head)
|
||||||
|
{
|
||||||
|
handle_request(client, pc, head);
|
||||||
|
ret = !iequals(head.connection, "close"s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch(const http::error &e)
|
||||||
|
{
|
||||||
|
log::debug("client[%s] HTTP %s in %ld$us %s",
|
||||||
|
string(remote(client)),
|
||||||
|
e.what(),
|
||||||
|
client.request_timer.at<microseconds>().count(),
|
||||||
|
e.content);
|
||||||
|
|
||||||
|
resource::response
|
||||||
|
{
|
||||||
|
client, e.content, "text/html; charset=utf8", e.code
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(e.code)
|
||||||
|
{
|
||||||
|
case http::BAD_REQUEST:
|
||||||
|
case http::REQUEST_TIMEOUT:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case http::INTERNAL_SERVER_ERROR:
|
||||||
|
throw;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error("client[%s]: in %ld$us: %s",
|
||||||
|
string(remote(client)),
|
||||||
|
client.request_timer.at<microseconds>().count(),
|
||||||
|
e.what());
|
||||||
|
|
||||||
|
resource::response
|
||||||
|
{
|
||||||
|
client, e.what(), {}, http::INTERNAL_SERVER_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::handle_request(client &client,
|
||||||
|
parse::capstan &pc,
|
||||||
|
const http::request::head &head)
|
||||||
|
{
|
||||||
|
log::debug("client[%s] HTTP %s `%s' (content-length: %zu)",
|
||||||
|
string(remote(client)),
|
||||||
|
head.method,
|
||||||
|
head.path,
|
||||||
|
head.content_length);
|
||||||
|
|
||||||
|
auto &resource
|
||||||
|
{
|
||||||
|
ircd::resource::find(head.path)
|
||||||
|
};
|
||||||
|
|
||||||
|
resource(client, pc, head);
|
||||||
|
}
|
||||||
|
|
||||||
ircd::resource &
|
ircd::resource &
|
||||||
ircd::resource::find(string_view path)
|
ircd::resource::find(string_view path)
|
||||||
|
@ -70,6 +160,10 @@ ircd::resource::find(string_view path)
|
||||||
return *it->second;
|
return *it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// resource
|
||||||
|
//
|
||||||
|
|
||||||
ircd::resource::resource(const string_view &path)
|
ircd::resource::resource(const string_view &path)
|
||||||
:resource
|
:resource
|
||||||
{
|
{
|
||||||
|
@ -278,6 +372,7 @@ try
|
||||||
}
|
}
|
||||||
catch(const std::out_of_range &e)
|
catch(const std::out_of_range &e)
|
||||||
{
|
{
|
||||||
|
//TODO: This will have to respond with an Accept header listing methods.
|
||||||
throw http::error
|
throw http::error
|
||||||
{
|
{
|
||||||
http::METHOD_NOT_ALLOWED
|
http::METHOD_NOT_ALLOWED
|
||||||
|
|
Loading…
Reference in a new issue