Commit Graph
95 Commits
Author SHA1 Message Date
George BarnettandGitHub 6c5058ee2c Add a control to limit connection reuses (#678)
Motivation:

Sometimes it can be helpful to limit the number of times a connection
can be used before discarding it. AHC has no such support for this at
the moment.

Modifications:

- Add a `maximumUsesPerConnection` configuration option which defaults
  to `nil` (i.e. no limit).
- For HTTP1 we count down uses in the state machine and close the
  connection if it hits zero.
- For HTTP2, each use maps to a stream so we count down remaining uses
  in the state machine which we combine with max concurrent streams to
  limit how many streams are available per connection. We also count
  remaining uses in the HTTP2 idle handler: we treat no remaining uses
  as receiving a GOAWAY frame and notify the pool which then drains the
  streams and replaces the connection.

Result:

Users can control how many times each connection can be used.
2023-04-11 14:49:44 +01:00
David NadobaandGitHub 98b45ed1cd Allow DNS override (#675)
Sometimes it can be useful to connect to one host e.g. `x.example.com` but request and validate the certificate chain as if we would connect to `y.example.com`. This is what this PR adds support for by adding a `dnsOverride` configuration to `HTTPClient.Configuration`. This is similar to curls `—resolve-to` option but only allows overriding host and not ports for now.
2023-03-30 08:21:41 +01:00
David NadobaandGitHub 423fd0bd6b Fix crash if connection is closed very early (#671)
* Fix crash if connection is closed very early

If the channel is closed before flatMap is executed, all ChannelHandler are removed and `TLSEventsHandler` is therefore not present either. We need to tolerate this even though it is very rare.

Testing ideas welcome.

Fixes #670

* drop precondition to assert
2023-03-14 03:35:06 -07:00
Fabian FettandGitHub e26459902c Fix HTTP2StreamChannel leak (#657)
* Fix HTTP2StreamChannel leak

* Update code comments.
2023-02-14 10:04:27 +01:00
1d24271fee Fix crash for large HTTP request headers (#661)
* Reproducer

* Refactor test case

* Refactor tests

* Remove debugging artefacts

* Fix typo

* Fix formatting

* Remove `promise?.succeed(())`

* Add test for HTTP2 request with large header

Motivation

We currently don't handle large headers well which trigger a channel writability change event.

Modification

Add failing (but currently skipped) tests which reproduces the issue
Result

We can reliably reproduce the large request header issue in an integration and unit test.
Note that the actual fix is not included to make reviewing easier and will come in a follow up PR.

* Remove logging

* Fix crash for large HTTP request headers

Fix crash for when sending HTTP request headers result in a channel writability change event

* Formatting and linux tests

* Formatting and linux tests

* Generate linux tests

* Use previous default max concurrent streams value of 10

* Fix crash if request is canceled after request header is send

* generate linux tests and run swift format

---------

Co-authored-by: Cory Benfield <lukasa@apple.com>
2023-02-10 15:41:26 +01:00
David NadobaandGitHub 67f99d1798 Add test for HTTP1 request with large header (#658)
* Reproducer

* Refactor test case

* Refactor tests

* Remove debugging artefacts

* Fix typo

* Fix formatting

* Remove `promise?.succeed(())`

* Rename `onRequestCompleted` to `onConnectionIdle`
2023-01-26 11:11:30 +00:00
iMikeandGitHub 49abfc30ae Add Host header (#650) (#651) 2022-12-06 11:13:10 +00:00
David NadobaandGitHub 5bee16a799 Switch over state in HTTPConnectionPool.HTTP2StateMachine.failedToCreateNewConnection (#647) 2022-11-09 15:30:16 +01:00
David NadobaandGitHub fd03ed01c7 Tolerate shutdown message after channel is closed (#646)
* Tolerate shutdown message after channel is closed

### Motivation
A channel can close unexpectedly if something goes wrong. We may in the meantime have scheduled the connection for graceful shutdown but the connection has not yet seen the message. We need to still accept the shutdown message and just ignore it if we are already closed.

### Modification
- ignore calls to shutdown if the channel is already closed
- add a test which would previously crash because we have transition from the closed state to the closing state and we hit the deinit precondition
- include the current state in preconditions if we are in the wrong state

### Result

We don’t hit the precondition in the deinit in the scenario described above and have more descriptive crashes if something still goes wrong.
2022-11-08 17:48:56 +01:00
David NadobaandGitHub 0b5bec741b Replace NIOSendable with Sendable (#640) 2022-10-12 08:50:28 +01:00
David NadobaandGitHub f17a47e916 Allow immediate request failure on connection error (#625) 2022-10-10 13:34:42 +01:00
Fabian FettandGitHub af5966f1d1 Reduce use of HTTPClient.Configuration in the Connection objects (#635) 2022-10-07 08:05:28 -07:00
Fabian FettandGitHub 897d49aa1b Replace Lock with NIOLock (#628)
SwiftNIO 2.42.0 has deprecated Lock and replaced it with a new NIOLock. This commit removes all uses of Lock and replaces them with NIOLock. Further, now, we must require SwiftNIO 2.42.0
2022-09-27 15:42:47 +02:00
David NadobaandGitHub c3c90aab58 Adopt Sendable (#621) 2022-08-25 11:45:13 +02:00
David NadobaandGitHub 46d1c76715 Support transparent decompression with HTTP/2 (#610) 2022-08-05 17:04:57 +02:00
David NadobaandGitHub 2adca4b003 Use swift-atomics instead of NIOAtomics (#603)
`NIOAtomics` was deprecated in https://github.com/apple/swift-nio/pull/2204 in favor of `swift-atomics` https://github.com/apple/swift-atomics
2022-07-13 13:48:24 +01:00
David NadobaandGitHub 14fa6d944d Report last connection error if request deadline is exceeded (#601) 2022-07-01 10:39:38 +02:00
Cory BenfieldandGitHub 9a8553e8aa Correctly close the connection if sendEnd fails (#599)
Motivation

If we receive an early HTTP response, the last action on a HTTP/1.1
connection is to send the .end message. While we had an error handling
path in the code, it wasn't tested, and when executed it would end up
leaking the connection by failing to close it _or_ return it to the
pool.

This patch fixes the issue by appropriately terminating the connection
and adding a test.

Modifications

Add a test
Terminate the connection if sendEnd fails

Result

Fewer connection leaks
2022-06-17 17:09:19 +01:00
Cory BenfieldandGitHub ac34f6debc Correctly handle Connection: close with streaming (#598)
Motivation

When users stream their bodies they may still want to send Connection:
close headers and terminate the connection early. This should work
properly.

Unfortunately it became clear that we didn't correctly pass the
information that the connection needed to be closed. As a result, we'd
inappropriately re-use the connection, potentially causing unnecessary
HTTP errors.

Modifications

Signal whether the connection needs to be closed when the final
connection action is to send .end.

Results

We behave better with streaming uploads.
2022-06-17 12:27:03 +01:00
Cory BenfieldandGitHub 062989efd8 Correctly reset our state after .sendEnd (#597)
Motivation

In some cases, the last thing that happens in a request-response pair is
that we send our HTTP/1.1 .end. This can happen when the peer has sent
an early response, before we have finished uploading our body. When it
does, we need to be diligent about cleaning up our connection state.

Unfortunately, there was an edge in the HTTP1ConnectionStateMachine that
processed .succeedRequest but that did not transition the state into
either .idle or .closing. That was an error, and needed to be fixed.

Modifications

Transition to .idle when we're returning
.succeedRequest(.sendRequestEnd).

Result

Fewer crashes
2022-06-17 11:52:29 +01:00
Cory BenfieldandGitHub 2483e08ffb Fix crash when receiving 2xx response before stream is complete. (#591)
Motivation

It's totally acceptable for a HTTP server to respond before a request
upload has completed. If the response is an error, we should abort the
upload (and we do), but if the response is a 2xx we should probably just
finish the upload.

In this case it turns out we'll actually hit a crash when we attempt to
deliver an empty body message. his is no good!

Once that bug was fixed it revealed another: while we'd attempted to
account for this case, we hadn't tested it, and so it turns out that
shutdown would hang. As a result, I've also cleaned that up.

Modifications

- Tolerate empty circular buffers of bytes when streaming an upload.
- Notify the connection that the task is complete when we're done.

Result

Fewer crashes and hangs.
2022-06-07 12:31:57 +01:00
3fcd67061f Improve errors and testing using NIOTS (#588)
Motivation

Currently error reporting with NIO Transport Services is often sub-par.
This occurs because the Network.framework connections may enter the
waiting state until the network connectivity state changes. We were not
watching for the user event that contains the error in that state, so if
we timed out in that state we'd just give a generic timeout error,
instead of telling the user anything more detailed.

Additionally, several of our tests assume that failure will be fast, but
in NIO Transport Services we will enter that .waiting state. This is
reasonable, as changed network connections may make a connection that
was not succeeding suddenly viable. However, it's inconvenient for
testing, where we're mostly interested in confirming that the error path
works as expected.

Modifications

- Add an observer of the WaitingForConnectivity event that records it
  into our state machine for later reporting.
- Add support for disabling waiting for connectivity for testing
  purposes.
- Add annotations to several tests to stop them waiting for
  connectivity.

Results

Faster tests, better coverage, better errors for our users.

Co-authored-by: David Nadoba <dnadoba@gmail.com>
2022-06-01 14:13:47 +01:00
24425989da Call didSendRequestPart after the write has hit the socket (#566)
### Motivation

Today `didSendRequestPart` is called after a request body part has been passed to the executor. However, this does not mean that the write hit the socket. Users may depend on this behavior to implement back-pressure. For this reason, we should only call this `didSendRequestPart` once the write was successful.

### Modification

Pass a promise to the actual channel write and only call the delegate once that promise succeeds.

### Result

The delegate method `didSendRequestPart` is only called after the write was successful. Fixes #565.

Co-authored-by: Fabian Fett <fabianfett@apple.com>
2022-04-26 16:04:54 +02:00
Fabian FettandGitHub 0a2004b1c6 [HTTP1] Tolerate immediate write errors (#579)
Same fix for HTTP/1 that landed for HTTP/2 in #558.

### Motivation

`HTTP1ClientChannelHandler` currently does not tolerate immediate write errors.

### Changes

Make `HTTP1ClientChannelHandler` resilient to failing writes.

### Result

Less crashes in AHC HTTP/1.
2022-04-11 16:41:10 +02:00
Fabian FettandGitHub d2da15c31c [HTTP2] Tolerate GoAway and Settings frames after connection close (#578) 2022-04-11 16:24:57 +02:00
Fabian FettandGitHub 71af9c7fb7 Crash fix: HTTPClientRequestHandler can deal with failing writes (#558) 2022-02-22 13:59:54 +01:00
5844a6b4ee Crash fix: HTTP2 can handle requests are cancelled (#555)
Co-authored-by: George Barnett <gbarnett@apple.com>
2022-02-10 10:42:27 +01:00
Fabian FettandGitHub ce3958ff92 Fix race between connection close and scheduling new request (#546) 2022-01-24 12:07:41 +00:00
Fabian FettandGitHub 2497a68427 Print invalid state, if hitting precondition (#545) 2022-01-24 12:26:08 +01:00
David NadobaandGitHub c2805dfa4e Prepare async/await API for public release (#531) 2022-01-14 14:35:17 +01:00
David NadobaandGitHub e531961906 remove dot from HTTP1.1 folder (#535)
Swift tools version 5.3 and higher (the version that is specified at very top of a Package.swift file) excludes folders with a dot in the name by default. It luckily produces a warning "found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target". However, this issue is buried under a lot of missing types Errors because of the 3 excluded files.
I run into this issue and it took me some time to figure out what the actual problem was. As we will eventually move from 5.2 to 5.3 we can already save the next person some time by resolving this issue now.
2021-12-23 00:09:38 +01:00
David NadobaandGitHub e4b11eb547 Fix HTTP1 to HTTP2 migration while shutdown is in progress (#530)
* Fix HTTP1 to HTTP2 migration while shutdown is in progress

### Motivation
Calling `HTTPClient.shutdown()` may never return if connections are still starting and one new established connection results in a state migration (i.e. from HTTP1 to HTTP2 or vice versa). We forgot to migrate the shutdown state. This could result in a large dealy until `.shutdown()` returns because we wait until connections are closed because of idle timeout. Worse, it could also never return if more requests are queued because the connections would not be idle and therefore not close itself.
###Changes
- Mirgrate shutdown state too
- add tests for this specific case

* simplify testMigrationFromHTTP1ToHTTP2WhileShuttingDown
* add http2 to http1 migration test
2021-12-17 14:44:11 +01:00
Fabian FettandGitHub 474b23d677 Typo: Replace HTTPExecutingRequest with HTTPExecutableRequest (#514) 2021-12-03 17:10:55 +01:00
David NadobaandGitHub 99bd384b38 Refactor deconstructURL and scheme parsing (#504)
* make `Scheme` a type

* introduce new Endpoint type

* use endpoint as storage in `HTTPClient.Request`

* fix merge conflicts

* rename Endpoint to DeconstructedURL

* swift-format

* make `DeconstructedURL` properties `var`'s

* move scheme into global namespace

- rename `useTLS` to `usesTLS` where posible without breaking public API
- only import Foundation.URL

* fix review comments
2021-12-01 13:03:09 +01:00
David NadobaandGitHub 591aa445d9 fix nits from #501 (#503) 2021-11-30 18:47:13 +01:00
KarlandGitHub f1a91872c5 Introduce a ConnectionTarget enum (#501)
* Add a ConnectionPool.Host enum

* Move Host out as a top-level ConnectionTarget type, and use it in Request.
2021-11-30 18:29:38 +01:00
David NadobaandGitHub 9eaecbbbce SwiftFormat --ifdef no-indent (#494)
* SwiftFormat --ifdef no-indent

* update `generate_linux_tests.rb` to use new indention rule
2021-11-25 17:09:19 +01:00
Fabian FettandGitHub e5022468bb Update swiftformat to 0.48.8 (#491)
### Motivation

Our current swiftformat version does not support async/await. Since we want to add support for async/await we must update swiftformat or disable it. I tried my very best to keep the number of changes as small as possible. I assume we want to stick with the new 0.48.8 for some time.

### Changes

- Update swiftformat to 0.48.8

### Result

We can land async/await code.
2021-11-25 10:15:36 +01:00
David NadobaandGitHub 4fd1150184 Fix bodyLengthMissmatch error handling (#490) 2021-11-24 15:46:51 +01:00
David NadobaandGitHub 0ed00b8b36 Always overwrite Transport-Encoding and Content-Length headers (#479) 2021-11-23 10:22:57 +01:00
David NadobaandGitHub b479afe4c8 Fix bug in migration from HTTP1 to HTTP2 and back to HTTP1 (#486) 2021-11-22 12:33:57 +01:00
Fabian FettandGitHub 2fe3f42fee Crash fix: HTTP2Connections emit events after the pool has closed them. (#481) 2021-11-19 12:33:08 +01:00
David NadobaandGitHub 1f3f141038 Remove redundant RequestFramingMetadata.Body.none case (#480) 2021-11-18 17:13:38 +01:00
7617c35db3 Handle NIOSSLError.uncleanShutdown correctly (#472)
### Motivation

Fixes #238 and #231.

### Changes

- Extracted the unclean shutdown test from `HTTPClientTests` into their own file `HTTPClientUncleanSSLConnectionShutdownTests`
- Copy and pasted @weissi great explanation from #238 into the test file
- Removed property `ignoreUncleanSSLShutdown` everywhere

### Result

`ignoreUncleanSSLShutdown` on `HTTPClient.Configuration` is deprecated and ignored.

Co-authored-by: Johannes Weiss <johannesweiss@apple.com>
2021-11-11 11:02:54 +01:00
Fabian FettandGitHub 170fd536f9 Support informational response heads (#469) 2021-11-10 13:33:38 +01:00
David NadobaandGitHub 7d1ed4cca5 [HTTP2] Forward HTTP2 events to StateMachine (#466) 2021-11-04 10:48:12 +00:00
David NadobaandGitHub 18a58bb874 [HTTP2] Improve performance of backoff timer done event (#464) 2021-11-03 14:29:24 +00:00
David NadobaandGitHub 60fef53db7 [HTTP2] Internal HTTPVersion configuration (#463) 2021-11-02 18:46:29 +00:00
David NadobaandGitHub 149b8d2656 [HTTP2] Integrate HTTP2StateMachine into HTTPConnectionPool.StateMachine (#462) 2021-11-02 11:51:18 +00:00
David NadobaandGitHub 4147fd647d [HTTP2] Create new connections during migration if needed (#459) 2021-10-27 18:28:51 +02:00