From acaa1098f7d9fc0d81a50d3801a6624e40984571 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 29 Oct 2014 13:22:57 -0700 Subject: [PATCH] Separate beast::http::body from beast::http::message (RIPD-660): This changes the http::message object to no longer contain a body. It modifies the parser to store the body in a separate object, or to pass the body data to a functor. This allows the body to be stored in more flexible ways. For example, in HTTP responses the body can be generated procedurally instead of being required to exist entirely in memory at once. --- src/beast/beast/http/body.h | 31 +++++++++++++++++----- src/beast/beast/http/message.h | 20 ++++++-------- src/beast/beast/http/parser.h | 28 ++++++++++++++++--- src/beast/beast/http/tests/parser.test.cpp | 10 ++++--- src/ripple/app/main/ServerHandlerImp.cpp | 7 +++-- src/ripple/http/Session.h | 10 +++++-- src/ripple/http/impl/Peer.h | 12 +++++++-- src/ripple/http/tests/Server.test.cpp | 2 +- src/ripple/overlay/impl/PeerImp.cpp | 8 ++++-- src/ripple/overlay/impl/PeerImp.h | 1 + 10 files changed, 92 insertions(+), 37 deletions(-) diff --git a/src/beast/beast/http/body.h b/src/beast/beast/http/body.h index 16bf77db0..69ac0b85a 100644 --- a/src/beast/beast/http/body.h +++ b/src/beast/beast/http/body.h @@ -50,17 +50,16 @@ public: body (body const&) = delete; body& operator= (body const&) = delete; + template + void + clear(); + void write (void const* data, std::size_t bytes); template void - write (ConstBufferSequence const& buffers) - { - for (auto const& buffer : buffers) - write (boost::asio::buffer_cast (buffer), - boost::asio::buffer_size (buffer)); - } + write (ConstBufferSequence const& buffers); std::size_t size() const; @@ -92,8 +91,9 @@ body::body() inline body::body (body&& other) + : buf_ (std::move(other.buf_)) { - buf_ = std::move(other.buf_); + other.clear(); } inline @@ -101,9 +101,17 @@ body& body::operator= (body&& other) { buf_ = std::move(other.buf_); + other.clear(); return *this; } +template +void +body::clear() +{ + buf_ = std::make_unique (); +} + inline void body::write (void const* data, std::size_t bytes) @@ -112,6 +120,15 @@ body::write (void const* data, std::size_t bytes) boost::asio::const_buffers_1 (data, bytes))); } +template +void +body::write (ConstBufferSequence const& buffers) +{ + for (auto const& buffer : buffers) + write (boost::asio::buffer_cast (buffer), + boost::asio::buffer_size (buffer)); +} + inline std::size_t body::size() const diff --git a/src/beast/beast/http/message.h b/src/beast/beast/http/message.h index 9f0ec6a66..66f398784 100644 --- a/src/beast/beast/http/message.h +++ b/src/beast/beast/http/message.h @@ -21,7 +21,6 @@ #define BEAST_HTTP_MESSAGE_H_INCLUDED #include -#include #include #include #include @@ -72,9 +71,8 @@ public: #endif - // Memberspaces + // Memberspace beast::http::headers headers; - beast::http::body body; bool request() const @@ -213,7 +211,6 @@ message::message (message&& other) , keep_alive_ (other.keep_alive_) , upgrade_ (other.upgrade_) , headers (std::move(other.headers)) - , body (std::move(other.body)) { } @@ -230,33 +227,32 @@ message::operator= (message&& other) keep_alive_ = other.keep_alive_; upgrade_ = other.upgrade_; headers = std::move(other.headers); - body = std::move(other.body); return *this; } #endif //------------------------------------------------------------------------------ -template +template void -write (AsioStreamBuf& stream, std::string const& s) +write (Streambuf& stream, std::string const& s) { stream.commit (boost::asio::buffer_copy ( stream.prepare (s.size()), boost::asio::buffer(s))); } -template +template void -write (AsioStreamBuf& stream, char const* s) +write (Streambuf& stream, char const* s) { auto const len (::strlen(s)); stream.commit (boost::asio::buffer_copy ( stream.prepare (len), boost::asio::buffer (s, len))); } -template +template void -write (AsioStreamBuf& stream, message const& m) +write (Streambuf& stream, message const& m) { if (m.request()) { @@ -311,4 +307,4 @@ to_string (message const& m) } // http } // beast -#endif \ No newline at end of file +#endif diff --git a/src/beast/beast/http/parser.h b/src/beast/beast/http/parser.h index 769f73d92..8d0e8ad81 100644 --- a/src/beast/beast/http/parser.h +++ b/src/beast/beast/http/parser.h @@ -21,6 +21,8 @@ #define BEAST_HTTP_PARSER_H_INCLUDED #include +#include +#include #include #include @@ -34,15 +36,33 @@ class parser : public beast::http::basic_parser { private: std::reference_wrapper message_; + std::function write_body_; public: /** Construct a parser for HTTP request or response. - The result is stored in the passed message. + The headers plus request or status line are stored in message. + The content-body, if any, is passed as a series of calls to + the write_body function. Transfer encodings are applied before + any data is passed to the write_body function. */ - parser (message& m, bool request) + parser (std::function write_body, + message& m, bool request) + : beast::http::basic_parser (request) + , message_(m) + , write_body_(std::move(write_body)) + { + message_.get().request(request); + } + + parser (message& m, body& b, bool request) : beast::http::basic_parser (request) , message_(m) { + write_body_ = [&b](void const* data, std::size_t size) + { + b.write(data, size); + }; + message_.get().request(request); } @@ -135,7 +155,7 @@ parser::operator= (parser&& other) template void -parser::do_start () +parser::do_start() { } @@ -176,7 +196,7 @@ template void parser::do_body (void const* data, std::size_t bytes) { - message_.get().body.write (data, bytes); + write_body_(data, bytes); } template diff --git a/src/beast/beast/http/tests/parser.test.cpp b/src/beast/beast/http/tests/parser.test.cpp index d81946383..c8e8ace4f 100644 --- a/src/beast/beast/http/tests/parser.test.cpp +++ b/src/beast/beast/http/tests/parser.test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace beast { namespace http { @@ -31,7 +32,8 @@ public: request (std::string const& text) { message m; - parser p (m, true); + body b; + parser p (m, b, true); auto result (p.write (boost::asio::buffer(text))); p.write_eof(); return std::make_pair (std::move(m), result.first); @@ -65,7 +67,8 @@ public: "\r\n" ; message m; - parser p (m, true); + body b; + parser p (m, b, true); auto result (p.write (boost::asio::buffer(text))); expect (! result.first); auto result2 (p.write_eof()); @@ -80,7 +83,8 @@ public: "\r\n" ; message m; - parser p (m, true); + body b; + parser p (m, b, true); auto result = p.write (boost::asio::buffer(text)); if (expect (result.first)) expect (result.first.message() == "invalid HTTP method"); diff --git a/src/ripple/app/main/ServerHandlerImp.cpp b/src/ripple/app/main/ServerHandlerImp.cpp index 89a6f4191..2ca7232f9 100644 --- a/src/ripple/app/main/ServerHandlerImp.cpp +++ b/src/ripple/app/main/ServerHandlerImp.cpp @@ -113,7 +113,7 @@ void ServerHandlerImp::onRequest (HTTP::Session& session) { // Check user/password authorization - auto const headers (build_map (session.message().headers)); + auto const headers = build_map (session.request().headers); if (! HTTPAuthorized (headers)) { session.write (HTTPReply (403, "Forbidden")); @@ -146,11 +146,10 @@ ServerHandlerImp::onStopped (HTTP::Server&) void ServerHandlerImp::processSession (Job& job, HTTP::Session& session) { - auto const s (to_string(session.message().body)); - session.write (processRequest (to_string(session.message().body), + session.write (processRequest (to_string(session.body()), session.remoteAddress().at_port(0))); - if (session.message().keep_alive()) + if (session.request().keep_alive()) { session.complete(); } diff --git a/src/ripple/http/Session.h b/src/ripple/http/Session.h index 17f671322..7889b7d3c 100644 --- a/src/ripple/http/Session.h +++ b/src/ripple/http/Session.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_HTTP_SESSION_H_INCLUDED #define RIPPLE_HTTP_SESSION_H_INCLUDED +#include #include #include #include @@ -59,10 +60,15 @@ public: beast::IP::Endpoint remoteAddress() = 0; - /** Returns the currently known set of headers. */ + /** Returns the current HTTP request. */ virtual beast::http::message& - message() = 0; + request() = 0; + + /** Returns the Content-Body of the current HTTP request. */ + virtual + beast::http::body const& + body() = 0; /** Send a copy of data asynchronously. */ /** @{ */ diff --git a/src/ripple/http/impl/Peer.h b/src/ripple/http/impl/Peer.h index fcaf68509..112a0229c 100644 --- a/src/ripple/http/impl/Peer.h +++ b/src/ripple/http/impl/Peer.h @@ -103,6 +103,7 @@ protected: boost::asio::streambuf read_buf_; beast::http::message message_; + beast::http::body body_; std::list write_queue_; std::mutex mutex_; bool graceful_ = false; @@ -181,11 +182,17 @@ protected: } beast::http::message& - message() override + request() override { return message_; } + beast::http::body const& + body() override + { + return body_; + } + void write (void const* buffer, std::size_t bytes) override; @@ -481,7 +488,8 @@ Peer::do_read (boost::asio::yield_context yield) error_code ec; bool eof = false; - beast::http::parser parser (message_, true); + body_.clear(); + beast::http::parser parser (message_, body_, true); for(;;) { if (read_buf_.size() == 0) diff --git a/src/ripple/http/tests/Server.test.cpp b/src/ripple/http/tests/Server.test.cpp index 62deafdb9..ff90feb80 100644 --- a/src/ripple/http/tests/Server.test.cpp +++ b/src/ripple/http/tests/Server.test.cpp @@ -66,7 +66,7 @@ public: onRequest (Session& session) override { session.write (std::string ("Hello, world!\n")); - if (session.message().keep_alive()) + if (session.request().keep_alive()) session.complete(); else session.close (true); diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index 01b73e92f..3af6d4049 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -413,7 +413,9 @@ PeerImp::on_write_http_request (error_code ec, std::size_t bytes_transferred) { // done sending request, now read the response http_message_ = boost::in_place (); - http_parser_ = boost::in_place (std::ref(*http_message_), false); + http_body_.clear(); + http_parser_ = boost::in_place (std::ref(*http_message_), + std::ref(http_body_), false); on_read_http_response (error_code(), 0); return; } @@ -559,7 +561,9 @@ PeerImp::on_read_http_detect (error_code ec, std::size_t bytes_transferred) else if (! is_peer_protocol) { http_message_ = boost::in_place (); - http_parser_ = boost::in_place (std::ref(*http_message_), true); + http_body_.clear(); + http_parser_ = boost::in_place (std::ref(*http_message_), + std::ref(http_body_), true); on_read_http_request (error_code(), 0); return; } diff --git a/src/ripple/overlay/impl/PeerImp.h b/src/ripple/overlay/impl/PeerImp.h index 1846a27b5..918aa0cad 100644 --- a/src/ripple/overlay/impl/PeerImp.h +++ b/src/ripple/overlay/impl/PeerImp.h @@ -173,6 +173,7 @@ private: beast::asio::streambuf read_buffer_; boost::optional http_message_; boost::optional http_parser_; + beast::http::body http_body_; message_stream message_stream_; beast::asio::streambuf write_buffer_;