Skip to content

Commit 03e83e2

Browse files
authored
Add timeout configuration if the connection does not receive a response (#101)
1 parent ce8213a commit 03e83e2

File tree

5 files changed

+44
-14
lines changed

5 files changed

+44
-14
lines changed

Sources/APNSwift/APNSwiftConfiguration.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ public struct APNSwiftConfiguration {
168168
public var topic: String
169169
public var environment: Environment
170170
internal var logger: Logger?
171+
/// Optional timeout time if the connection does not receive a response.
172+
public var timeout: TimeAmount? = nil
171173

172174
public var url: URL {
173175
switch environment {
@@ -197,7 +199,8 @@ public struct APNSwiftConfiguration {
197199
authenticationMethod: AuthenticationMethod,
198200
topic: String,
199201
environment: APNSwiftConfiguration.Environment,
200-
logger: Logger? = nil
202+
logger: Logger? = nil,
203+
timeout: TimeAmount? = nil
201204
) {
202205
self.topic = topic
203206
self.authenticationMethod = authenticationMethod
@@ -206,6 +209,7 @@ public struct APNSwiftConfiguration {
206209
logger[metadataKey: "origin"] = "APNSwift"
207210
self.logger = logger
208211
}
212+
self.timeout = timeout
209213
}
210214
}
211215

Sources/APNSwift/APNSwiftConnection.swift

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,20 +200,43 @@ public final class APNSwiftConnection: APNSwiftClient {
200200
request: payload,
201201
responsePromise: responsePromise
202202
)
203-
204203
streamPromise.futureResult.cascadeFailure(to: responsePromise)
205-
return streamPromise.futureResult.flatMap { stream in
206-
logger?.info("Send - sending")
207-
return stream.writeAndFlush(context).flatMapErrorThrowing { error in
208-
logger?.info("Send - sending - failed - \(error)")
209-
responsePromise.fail(error)
210-
throw error
204+
205+
let timeoutPromise = self.channel.eventLoop.makePromise(of: Void.self)
206+
responsePromise.futureResult.cascade(to: timeoutPromise)
207+
timeoutPromise.futureResult.cascadeFailure(to: responsePromise)
208+
var timeoutTask: Scheduled<Any>? = nil
209+
let timeoutTime = configuration.timeout
210+
211+
return streamPromise.futureResult
212+
.flatMap { stream in
213+
logger?.info("Send - sending")
214+
if let timeoutTime = timeoutTime {
215+
timeoutTask = stream.eventLoop.scheduleTask(in: timeoutTime) {
216+
logger?.warning("Send - sending - failed - No response was received within the timeout.")
217+
return timeoutPromise.fail(NoResponseWithinTimeoutError())
218+
}
219+
} else {
220+
timeoutPromise.succeed(())
221+
}
222+
223+
return stream.writeAndFlush(context).flatMapErrorThrowing { error in
224+
logger?.info("Send - sending - failed - \(error)")
225+
responsePromise.fail(error)
226+
throw error
227+
}
228+
}
229+
.flatMap {
230+
responsePromise
231+
.futureResult
232+
.and(timeoutPromise.futureResult)
233+
.map { _ in () }
234+
}
235+
.always { _ in
236+
timeoutTask?.cancel()
211237
}
212-
}.flatMap {
213-
responsePromise.futureResult
214-
}
215238
}
216-
239+
217240
var onClose: EventLoopFuture<Void> {
218241
logger?.debug("Connection - closed")
219242
return self.channel.closeFuture

Sources/APNSwift/APNSwiftErrors.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import Foundation
1818
public struct NoResponseReceivedBeforeConnectionEnded: Error, Equatable {}
1919
/// An error where a request was made to Apple, but the response body buffer was nil
2020
public struct NoResponseBodyFromApple: Error, Equatable {}
21+
/// An error where no the connection received no response within the timeout period
22+
public struct NoResponseWithinTimeoutError: Error, Equatable {}
2123

2224
public struct APNSwiftError: Equatable {
2325
public enum ResponseError: Error, Equatable {

Tests/APNSwiftTests/APNSwiftConfigurationTests.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class APNSwiftConfigurationTests: XCTestCase {
2727
teamIdentifier: "MY_TEAM_ID"
2828
),
2929
topic: "MY_TOPIC",
30-
environment: environment
30+
environment: environment,
31+
timeout: .seconds(5)
3132
)
3233

3334
switch environment {
@@ -46,6 +47,7 @@ class APNSwiftConfigurationTests: XCTestCase {
4647
XCTFail("expected JWT auth method")
4748
}
4849
XCTAssertEqual(apnsConfiguration.topic, "MY_TOPIC")
50+
XCTAssertEqual(apnsConfiguration.timeout, .seconds(5))
4951

5052
}
5153

Tests/APNSwiftTests/APNSwiftRequestTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ final class APNSwiftRequestTests: XCTestCase {
304304
// Should have changed
305305
XCTAssertFalse(newCachedToken == bearerToken.currentBearerToken)
306306
bearerToken.cancel()
307-
308307
}
309308

310309
let validAuthKey = """

0 commit comments

Comments
 (0)