keeping them around was resulting in some busy loops on timer events
(i.e. retry after), making them unreliable, innacurate and CPU
draining. they're now kept out whenever they're inactive.
an issue was observed when stream was closed from our side, that the
the request in-flight count on the connection. This was fixed by not
reacting to :stream_closed events if request has been previously deleted.
during interest calculation
A quirk was found whereby a connection which failed while connecting
(such as the badssl test) was properly unregistered from the pool, was
however kept in the selectables selector pool, because if this operation
happening during the interest calculation pool, and the var substitution
being performed right afterwards, leaving the pool and selector out of
sync and causing all sorts of miscalculations around timers later on.
The HTTPX::Timers class mimicks the same top-level API as its
predecessors, but simplifies its implementation. Adding a timer will
resort all timers, while lookups are roughly the same complexity. The
key difference is that callbacks are now aggregated by interval, i.e.
different requests setting the same timeout, will reuse the same timer.
This is a more simple design than Timers::Group, which stores timers in
a binary search tree; the latter will perform well in any environment,
whereas the first one is more tailored for the use-case of httpx, where
most of the times no timers will be set, and when they do, the same
timer will be reused for all requests because they usually have the same
set of options (and therefore timeouts).
Instead, timeout accounting is done using total_timeout math. It
supersedes all other timeouts in the connection, and will start from th
moment the connection is established.
* `Response#error`, which, coupled with `ErrorResponse#error`, allows
for `if response.error` kind of conditional;
* `Response#raise_for_status` now returns the response when no error is
raise (for method chaining);
Closes#153
names
DNS answers are decoded using `resolv` internal structures. In this
particular case, we were using `Resolv::DNS::Name#to_s` to infer the
query dns name an answer refers to. This failed, because, as per
documentaation:
```ruby
p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
```
this caused issues when marking the DNS resolving as done. As a fix, I
applied the same logic from `Resolv::DNS::Name#inspect`. which correctly
prints absolute names.
Fixes#145
the traversal seems unnnecessary, given the subsequent cheaper checks. A
request with a stream will always be writable unless it's done, or is
expecting a 1xx response; however, there'll be drains in place, and
that's a cheaper check.
This avoids traversing all requests in the parser. this is possible for
HTTP/1.1 because, given that requests are sequentially concluded, one
can get away with checking only first and last request state.
multi-request calls
request options already get aggregated in #build_requests. this
optimization removes the Options object rebuild for each request,
thereby sharing the same across concurrent requests.
this is possible because, although #build_request is public, we document
it only with passing hash of options, never an Options object. This
would probably result in errors.