From c75974c79ce66cde12badbcc175335256f11bdd0 Mon Sep 17 00:00:00 2001 From: Nick Everitt Date: Sat, 27 Jun 2020 23:10:15 +0100 Subject: [PATCH 1/4] Allows redirect configuration per request Motivation: Redirect configuration is set globally when configuring the `HTTPClient`. However, it would also be useful to configure redirects on a per request basis. This would mean that a single `HTTPClient` could be configured and shared widely throughout an application, while retaining the flexibility to tailor specific requests with custom redirect handling should the need arise. Modifications: This change adds an optional `redirectConfiguration` parameter to all request methods of `HTTPClient`. When the request is executed the `prevailingConfiguration` is determined using the `redirectConfiguration` argument, if set. Otherwise the global configuration is used. Result: Allows redirect handling to be configured on a per request basis, closes #196 --- Sources/AsyncHTTPClient/HTTPClient.swift | 137 ++++++++++++++++------- 1 file changed, 94 insertions(+), 43 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index 6a5f19277..aea1d5d96 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -220,14 +220,18 @@ public class HTTPClient { } } } + + /// Specifies redirect processing settings. + public typealias RedirectConfiguration = Configuration.RedirectConfiguration /// Execute `GET` request using specified URL. /// /// - parameters: /// - url: Remote URL. /// - deadline: Point in time by which the request must complete. - public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled) + /// - redirectConfiguration: Redirect configurations for this request. + public func get(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } /// Execute `GET` request using specified URL. @@ -236,8 +240,9 @@ public class HTTPClient { /// - url: Remote URL. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.execute(.GET, url: url, deadline: deadline, logger: logger) + /// - redirectConfiguration: Redirect configurations for this request. + public func get(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.execute(.GET, url: url, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } /// Execute `POST` request using specified URL. @@ -246,8 +251,9 @@ public class HTTPClient { /// - url: Remote URL. /// - body: Request body. /// - deadline: Point in time by which the request must complete. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) + /// - redirectConfiguration: Redirect configurations for this request. + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } /// Execute `POST` request using specified URL. @@ -257,8 +263,9 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.execute(.POST, url: url, body: body, deadline: deadline, logger: logger) + /// - redirectConfiguration: Redirect configurations for this request. + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.execute(.POST, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } /// Execute `PATCH` request using specified URL. @@ -267,8 +274,9 @@ public class HTTPClient { /// - url: Remote URL. /// - body: Request body. /// - deadline: Point in time by which the request must complete. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) + /// - redirectConfiguration: Redirect configurations for this request. + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } /// Execute `PATCH` request using specified URL. @@ -278,8 +286,9 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.execute(.PATCH, url: url, body: body, deadline: deadline, logger: logger) + /// - redirectConfiguration: Redirect configurations for this request. + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.execute(.PATCH, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } /// Execute `PUT` request using specified URL. @@ -288,8 +297,9 @@ public class HTTPClient { /// - url: Remote URL. /// - body: Request body. /// - deadline: Point in time by which the request must complete. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) + /// - redirectConfiguration: Redirect configurations for this request. + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } /// Execute `PUT` request using specified URL. @@ -299,8 +309,9 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.execute(.PUT, url: url, body: body, deadline: deadline, logger: logger) + /// - redirectConfiguration: Redirect configurations for this request. + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.execute(.PUT, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } /// Execute `DELETE` request using specified URL. @@ -308,8 +319,9 @@ public class HTTPClient { /// - parameters: /// - url: Remote URL. /// - deadline: The time when the request must have been completed by. - public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled) + /// - redirectConfiguration: Redirect configurations for this request. + public func delete(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } /// Execute `DELETE` request using specified URL. @@ -318,8 +330,9 @@ public class HTTPClient { /// - url: Remote URL. /// - deadline: The time when the request must have been completed by. /// - logger: The logger to use for this request. - public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.execute(.DELETE, url: url, deadline: deadline, logger: logger) + /// - redirectConfiguration: Redirect configurations for this request. + public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.execute(.DELETE, url: url, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } /// Execute arbitrary HTTP request using specified URL. @@ -330,10 +343,11 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { + /// - redirectConfiguration: Redirect configurations for this request. + public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { do { let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled) + return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } catch { return self.eventLoopGroup.next().makeFailedFuture(error) } @@ -348,13 +362,14 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { + /// - redirectConfiguration: Redirect configurations for this request. + public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { do { guard let url = URL(httpURLWithSocketPath: socketPath, uri: urlPath) else { throw HTTPClientError.invalidURL } let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled) + return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } catch { return self.eventLoopGroup.next().makeFailedFuture(error) } @@ -369,13 +384,14 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { + /// - redirectConfiguration: Redirect configurations for this request. + public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { do { guard let url = URL(httpsURLWithSocketPath: secureSocketPath, uri: urlPath) else { throw HTTPClientError.invalidURL } let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled) + return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } catch { return self.eventLoopGroup.next().makeFailedFuture(error) } @@ -386,8 +402,9 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - deadline: Point in time by which the request must complete. - public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.execute(request: request, deadline: deadline, logger: HTTPClient.loggingDisabled) + /// - redirectConfiguration: Redirect configurations for this request. + public func execute(request: Request, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + return self.execute(request: request, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } /// Execute arbitrary HTTP request using specified URL. @@ -396,9 +413,10 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + /// - redirectConfiguration: Redirect configurations for this request. + public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { let accumulator = ResponseAccumulator(request: request) - return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger).futureResult + return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration).futureResult } /// Execute arbitrary HTTP request using specified URL. @@ -407,11 +425,16 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. - public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture { + /// - redirectConfiguration: Redirect configurations for this request. + public func execute(request: Request, + eventLoop: EventLoopPreference, + deadline: NIODeadline? = nil, + redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { return self.execute(request: request, eventLoop: eventLoop, deadline: deadline, - logger: HTTPClient.loggingDisabled) + logger: HTTPClient.loggingDisabled, + redirectConfiguration: redirectConfiguration) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -421,12 +444,19 @@ public class HTTPClient { /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. + /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, - logger: Logger?) -> EventLoopFuture { + logger: Logger?, + redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { let accumulator = ResponseAccumulator(request: request) - return self.execute(request: request, delegate: accumulator, eventLoop: eventLoopPreference, deadline: deadline, logger: logger).futureResult + return self.execute(request: request, + delegate: accumulator, + eventLoop: eventLoopPreference, + deadline: deadline, + logger: logger, + redirectConfiguration: redirectConfiguration).futureResult } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -435,10 +465,16 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. /// - deadline: Point in time by which the request must complete. + /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, delegate: Delegate, - deadline: NIODeadline? = nil) -> Task { - return self.execute(request: request, delegate: delegate, deadline: deadline, logger: HTTPClient.loggingDisabled) + deadline: NIODeadline? = nil, + redirectConfiguration: RedirectConfiguration? = nil) -> Task { + return self.execute(request: request, + delegate: delegate, + deadline: deadline, + logger: HTTPClient.loggingDisabled, + redirectConfiguration: redirectConfiguration) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -448,11 +484,18 @@ public class HTTPClient { /// - delegate: Delegate to process response parts. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. + /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, delegate: Delegate, deadline: NIODeadline? = nil, - logger: Logger) -> Task { - return self.execute(request: request, delegate: delegate, eventLoop: .indifferent, deadline: deadline, logger: logger) + logger: Logger, + redirectConfiguration: RedirectConfiguration? = nil) -> Task { + return self.execute(request: request, + delegate: delegate, + eventLoop: .indifferent, + deadline: deadline, + logger: logger, + redirectConfiguration: redirectConfiguration) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -463,15 +506,18 @@ public class HTTPClient { /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. + /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, - deadline: NIODeadline? = nil) -> Task { + deadline: NIODeadline? = nil, + redirectConfiguration: RedirectConfiguration? = nil) -> Task { return self.execute(request: request, delegate: delegate, eventLoop: eventLoopPreference, deadline: deadline, - logger: HTTPClient.loggingDisabled) + logger: HTTPClient.loggingDisabled, + redirectConfiguration: redirectConfiguration) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -481,11 +527,13 @@ public class HTTPClient { /// - delegate: Delegate to process response parts. /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. + /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, - logger originalLogger: Logger?) -> Task { + logger originalLogger: Logger?, + redirectConfiguration: RedirectConfiguration? = nil) -> Task { let logger = (originalLogger ?? HTTPClient.loggingDisabled).attachingRequestInformation(request, requestID: globalRequestID.add(1)) let taskEL: EventLoop switch eventLoopPreference.preference { @@ -521,7 +569,9 @@ public class HTTPClient { } let redirectHandler: RedirectHandler? - switch self.configuration.redirectConfiguration.configuration { + let prevailingConfiguration = redirectConfiguration?.configuration ?? self.configuration.redirectConfiguration.configuration + + switch prevailingConfiguration { case .follow(let max, let allowCycles): var request = request if request.redirectState == nil { @@ -531,7 +581,8 @@ public class HTTPClient { self.execute(request: newRequest, delegate: delegate, eventLoop: eventLoopPreference, - deadline: deadline) + deadline: deadline, + redirectConfiguration: .follow(max: max, allowCycles: allowCycles)) } case .disallow: redirectHandler = nil From 699df050fe2bbc631e2fe7948e3a7cd25fa4ad80 Mon Sep 17 00:00:00 2001 From: Nick Everitt Date: Sat, 27 Jun 2020 23:17:23 +0100 Subject: [PATCH 2/4] Add tests for per request redirection Motivation: Test support for redirection configuration on a per request basis. Modifications: Test that the configured request will refuse to follow redirection cycles even when the client allows them. Test that the configured request will throw due to reach the redirection limit even when the client does not follow redirections. Test that the configured request does not follow redirections even when the client allows them. Result: Per request redirection configured tested. --- .../HTTPClientTests+XCTest.swift | 3 ++ .../HTTPClientTests.swift | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift index 5fe7b2417..140baff30 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift @@ -68,7 +68,10 @@ extension HTTPClientTests { ("testDecompression", testDecompression), ("testDecompressionLimit", testDecompressionLimit), ("testLoopDetectionRedirectLimit", testLoopDetectionRedirectLimit), + ("testLoopDetectionRedirectLimitPerRequest", testLoopDetectionRedirectLimitPerRequest), ("testCountRedirectLimit", testCountRedirectLimit), + ("testCountRedirectLimitPerRequest", testCountRedirectLimitPerRequest), + ("testNoRedirectPerRequest", testNoRedirectPerRequest), ("testMultipleConcurrentRequests", testMultipleConcurrentRequests), ("testWorksWith500Error", testWorksWith500Error), ("testWorksWithHTTP10Response", testWorksWithHTTP10Response), diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index df144b70b..56d4e7e40 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -928,6 +928,21 @@ class HTTPClientTests: XCTestCase { XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) } } + + func testLoopDetectionRedirectLimitPerRequest() throws { + let localHTTPBin = HTTPBin(ssl: true) + let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), + configuration: HTTPClient.Configuration(certificateVerification: .none, redirectConfiguration: .follow(max: 5, allowCycles: true))) + + defer { + XCTAssertNoThrow(try localClient.syncShutdown()) + XCTAssertNoThrow(try localHTTPBin.shutdown()) + } + + XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .follow(max: 5, allowCycles: false)).wait(), "Should fail with redirect limit") { error in + XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) + } + } func testCountRedirectLimit() throws { let localHTTPBin = HTTPBin(ssl: true) @@ -943,6 +958,34 @@ class HTTPClientTests: XCTestCase { XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectLimitReached) } } + + func testCountRedirectLimitPerRequest() throws { + let localHTTPBin = HTTPBin(ssl: true) + let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), + configuration: HTTPClient.Configuration(certificateVerification: .none, redirectConfiguration: .disallow)) + + defer { + XCTAssertNoThrow(try localClient.syncShutdown()) + XCTAssertNoThrow(try localHTTPBin.shutdown()) + } + + XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .follow(max: 5, allowCycles: true)).wait(), "Should fail with redirect limit") { error in + XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectLimitReached) + } + } + + func testNoRedirectPerRequest() throws { + let localHTTPBin = HTTPBin(ssl: true) + let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), + configuration: HTTPClient.Configuration(certificateVerification: .none, redirectConfiguration: .follow(max: 5, allowCycles: true))) + + defer { + XCTAssertNoThrow(try localClient.syncShutdown()) + XCTAssertNoThrow(try localHTTPBin.shutdown()) + } + + XCTAssertNoThrow(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .disallow).wait()) + } func testMultipleConcurrentRequests() throws { let numberOfRequestsPerThread = 1000 From abc6d806155c15ffc15d7f3bc1e700917d0d8822 Mon Sep 17 00:00:00 2001 From: Nick Everitt Date: Mon, 29 Jun 2020 19:29:15 +0100 Subject: [PATCH 3/4] Allow redirect configuration per request Motivation: Redirect configuration is set globally when configuring the `HTTPClient`. However, it would also be useful to configure redirects on a per request basis. This would mean that a single `HTTPClient` could be configured and shared widely throughout an application, while retaining the flexibility to tailor specific requests with custom redirect handling should the need arise. Modifications: Adds new methods providing a `redirectConfiguration` argument to support per request redirection. When the request is executed the `prevailingConfiguration` is determined using the `redirectConfiguration` argument, if set. Otherwise the global configuration is used. Fixes code formatting. Result: Allows redirect handling to be configured on a per request basis, closes #196 --- Sources/AsyncHTTPClient/HTTPClient.swift | 323 ++++++++++++++---- .../HTTPClientTests.swift | 8 +- 2 files changed, 265 insertions(+), 66 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index aea1d5d96..880e0255d 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -220,7 +220,7 @@ public class HTTPClient { } } } - + /// Specifies redirect processing settings. public typealias RedirectConfiguration = Configuration.RedirectConfiguration @@ -229,8 +229,17 @@ public class HTTPClient { /// - parameters: /// - url: Remote URL. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. - public func get(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { + return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + } + + /// Execute `GET` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - deadline: Point in time by which the request must complete. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func get(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } @@ -240,8 +249,18 @@ public class HTTPClient { /// - url: Remote URL. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func get(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + return self.get(url: url, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute `GET` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func get(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.execute(.GET, url: url, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } @@ -251,8 +270,18 @@ public class HTTPClient { /// - url: Remote URL. /// - body: Request body. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { + return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + } + + /// Execute `POST` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } @@ -263,8 +292,19 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + return self.post(url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute `POST` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.execute(.POST, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } @@ -274,8 +314,18 @@ public class HTTPClient { /// - url: Remote URL. /// - body: Request body. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { + return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + } + + /// Execute `PATCH` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } @@ -286,8 +336,19 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + return self.patch(url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute `PATCH` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.execute(.PATCH, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } @@ -297,8 +358,18 @@ public class HTTPClient { /// - url: Remote URL. /// - body: Request body. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { + return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) + } + + /// Execute `PUT` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } @@ -309,8 +380,19 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + return self.put(url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute `PUT` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.execute(.PUT, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } @@ -319,8 +401,17 @@ public class HTTPClient { /// - parameters: /// - url: Remote URL. /// - deadline: The time when the request must have been completed by. - /// - redirectConfiguration: Redirect configurations for this request. - public func delete(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { + return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + } + + /// Execute `DELETE` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - deadline: The time when the request must have been completed by. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func delete(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } @@ -330,8 +421,18 @@ public class HTTPClient { /// - url: Remote URL. /// - deadline: The time when the request must have been completed by. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + return self.delete(url: url, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute `DELETE` request using specified URL. + /// + /// - parameters: + /// - url: Remote URL. + /// - deadline: The time when the request must have been completed by. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { return self.execute(.DELETE, url: url, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) } @@ -343,8 +444,20 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { + return self.execute(method, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute arbitrary HTTP request using specified URL. + /// + /// - parameters: + /// - method: Request method. + /// - url: Request url. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { do { let request = try Request(url: url, method: method, body: body) return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) @@ -362,8 +475,21 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { + return self.execute(method, socketPath: socketPath, urlPath: urlPath, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server. + /// + /// - parameters: + /// - method: Request method. + /// - socketPath: The path to the unix domain socket to connect to. + /// - urlPath: The URL path and query that will be sent to the server. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { do { guard let url = URL(httpURLWithSocketPath: socketPath, uri: urlPath) else { throw HTTPClientError.invalidURL @@ -384,8 +510,21 @@ public class HTTPClient { /// - body: Request body. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { + return self.execute(method, secureSocketPath: secureSocketPath, urlPath: urlPath, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + } + + /// Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server. + /// + /// - parameters: + /// - method: Request method. + /// - secureSocketPath: The path to the unix domain socket to connect to. + /// - urlPath: The URL path and query that will be sent to the server. + /// - body: Request body. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { do { guard let url = URL(httpsURLWithSocketPath: secureSocketPath, uri: urlPath) else { throw HTTPClientError.invalidURL @@ -402,9 +541,8 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. - public func execute(request: Request, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { - return self.execute(request: request, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture { + return self.execute(request: request, deadline: deadline, logger: HTTPClient.loggingDisabled) } /// Execute arbitrary HTTP request using specified URL. @@ -413,8 +551,19 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. - public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { + let accumulator = ResponseAccumulator(request: request) + return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger).futureResult + } + + /// Execute arbitrary HTTP request using specified URL. + /// + /// - parameters: + /// - request: HTTP request to execute. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { let accumulator = ResponseAccumulator(request: request) return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration).futureResult } @@ -425,16 +574,11 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. - public func execute(request: Request, - eventLoop: EventLoopPreference, - deadline: NIODeadline? = nil, - redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { + public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture { return self.execute(request: request, eventLoop: eventLoop, deadline: deadline, - logger: HTTPClient.loggingDisabled, - redirectConfiguration: redirectConfiguration) + logger: HTTPClient.loggingDisabled) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -444,19 +588,32 @@ public class HTTPClient { /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, - logger: Logger?, - redirectConfiguration: RedirectConfiguration? = nil) -> EventLoopFuture { - let accumulator = ResponseAccumulator(request: request) + logger: Logger?) -> EventLoopFuture { return self.execute(request: request, - delegate: accumulator, eventLoop: eventLoopPreference, deadline: deadline, logger: logger, - redirectConfiguration: redirectConfiguration).futureResult + redirectConfiguration: nil) + } + + /// Execute arbitrary HTTP request and handle response processing using provided delegate. + /// + /// - parameters: + /// - request: HTTP request to execute. + /// - eventLoop: NIO Event Loop preference. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. + public func execute(request: Request, + eventLoop eventLoopPreference: EventLoopPreference, + deadline: NIODeadline? = nil, + logger: Logger?, + redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { + let accumulator = ResponseAccumulator(request: request) + return self.execute(request: request, delegate: accumulator, eventLoop: eventLoopPreference, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration).futureResult } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -465,16 +622,58 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. + public func execute(request: Request, + delegate: Delegate, + deadline: NIODeadline? = nil) -> Task { + return self.execute(request: request, delegate: delegate, deadline: deadline, logger: HTTPClient.loggingDisabled) + } + + /// Execute arbitrary HTTP request and handle response processing using provided delegate. + /// + /// - parameters: + /// - request: HTTP request to execute. + /// - delegate: Delegate to process response parts. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + public func execute(request: Request, + delegate: Delegate, + deadline: NIODeadline? = nil, + logger: Logger) -> Task { + return self.execute(request: request, delegate: delegate, eventLoop: .indifferent, deadline: deadline, logger: logger) + } + + /// Execute arbitrary HTTP request and handle response processing using provided delegate. + /// + /// - parameters: + /// - request: HTTP request to execute. + /// - delegate: Delegate to process response parts. + /// - deadline: Point in time by which the request must complete. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. public func execute(request: Request, delegate: Delegate, deadline: NIODeadline? = nil, - redirectConfiguration: RedirectConfiguration? = nil) -> Task { + logger: Logger, + redirectConfiguration: RedirectConfiguration?) -> Task { + return self.execute(request: request, delegate: delegate, eventLoop: .indifferent, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + } + + /// Execute arbitrary HTTP request and handle response processing using provided delegate. + /// + /// - parameters: + /// - request: HTTP request to execute. + /// - delegate: Delegate to process response parts. + /// - eventLoop: NIO Event Loop preference. + /// - deadline: Point in time by which the request must complete. + public func execute(request: Request, + delegate: Delegate, + eventLoop eventLoopPreference: EventLoopPreference, + deadline: NIODeadline? = nil) -> Task { return self.execute(request: request, delegate: delegate, + eventLoop: eventLoopPreference, deadline: deadline, - logger: HTTPClient.loggingDisabled, - redirectConfiguration: redirectConfiguration) + logger: HTTPClient.loggingDisabled) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -482,19 +681,19 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. + /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. public func execute(request: Request, delegate: Delegate, + eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, - logger: Logger, - redirectConfiguration: RedirectConfiguration? = nil) -> Task { + redirectConfiguration: RedirectConfiguration?) -> Task { return self.execute(request: request, delegate: delegate, - eventLoop: .indifferent, + eventLoop: eventLoopPreference, deadline: deadline, - logger: logger, + logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) } @@ -506,18 +705,17 @@ public class HTTPClient { /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. - /// - redirectConfiguration: Redirect configurations for this request. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, - redirectConfiguration: RedirectConfiguration? = nil) -> Task { + logger: Logger?) -> Task { return self.execute(request: request, delegate: delegate, eventLoop: eventLoopPreference, deadline: deadline, - logger: HTTPClient.loggingDisabled, - redirectConfiguration: redirectConfiguration) + logger: logger, + redirectConfiguration: nil) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -527,13 +725,14 @@ public class HTTPClient { /// - delegate: Delegate to process response parts. /// - eventLoop: NIO Event Loop preference. /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: Redirect configurations for this request. + /// - logger: The logger to use for this request. + /// - redirectConfiguration: The configuration of redirect handling for this request. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, logger originalLogger: Logger?, - redirectConfiguration: RedirectConfiguration? = nil) -> Task { + redirectConfiguration: RedirectConfiguration?) -> Task { let logger = (originalLogger ?? HTTPClient.loggingDisabled).attachingRequestInformation(request, requestID: globalRequestID.add(1)) let taskEL: EventLoop switch eventLoopPreference.preference { @@ -570,7 +769,7 @@ public class HTTPClient { let redirectHandler: RedirectHandler? let prevailingConfiguration = redirectConfiguration?.configuration ?? self.configuration.redirectConfiguration.configuration - + switch prevailingConfiguration { case .follow(let max, let allowCycles): var request = request diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index 56d4e7e40..737b3938f 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -928,7 +928,7 @@ class HTTPClientTests: XCTestCase { XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) } } - + func testLoopDetectionRedirectLimitPerRequest() throws { let localHTTPBin = HTTPBin(ssl: true) let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), @@ -938,7 +938,7 @@ class HTTPClientTests: XCTestCase { XCTAssertNoThrow(try localClient.syncShutdown()) XCTAssertNoThrow(try localHTTPBin.shutdown()) } - + XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .follow(max: 5, allowCycles: false)).wait(), "Should fail with redirect limit") { error in XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) } @@ -958,7 +958,7 @@ class HTTPClientTests: XCTestCase { XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectLimitReached) } } - + func testCountRedirectLimitPerRequest() throws { let localHTTPBin = HTTPBin(ssl: true) let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), @@ -973,7 +973,7 @@ class HTTPClientTests: XCTestCase { XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectLimitReached) } } - + func testNoRedirectPerRequest() throws { let localHTTPBin = HTTPBin(ssl: true) let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), From db6fb06beec6cbecf2be1605e1921a71059d36fc Mon Sep 17 00:00:00 2001 From: Nick Everitt Date: Thu, 2 Jul 2020 21:46:50 +0100 Subject: [PATCH 4/4] Allow redirect configuration per request Motivation: Redirect configuration is set globally when configuring the `HTTPClient`. However, it would also be useful to configure redirects on a per request basis. This would mean that a single `HTTPClient` could be configured and shared widely throughout an application, while retaining the flexibility to tailor specific requests with custom redirect handling should the need arise. Modifications: Adds docstrings explanation for nil arguments. Renames the new `redirectConfiguration` to `redirects`, where it is specialising a specific request. Result: Allows redirect handling to be configured on a per request basis, closes #196 --- Sources/AsyncHTTPClient/HTTPClient.swift | 270 +++++++++--------- .../HTTPClientTests.swift | 6 +- 2 files changed, 138 insertions(+), 138 deletions(-) diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index 44869076b..aaad48487 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -228,136 +228,136 @@ public class HTTPClient { /// /// - parameters: /// - url: Remote URL. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: nil) } /// Execute `GET` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func get(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func get(url: String, deadline: NIODeadline? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: redirects) } /// Execute `GET` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.get(url: url, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.get(url: url, deadline: deadline, logger: logger, redirects: nil) } /// Execute `GET` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func get(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.execute(.GET, url: url, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func get(url: String, deadline: NIODeadline? = nil, logger: Logger, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.execute(.GET, url: url, deadline: deadline, logger: logger, redirects: redirects) } /// Execute `POST` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: nil) } /// Execute `POST` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: redirects) } /// Execute `POST` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.post(url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.post(url: url, body: body, deadline: deadline, logger: logger, redirects: nil) } /// Execute `POST` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.execute(.POST, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.execute(.POST, url: url, body: body, deadline: deadline, logger: logger, redirects: redirects) } /// Execute `PATCH` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: nil) } /// Execute `PATCH` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: redirects) } /// Execute `PATCH` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.patch(url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.patch(url: url, body: body, deadline: deadline, logger: logger, redirects: nil) } /// Execute `PATCH` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.execute(.PATCH, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.execute(.PATCH, url: url, body: body, deadline: deadline, logger: logger, redirects: redirects) } /// Execute `PUT` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) } @@ -366,74 +366,74 @@ public class HTTPClient { /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: redirects) } /// Execute `PUT` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.put(url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.put(url: url, body: body, deadline: deadline, logger: logger, redirects: nil) } /// Execute `PUT` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.execute(.PUT, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.execute(.PUT, url: url, body: body, deadline: deadline, logger: logger, redirects: redirects) } /// Execute `DELETE` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: The time when the request must have been completed by. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { - return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: nil) + return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: nil) } /// Execute `DELETE` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: The time when the request must have been completed by. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func delete(url: String, deadline: NIODeadline? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func delete(url: String, deadline: NIODeadline? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled, redirects: redirects) } /// Execute `DELETE` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: The time when the request must have been completed by. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - return self.delete(url: url, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.delete(url: url, deadline: deadline, logger: logger, redirects: nil) } /// Execute `DELETE` request using specified URL. /// /// - parameters: /// - url: Remote URL. - /// - deadline: The time when the request must have been completed by. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { - return self.execute(.DELETE, url: url, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger, redirects: RedirectConfiguration?) -> EventLoopFuture { + return self.execute(.DELETE, url: url, deadline: deadline, logger: logger, redirects: redirects) } /// Execute arbitrary HTTP request using specified URL. @@ -441,11 +441,11 @@ public class HTTPClient { /// - parameters: /// - method: Request method. /// - url: Request url. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { - return self.execute(method, url: url, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.execute(method, url: url, body: body, deadline: deadline, logger: logger, redirects: nil) } /// Execute arbitrary HTTP request using specified URL. @@ -453,14 +453,14 @@ public class HTTPClient { /// - parameters: /// - method: Request method. /// - url: Request url. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { do { let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirects: redirects) } catch { return self.eventLoopGroup.next().makeFailedFuture(error) } @@ -472,11 +472,11 @@ public class HTTPClient { /// - method: Request method. /// - socketPath: The path to the unix domain socket to connect to. /// - urlPath: The URL path and query that will be sent to the server. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { - return self.execute(method, socketPath: socketPath, urlPath: urlPath, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.execute(method, socketPath: socketPath, urlPath: urlPath, body: body, deadline: deadline, logger: logger, redirects: nil) } /// Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server. @@ -485,17 +485,17 @@ public class HTTPClient { /// - method: Request method. /// - socketPath: The path to the unix domain socket to connect to. /// - urlPath: The URL path and query that will be sent to the server. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { do { guard let url = URL(httpURLWithSocketPath: socketPath, uri: urlPath) else { throw HTTPClientError.invalidURL } let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirects: redirects) } catch { return self.eventLoopGroup.next().makeFailedFuture(error) } @@ -507,11 +507,11 @@ public class HTTPClient { /// - method: Request method. /// - secureSocketPath: The path to the unix domain socket to connect to. /// - urlPath: The URL path and query that will be sent to the server. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture { - return self.execute(method, secureSocketPath: secureSocketPath, urlPath: urlPath, body: body, deadline: deadline, logger: logger, redirectConfiguration: nil) + return self.execute(method, secureSocketPath: secureSocketPath, urlPath: urlPath, body: body, deadline: deadline, logger: logger, redirects: nil) } /// Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server. @@ -520,17 +520,17 @@ public class HTTPClient { /// - method: Request method. /// - secureSocketPath: The path to the unix domain socket to connect to. /// - urlPath: The URL path and query that will be sent to the server. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { + /// - body: Request body, the request body is empty if nil. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil, redirects: RedirectConfiguration?) -> EventLoopFuture { do { guard let url = URL(httpsURLWithSocketPath: secureSocketPath, uri: urlPath) else { throw HTTPClientError.invalidURL } let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirectConfiguration: redirectConfiguration) + return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled, redirects: redirects) } catch { return self.eventLoopGroup.next().makeFailedFuture(error) } @@ -540,7 +540,7 @@ public class HTTPClient { /// /// - parameters: /// - request: HTTP request to execute. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture { return self.execute(request: request, deadline: deadline, logger: HTTPClient.loggingDisabled) } @@ -549,7 +549,7 @@ public class HTTPClient { /// /// - parameters: /// - request: HTTP request to execute. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { let accumulator = ResponseAccumulator(request: request) @@ -560,12 +560,12 @@ public class HTTPClient { /// /// - parameters: /// - request: HTTP request to execute. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. - public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger, redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. + public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger, redirects: RedirectConfiguration?) -> EventLoopFuture { let accumulator = ResponseAccumulator(request: request) - return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration).futureResult + return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger, redirects: redirects).futureResult } /// Execute arbitrary HTTP request using specified URL. @@ -573,7 +573,7 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture { return self.execute(request: request, eventLoop: eventLoop, @@ -586,8 +586,8 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. public func execute(request: Request, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, @@ -596,7 +596,7 @@ public class HTTPClient { eventLoop: eventLoopPreference, deadline: deadline, logger: logger, - redirectConfiguration: nil) + redirects: nil) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -604,16 +604,16 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. public func execute(request: Request, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, logger: Logger?, - redirectConfiguration: RedirectConfiguration?) -> EventLoopFuture { + redirects: RedirectConfiguration?) -> EventLoopFuture { let accumulator = ResponseAccumulator(request: request) - return self.execute(request: request, delegate: accumulator, eventLoop: eventLoopPreference, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration).futureResult + return self.execute(request: request, delegate: accumulator, eventLoop: eventLoopPreference, deadline: deadline, logger: logger, redirects: redirects).futureResult } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -621,7 +621,7 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func execute(request: Request, delegate: Delegate, deadline: NIODeadline? = nil) -> Task { @@ -633,7 +633,7 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. public func execute(request: Request, delegate: Delegate, @@ -647,15 +647,15 @@ public class HTTPClient { /// - parameters: /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. public func execute(request: Request, delegate: Delegate, deadline: NIODeadline? = nil, logger: Logger, - redirectConfiguration: RedirectConfiguration?) -> Task { - return self.execute(request: request, delegate: delegate, eventLoop: .indifferent, deadline: deadline, logger: logger, redirectConfiguration: redirectConfiguration) + redirects: RedirectConfiguration?) -> Task { + return self.execute(request: request, delegate: delegate, eventLoop: .indifferent, deadline: deadline, logger: logger, redirects: redirects) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -664,7 +664,7 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, @@ -682,19 +682,19 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - redirectConfiguration: The configuration of redirect handling for this request. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, - redirectConfiguration: RedirectConfiguration?) -> Task { + redirects: RedirectConfiguration?) -> Task { return self.execute(request: request, delegate: delegate, eventLoop: eventLoopPreference, deadline: deadline, logger: HTTPClient.loggingDisabled, - redirectConfiguration: redirectConfiguration) + redirects: redirects) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -703,8 +703,8 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, @@ -715,7 +715,7 @@ public class HTTPClient { eventLoop: eventLoopPreference, deadline: deadline, logger: logger, - redirectConfiguration: nil) + redirects: nil) } /// Execute arbitrary HTTP request and handle response processing using provided delegate. @@ -724,15 +724,15 @@ public class HTTPClient { /// - request: HTTP request to execute. /// - delegate: Delegate to process response parts. /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - redirectConfiguration: The configuration of redirect handling for this request. + /// - deadline: Point in time by which the request must complete, determined by the library if nil. + /// - logger: The logger to use for this request, logging is disabled if nil. + /// - redirects: Overrides the client's configuration of redirect handling for this request, uses client's configuration if nil. public func execute(request: Request, delegate: Delegate, eventLoop eventLoopPreference: EventLoopPreference, deadline: NIODeadline? = nil, logger originalLogger: Logger?, - redirectConfiguration: RedirectConfiguration?) -> Task { + redirects: RedirectConfiguration?) -> Task { let logger = (originalLogger ?? HTTPClient.loggingDisabled).attachingRequestInformation(request, requestID: globalRequestID.add(1)) let taskEL: EventLoop switch eventLoopPreference.preference { @@ -768,7 +768,7 @@ public class HTTPClient { } let redirectHandler: RedirectHandler? - let prevailingConfiguration = redirectConfiguration?.configuration ?? self.configuration.redirectConfiguration.configuration + let prevailingConfiguration = redirects?.configuration ?? self.configuration.redirectConfiguration.configuration switch prevailingConfiguration { case .follow(let max, let allowCycles): @@ -781,7 +781,7 @@ public class HTTPClient { delegate: delegate, eventLoop: eventLoopPreference, deadline: deadline, - redirectConfiguration: .follow(max: max, allowCycles: allowCycles)) + redirects: .follow(max: max, allowCycles: allowCycles)) } case .disallow: redirectHandler = nil diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift index b2a887f42..ed94b853d 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift @@ -939,7 +939,7 @@ class HTTPClientTests: XCTestCase { XCTAssertNoThrow(try localHTTPBin.shutdown()) } - XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .follow(max: 5, allowCycles: false)).wait(), "Should fail with redirect limit") { error in + XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirects: .follow(max: 5, allowCycles: false)).wait(), "Should fail with redirect limit") { error in XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) } } @@ -969,7 +969,7 @@ class HTTPClientTests: XCTestCase { XCTAssertNoThrow(try localHTTPBin.shutdown()) } - XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .follow(max: 5, allowCycles: true)).wait(), "Should fail with redirect limit") { error in + XCTAssertThrowsError(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirects: .follow(max: 5, allowCycles: true)).wait(), "Should fail with redirect limit") { error in XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectLimitReached) } } @@ -984,7 +984,7 @@ class HTTPClientTests: XCTestCase { XCTAssertNoThrow(try localHTTPBin.shutdown()) } - XCTAssertNoThrow(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirectConfiguration: .disallow).wait()) + XCTAssertNoThrow(try localClient.get(url: "https://localhost:\(localHTTPBin.port)/redirect/infinite1", redirects: .disallow).wait()) } func testMultipleConcurrentRequests() throws {