mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 13:27:33 +02:00
Advanced servers use HTTP parser interfaces for reading
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
Version 224:
|
Version 224:
|
||||||
|
|
||||||
* Remove extraneous error check in advanced-server-flex
|
* Remove extraneous error check in advanced-server-flex
|
||||||
|
* Advanced servers use HTTP parser interfaces for reading
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@@ -148,6 +148,7 @@ and illustrate the implementation of advanced features.
|
|||||||
[Timeouts]
|
[Timeouts]
|
||||||
[Multi-threaded]
|
[Multi-threaded]
|
||||||
[HTTP pipelining]
|
[HTTP pipelining]
|
||||||
|
[Parser-oriented HTTP reading]
|
||||||
[Dual protocols: HTTP and WebSocket]
|
[Dual protocols: HTTP and WebSocket]
|
||||||
[Clean exit via SIGINT (CTRL+C) or SIGTERM (kill)]
|
[Clean exit via SIGINT (CTRL+C) or SIGTERM (kill)]
|
||||||
]]
|
]]
|
||||||
@@ -158,6 +159,7 @@ and illustrate the implementation of advanced features.
|
|||||||
[Timeouts]
|
[Timeouts]
|
||||||
[Multi-threaded]
|
[Multi-threaded]
|
||||||
[HTTP pipelining]
|
[HTTP pipelining]
|
||||||
|
[Parser-oriented HTTP reading]
|
||||||
[Dual protocols: HTTP and WebSocket]
|
[Dual protocols: HTTP and WebSocket]
|
||||||
[Flexible ports: plain and SSL on the same port]
|
[Flexible ports: plain and SSL on the same port]
|
||||||
[Clean exit via SIGINT (CTRL+C) or SIGTERM (kill)]
|
[Clean exit via SIGINT (CTRL+C) or SIGTERM (kill)]
|
||||||
@@ -170,6 +172,7 @@ and illustrate the implementation of advanced features.
|
|||||||
[Broadcasting Messages]
|
[Broadcasting Messages]
|
||||||
[Multi-user Chat Server]
|
[Multi-user Chat Server]
|
||||||
[JavaScript Browser Client]
|
[JavaScript Browser Client]
|
||||||
|
[Parser-oriented HTTP reading]
|
||||||
[Dual protocols: HTTP and WebSocket]
|
[Dual protocols: HTTP and WebSocket]
|
||||||
[Clean exit via SIGINT (CTRL+C) or SIGTERM (kill)]
|
[Clean exit via SIGINT (CTRL+C) or SIGTERM (kill)]
|
||||||
]]
|
]]
|
||||||
|
@@ -305,6 +305,8 @@
|
|||||||
* ([issue 1401]) Examples use
|
* ([issue 1401]) Examples use
|
||||||
`flat_buffer`
|
`flat_buffer`
|
||||||
|
|
||||||
|
* Advanced servers use HTTP parser interfaces for reading
|
||||||
|
|
||||||
* detect-ssl is rewritten
|
* detect-ssl is rewritten
|
||||||
|
|
||||||
* New example [path_link example/websocket/server/chat-multi example/websocket/server/chat-multi]
|
* New example [path_link example/websocket/server/chat-multi example/websocket/server/chat-multi]
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/asio/strand.hpp>
|
#include <boost/asio/strand.hpp>
|
||||||
#include <boost/make_unique.hpp>
|
#include <boost/make_unique.hpp>
|
||||||
#include <boost/config.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -561,9 +561,12 @@ class http_session
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<std::string const> doc_root_;
|
std::shared_ptr<std::string const> doc_root_;
|
||||||
http::request<http::string_body> req_;
|
|
||||||
queue queue_;
|
queue queue_;
|
||||||
|
|
||||||
|
// The parser is stored in an optional container so we can
|
||||||
|
// construct it from scratch it at the beginning of each new message.
|
||||||
|
boost::optional<http::request_parser<http::string_body>> parser_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
beast::flat_buffer buffer_;
|
beast::flat_buffer buffer_;
|
||||||
|
|
||||||
@@ -581,19 +584,22 @@ public:
|
|||||||
void
|
void
|
||||||
do_read()
|
do_read()
|
||||||
{
|
{
|
||||||
// Make the request empty before reading,
|
// Construct a new parser for each message
|
||||||
// otherwise the operation behavior is undefined.
|
parser_.emplace();
|
||||||
req_ = {};
|
|
||||||
|
// Apply a reasonable limit to the allowed size
|
||||||
|
// of the body in bytes to prevent abuse.
|
||||||
|
parser_->body_limit(10000);
|
||||||
|
|
||||||
// Set the timeout.
|
// Set the timeout.
|
||||||
beast::get_lowest_layer(
|
beast::get_lowest_layer(
|
||||||
derived().stream()).expires_after(std::chrono::seconds(30));
|
derived().stream()).expires_after(std::chrono::seconds(30));
|
||||||
|
|
||||||
// Read a request
|
// Read a request using the parser-oriented interface
|
||||||
http::async_read(
|
http::async_read(
|
||||||
derived().stream(),
|
derived().stream(),
|
||||||
buffer_,
|
buffer_,
|
||||||
req_,
|
*parser_,
|
||||||
beast::bind_front_handler(
|
beast::bind_front_handler(
|
||||||
&http_session::on_read,
|
&http_session::on_read,
|
||||||
derived().shared_from_this()));
|
derived().shared_from_this()));
|
||||||
@@ -612,20 +618,21 @@ public:
|
|||||||
return fail(ec, "read");
|
return fail(ec, "read");
|
||||||
|
|
||||||
// See if it is a WebSocket Upgrade
|
// See if it is a WebSocket Upgrade
|
||||||
if(websocket::is_upgrade(req_))
|
if(websocket::is_upgrade(parser_->get()))
|
||||||
{
|
{
|
||||||
// Disable the timeout.
|
// Disable the timeout.
|
||||||
// The websocket::stream uses its own timeout settings.
|
// The websocket::stream uses its own timeout settings.
|
||||||
beast::get_lowest_layer(derived().stream()).expires_never();
|
beast::get_lowest_layer(derived().stream()).expires_never();
|
||||||
|
|
||||||
// Transfer the stream to a new WebSocket session.
|
// Create a websocket session, transferring ownership
|
||||||
|
// of both the socket and the HTTP request.
|
||||||
return make_websocket_session(
|
return make_websocket_session(
|
||||||
derived().release_stream(),
|
derived().release_stream(),
|
||||||
std::move(req_));
|
parser_->release());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response
|
// Send the response
|
||||||
handle_request(*doc_root_, std::move(req_), queue_);
|
handle_request(*doc_root_, parser_->release(), queue_);
|
||||||
|
|
||||||
// If we aren't at the queue limit, try to pipeline another request
|
// If we aren't at the queue limit, try to pipeline another request
|
||||||
if(! queue_.is_full())
|
if(! queue_.is_full())
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#include <boost/asio/signal_set.hpp>
|
#include <boost/asio/signal_set.hpp>
|
||||||
#include <boost/asio/strand.hpp>
|
#include <boost/asio/strand.hpp>
|
||||||
#include <boost/make_unique.hpp>
|
#include <boost/make_unique.hpp>
|
||||||
#include <boost/config.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -320,6 +320,8 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Handles an HTTP server connection
|
// Handles an HTTP server connection
|
||||||
class http_session : public std::enable_shared_from_this<http_session>
|
class http_session : public std::enable_shared_from_this<http_session>
|
||||||
{
|
{
|
||||||
@@ -416,9 +418,12 @@ class http_session : public std::enable_shared_from_this<http_session>
|
|||||||
beast::tcp_stream stream_;
|
beast::tcp_stream stream_;
|
||||||
beast::flat_buffer buffer_;
|
beast::flat_buffer buffer_;
|
||||||
std::shared_ptr<std::string const> doc_root_;
|
std::shared_ptr<std::string const> doc_root_;
|
||||||
http::request<http::string_body> req_;
|
|
||||||
queue queue_;
|
queue queue_;
|
||||||
|
|
||||||
|
// The parser is stored in an optional container so we can
|
||||||
|
// construct it from scratch it at the beginning of each new message.
|
||||||
|
boost::optional<http::request_parser<http::string_body>> parser_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Take ownership of the socket
|
// Take ownership of the socket
|
||||||
http_session(
|
http_session(
|
||||||
@@ -441,16 +446,21 @@ private:
|
|||||||
void
|
void
|
||||||
do_read()
|
do_read()
|
||||||
{
|
{
|
||||||
|
// Construct a new parser for each message
|
||||||
|
parser_.emplace();
|
||||||
|
|
||||||
// Make the request empty before reading,
|
// Apply a reasonable limit to the allowed size
|
||||||
// otherwise the operation behavior is undefined.
|
// of the body in bytes to prevent abuse.
|
||||||
req_ = {};
|
parser_->body_limit(10000);
|
||||||
|
|
||||||
// Set the timeout.
|
// Set the timeout.
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
stream_.expires_after(std::chrono::seconds(30));
|
||||||
|
|
||||||
// Read a request
|
// Read a request using the parser-oriented interface
|
||||||
http::async_read(stream_, buffer_, req_,
|
http::async_read(
|
||||||
|
stream_,
|
||||||
|
buffer_,
|
||||||
|
*parser_,
|
||||||
beast::bind_front_handler(
|
beast::bind_front_handler(
|
||||||
&http_session::on_read,
|
&http_session::on_read,
|
||||||
shared_from_this()));
|
shared_from_this()));
|
||||||
@@ -469,16 +479,17 @@ private:
|
|||||||
return fail(ec, "read");
|
return fail(ec, "read");
|
||||||
|
|
||||||
// See if it is a WebSocket Upgrade
|
// See if it is a WebSocket Upgrade
|
||||||
if(websocket::is_upgrade(req_))
|
if(websocket::is_upgrade(parser_->get()))
|
||||||
{
|
{
|
||||||
// Create a websocket session by transferring the socket
|
// Create a websocket session, transferring ownership
|
||||||
|
// of both the socket and the HTTP request.
|
||||||
std::make_shared<websocket_session>(
|
std::make_shared<websocket_session>(
|
||||||
stream_.release_socket())->do_accept(std::move(req_));
|
stream_.release_socket())->do_accept(parser_->release());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response
|
// Send the response
|
||||||
handle_request(*doc_root_, std::move(req_), queue_);
|
handle_request(*doc_root_, std::move(parser_->release()), queue_);
|
||||||
|
|
||||||
// If we aren't at the queue limit, try to pipeline another request
|
// If we aren't at the queue limit, try to pipeline another request
|
||||||
if(! queue_.is_full())
|
if(! queue_.is_full())
|
||||||
|
@@ -188,6 +188,40 @@ handle_request(
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct http_session::send_lambda
|
||||||
|
{
|
||||||
|
http_session& self_;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
send_lambda(http_session& self)
|
||||||
|
: self_(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
void
|
||||||
|
operator()(http::message<isRequest, Body, Fields>&& msg) const
|
||||||
|
{
|
||||||
|
// The lifetime of the message has to extend
|
||||||
|
// for the duration of the async operation so
|
||||||
|
// we use a shared_ptr to manage it.
|
||||||
|
auto sp = boost::make_shared<
|
||||||
|
http::message<isRequest, Body, Fields>>(std::move(msg));
|
||||||
|
|
||||||
|
// Write the response
|
||||||
|
auto self = self_.shared_from_this();
|
||||||
|
http::async_write(
|
||||||
|
self_.stream_,
|
||||||
|
*sp,
|
||||||
|
[self, sp](beast::error_code ec, std::size_t bytes)
|
||||||
|
{
|
||||||
|
self->on_write(ec, bytes, sp->need_eof());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
http_session::
|
http_session::
|
||||||
http_session(
|
http_session(
|
||||||
tcp::socket&& socket,
|
tcp::socket&& socket,
|
||||||
@@ -201,14 +235,7 @@ void
|
|||||||
http_session::
|
http_session::
|
||||||
run()
|
run()
|
||||||
{
|
{
|
||||||
// Set the timeout.
|
do_read();
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
|
||||||
|
|
||||||
// Read a request
|
|
||||||
http::async_read(stream_, buffer_, req_,
|
|
||||||
beast::bind_front_handler(
|
|
||||||
&http_session::on_read,
|
|
||||||
shared_from_this()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report a failure
|
// Report a failure
|
||||||
@@ -223,27 +250,28 @@ fail(beast::error_code ec, char const* what)
|
|||||||
std::cerr << what << ": " << ec.message() << "\n";
|
std::cerr << what << ": " << ec.message() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
|
||||||
void
|
void
|
||||||
http_session::
|
http_session::
|
||||||
send_lambda::
|
do_read()
|
||||||
operator()(http::message<isRequest, Body, Fields>&& msg) const
|
|
||||||
{
|
{
|
||||||
// The lifetime of the message has to extend
|
// Construct a new parser for each message
|
||||||
// for the duration of the async operation so
|
parser_.emplace();
|
||||||
// we use a shared_ptr to manage it.
|
|
||||||
auto sp = boost::make_shared<
|
|
||||||
http::message<isRequest, Body, Fields>>(std::move(msg));
|
|
||||||
|
|
||||||
// Write the response
|
// Apply a reasonable limit to the allowed size
|
||||||
auto self = self_.shared_from_this();
|
// of the body in bytes to prevent abuse.
|
||||||
http::async_write(
|
parser_->body_limit(10000);
|
||||||
self_.stream_,
|
|
||||||
*sp,
|
// Set the timeout.
|
||||||
[self, sp](beast::error_code ec, std::size_t bytes)
|
stream_.expires_after(std::chrono::seconds(30));
|
||||||
{
|
|
||||||
self->on_write(ec, bytes, sp->need_eof());
|
// Read a request
|
||||||
});
|
http::async_read(
|
||||||
|
stream_,
|
||||||
|
buffer_,
|
||||||
|
parser_->get(),
|
||||||
|
beast::bind_front_handler(
|
||||||
|
&http_session::on_read,
|
||||||
|
shared_from_this()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -262,12 +290,13 @@ on_read(beast::error_code ec, std::size_t)
|
|||||||
return fail(ec, "read");
|
return fail(ec, "read");
|
||||||
|
|
||||||
// See if it is a WebSocket Upgrade
|
// See if it is a WebSocket Upgrade
|
||||||
if(websocket::is_upgrade(req_))
|
if(websocket::is_upgrade(parser_->get()))
|
||||||
{
|
{
|
||||||
// Create a WebSocket session by transferring the socket
|
// Create a websocket session, transferring ownership
|
||||||
|
// of both the socket and the HTTP request.
|
||||||
boost::make_shared<websocket_session>(
|
boost::make_shared<websocket_session>(
|
||||||
stream_.release_socket(),
|
stream_.release_socket(),
|
||||||
state_)->run(std::move(req_));
|
state_)->run(parser_->release());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +344,7 @@ on_read(beast::error_code ec, std::size_t)
|
|||||||
//
|
//
|
||||||
handle_request(
|
handle_request(
|
||||||
state_->doc_root(),
|
state_->doc_root(),
|
||||||
std::move(req_),
|
parser_->release(),
|
||||||
send_lambda(*this));
|
send_lambda(*this));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -337,16 +366,6 @@ on_write(beast::error_code ec, std::size_t, bool close)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear contents of the request message,
|
|
||||||
// otherwise the read behavior is undefined.
|
|
||||||
req_ = {};
|
|
||||||
|
|
||||||
// Set the timeout.
|
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
|
||||||
|
|
||||||
// Read another request
|
// Read another request
|
||||||
http::async_read(stream_, buffer_, req_,
|
do_read();
|
||||||
beast::bind_front_handler(
|
|
||||||
&http_session::on_read,
|
|
||||||
shared_from_this()));
|
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
#include "net.hpp"
|
#include "net.hpp"
|
||||||
#include "beast.hpp"
|
#include "beast.hpp"
|
||||||
#include "shared_state.hpp"
|
#include "shared_state.hpp"
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include <boost/smart_ptr.hpp>
|
#include <boost/smart_ptr.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -24,24 +25,15 @@ class http_session : public boost::enable_shared_from_this<http_session>
|
|||||||
beast::tcp_stream stream_;
|
beast::tcp_stream stream_;
|
||||||
beast::flat_buffer buffer_;
|
beast::flat_buffer buffer_;
|
||||||
boost::shared_ptr<shared_state> state_;
|
boost::shared_ptr<shared_state> state_;
|
||||||
http::request<http::string_body> req_;
|
|
||||||
|
|
||||||
struct send_lambda
|
// The parser is stored in an optional container so we can
|
||||||
{
|
// construct it from scratch it at the beginning of each new message.
|
||||||
http_session& self_;
|
boost::optional<http::request_parser<http::string_body>> parser_;
|
||||||
|
|
||||||
explicit
|
struct send_lambda;
|
||||||
send_lambda(http_session& self)
|
|
||||||
: self_(self)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
|
||||||
void
|
|
||||||
operator()(http::message<isRequest, Body, Fields>&& msg) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
void fail(beast::error_code ec, char const* what);
|
void fail(beast::error_code ec, char const* what);
|
||||||
|
void do_read();
|
||||||
void on_read(beast::error_code ec, std::size_t);
|
void on_read(beast::error_code ec, std::size_t);
|
||||||
void on_write(beast::error_code ec, std::size_t, bool close);
|
void on_write(beast::error_code ec, std::size_t, bool close);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user