Commit Graph

63 Commits

Author SHA1 Message Date
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
Artem Redkin c097c17e3e fix flaky testContentLengthTooLongFails test (#269) 2020-06-22 15:42:37 +01:00
Dimitri Bouniol ec48f4f114 Convenience methods for socket paths (#235)
* Added additional tests for socketPath-based requests

Motivation:

While going through the existing tests, I identified a few more instances where we could add some testing.

Modifications:

Added one test that verifies Requests are being decoded correctly, and improved three others to check for path parsing, error throwing, and schema casing respectively.

Result:

Tests that continue to pass, but that will also catch any incompatible changes in the future.

* Added some convenience initializers to URL and methods to Request for making requests to socket paths

Motivation:

Creating URLs for connecting to servers bound to socket paths currently requires some additional code to get exactly right. It would be nice to have convenience methods on both URL and Request to assist here.

Modifications:

- Refactored the get/post/patch/put/delete methods so they all call into a one line execute() method.
- Added variations on the above methods so they can be called with socket paths (both over HTTP and HTTPS).
- Added public convenience initializers to URL to support the above, and so socket path URLs can be easily created in other situations.
- Added unit tests for creating socket path URLs, and testing the new suite of convenience execute methods (that, er, test `HTTPMETHOD`s). (patch, put, and delete are now also tested as a result of these tests)
- Updated the read me with basic usage instructions.

Result:

New methods that allow for easily creating requests to socket paths, and passing tests to go with them.

* Removed some of the new public methods added for creating a socket-path based request

Motivation:

I previously added too much new public API that will most likely not be necessary, and can be better accessed using a generic execute method.

Modifications:

Removed the get/post/patch/put/delete methods that were specific to socket paths.

Result:

Less new public API.

* Renamed execute(url:) methods such that the HTTP method is the first argument in the parameter list

Motivation:

If these are intended to be general methods for building simple requests, then it makes sense to have the method be the first parameter in the list.

Modifications:

Moved the `method: HTTPMethod` parameter to the front of the list for all `execute([...] url: [...])` methods, and made it default to .GET. I also changed the url parameter to be `urlPath` for the two socketPath based execute methods.

Result:

A cleaner public interface for users of the API.

* Fixed some minor issues introduces with logging

Motivation:

Some of the convenience request methods weren't properly adapted for logging.

Modifications:

- Removed a doc comment from patch() that incorrectly referenced a logger.
- Fixed an issue where patch() would call into post().
- Added a doc comment to delete() that references the logger.
- Tests for the above come in the next commit...

Result:

Correct documentation and functionality for the patch() and delete() methods.

* Updated logging tests to also check the new execute methods

Motivation:

The logging tests previously didn't check for socket path-based requests.

Modifications:

Updated the `testAllMethodsLog()` and `testAllMethodsLog()` tests to include checks for each of the new `execute()` methods.

Result:

Two more tests that pass.
2020-06-19 10:51:03 +01:00
Johannes Weiss 8e60b94178 use standard ByteBuffer construction methods (#262) 2020-06-17 15:48:11 +01:00
Artem Redkin 606ab0eaea check body length (#255) 2020-06-16 09:17:09 +01:00
Adam Fowler 785ced571c The host header should also include the port (#237)
See https://tools.ietf.org/html/rfc7230#section-5.4

If port is not 80 or 443 then add to host header.
Fixed up tests
2020-06-15 15:46:29 +01:00
Johannes Weiss 86db162a11 logging support (#227)
Motivation:

AsyncHTTPClient is not a simple piece of software and nowadays also
quite stateful. To debug issues, the user may want logging.

Modification:

Support passing a logger to the request methods.

Result:

Debugging simplified.
2020-06-09 15:55:23 +01:00
Dimitri Bouniol 364d1069a4 Better support for UNIX Domain sockets (#228)
* Added tests for http+unix and https+unix url schemes

Motivation:

Using a base URL as the socket path only works when the URL object is maintained as long as possible through the stack. Additionally, it doesn't currently provide a way to use TLS over UNIX sockets.

Modifications:

Added two tests to test out the to-be supported URL schemes, http+unix, and https+unix, which encode the socket path as a %-escaped hostname, as some existing services already do.

Result:

Better UNIX domain socket support.
2020-06-02 15:22:20 +01:00
Johannes Weiss 070c1e5f37 cpool: don't reuse connection if we sent close (#225)
Motivation:

Previously, we'd only use the server's connection header to determine if
we should close the connection or not. That's wrong because if we set
`connection: close` ourselves, we must not reuse again.

Modification:

Set `TaskHandler.closing = false` if we send a close header.

Result:

More HTTP correctness.
2020-05-21 15:43:38 +01:00
Artem Redkin f81d0fec12 draft for streaming el fixes (#215) 2020-05-20 15:09:49 +01:00
Artem Redkin ce82178164 fix validation error propagation (#221) 2020-05-20 11:50:13 +01:00
Artem Redkin 8adfdb9bc5 refactor pool (#192) 2020-05-18 17:47:21 +01:00
Fabian Fett b72756af52 Typo fix: Trasfer-Encoding -> Transfer-Encoding (#211) 2020-05-15 14:30:00 +01:00
Iain Smith be517e3cc1 Enable clients to call URLs that include %2F as an escaped backslash (#201)
* Enable clients to call URLs that include %2F as an escaped backslash

Previously `percentEncodedPath` was using `path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)`
which converts %2F to a literal '/'. This prevented users fetching URLs like https://api.travis-ci.org/repo/github/rails%2Frails
which use %2F as part of a path segment.

Migrating to `URLComponents(url: self, resolvingAgainstBaseURL: false)?.percentEncodedPath` has the desired behaviour
for the couple of test cases that exist.

Updated the test server to switch on the `percentEncodedPath` so it's easier
to understand the desired behaviour.
2020-04-15 11:24:30 +01:00
Fabian Fett 2d88de3eb6 Verify header field names (#191)
* HTTPRequest without body: Content-Length shall not be send
* Verify field names comply with RFC7230
2020-04-01 16:48:01 +01:00
Artem Redkin 2fcf0a9fdd fix location header lookup (#186) 2020-03-31 09:56:44 +01:00
Artem Redkin 7211025f2a add version to response (#182)
* add version to response

* fix API breakage by deprecating old init

* review fix: use renamed instead of message
2020-03-26 16:46:59 +00:00
Trevör 9cdd1dc389 Close idle pool connections (#170)
* Close idle pool connections

Motivation: Pooled connections should close at some point (see #168)

Changes:
- Add new poolingTimeout property to HTTPClient.Configuration, it's
default value is .seconds(60), it can be set to nil if one wishes to
disable this timeout.
- Add relevant unit test

Co-authored-by: Johannes Weiss <johannesweiss@apple.com>
2020-03-03 21:51:50 +00:00
Trevör 19e2ea727e Add an HTTP/1.1 connection pool (#105)
motivation: Better performance thanks to connection reuse

changes:
- Added a connection pool for HTTP/1.1
- All requests automatically use the connection pool
- Up to 8 parallel connections per (scheme, host, port)
- Multiple additional unit tests
2020-02-25 15:43:16 +00:00
Johannes Weiss de7421906c fix UDS without a baseURL (#165)
Previously, UNIX Domain Sockets would only work if the URL also had a
"base URL". If it didn't have a base URL, it would try to connect to the
empty string which would fail :).

Now, we support both cases:
- URLs with a baseURL (the path to the UDS) and a path (actual path)
- URLs that just have an actual path (path to the UDS) where we'll just
  use "/" as the URL's path
2020-02-24 17:52:36 +00:00
Johannes Weiss f648c04d18 some lets in Requests were vars by accident (#163) 2020-02-11 12:33:25 +00:00
Artem Redkin 70ad494183 add a public init to StreamWriter (#161) 2020-02-05 22:31:35 +00:00
Marcin Krzyzanowski e90f5fd03d Support UNIX Domain Sockets (#151)
Adds support for UNIX Domain Socket requests.

Usage:

```
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let socketURL = URL(string: "unix:///var/run/docker.sock")!
let req = try HTTPClient.Request(url: URL(string: "/users/list", relativeTo: socketURL)!, method: .GET)
let response = try httpClient.execute(request: req).wait()
```
2020-01-27 15:55:12 +00:00
Fabian Fett 84026a7ba0 ResponseAccumulator is now public (#155)
As discussed in #128. We make the ResponseAccumulator public to give developers an easy time to create a Task. With the ResponseAccumulator the developer using this does not have to worry, about implementing the HTTPClientResponseDelegate all by himself.
2020-01-20 17:11:39 +00:00
Johannes Weiss 7aeaf6f0e1 a set of extra test cases (#132)
Motivation:

More unit tests are good and now that SwiftNIO shipd
`NIOHTTP1TestServer`, writing integration tests for async-http-client is also
more straightforward.

Modification:

Demonstrate some tests using NIOHTTP1TestServer.

Result:

More tests.
2019-11-18 16:11:30 +01:00
tomer doron 018e139eef update copyrights note (#125)
motivation: the Swift Server Workgroup is not a legal entity and cannot hold copyrights. with this change, code authors continue and retain their copyrights under the apache license and previous copyrights note, but Apple steps up instead of the workgroup which has no legal status

changes:
* update header files to say "Apple Inc. and the AsyncHTTPClient project authors" instead of "Swift Server Workgroup and the AsyncHTTPClient project authors"
* update validation scripts to check for the correct header
* add CONTRIBUTING.md file to explain how to make contributions and include a legal notice about licensing the contribution to Apple and the project
* regenerate CONTRIBUTORS.md to reflect most recent contributions
2019-11-06 11:20:30 -08:00
Artem Redkin 51dc885a30 add support for redirect limits (#113) 2019-10-23 19:29:25 +03:00
Johannes Weiss c1c3da3990 remove all deprecations (#116) 2019-10-22 17:52:11 -07:00
Artem Redkin 191c4ba506 add response decompression support (#86)
fixes #44
2019-10-22 17:45:12 -07:00
Artem Redkin 54b0b96b74 cancel should be done with outbound user event (#110)
cancel should be done with outbound user event
2019-09-23 16:43:15 +01:00
Johannes Weiss 513be15050 EventLoop preference overhaul (#102) 2019-09-21 21:33:15 +02:00
Artem Redkin 79cd718c35 preserve trailing slash in uri path (#107) 2019-09-20 15:32:49 +01:00
Johannes Weiss 2d7ae2bbae minor: improve code style (#103) 2019-09-15 11:04:11 +01:00