Skip to content

Commit 9b1e9d3

Browse files
authored
add deadlines (#57)
1 parent daf66bd commit 9b1e9d3

File tree

3 files changed

+48
-19
lines changed

3 files changed

+48
-19
lines changed

Sources/AsyncHTTPClient/HTTPClient.swift

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,64 +58,63 @@ public class HTTPClient {
5858
}
5959
}
6060

61-
public func get(url: String, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
61+
public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
6262
do {
6363
let request = try Request(url: url, method: .GET)
64-
return self.execute(request: request, timeout: timeout)
64+
return self.execute(request: request, deadline: deadline)
6565
} catch {
6666
return self.eventLoopGroup.next().makeFailedFuture(error)
6767
}
6868
}
6969

70-
public func post(url: String, body: Body? = nil, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
70+
public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
7171
do {
7272
let request = try HTTPClient.Request(url: url, method: .POST, body: body)
73-
return self.execute(request: request, timeout: timeout)
73+
return self.execute(request: request, deadline: deadline)
7474
} catch {
7575
return self.eventLoopGroup.next().makeFailedFuture(error)
7676
}
7777
}
7878

79-
public func patch(url: String, body: Body? = nil, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
79+
public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
8080
do {
8181
let request = try HTTPClient.Request(url: url, method: .PATCH, body: body)
82-
return self.execute(request: request, timeout: timeout)
82+
return self.execute(request: request, deadline: deadline)
8383
} catch {
8484
return self.eventLoopGroup.next().makeFailedFuture(error)
8585
}
8686
}
8787

88-
public func put(url: String, body: Body? = nil, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
88+
public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
8989
do {
9090
let request = try HTTPClient.Request(url: url, method: .PUT, body: body)
91-
return self.execute(request: request, timeout: timeout)
91+
return self.execute(request: request, deadline: deadline)
9292
} catch {
9393
return self.eventLoopGroup.next().makeFailedFuture(error)
9494
}
9595
}
9696

97-
public func delete(url: String, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
97+
public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
9898
do {
9999
let request = try Request(url: url, method: .DELETE)
100-
return self.execute(request: request, timeout: timeout)
100+
return self.execute(request: request, deadline: deadline)
101101
} catch {
102102
return self.eventLoopGroup.next().makeFailedFuture(error)
103103
}
104104
}
105105

106-
public func execute(request: Request, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
106+
public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
107107
let accumulator = ResponseAccumulator(request: request)
108-
return self.execute(request: request, delegate: accumulator, timeout: timeout).futureResult
108+
return self.execute(request: request, delegate: accumulator, deadline: deadline).futureResult
109109
}
110110

111-
public func execute<T: HTTPClientResponseDelegate>(request: Request, delegate: T, timeout: Timeout? = nil) -> Task<T.Response> {
112-
let timeout = timeout ?? configuration.timeout
111+
public func execute<T: HTTPClientResponseDelegate>(request: Request, delegate: T, deadline: NIODeadline? = nil) -> Task<T.Response> {
113112
let eventLoop = self.eventLoopGroup.next()
114113

115114
let redirectHandler: RedirectHandler<T.Response>?
116115
if self.configuration.followRedirects {
117116
redirectHandler = RedirectHandler<T.Response>(request: request) { newRequest in
118-
self.execute(request: newRequest, delegate: delegate, timeout: timeout)
117+
self.execute(request: newRequest, delegate: delegate, deadline: deadline)
119118
}
120119
} else {
121120
redirectHandler = nil
@@ -136,8 +135,8 @@ public class HTTPClient {
136135
return channel.pipeline.addProxyHandler(for: request, decoder: decoder, encoder: encoder, tlsConfiguration: self.configuration.tlsConfiguration)
137136
}
138137
}.flatMap {
139-
if let readTimeout = timeout.read {
140-
return channel.pipeline.addHandler(IdleStateHandler(readTimeout: readTimeout))
138+
if let timeout = self.resolve(timeout: self.configuration.timeout.read, deadline: deadline) {
139+
return channel.pipeline.addHandler(IdleStateHandler(readTimeout: timeout))
141140
} else {
142141
return channel.eventLoop.makeSucceededFuture(())
143142
}
@@ -147,8 +146,8 @@ public class HTTPClient {
147146
}
148147
}
149148

150-
if let connectTimeout = timeout.connect {
151-
bootstrap = bootstrap.connectTimeout(connectTimeout)
149+
if let timeout = self.resolve(timeout: self.configuration.timeout.connect, deadline: deadline) {
150+
bootstrap = bootstrap.connectTimeout(timeout)
152151
}
153152

154153
let address = self.resolveAddress(request: request, proxy: self.configuration.proxy)
@@ -166,6 +165,19 @@ public class HTTPClient {
166165
return task
167166
}
168167

168+
private func resolve(timeout: TimeAmount?, deadline: NIODeadline?) -> TimeAmount? {
169+
switch (timeout, deadline) {
170+
case (.some(let timeout), .some(let deadline)):
171+
return min(timeout, deadline - .now())
172+
case (.some(let timeout), .none):
173+
return timeout
174+
case (.none, .some(let deadline)):
175+
return deadline - .now()
176+
case (.none, .none):
177+
return nil
178+
}
179+
}
180+
169181
private func resolveAddress(request: Request, proxy: Proxy?) -> (host: String, port: Int) {
170182
switch self.configuration.proxy {
171183
case .none:

Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ extension HTTPClientTests {
3838
("testStreaming", testStreaming),
3939
("testRemoteClose", testRemoteClose),
4040
("testReadTimeout", testReadTimeout),
41+
("testDeadline", testDeadline),
4142
("testCancel", testCancel),
4243
("testProxyPlaintext", testProxyPlaintext),
4344
("testProxyTLS", testProxyTLS),

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,22 @@ class HTTPClientTests: XCTestCase {
236236
}
237237
}
238238

239+
func testDeadline() throws {
240+
let httpBin = HttpBin()
241+
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
242+
243+
defer {
244+
try! httpClient.syncShutdown()
245+
httpBin.shutdown()
246+
}
247+
248+
XCTAssertThrowsError(try httpClient.get(url: "http://localhost:\(httpBin.port)/wait", deadline: .now() + .milliseconds(150)).wait(), "Should fail") { error in
249+
guard case let error = error as? HTTPClientError, error == .readTimeout else {
250+
return XCTFail("Should fail with readTimeout")
251+
}
252+
}
253+
}
254+
239255
func testCancel() throws {
240256
let httpBin = HttpBin()
241257
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)

0 commit comments

Comments
 (0)