Beast.HTTP:

New classes are introduced to represent HTTP messages and their
associated bodies. The parser interface is reworked to use CRTP,
error codes, and trait checks.

New classes:

* basic_headers

  Models field/value pairs in a HTTP message.

* message

  Models a HTTP message, body behavior defined by template argument.
  Parsed message carries metadata generated during parsing.

* parser

  Produces parsed messages.

* empty_body, string_body, basic_streambuf_body

  Classes used to represent content bodies in various ways.

New functions:

* read, async_read, write, async_write

  Read and write HTTP messages on a socket.

New concepts:

* Body: Represents the HTTP Content-Body.
* Field: A HTTP header field.
* FieldSequence: A forward sequence of fields.
* Reader: Parses a Body from a stream of bytes.
* Writer: Serializes a Body to buffers.

basic_parser changes:

* add write methods which throw exceptions instead
* error_code passed via parameter instead of return value
* fold private member calls into existing callbacks
* basic_parser uses CRTP instead of virtual members
* add documentation on Derived requirements for CRTP

impl/http-parser changes:

* joyent renamed to nodejs to reflect upstream changes
This commit is contained in:
Vinnie Falco
2016-03-11 06:40:37 -05:00
parent f25b448a49
commit bcbe22c780
88 changed files with 6843 additions and 1867 deletions

View File

@@ -22,7 +22,6 @@
#include <ripple/server/Handoff.h>
#include <beast/asio/ssl_bundle.h>
#include <beast/http/message.h>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/error_code.hpp>
@@ -61,18 +60,18 @@ struct Handler
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) = 0;
virtual
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) = 0;
/** @} */
/** Called when we have a complete HTTP request. */
// VFALCO TODO Pass the beast::http::message as a parameter
// VFALCO TODO Pass the beast::deprecated_http::message as a parameter
virtual void onRequest (Session& session) = 0;
/** Called when the session ends.

View File

@@ -21,10 +21,15 @@
#define RIPPLE_SERVER_HANDOFF_H_INCLUDED
#include <ripple/server/Writer.h>
#include <beast/http/message.h>
#include <beast/http/streambuf_body.h>
#include <memory>
namespace ripple {
using http_request_type =
beast::http::request<beast::http::streambuf_body>;
/** Used to indicate the result of a server connection handoff. */
struct Handoff
{

View File

@@ -144,16 +144,16 @@ write(Streambuf& buf, Json::Value const& json)
*/
template <class = void>
std::shared_ptr<Writer>
make_JsonWriter (beast::http::message& m, Json::Value const& json)
make_JsonWriter (beast::deprecated_http::message& m, Json::Value const& json)
{
beast::streambuf prebody;
beast::streambuf body;
write(body, json);
// VFALCO TODO Better way to set a field
m.headers.erase ("Content-Length");
m.headers.append("Content-Length", std::to_string(body.size()));
m.headers.insert("Content-Length", std::to_string(body.size()));
m.headers.erase ("Content-Type");
m.headers.append("Content-Type", "application/json");
m.headers.insert("Content-Type", "application/json");
write(prebody, m);
return std::make_shared<streambufs_writer>(
std::move(prebody), std::move(body));

View File

@@ -88,7 +88,7 @@ public:
/** Fills in boilerplate HTTP header field values. */
static
void
appendStandardFields (beast::http::message& message);
appendStandardFields (beast::deprecated_http::message& message);
};
//------------------------------------------------------------------------------

View File

@@ -66,14 +66,9 @@ public:
/** Returns the current HTTP request. */
virtual
beast::http::message&
http_request_type&
request() = 0;
/** Returns the Content-Body of the current HTTP request. */
virtual
beast::http::body const&
body() = 0;
/** Send a copy of data asynchronously. */
/** @{ */
void

View File

@@ -28,8 +28,10 @@
#include <ripple/beast/net/IPAddressConversion.h>
#include <beast/asio/placeholders.h>
#include <beast/asio/ssl_error.h> // for is_short_read?
#include <beast/http/read.h>
#include <beast/http/message.h>
#include <beast/http/parser.h>
#include <beast/http/streambuf_body.h>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/streambuf.hpp>
@@ -95,8 +97,7 @@ protected:
std::size_t nid_;
boost::asio::streambuf read_buf_;
beast::http::message message_;
beast::http::body body_;
http_request_type message_;
std::vector<buffer> wq_;
std::vector<buffer> wq2_;
std::mutex mutex_;
@@ -186,18 +187,12 @@ protected:
return beast::IPAddressConversion::from_asio(remote_address_);
}
beast::http::message&
http_request_type&
request() override
{
return message_;
}
beast::http::body const&
body() override
{
return body_;
}
void
write (void const* buffer, std::size_t bytes) override;
@@ -319,64 +314,12 @@ void
BaseHTTPPeer<Impl>::do_read (yield_context yield)
{
complete_ = false;
error_code ec;
bool eof = false;
body_.clear();
beast::http::parser parser (message_, body_, true);
for(;;)
{
if (read_buf_.size() == 0)
{
start_timer();
auto const bytes_transferred = boost::asio::async_read (
impl().stream_, read_buf_.prepare (bufferSize),
boost::asio::transfer_at_least(1), yield[ec]);
cancel_timer();
eof = ec == boost::asio::error::eof;
if (eof)
{
ec = error_code{};
}
else if (! ec)
{
bytes_in_ += bytes_transferred;
read_buf_.commit (bytes_transferred);
}
}
if (! ec)
{
if (! eof)
{
// VFALCO TODO Currently parsing errors are treated the
// same as the connection dropping. Instead, we
// should request that the handler compose a proper HTTP error
// response. This requires refactoring HTTPReply() into
// something sensible.
std::size_t used;
std::tie (ec, used) = parser.write (read_buf_.data());
if (! ec)
read_buf_.consume (used);
}
else
{
ec = parser.write_eof();
}
}
if (! ec)
{
if (parser.complete())
return do_request();
if (eof)
ec = boost::asio::error::eof; // incomplete request
}
if (ec)
return fail (ec, "read");
}
beast::http::async_read(impl().stream_,
read_buf_, message_, yield[ec]);
// VFALCO What if the connection was closed?
cancel_timer();
do_request();
}
// Send everything in the write queue.
@@ -513,7 +456,7 @@ BaseHTTPPeer<Impl>::complete()
return strand_.post(std::bind (&BaseHTTPPeer<Impl>::complete,
impl().shared_from_this()));
message_ = beast::http::message{};
message_ = {};
complete_ = true;
{

View File

@@ -108,7 +108,7 @@ PlainHTTPPeer::do_request()
}
// Perform half-close when Connection: close and not SSL
if (! message_.keep_alive())
if (! is_keep_alive(message_))
stream_.shutdown (socket_type::shutdown_receive, ec);
if (ec)
return fail (ec, "request");

