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.
This commit is contained in:
Vinnie Falco
2014-10-29 13:22:57 -07:00
parent c1a5e88752
commit acaa1098f7
10 changed files with 92 additions and 37 deletions

View File

@@ -50,17 +50,16 @@ public:
body (body const&) = delete;
body& operator= (body const&) = delete;
template <class = void>
void
clear();
void
write (void const* data, std::size_t bytes);
template <class ConstBufferSequence>
void
write (ConstBufferSequence const& buffers)
{
for (auto const& buffer : buffers)
write (boost::asio::buffer_cast <void const*> (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 <class>
void
body::clear()
{
buf_ = std::make_unique <buffer_type>();
}
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 <class ConstBufferSequence>
void
body::write (ConstBufferSequence const& buffers)
{
for (auto const& buffer : buffers)
write (boost::asio::buffer_cast <void const*> (buffer),
boost::asio::buffer_size (buffer));
}
inline
std::size_t
body::size() const

View File

@@ -21,7 +21,6 @@
#define BEAST_HTTP_MESSAGE_H_INCLUDED
#include <beast/http/basic_parser.h>
#include <beast/http/body.h>
#include <beast/http/method.h>
#include <beast/http/headers.h>
#include <beast/utility/ci_char_traits.h>
@@ -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 <class AsioStreamBuf>
template <class Streambuf>
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 <class AsioStreamBuf>
template <class Streambuf>
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 <class AsioStreamBuf>
template <class Streambuf>
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
#endif

View File

@@ -21,6 +21,8 @@
#define BEAST_HTTP_PARSER_H_INCLUDED
#include <beast/http/message.h>
#include <beast/http/body.h>
#include <functional>
#include <string>
#include <utility>
@@ -34,15 +36,33 @@ class parser : public beast::http::basic_parser
{
private:
std::reference_wrapper <message> message_;
std::function<void(void const*, std::size_t)> 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<void(void const*, std::size_t)> 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 <class>
void
parser::do_start ()
parser::do_start()
{
}
@@ -176,7 +196,7 @@ template <class>
void
parser::do_body (void const* data, std::size_t bytes)
{
message_.get().body.write (data, bytes);
write_body_(data, bytes);
}
template <class>

View File

@@ -20,6 +20,7 @@
#include <beast/http/message.h>
#include <beast/http/parser.h>
#include <beast/unit_test/suite.h>
#include <utility>
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");

View File

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

View File

@@ -20,6 +20,7 @@
#ifndef RIPPLE_HTTP_SESSION_H_INCLUDED
#define RIPPLE_HTTP_SESSION_H_INCLUDED
#include <beast/http/body.h>
#include <beast/http/message.h>
#include <beast/net/IPEndpoint.h>
#include <beast/utility/Journal.h>
@@ -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. */
/** @{ */

View File

@@ -103,6 +103,7 @@ protected:
boost::asio::streambuf read_buf_;
beast::http::message message_;
beast::http::body body_;
std::list <buffer> 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<Impl>::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)

View File

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

View File

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

View File

@@ -173,6 +173,7 @@ private:
beast::asio::streambuf read_buffer_;
boost::optional <beast::http::message> http_message_;
boost::optional <beast::http::parser> http_parser_;
beast::http::body http_body_;
message_stream message_stream_;
beast::asio::streambuf write_buffer_;