mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 18:45:52 +00:00
213 lines
7.4 KiB
Plaintext
213 lines
7.4 KiB
Plaintext
[/
|
|
Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
|
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
]
|
|
|
|
[section:http HTTP]
|
|
|
|
Beast.HTTP offers programmers simple and performant models of HTTP messages and
|
|
their associated operations including synchronous and asynchronous reading and
|
|
writing using Boost.Asio.
|
|
|
|
The HTTP protocol is described fully in
|
|
[@https://tools.ietf.org/html/rfc2616 rfc2616]
|
|
|
|
|
|
|
|
[section:motivation Motivation]
|
|
|
|
The HTTP protocol is pervasive in network applications. As C++ is a logical
|
|
choice for high performance network servers, there is great utility in solid
|
|
building blocks for manipulating, sending, and receiving HTTP messages
|
|
compliant with the Hypertext Transfer Protocol and the supplements that
|
|
follow. Unfortunately reliable implementations or industry standards do not
|
|
exist in C++.
|
|
|
|
Beast.HTTP is built on Boost.Asio and uses HTTP parser from NodeJS, which is
|
|
extensively field tested and exceptionally robust. A proposal to add networking
|
|
functionality to the C++ standard library, based on Boost.Asio, is under
|
|
consideration by the standards committee. Since the final approved networking
|
|
interface for the C++ standard library will likely closely resemble the current
|
|
interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its
|
|
network transport.
|
|
|
|
[note The documentation which follows assumes familiarity with
|
|
both Boost.Asio and the HTTP protocol specification described in
|
|
[@https://tools.ietf.org/html/rfc2616 rfc2616] ]
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:example Example]
|
|
|
|
All examples and identifiers mentioned in this document are written as
|
|
if the following declarations are in effect:
|
|
```
|
|
#include <boost/asio.hpp>
|
|
#include <beast/http.hpp>
|
|
using namespace beast;
|
|
using namespace boost::asio;
|
|
```
|
|
|
|
Create a HTTP request:
|
|
```
|
|
http::request<http::empty_body> req({http::method_t::http_get, "/", 11});
|
|
req.headers.insert("Host", "127.0.0.1:80");
|
|
req.headers.insert("User-Agent", "Beast.HTTP");
|
|
|
|
```
|
|
|
|
When sending a message, the Content-Length and Transfer-Encoding are set
|
|
automatically based on the properties of the body. Depending on the HTTP
|
|
version of the message and properties of the message body, the implementation
|
|
will automaticaly chunk-encode the data sent on the wire:
|
|
```
|
|
ip::tcp::socket sock(ios);
|
|
...
|
|
http::response<http::string_body> resp({200, "OK", 11});
|
|
resp.headers.insert("Server", "Beast.HTTP");
|
|
resp.body = "The command was successful";
|
|
|
|
write(sock, resp);
|
|
}
|
|
```
|
|
|
|
Receiving a message is simple, declare a value of type message and call `read`.
|
|
This example reads a message, builds a response, and sends it.
|
|
```
|
|
void handle_connection(ip::tcp::socket& sock)
|
|
{
|
|
request<string_body> req;
|
|
read(sock, req);
|
|
response<string_body> resp;
|
|
...
|
|
write(sock, resp);
|
|
}
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:message Message model]
|
|
|
|
A HTTP message (referred to hereafter as "message") contains a request or
|
|
response line, a series of zero or more name/value pairs (collectively
|
|
termed "headers"), and a series of octets called the message body which may
|
|
be zero in length. Applications using HTTP often perform these operations
|
|
on messages:
|
|
|
|
* [*Parse] a new message from a series of octets.
|
|
|
|
* [*Assemble] a new message from scratch or from an existing message.
|
|
|
|
* [*Serialize] a message into a series of octets.
|
|
|
|
* [*Read] a message from a stream. This can be thought of as a compound
|
|
operation; a network read, followed by a [*parse].
|
|
|
|
* [*Write] a message to a stream. This can be thought of as a compound
|
|
operation: a [*serialize] followed by a network write.
|
|
|
|
The spectrum of hardware and software platforms which perform these typical
|
|
HTTP operations is vast, ranging from powerful servers in large datacenters
|
|
to tiny resource-limited embedded devices. No single concrete implementation
|
|
of a class intended to model messages can efficiently serve all needs.
|
|
For example, an object that minimizes resources during parsing may not be
|
|
able to edit and change headers dynamically. A message that represents the
|
|
message body as a disk file may support sending but not parsing. Many efficient
|
|
and correct models of messages exist, supporting some or all of the
|
|
operations listed above.
|
|
|
|
To support a variety of implementation strategies, we introduce customization
|
|
points for the header and body via class template arguments. This illustration
|
|
shows the message class template (boilerplate present in the actual
|
|
declaration has been removed for clarity):
|
|
|
|
[$images/message.png [width 580px] [height 225px]]
|
|
|
|
The default constructor, move special members, and copy special members are
|
|
all defaulted. A message is movable, copyable, or default constructible based
|
|
on the capabilities of its template arguments.
|
|
|
|
Messages modeled in this fashion are ['complete], containing all of the
|
|
information required to perform the supported set of operations. They are
|
|
['first-class types], returnable from functions and composable. HTTP
|
|
requests and responses are distinct types, allowing functions to be
|
|
overloaded on the type of message.
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:body Body parameter]
|
|
|
|
The `Body` template argument in the `message` class must meet the
|
|
[link beast.types.Body [*`Body`] requirements]. It provides customization
|
|
of the data member in the message, the algorithm for parsing, and the
|
|
algorithm for serialization:
|
|
|
|
[$images/body.png [width 510px] [height 210px]]
|
|
|
|
The following types, provided by the library, meet the `Body` requirements:
|
|
|
|
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
|
|
Used in GET requests where there is no message body.
|
|
|
|
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
|
|
`value_type` of `std::string`. Useful for quickly putting together a request
|
|
or response with simple text in the message body (such as an error message).
|
|
Subject to the performance limit of strings if large amounts of data are
|
|
inserted.
|
|
|
|
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
|
|
`value_type` of `streambuf`. A streambuf is an efficient storage object which
|
|
uses multiple octet arrays of varying lengths to represent data.
|
|
|
|
Instances of the optional nested types `writer` and `reader` perform
|
|
serialization and deserialization of the message body. If either or
|
|
both of these types are present, the message becomes serializable, parsable,
|
|
or both. They model [link beast.types.Reader [*`Reader`]] and
|
|
[link beast.types.Writer [*`Writer`]] respectively.
|
|
|
|
For specialized applications, users may implement their own types which
|
|
meet the requirements. The examples included with this library provide a
|
|
Body implementation used to serve files in a HTTP server.
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:headers Headers container]
|
|
|
|
The `Headers` type represents the field/value pairs present in every HTTP
|
|
message. These types implement the
|
|
[link beast.types.FieldSequence [*`FieldSequence`]]
|
|
concept. The value type of a field sequence is an object meeting the
|
|
requirements of [link beast.types.Field [*`Field`]]. The implementation can
|
|
serialize any instance of `Headers` that meets the field sequence requirements.
|
|
This example shows a function which returns `true` if the specified field
|
|
sequence has a connect field:
|
|
```
|
|
template<class FieldSequence>
|
|
bool
|
|
has_connect(FieldSequence const& fs)
|
|
{
|
|
return std::find_if(fs.begin(), fs.end(),
|
|
[&](auto const& field)
|
|
{
|
|
return ci_equal(field.name(), "Connect");
|
|
});
|
|
}
|
|
```
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[endsect]
|
|
|