Not handling this was an issue for keep-alive connections because
requests would get assigned to a closed connection and then immediately
error. Handling the HUP event makes this situation much less likely. It
is still possible however; consider the situation where a HUP arrives
while the event loop is busy processing new requests to add. The
connection is disconnected, but the HUP hasn't been processed, and a
request could be assigned to it. This case is, however, unlikely.
We've been seeing a strange number of timeouts in our benchmarking.
Handling spurious timeouts as in this patch seems to fix it!
Note that managing the `timeout_start` needs to be done carefully. If
the current time is provided in the wrong place, it's possible requests
would never timeout.
I've had a couple of instances during stress testing now where
Conn::ready would overflow its stack due to recursing on itself. This
moves subsequent calls to ready() into a loop outside the function.
When loading up a client suddenly with thousands of connections, the
default DNS worker count of four cannot keep up and many requests
timeout as a result. Most people don't need a large pool, so making this
configurable is a natural choice.
We observed an issue where connection were not ever entering the
keep-alive state due to a bug with `State::update`. The issue is
resolved with resetting the write state to KeepAlive when it arrives as
KeepAlive. Otherwise, it would be marked incorrectly as Closed.
The `trace!` lines in here are useful for debugging keep-alive issues so
I've left them in.
In the scenario where a request is started on the `Client`, the client
has a full slab, and sockets for a *different* domain are idling in
keep-alive, the new request would previously cause the client to panic!.
This patch adds a `spawn_error` handler which attempts to evict an idle
connection to make space for the new request. If space cannot be made,
the error handler is run (passed `Error::Full`) and the `Handler` is
dropped.
This is a breaking change because of the new variant of `Error`.
Some inefficient use of `Vec` in the client was replaced with `VecDeque`
to support push/pop from either end.
Closes#896Closes#897
BREAKING CHANGE: `RequestUri::AbsolutePath` variant is changed to a struct variant. Consider using `req.path()` or `req.query()` to get the relevant slice.
Closes#891
BREAKING CHANGE: `Headers.remove()` used to return a `bool`,
it now returns `Option<H>`. To determine if a a header exists,
switch to `Headers.has()`.
The Raw type repesents the raw bytes of a header-value.
Having a special type allows a couple of benefits:
- The exact representation has become private, allowing "uglier"
internals. Specifically, since the common case is for a header to only
have 1 line of bytes, an enum is used to skip allocating a Vec for only
1 line. Additionally, a Cow<'static, [u8]> is used, so static bytes
don't require a copy. Finally, since we can use static bytes, when
parsing, we can compare the incoming bytes against a couple of the most
common header-values, and possibly remove another copy.
- As its own type, the `Headers.set_raw` method can be generic over
`Into<Raw>`, which allows for more ergnomic method calls.
BREAKING CHANGE: `Header::parse_header` now receives `&Raw`, instead of
a `&[Vec<u8>]`. `Raw` provides several methods to ease using it, but
may require some changes to existing code.