114 Commits
Author SHA1 Message Date
Johannes WeissandJohannes Weiss 8c90405f0c use NIOSingletons EventLoops/NIOThreadPool instead of spawning new (#697)
Co-authored-by: Johannes Weiss <johannes@jweiss.io>
2023-08-14 14:59:33 +01:00
Cory Benfield 62c06d47c8 Remove tests relying on OS-dependent behaviour (#703)
Motivation

URL parsing has changed in macOS Sonoma and associated releases. Our
tests were reliant on the old behaviour. The behaviour is controlled
by the OS on which the program was linked, which makes it impossible
for us to programmatically work out which result we should see.

The affected tests are not actually useful, we don't really care how
the URLs parse, so we can safely just remove them.

Modifications

Remove the affected tests.

Result

Tests pass!
2023-08-10 03:33:18 -07:00
David Nadoba b9029ef67c Add support for custom cancellation error (#683) 2023-04-13 17:00:03 +01:00
George Barnett 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 NadobaandCory Benfield 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 Nadoba 59bfb96afb Add test for HTTP2 request with large header (#659)
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.
2023-01-26 14:26:58 +01:00
David Nadoba 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
carolinacass 8b84142a70 Use #fileID/#filePath instead of #file (#644) 2022-11-04 08:20:19 -07:00
David Nadoba 9195d3bcb4 Speedup tests (#639)
* Enable fast failure mode for testing

* Split-up HTTPClientTests into multiple subclasses

* Move test subclasses into separate files
2022-10-11 16:40:26 +01:00
David Nadoba 9937d8751a Handle ResponseAccumulator not being able to buffer large response in memory (#637)
* Handle ResponseAccumulator not being able to buffer large response in memory

Check content length header for early exit

* Add test which currently hangs indefinitely

* Run `generate_linux_tests.rb` and `SwiftFormat`

* Print type and value if assert fails

* Run `generate_linux_tests.rb` and `SwiftFormat`

* Remove duplicate test due too merge conflict

* Validate that maxBodySize is positive

* Address review comments
2022-10-10 11:18:58 +01:00
Johannes WeissandJohannes Weiss 7f998f5118 add a future-returning shutdown method (#626)
Co-authored-by: Johannes Weiss <johannes@jweiss.io>
2022-09-07 06:12:05 -07:00
David Nadoba c3c90aab58 Adopt Sendable (#621) 2022-08-25 11:45:13 +02:00
Cory BenfieldandDavid Nadoba df87a860fd Limit max recursion depth delivering body parts (#611)
Motivation

When receiving certain patterns of response body parts, we can end up
recursing almost indefinitely to deliver them to the application. This
can lead to crashes, so we might politely describe it as "sub-optimal".

Modifications

Keep track of our stack depth and avoid creating too many stack frames.
Added some unit tests.

Result

We no longer explode when handling bodies with lots of tiny parts.

Co-authored-by: David Nadoba <d_nadoba@apple.com>
2022-08-05 17:53:21 +01:00
David Nadoba 46d1c76715 Support transparent decompression with HTTP/2 (#610) 2022-08-05 17:04:57 +02:00
David Nadoba 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 Nadoba 14fa6d944d Report last connection error if request deadline is exceeded (#601) 2022-07-01 10:39:38 +02:00
David Nadoba 794dc9d427 Add testSelfSignedCertificateIsRejectedWithCorrectError (#594) 2022-06-17 20:32:33 +02:00
Cory Benfield 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 Benfield 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
David Nadoba 0f21b44d1a Use a local TCP server that doesn’t accept connections on macOS for testConnectTimeout() (#592)
* Use a local TCP server that doesn’t accept connections on macOS for `testConnectTimeout()`

* fix linting
2022-06-09 18:35:05 +01:00
Cory Benfield 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
Cory BenfieldandDavid Nadoba 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
David Nadoba 3725095966 Fix flaky HTTPClientTests.testResponseDelayGet() test (#584) 2022-04-22 10:37:00 +02:00
Karl 6474d8d2b2 Add HTTPClient.Body.bytes as an alternative to .data (#534) 2022-01-13 14:19:48 +00:00
David Nadoba c4feafd813 Refactor redirect logic to be reusable for async/await (#522)
* refactor RedirectHandler
- `redirectState` is no longer a property of `HTTPClient.Request`. RedirectHandler now stores this state directly and therefore no longer optional.
- we no longer count the number of allowed redirects down. Instead the number of redirects is dervied from `self.visited.count` and we compare it to the maxRedirect to check if we git the limit.

* `HTTPClient.Configuration.RedirectConfiguration.Configuration` is now called `HTTPClient.Configuration.RedirectConfiguration.Mode`
only two `Configuration`s left in the type name

* add redirect logger test
2021-12-08 19:16:06 +01:00
Fabian Fett 83c2625612 [Tests] Remove XCTAssertNoThrow that wraps XCTAssertEqual (#500) 2021-11-26 13:16:40 +01:00
f2c2f73fb9 Refactor URL component extraction (#485)
* Refactor URL component extraction

* Remove superfluous test message

Co-authored-by: Fabian Fett <fabianfett@apple.com>
Co-authored-by: David Nadoba <d_nadoba@apple.com>
2021-11-26 12:49:43 +01:00
David Nadoba 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 Fett 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
KarlandCory Benfield 6426c00d84 [Tests] Add some basic IPv6 tests (#483)
Co-authored-by: Cory Benfield <lukasa@apple.com>
2021-11-23 12:25:06 +00:00
David Nadoba 0ed00b8b36 Always overwrite Transport-Encoding and Content-Length headers (#479) 2021-11-23 10:22:57 +01:00
Fabian FettandJohannes Weiss 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 Fett d0d4598f56 Replace tests that depend on task handler (#444) 2021-09-29 12:14:17 +02:00
Fabian Fett 2a47a1da75 [HTTPClient.Configuration] Make connection pool size configurable (#437) 2021-09-27 09:35:40 +02:00
Fabian Fett 2565dfe0dc [HTTPConnectionPool] Implementation switch (#427) 2021-09-24 09:28:09 +02:00
Fabian Fett 64fbfdaeda [HTTP1Connection] Option to ignore unclean ssl shutdown errors (#432)
- a new `RequestOptions` struct was created, that can be used to set request specific options. It is required by the `HTTPExecutableRequest`
- Added support for `ignoreUncleanSSLShutdown` in the `HTTPRequestStateMachine` and the `HTTP1ConnectionStateMachine`. In http/2 `ignoreUncleanSSLShutdown` is always off.
2021-09-21 10:04:17 +02:00
Fabian Fett b0180d15e3 Fix potentially flaky HTTPClientTests.testNoBytesSentOverBodyLimit (#426)
### Motivation

If we call 
```
context.write(ByteBuffer(string: "foo"), promise: nil)
context.flush()
context.close()
```
there is no guarantee that `"foo"` will be written to the network. Currently the test `testNoBytesSentOverBodyLimit` assumes this however.

### Changes

- `testNoBytesSentOverBodyLimit` does not assume anymore, that bytes will be sent before a close of the connection.
- Use `XCTAssertEqual` to compare `Equatable` errors.
2021-09-14 10:41:29 +02:00
Fabian Fett 75b716e1eb Tests: Use HTTPClientError with XCTAssertEqual (#424) 2021-09-13 17:49:45 +02:00
Fabian Fett eab2a84b1c Use explicit NIO imports (#407)
* Use explicit NIO imports for `NIOCore`, `NIOPosix` and `NIOEmbedded`
* Updated dependencies
2021-08-19 21:11:49 +02:00
Fabian Fett da5da25c60 Fix bi directional streaming test (#405)
The bi directional streaming test writes and reads from an echo server.
2021-08-12 12:45:41 +02:00
Fabian Fett 8e4d51908d Refactor Channel creation (#377)
- The connection creation logic has been refactored into a number of smaller methods that can be combined
- Connection creation now has a logical home. It is moved from `Utils.swift` into a `ConnectionFactory`
- There are explicit `ChannelHandlers` that are used for connection creation:
  - `TLSEventsHandler` got its own file and unit tests
  - `HTTP1ProxyConnectHandler` got its own file and unit tests
  - `SOCKSEventsHandler` got its own file and unit tests
- Some small things are already part of this pr that will get their context later. For example: 
  - `HTTPConnectionPool` is added as a namespace to not cause major renames in follow up PRs
  - `HTTPConnectionPool.Connection.ID` and its generator were added now. (This will be used later to identify a connection during its lifetime)
  - the file `HTTPConnectionPool+Manager` was added to give `HTTPConnectionPool.Connection.ID.Generator` already its final destination.
2021-06-28 13:17:33 +02:00
Fabian Fett af837edfcb Add http/2 support to HTTPBin (#382)
- Add `swift-nio-http2` as a dependency to the Package.swift (only used in the test target so far).
- Remove unused initialization options for `HTTPBin`
- The initialization options `ssl: Bool = false, compress: Bool = false, refusesConnections: Bool = false` move into a `Mode` enum. The mode might be `.http1_1(ssl: Bool, compress: Bool)`, `.http2(compress: Bool)`, or `.refuse`
- Added support for `http/2` (not used in any tests yet, however I verified the support with curl locally.)
- Nearly all channel pipeline modifications in `HTTPBin` are synchronous now.
- HTTPBin's request handler is configurable. This allows testing of more esoteric cases without having to create a new server every time. The default `HTTPBin` continues to use the `HTTPBinHandler`.
2021-06-25 11:53:58 +02:00
David Evans 102b7e4bce Update NIO family dependencies to 5.2+ versions and fix deprecations (#381)
Updated:

NIO
NIOSSL
NIO Extras
NIOTS
Also fix TLSConfiguration.forClient() warnings by converting to TLSConfiguration.makeClientConfiguration(). Also the same for forServer().
2021-06-23 14:58:08 +01:00
Fabian Fett 132dc3e3f7 Make testSSLHandshakeErrorPropagation less flaky (#380)
### Motivation

- The `testSSLHandshakeErrorPropagation` has a very short connect timeout (100ms). The reason for this is, that a failing TLS handshake manifest in NIOTS as a connect timeout and we don’t want to wait for the timeout for to long in our tests.
- Using a the NIOSSL however we expect a proper NIOSSLError. Sometimes though the connection is not setup fast enough (because of the very short 100ms connect timeout), that we get a connectionTimeout error instead.

### Modifications

- Setting the short connect timeout to 100ms when using NIOTS only. This will make sure we get the proper NIOSSLError when using NIOSSL
2021-06-23 09:10:36 +01:00
Fabian Fett 210b54f0c0 Fixes the DelayOnHeadDelegate test utility (#374)
Currently the DelayOnHeadDelegate test utility depends on correct timings. Right now, if a request is slower than 50ms in  `testErrorAfterCloseWhileBackpressureExerted` the test will fail, since the backpressure promise is failed, before a head was received. This pr fixes this, by giving the user a callback, when the head was received.
2021-06-16 12:29:16 +01:00
Johannes Weiss e2d03ffb32 cache NIOSSLContext (saves 27k allocs per conn) (#362)
Motivation:

At the moment, AHC assumes that creating a `NIOSSLContext` is both cheap
and doesn't block.

Neither of these two assumptions are true.

To create a `NIOSSLContext`, BoringSSL will have to read a lot of
certificates in the trust store (on disk) which require a lot of ASN1
parsing and much much more.

On my Ubuntu test machine, creating one `NIOSSLContext` is about 27,000
allocations!!! To make it worse, AHC allocates a fresh `NIOSSLContext`
for _every single connection_, whether HTTP or HTTPS. Yes, correct.

Modification:

- Cache NIOSSLContexts per TLSConfiguration in a LRU cache
- Don't get an NIOSSLContext for HTTP (plain text) connections

Result:

New connections should be _much_ faster in general assuming that you're
not using a different TLSConfiguration for every connection.
2021-05-13 12:16:52 +01:00
Mads Odgaard ca722d8337 Support request specific TLS configuration (#358)
Adds support for request-specific TLS configuration:
Request(url: "https://webserver.com", tlsConfiguration: .forClient())
2021-05-07 13:47:02 +01:00
David Evans f3521033ef Fix tests (#356) 2021-04-27 15:43:07 +01:00
Cory Benfield e4fded76ac Better backpressure management. (#352)
Motivation:

Users of the HTTPClientResponseDelegate expect that the event loop
futures returned from didReceiveHead and didReceiveBodyPart can be used
to exert backpressure. To be fair to them, they somewhat can. However,
the TaskHandler has a bit of a misunderstanding about how NIO
backpressure works, and does not correctly manage the buffer of inbound
data.

The result of this misunderstanding is that multiple calls to
didReceiveBodyPart and didReceiveHead can be outstanding at once. This
would likely lead to severe bugs in most delegates, as they do not
expect it.

We should make things work the way delegate implementers believe it
works.

Modifications:

- Added a buffer to the TaskHandler to avoid delivering data that the
   delegate is not ready for.
- Added a new "pending close" state that keeps track of a state where
   the TaskHandler has received .end but not yet delivered it to the
   delegate. This allows better error management.
- Added some more tests.
- Documented our backpressure commitments.

Result:

Better respect for backpressure.

Resolves #348
2021-03-30 11:39:35 +01:00
Cory Benfield b075d19007 Unconditionally insert TLSEventsHandler (#349)
Motivation:

AsyncHTTPClient attempts to avoid the problem of Happy Eyeballs making
it hard to know which Channel will be returned by only inserting the
TLSEventsHandler upon completion of the connect promise. Unfortunately,
as this may involve event loop hops, there are some awkward timing
windows in play where the connect may complete before this handler gets
added.

We should remove that timing window by ensuring that all channels always
have this handler in place, and instead of trying to wait until we know
which Channel will win, we can find the TLSEventsHandler that belongs to
the winning channel after the fact.

Modifications:

- TLSEventsHandler no longer removes itself from the pipeline or throws
  away its promise.
- makeHTTP1Channel now searches for the TLSEventsHandler from the
  pipeline that was created and is also responsible for removing it.
- Better sanity checking that the proxy TLS case does not overlap with
  the connection-level TLS case.

Results:

Further shrinking windows for pipeline management issues.
2021-03-18 12:21:23 +00:00