mirror of
https://github.com/boostorg/beast.git
synced 2025-06-25 03:51:36 +02:00
368 lines
13 KiB
Plaintext
368 lines
13 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.
|
|
|
|
[heading Scope]
|
|
|
|
The scope of this library is meant to include only the functionality of
|
|
modeling the HTTP message, serializing and deserializing the message, and
|
|
sending and receiving messages on sockets or streams. It is designed to
|
|
be a building block for creating higher level abstractions.
|
|
|
|
[note The documentation which follows assumes familiarity with
|
|
both Boost.Asio and the HTTP protocol specification described in
|
|
[@https://tools.ietf.org/html/rfc7230 rfc7230] ]
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:usage Usage]
|
|
|
|
[note
|
|
Sample code and identifiers mentioned in this section are written
|
|
as if the following declarations are in effect:
|
|
```
|
|
#include <beast/http.hpp>
|
|
using namespace beast;
|
|
```
|
|
]
|
|
|
|
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. The HTTP protocol defines the client and server roles:
|
|
clients send messages called requests and servers send back messages called
|
|
responses. `http::message` models both requests and responses. The library
|
|
provides interfaces to 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.
|
|
|
|
In the paragraphs that follow we describe simple interfaces that will serve
|
|
the majority of users looking merely to interact with a HTTP server, or
|
|
handle simple HTTP requests from clients. Subsequent sections cover the
|
|
message model in more depth, for advanced applications.
|
|
|
|
[heading Declarations]
|
|
|
|
To do anything, a message must be declared. The message class template
|
|
requires at mininum, a bool indicating whether the message is a request
|
|
(versus a response), and a `Body` type. The choice of `Body` determines the
|
|
kind of container used to represent the message body. Here we will
|
|
declare a request that has a `std::string` for the body container:
|
|
```
|
|
http::message<true, http::string_body> req;
|
|
```
|
|
|
|
Two type aliases are provided for notational convenience when declaring
|
|
messages. These two statements declare a request and a response respectively:
|
|
```
|
|
http::request<http::string_body> req;
|
|
http::response<http::string_body> resp;
|
|
```
|
|
|
|
[heading Members]
|
|
|
|
Message objects are default constructible, with public access to data members.
|
|
Request and response objects have some common members, and some members unique
|
|
to the message type. These statements set all the members in each message:
|
|
```
|
|
http::request<http::string_body> req;
|
|
req.method = http::method_t::http_get;
|
|
req.url = "/index.html";
|
|
req.version = 11; // HTTP/1.1
|
|
req.headers.insert("User-Agent", "hello_world");
|
|
req.body = "";
|
|
|
|
http::response<http::string_body> resp;
|
|
resp.status = 404;
|
|
resp.reason = "Not Found";
|
|
resp.version = 10; // HTTP/1.0
|
|
resp.headers.insert("Server", "Beast.HTTP");
|
|
resp.body = "The requested resource was not found.";
|
|
```
|
|
|
|
The following statements achieve the same effects as the statements above:
|
|
```
|
|
http::request<http::string_body> req({http::method_t::http_get, "/index.html", 11});
|
|
req.headers.insert("User-Agent", "hello_world");
|
|
req.body = "";
|
|
|
|
http::response<http::string_body> resp({404, "Not Found", 10});
|
|
resp.headers.insert("Server", "Beast.HTTP");
|
|
resp.body = "The requested resource was not found.";
|
|
```
|
|
|
|
[heading Headers]
|
|
|
|
The `message::headers` member is a container for setting the field/value
|
|
pairs in the message. These statements change the values of the headers
|
|
in the message passed:
|
|
```
|
|
template<class Body>
|
|
void set_fields(http::request<Body>& req)
|
|
{
|
|
if(! req.exists("User-Agent"))
|
|
req.insert("User-Agent", "myWebClient");
|
|
|
|
if(req.exists("Accept-Charset"))
|
|
req.erase("Accept-Charset");
|
|
|
|
req.replace("Accept", "text/plain");
|
|
}
|
|
```
|
|
|
|
[heading Body]
|
|
|
|
The `message::body` member represents the message body. Depending on the
|
|
`Body` template argument type, this could be a writable container. The
|
|
following types, provided by the library, are suitable choices for the
|
|
`Body` type:
|
|
|
|
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
|
|
Used in GET requests where there is no message body. Example:
|
|
```
|
|
http::request<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
|
|
```
|
|
|
|
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
|
|
`value_type` as `std::string`. Useful for quickly putting together a request
|
|
or response with simple text in the message body (such as an error message).
|
|
Has the same insertion complexity of `std::string`. This is the type of body
|
|
used in the examples:
|
|
```
|
|
http::response<http::string_body> resp;
|
|
static_assert(std::is_same<decltype(resp.body), std::string>::value);
|
|
resp.body = "Here is the data you requested";
|
|
```
|
|
|
|
* [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. Here is
|
|
a body that uses a `boost::asio::streambuf` as its container:
|
|
```
|
|
template<class ConstBufferSequence>
|
|
http::response<http::basic_streambuf_body<boost::asio::streambuf>>
|
|
make_response(ConstBufferSequence const& buffers)
|
|
{
|
|
http::response<http::streambuf_body<boost::asio::streambuf>> resp;
|
|
resp.body.commit(boost::asio::buffer_copy(resp.body.prepare(
|
|
boost::asio::buffer_size(buffers)), buffers));
|
|
return resp;
|
|
}
|
|
```
|
|
|
|
[heading Sockets]
|
|
|
|
The library provides simple free functions modeled after Boost.Asio to
|
|
send and receive messages on TCP/IP sockets, SSL streams, or any object
|
|
which meets the Boost.Asio type requirements (SyncReadStream, SyncWriteStream,
|
|
AsyncReadStream, and AsyncWriteStream depending on the types of operations
|
|
performed). To send messages synchronously, use one of the `http:write`
|
|
functions:
|
|
```
|
|
void send_request(boost::asio::ip::tcp::socket& sock)
|
|
{
|
|
http::request<http::empty_body> req({http::method_t::http_get, "/index.html", 11});
|
|
...
|
|
http::write(sock, req); // Throws exception on error
|
|
...
|
|
// Alternatively
|
|
boost::system::error:code ec;
|
|
http::write(sock, req, ec);
|
|
if(ec)
|
|
std::cerr << "error writing http message: " << ec.message();
|
|
}
|
|
```
|
|
|
|
An asynchronous interface is available:
|
|
```
|
|
void handle_write(boost::system::error_code);
|
|
...
|
|
http::request<http::empty_body> req;
|
|
...
|
|
http::async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
|
|
```
|
|
|
|
When the implementation reads messages from a socket, it can read bytes lying
|
|
after the end of the message if they are present (the alternative is to read
|
|
a single byte at a time which is unsuitable for performance reasons). To
|
|
store and re-use these extra bytes on subsequent messages, the read interface
|
|
requires an additional paramter: a [link beast.types.Streambuf [*`Streambuf`]]
|
|
object. This example reads a message from the socket, with the extra bytes
|
|
stored in the streambuf parameter for use in a subsequent call to read:
|
|
```
|
|
boost::asio::streambuf sb;
|
|
...
|
|
http::response<http::string_body> resp;
|
|
http::read(sock, sb, resp); // Throws exception on error
|
|
...
|
|
// Alternatively
|
|
boost::system::error:code ec;
|
|
http::read(sock, sb, resp, ec);
|
|
if(ec)
|
|
std::cerr << "error reading http message: " << ec.message();
|
|
```
|
|
|
|
As with the write function, an asynchronous interface is available. The
|
|
stream buffer parameter must remain valid until the completion handler is
|
|
called:
|
|
```
|
|
void handle_read(boost::system::error_code);
|
|
...
|
|
boost::asio::streambuf sb;
|
|
http::response<http::string_body> resp;
|
|
...
|
|
http::async_read(sock, resp, std::bind(&handle_read, std::placeholders::_1));
|
|
```
|
|
|
|
An alternative to using a `boost::asio::streambuf` is to use a
|
|
[link beast.ref.streambuf `beast::streambuf`], which meets the requirements of
|
|
[*`Streambuf`] and is optimized for performance:
|
|
```
|
|
void handle_read(boost::system::error_code);
|
|
...
|
|
beast::streambuf sb;
|
|
http::response<http::string_body> resp;
|
|
http::read(sock, sb, resp);
|
|
```
|
|
|
|
The `read` implementation can use any object meeting the requirements of
|
|
[link beast.types.Streambuf [*`Streambuf`]], allowing callers to define custom
|
|
memory management strategies used by the implementation.
|
|
|
|
[endsect]
|
|
|
|
|
|
|
|
[section:advanced Advanced]
|
|
|
|
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.
|
|
|
|
[heading Message model]
|
|
|
|
The message class template and provided Body types are suitable for casual
|
|
library users. This section explains the message model for advanced users
|
|
who wish to take control over aspects of the implementation. We introduce
|
|
customization points for the header and body via class template arguments.
|
|
This illustration shows more detail about the
|
|
[link beast.ref.http__message [*`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:headers Headers Type]
|
|
|
|
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]
|
|
|
|
|
|
|
|
[section:body Body Type]
|
|
|
|
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]]
|
|
|
|
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]
|
|
|
|
|
|
[endsect]
|
|
|