Commit Graph

95 Commits

Author SHA1 Message Date
Greg Cotten 31122eaf7c Add Request/Response History to all public Response types (#817)
Work to close
https://github.com/swift-server/async-http-client/issues/790

The fact that `HTTPClient.Request` is not Sendable make me think we're
going to need to store something else, such as a `URL` and
`HTTPRequestHead`, instead?
2025-03-03 15:48:27 +00:00
Greg Cotten da621ce4a8 Add didVisitURL delegate method (#816)
Trying to pave the way for closing
https://github.com/swift-server/async-http-client/issues/790 with some
direction from @Lukasa.

I have no idea where is best to insert this new delegate method. I'm
currently doing it first thing in `receiveResponseHead0`, and not using
an EventLoopFuture for back pressure management. The state machine seems
pretty fragile and I don't want to leave too much of an imprint. Trying
to be a part of an EventLoopFuture chain seems really complicated and
would really leave a mark on the codebase, so I'm wondering if it's
possible to just warn the user "do not block"?

Anyway, just a jumping-off point and happy to take direction!
2025-02-21 08:30:24 +00:00
Greg Cotten ad262cc3d2 Propagate HTTPClient.Task<Response> failures to subsequent redirect tasks (#814)
Discussed in
https://github.com/swift-server/async-http-client/issues/753
2025-02-20 10:22:40 +00:00
Johannes Weiss 89dc8d0068 baby steps towards a Structured Concurrency API (#806)
At the moment, `HTTPClient`'s entire API surface violates Structured
Concurrency. Both the creation & shutdown of a HTTP client as well as
making requests (#807) doesn't follow Structured Concurrency. Some of
the problems are:

1. Upon return of methods, resources are still in active use in other
threads/tasks
2. Cancellation doesn't always work

This PR is baby steps towards a Structured Concurrency API, starting
with start/shutdown of the HTTP client.

Co-authored-by: Johannes Weiss <johannes@jweiss.io>
2025-02-06 17:11:37 +00:00
Johannes Weiss 2119f0d9cc fix 784: dont crash on huge in-memory bodies (#785)
fixes #784 

`writeChunks` had 3 bugs:
1. An actually wrong `UnsafeMutableTransferBox` -> removed that type
which should never be created
2. A loooong future chain (instead of one final promise) -> implemented
3. Potentially infinite recursion which lead to the crash in #784) ->
fixed too
2024-11-26 14:52:39 +00:00
Rick Newton-Rogers c621142327 Adopt GitHub actions (#780)
Migrate CI to use GitHub Actions.

### Motivation:

To migrate to GitHub actions and centralised infrastructure.

### Modifications:

Changes of note:
* Adopt swift-format using rules from SwiftNIO.
* Remove scripts and docker files which are no longer needed.
* Disabled warnings-as-errors on Swift 6.0 CI pipelines for now.

### Result:

Feature parity with old CI.
2024-10-29 15:01:46 +00:00
Agam Dua acaca2d50d Added: ability to set basic authentication on requests (#778)
Motivation:

As an HTTP library, async-http-client should have authentication
support.

Modifications:

This adds a `setBasicAuth()` method to both HTTPClientRequest and
`HTTPClient.Request` and their related unit tests.

Result:

Library users will be able to leverage this method to use basic
authentication on their requests without implementing this in their own
projects.

Note: I also ran the tests (`swift test`) with the
`docker.io/library/swift:6.0-focal` and
`docker.io/library/swift:5.10.1-focal` to ensure linux compatibility.

Signed-off-by: Agam Dua <agam_dua@apple.com>
2024-10-21 16:27:28 +01:00
Johannes Weiss 0bd9111ca7 mark HTTPClient.Response Sendable (#759)
`HTTPClient.Response` is trivially `Sendable`, let's mark it `Sendable`.
2024-08-07 08:28:39 -07:00
aryan-25 54d1006dc9 Add leading slash in relative URL requests if necessary (#747) 2024-07-09 12:47:07 +02:00
aryan-25 07536f6a4e Resolve ambiguity issue for the stream function, remove @deprecated marking from the original implementation, and make argument labels consistent. (#749) 2024-07-03 16:14:47 +01:00
aryan-25 4316ecae09 Add support for request body to be larger than 2GB on 32-bit devices (#746)
### Motivation:

- The properties that store the request body length and the cumulative number of bytes sent as part of a request are of type `Int`. 
- On 32-bit devices, when sending requests larger than `Int32.max`, these properties overflow and cause a crash.
- To solve this problem, the properties should use the explicit `Int64` type.

### Modifications:

- Changed the type of the `known` field of the `RequestBodyLength` enum to `Int64`.
- Changed the type of `expectedBodyLength` and `sentBodyBytes` in `HTTPRequestStateMachine` to `Int64?` and `Int64` respectively.
- Deprecated the `public var length: Int?` property of `HTTPClient.Body` and backed it with a new property: `contentLength: Int64?`
  - Added a new initializer and "overloaded" the `stream` function in `HTTPClient.Body` to work with the new `contentLength` property.
  - **Note:** The newly added `stream` function has different parameter names (`length` -> `contentLength` and `stream` -> `bodyStream`) to avoid ambiguity problems.
- Added a test case that streams a 3GB request -- verified this fails with the types of the properties set explicitly to `Int32`. 

### Result:

- 32-bit devices can send requests larger than 2GB without integer overflow issues.
2024-06-28 11:33:04 +02:00
Rick Newton-Rogers de7c84a607 Bump minimum Swift version to 5.7 (#712)
Motivation:

Now that Swift 5.9 is GM we should update the supported versions and
remove 5.6

Modifications:

* Update `Package.swift`
* Remove `#if swift(>=5.7)` guards
* Delete the 5.6 docker compose file and make a 5.10 one
* Update docs

Result:

Remove support for Swift 5.6, add 5.10
2023-10-04 17:43:21 +01:00
David Nadoba 75d7f63abf Automatically chunk large request bodies (#710)
* ChunkedCollection

* Use swift-algorithms

* SwiftFormat

* test chunking

* add documentation

* SwiftFormat

* fix old swift versions

* fix older swift versions

second attempt

* fix old swift versions

third attempt

* fix old swift versions

fourth attempt

* update documentation
2023-09-06 11:08:09 +01:00
David Nadoba d62c475401 Drop Swift 5.5 (#686) 2023-04-14 17:05:09 +01:00
David Nadoba b9029ef67c Add support for custom cancellation error (#683) 2023-04-13 17:00:03 +01:00
Cory Benfield 343cdf4639 Fix documentation and add support for CI-ing it (#679)
* Fix documentation and add support for CI-ing it

* Fixup license header
2023-04-11 13:35:46 +01:00
Cory Benfield 864c8d9e0e Mark Task.wait() noasync and provide Task.get() (#668)
Motivation

Task.wait() is a convenience method to avoid needing to wait for the
response future. This has had the effect of "laundering" a noasync
warning from EventLoopFuture.wait(), hiding it within a purely sync call
that may itself be used in an async context.

We should discourage using these and prefer using .get() instead.

Modifications

Mark Task.wait() noasync.
Add Task.get() for backward compatibility.

Result

Safer migration to Swift concurrency
2023-02-14 01:48:22 -08:00
Fabian Fett f65f45b727 Fix Request streaming memory leak (#665) 2023-02-07 17:18:56 +01:00
Felix Schlegel 817d9aa99f Make Task.logger accessible to delegate implementations outside of Package (#587)
Motivation:

This change was proposed in issue
[#389](https://github.com/swift-server/async-http-client/issues/389).
Users writing their own `HttpClientResponseDelegate` implementation
might want to emit log messages to the `task`'s `logger`.

Modifications:

Changed the access level of `Task`'s `logger` property from `internal`
to `public`.

Result:

Users can access the `logger` property of a `Task` outside of the
`async-http-client`.
2023-01-22 09:32:01 -08:00
David Nadoba 0bdc425a84 Remove #if compiler(>=5.5) (#641)
* Remove `#if compiler(>=5.5)`

* Run SwiftFormat
2022-10-12 16:18:47 +01:00
David Nadoba 0b5bec741b Replace NIOSendable with Sendable (#640) 2022-10-12 08:50:28 +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
Fabian Fett 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 Nadoba c3c90aab58 Adopt Sendable (#621) 2022-08-25 11:45:13 +02:00
David Nadoba fc510a39cf Fix thread leak in FileDownloadDelegate (#614)
* Fix thread leak in `FileDownloadDelegate`

* `SwiftFormat`

* Add a shared file IO thread pool per HTTPClient

* User bigger thread pool and initlize lazily during first file write

* thread pool is actually not used in tests

* Update documentation

* fix review comments

* make `fileIOThreadPool` internal

* Add test to verify that we actually share the same thread pool across all delegates for a given HTTPClient

Co-authored-by: Cory Benfield <lukasa@apple.com>
2022-08-18 11:55:55 +02:00
Cory Benfield 5e3e58dafd Use Docc for documentation (#613)
Motivation

Documentation is nice, and we can help support users by providing useful
clear docs.

Modifications

Add Docc to 5.6 and later builds
Make sure symbol references work
Add overview docs

Result

Nice rendering docs
2022-08-09 16:23:14 +01:00
Karl 609b4368b8 Used vendored base64 encoding implementation rather than Foundation.Data (#539) 2022-01-21 15:28:26 +01:00
David Nadoba c2805dfa4e Prepare async/await API for public release (#531) 2022-01-14 14:35:17 +01: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
David Nadoba 5ce7377a8a Add HTTPClientReuqest.Prepared (#511)
* add HTTPClientReuqest.Prepared

* make `prepared()` an init of `Prepared`

* make all stored properties of `Prepared` `var`s
2021-12-03 09:54:01 +01:00
David Nadoba 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
Karl 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
Karl 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 0ed00b8b36 Always overwrite Transport-Encoding and Content-Length headers (#479) 2021-11-23 10:22:57 +01:00
Fabian Fett 2bd88855b4 Remove deprecated connection pool (#443)
- Remove now unused Connection/ConnectionPool
- Removed TaskHandler
- Remove ResponseReadBuffer
- Removed further now unused code
- Remove unused imports
- Remove test util `RecordingHandler`
2021-09-29 18:42:24 +02:00
Fabian Fett 2565dfe0dc [HTTPConnectionPool] Implementation switch (#427) 2021-09-24 09:28:09 +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 bccb075309 Refactor Request Validation (#391)
- Refactored code from `TaskHandler` into new method `HTTPClient.Request.createRequestHead` that creates an `HTTPRequestHead` and matching `RequestFramingMetadata`
- Added required property `requestFramingMetadata` to `HTTPExecutingRequest`
- Added property `requestFramingMetadata` to `RequestBag`

Co-authored-by: Cory Benfield <lukasa@apple.com>
2021-07-07 19:50:00 +02:00
Fabian Fett a9aee2675f Add HTTPScheduledRequest and HTTPExecutingRequest (#384)
- Adding four new protocols:
  - `HTTPExecutingRequest` a protocol representing an HTTP task that is executed on a ChannelHandler.
  - `HTTPScheduledRequest` a protocol representing an HTTP task that is scheduled for execution, but in a waiting state (example: Waiting for an idle connection in the connection pool).
  - `HTTPRequestExecutor` a protocol that can be used from the `HTTPExecutingRequest` abstracting away functionality that will normally be implemented by a `ChannelHandler`
  - `HTTPRequestScheduler` a protocol that can be used from the `HTTPScheduledRequest` abstracting away functionality that will normally be implemented by a `ConnectionPool`
- An implementation of the `HTTPExecutingTask` and `HTTPScheduledRequest` called `RequestBag`. It implements  our current API using the new protocols

Co-authored-by: George Barnett <gbarnett@apple.com>
Co-authored-by: Cory Benfield <lukasa@apple.com>
2021-07-07 13:44:48 +02:00
David Evans 3fd0658dd9 Implement SOCKS proxy functionality (#375)
Add a new Proxy type to enable a HTTPClient to send requests via a SOCKSv5 Proxy server.
2021-06-18 13:02:58 +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
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 0dda95cffc Fix CoW in HTTPResponseAggregator (#345)
Motivation:

HTTPResponseAggregator attempts to build a single, complete response
object. This necessarily means it loads the entire response payload into
memory. It wants to provide this payload as a single contiguous buffer
of data, and it does so by aggregating the data into a single contiguous
buffer as it goes.

Because ByteBuffer does exponential reallocation, the cost of doing this
should be amortised constant-time, even though we do have to copy some
data sometimes. However, if this operation triggers a copy-on-write then
the operation will become quadratic. For large buffers this will rapidly
come to dominate the runtime.

Unfortunately in at least Swift 5.3 Swift cannot safely see that during
the body stanza the state variable is dead. Swift is not necessarily
wrong about this: there's a cross-module call to ByteBuffer.writeBuffer
in place and Swift cannot easily prove that that call will not lead to a
re-entrant access of the `HTTPResponseAggregator` object. For this
reason, during the call to `didReceiveBodyPart` there will be two copies
of the body buffer alive, and so the write will CoW.

This quadratic behaviour is a nasty performance trap that can become
highly apparent even at quite small body sizes.

Modifications:

While Swift can't prove that the `self.state` variable is dead, we can!
To that end, we temporarily set it to a different value that does not
store the buffer in question. This will force Swift to drop the ref on
the buffer, making it uniquely owned and avoiding the CoW.

Sadly, it's extremely difficult to test for "does not CoW", so this
patch does not currently come with any tests. I have experimentally
verified the behaviour.

Result:

No copy-on-write in the HTTPResponseAggregator during body aggregation.
2021-03-08 10:52:57 +00:00
Artem Redkin 5d9b784d69 Fixes bi-directional streaming (#344)
Motivation:
When we stream request body, current implementation expects that body
will finish streaming _before_ we start to receive response body parts.
This is not correct, reponse body parts can start to arrive before we
finish sending the request.

Modifications:
 - Simplifies state machine, we only case about request being fully sent
   to prevent sending body parts after .end, but response state machine
   is mostly ignored and correct flow will be handled by NIOHTTP1
   pipeline
 - Adds HTTPEchoHandler, that replies to each response body part
 - Adds bi-directional streaming test

Result:
Closes #327
2021-03-03 17:10:48 +00:00
Artem Redkin 4b4d6605aa Fix state reverting (#298)
* fail if we get part when state is endOrError

* Prevent TaskHandler state change after `.endOrError`

Motivation:
Right now if task handler encounters an error, it changes state to
`.endOrError`. We gate on that state to make sure that we do not
process errors in the pipeline twice. Unfortunately, that state
can be reset when we upload body or receive response parts.

Modifications:
Adds state validation before state is updated to a new value
Adds a test

Result:
Fixes #297
2020-08-24 11:28:28 +01:00
Artem Redkin ffcd1e1a1c Fixes double-release of a connection. (#295)
Motivation:
TaskHandler unconditionally releases it's connection on error,
this can lead to double release. This issue actually indicates
a more general issue where handler continues to handle errors
even after its state is `.endOrError`. We need to fix this by
ignoring all subsequent errors.

Modifications:
1. Check state before calling out delegate and pool
2. Replace all error callouts with call to `errorCaught`

Result:
Fixes #294
2020-08-20 15:35:06 +01:00
Tim Condon 1432843305 Fix typo in Task<Response> (#293) 2020-08-20 12:12:18 +01:00
Artem Redkin f69b68ffa8 fail if user tries writing bytes after request is sent (#270) 2020-07-30 11:22:55 +01:00
Dimitri Bouniol 5c7a317f5b Fixed an issue where redirects to socket path-based servers from any server was always allowed (#259)
* Added tests that ensure redirects to unix socket paths from a regular HTTP server are disallowed.

Motivation:

Currently, redirects to any supported URL scheme will always be allowed, despite code being in place to seemingly prevent it. See #230.

Modifications:

- Added a method to HTTPBin to redirect to the specified target.
- Added failing tests that perform redirects from a regular server to a socket-based server and vice versa.

Result:

Failing tests that show that the existing redirect checks were inadequate.

* Fixed an issue where redirects to socket path-based servers from any server was always allowed.

Motivation:

An arbitrary HTTP(S) server should not be able to trigger redirects, and thus activity, to a local socket-path based server, though the opposite may be a valid scenario. Currently, requests in either direction are allowed since the checks don't actually check the destination scheme.

Modifications:

- Refactored `hostSchemes`/`unixSchemes` to `hostRestrictedSchemes`/`allSupportedSchemes`, which better describes what they do.
- Refactored `Request.supports()` to `Request.supportsRedirects(to:)` since it is only used by Redirects now.
- Check the destination URL's scheme rather than the current URL's scheme when validating a redirect.

Result:

Closes #230

Co-authored-by: Artem Redkin <artem@redkin.me>
2020-06-22 17:56:01 +01:00