[/ 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] [/-----------------------------------------------------------------------------]