beast/doc/qbk/03_core/3_timeouts.qbk
2019-07-07 12:29:56 -07:00

222 lines
9.2 KiB
Plaintext

[/
Copyright (c) 2016-2019 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)
Official repository: https://github.com/boostorg/beast
]
[section:timeouts Timeouts __example__]
Network programs must handle adverse connection conditions; the most common
is that a connected peer goes offline unexpectedly. Protocols have no way of
identifying this reliably: the peer is offline after all, and unable to send
a message announcing the absence. A peer can go offline for various reasons:
[itemized_list
[The peer experiences a power loss]
[The peer becomes disconnected from the network]
[The local host becomes disconnected from the network]
[The network itself becomes unavailable]
]
To determine when a peer is offline or idle, a program will implement a
[@https://en.wikipedia.org/wiki/Timeout_(computing) timeout]
algorithm, which closes the connection after a specified amount of time if
some condition is met. For example, if no data is received for the duration.
A timeout may be used to:
[itemized_list
[Drop malicious or poorly performing hosts]
[Close idle connections to free up resources]
[Determine if a peer is offline or no longer available]
]
Traditionally, programs use a
[@boost:/doc/html/boost_asio/reference/steady_timer.html `net::steady_timer`]
to determine when a timeout occurs, and then call
[@boost:/doc/html/boost_asio/reference/basic_socket/close/overload2.html `close`]
on the socket to release the resources. The complexity of managing a separate
timer is often a source of
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1269r0.html#timers frustration]
for non-experts.
[note
For portability reasons, networking does not provide timeouts
or cancellation features for synchronous stream operations.
]
To simplify the handling of timeouts, these provided types wrap a
[@boost:/doc/html/boost_asio/reference/basic_stream_socket.html `net::basic_stream_socket`]
to provide additional features:
[table
[[Name][Features]]
[
[[link beast.ref.boost__beast__tcp_stream `tcp_stream`]]
[[itemized_list
[Timeouts for logical operations]
[[@boost:/doc/html/boost_asio/reference/ip__tcp.html `net::ip::tcp`] protocol]
[[@boost:/doc/html/boost_asio/reference/executor.html `net::executor`] executor]
[[link beast.ref.boost__beast__unlimited_rate_policy `unlimited_rate_policy`] rate limits]
]]
][
[[link beast.ref.boost__beast__basic_stream `basic_stream`]]
[[itemized_list
[Timeouts for logical operations]
[Configurable __Protocol__ type]
[Configurable __Executor__ type]
[Configurable __RatePolicy__ type]
]]
]]
[/-----------------------------------------------------------------------------]
[heading Construction]
The `tcp_stream` is designed as a replacement for
[@boost:/doc/html/boost_asio/reference/ip__tcp/socket.html `net::ip::tcp::socket`].
Any program which currently uses a socket, can switch to a `tcp_stream` and achieve
the features above (although some interfaces are different, see below).
Networking now allows I/O objects to construct with any instance of
__ExecutionContext__ or __Executor__ objects. Here we construct a stream which
uses a particular I/O context to dispatch completion handlers:
[code_core_3_timeouts_1]
Alternatively, we can construct the stream from an executor:
[code_core_3_timeouts_2]
The function
[@boost:/doc/html/boost_asio/reference/make_strand.html `make_strand`]
returns a strand constructed from an execution context or executor. When a
[@boost:/doc/html/boost_asio/reference/strand.html `net::strand`]
is chosen for the stream's executor, all completion handlers which do not
already have an associated executor will use the strand. This is both a
notational convenience (no need for `strand::wrap` or `bind_executor` at
call sites) and a measure of safety, as it is no longer possible to forget
to use the strand.
[code_core_3_timeouts_3]
[/-----------------------------------------------------------------------------]
[heading Connecting]
Before data can be exchanged, the stream needs to be connected to a peer.
The following code sets a timeout for an asynchronous connect operation.
In Beast, functions to connect to a range of endpoints (such as the range
returned by
[@boost:/doc/html/boost_asio/reference/ip__basic_resolver/resolve/overload3.html `net::ip::tcp::resolver::resolve`])
are members of the class rather than free functions such as
[@boost:/doc/html/boost_asio/reference/async_connect.html `net::async_connect`].
[code_core_3_timeouts_4]
A server will use an acceptor bound to a particular IP address and port to
listen to and receive incoming connection requests. The acceptor returns
an ordinary socket. A `tcp_stream` can be move-constructed from the
underlying `basic_stream_socket` thusly:
[code_core_3_timeouts_5]
[/-----------------------------------------------------------------------------]
[heading Reading and Writing]
Timeouts apply to the logical operation, expressed as a series of asynchronous
calls, rather than just the next call. This code reads a line from the stream
and writes it back. Both the read and the write must complete within 30 seconds
from when the timeout was set; the timer is not reset between operations.
[code_core_3_timeouts_6]
Since reads and writes can take place concurrently, it is possible to have
two simultaneous logical operations where each operation either only reads,
or only writes. The beginning of a new read or write operation will use
the most recently set timeout. This will not affect operations that are
already outstanding.
[code_core_3_timeouts_7]
When a timeout is set, it cancels any previous read or write timeout for which
no outstanding operation is in progress. Algorithms which loop over logical
operations simply need to set the timeout once before the logical operation,
it is not necessary to call `expires_never` in this case. Here we implement
an algorithm which continuously echoes lines back, with a timeout. This example
is implemented as a complete function.
[code_core_3_timeouts_1f]
[/-----------------------------------------------------------------------------]
[heading https_get]
It is important to note that all of the examples thus far which perform
reads and writes with a timeout, make use of the existing networking stream
algorithms. As these algorithms are written generically to work with any
object meeting the stream requirements, they transparently support timeouts
when used with `tcp_stream`. This can be used to enable timeouts for stream
wrappers that do not currently support timeouts.
The following code establishes an encrypted connection, writes an HTTP
request, reads the HTTP response, and closes the connection gracefully.
If these operations take longer than 30 seconds total, a timeout occurs.
This code is intended to show how `tcp_stream` can be used to enable
timeouts across unmodified stream algorithms which were not originally
written to support timing out, and how a blocking algorithm may be written
from asynchronous intermediate operations.
[code_core_3_timeouts_2f]
[endsect]
[/-----------------------------------------------------------------------------]
[section:rate_limiting Rate Limiting __example__]
The
[link beast.ref.boost__beast__basic_stream `basic_stream`]
class template supports an additional `RatePolicy` template parameter. Objects
of this type must meet the requirements of __RatePolicy__. They are used to
implement rate limiting or bandwidth management. The default policy for
`basic_stream` and `tcp_stream` is
[link beast.ref.boost__beast__unlimited_rate_policy `unlimited_rate_policy`],
which places no limits on reading and writing. The library comes with the
[link beast.ref.boost__beast__simple_rate_policy `simple_rate_policy`],
allowing for independent control of read and write limits expressed in terms
of bytes per second. The follow code creates an instance of the basic stream
with a simple rate policy, and sets the read and write limits:
[code_core_3_timeouts_8]
More sophisticated rate policies can be implemented as user-defined types which
meet the requirements of __RatePolicy__. Here, we develop a rate policy that
measures the instantaneous throughput of reads and writes. First we write a
small utility class that applies an exponential smoothing function to a series
of discrete rate samples, to calculate instantaneous throughput.
[code_core_3_timeouts_3f]
Then we define our rate policy object. We friend the type
[link beast.ref.boost__beast__rate_policy_access `rate_policy_access`] to
allow our implementation to be private, but still allow the `basic_stream`
access to call the required functions. This lets us avoid having to write
a cumbersome friend declaration for the `basic_stream` class template.
Public members of rate policy objects become part of the stream object's
interface, through a call to `rate_policy`.
[code_core_3_timeouts_4f]
To use our new policy we declare an instance of the stream, and then use it
with stream algorithms as usual. At any time, we can determine the current
read or write rates by calling into the policy.
[code_core_3_timeouts_9]
[endsect]
[/-----------------------------------------------------------------------------]