View File

@@ -19,7 +19,7 @@
#include <ripple/server/Port.h>
#include <beast/http/rfc2616.h>
#include <beast/module/core/text/LexicalCast.h>
#include <ripple/beast/core/LexicalCast.h>
namespace ripple {
@@ -169,7 +169,7 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
if (*port.port == 0)
Throw<std::exception> ();
}
catch (std::exception const& ex)
catch (std::exception const&)
{
log <<
"Invalid value '" << result.first << "' for key " <<
@@ -199,7 +199,7 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
port.limit = static_cast<int> (
beast::lexicalCastThrow<std::uint16_t>(lim));
}
catch (std::exception const& ex)
catch (std::exception const&)
{
log <<
"Invalid value '" << lim << "' for key " <<

View File

@@ -116,7 +116,7 @@ ServerHandlerImp::onAccept (Session& session,
auto
ServerHandlerImp::onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) ->
Handoff
{
@@ -138,7 +138,7 @@ ServerHandlerImp::onHandoff (Session& session,
auto
ServerHandlerImp::onHandoff (Session& session,
boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) ->
Handoff
{
@@ -163,6 +163,23 @@ Json::Output makeOutput (Session& session)
};
}
// HACK!
template<class Allocator>
static
std::map<std::string, std::string>
build_map(beast::http::headers<Allocator> const& h)
{
std::map <std::string, std::string> c;
for (auto const& e : h)
{
auto key (e.first);
// TODO Replace with safe C++14 version
std::transform (key.begin(), key.end(), key.begin(), ::tolower);
c [key] = e.second;
}
return c;
}
void
ServerHandlerImp::onRequest (Session& session)
{
@@ -207,12 +224,32 @@ ServerHandlerImp::onStopped (Server&)
//------------------------------------------------------------------------------
template<class ConstBufferSequence>
static
std::string
buffers_to_string(ConstBufferSequence const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
// Run as a couroutine.
void
ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
std::shared_ptr<JobCoro> jobCoro)
{
processRequest (session->port(), to_string (session->body()),
processRequest (session->port(), buffers_to_string(session->request().body.data()),
session->remoteAddress().at_port (0), makeOutput (*session), jobCoro,
[&]
{
@@ -233,7 +270,7 @@ ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
return std::string{};
}());
if (session->request().keep_alive())
if(is_keep_alive(session->request()))
session->complete();
else
session->close (true);
@@ -425,9 +462,9 @@ ServerHandlerImp::processRequest (Port const& port,
// Returns `true` if the HTTP request is a Websockets Upgrade
// http://en.wikipedia.org/wiki/HTTP/1.1_Upgrade_header#Use_with_WebSockets
bool
ServerHandlerImp::isWebsocketUpgrade (beast::http::message const& request)
ServerHandlerImp::isWebsocketUpgrade (http_request_type const& request)
{
if (request.upgrade())
if (is_upgrade(request))
return request.headers["Upgrade"] == "websocket";
return false;
}
@@ -457,7 +494,7 @@ ServerHandlerImp::authorized (Port const& port,
//------------------------------------------------------------------------------
void
ServerHandler::appendStandardFields (beast::http::message& message)
ServerHandler::appendStandardFields (beast::deprecated_http::message& message)
{
}

View File

@@ -95,12 +95,12 @@ private:
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override;
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override;
void
onRequest (Session& session) override;
@@ -126,7 +126,7 @@ private:
private:
bool
isWebsocketUpgrade (beast::http::message const& request);
isWebsocketUpgrade (http_request_type const& request);
bool
authorized (Port const& port,

View File

@@ -107,7 +107,7 @@ public:
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};
@@ -115,7 +115,7 @@ public:
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};
@@ -125,7 +125,7 @@ public:
onRequest (Session& session) override
{
session.write (std::string ("Hello, world!\n"));
if (session.request().keep_alive())
if (is_keep_alive(session.request()))
session.complete();
else
session.close (true);
@@ -309,7 +309,7 @@ public:
Handoff
onHandoff (Session& session,
std::unique_ptr <beast::asio::ssl_bundle>&& bundle,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};
@@ -317,7 +317,7 @@ public:
Handoff
onHandoff (Session& session, boost::asio::ip::tcp::socket&& socket,
beast::http::message&& request,
http_request_type&& request,
boost::asio::ip::tcp::endpoint remote_address) override
{
return Handoff{};