Skip to content

Commit

Permalink
Add synchronized access to taskToRequest value
Browse files Browse the repository at this point in the history
  • Loading branch information
jguz-pubnub committed Feb 28, 2025
1 parent d1f8296 commit 06ef868
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 29 deletions.
12 changes: 7 additions & 5 deletions Sources/PubNub/Networking/HTTPSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public final class HTTPSession {
/// The `RequestOperator` that is attached to every request
public var defaultRequestOperator: RequestOperator?
/// The collection of associations between `URLSessionTask` and their corresponding `Request`
var taskToRequest: [URLSessionTask: RequestReplaceable] = [:]
let taskToRequest: Atomic<[URLSessionTask: RequestReplaceable]> = Atomic([:])
/// Default HTTPSession configuration for PubNub REST endpoints
static var pubnub = HTTPSession(configuration: .pubnub)

Expand Down Expand Up @@ -88,10 +88,12 @@ public final class HTTPSession {
}

deinit {
PubNub.log.debug("Session Destroyed \(sessionID) with active requests \(taskToRequest.values.map { $0.requestID })")
PubNub.log.debug("Session Destroyed \(sessionID) with active requests \((taskToRequest.lockedRead { $0 }).values.map { $0.requestID })")

for value in taskToRequest.values {
value.cancel(PubNubError(.sessionDeinitialized, router: value.router))
taskToRequest.lockedRead {
$0.values.forEach {
$0.cancel(PubNubError(.sessionDeinitialized, router: $0.router))
}
}
invalidateAndCancel()
}
Expand Down Expand Up @@ -203,7 +205,7 @@ public final class HTTPSession {
// so we lock to avoid crashes from creating tasks while invalidated
if !isInvalidated {
let task = session.dataTask(with: urlRequestCopy)
taskToRequest[task] = request
taskToRequest.lockedWrite { $0[task] = request }
request.didCreate(task)
} else {
PubNub.log.warn("Attempted to create task from invalidated session: \(sessionID)")
Expand Down
18 changes: 9 additions & 9 deletions Sources/PubNub/Networking/HTTPSessionDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ extension HTTPSessionDelegate: URLSessionDataDelegate {

// Set invalidated in case this happened unexpectedly
sessionBridge?.isInvalidated = true

sessionBridge?.sessionInvalidated(with: error)
}

Expand All @@ -47,7 +46,6 @@ extension HTTPSessionDelegate: URLSessionDataDelegate {

// Remove request/task from list
sessionBridge?.didComplete(task)

sessionBridge?.sessionStream?.emitURLSession(session, task: task, didCompleteWith: error)
}

Expand All @@ -67,7 +65,6 @@ extension HTTPSessionDelegate: URLSessionDataDelegate {
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
sessionBridge?.sessionStream?.emitURLSession(session, didReceive: challenge)

completionHandler(.performDefaultHandling, nil)
}
}
Expand All @@ -93,21 +90,24 @@ protocol SessionStateBridge: AnyObject {

extension HTTPSession: SessionStateBridge {
func request(for task: URLSessionTask) -> RequestReplaceable? {
return taskToRequest[task]
taskToRequest.lockedRead { $0[task] }
}

func didComplete(_ task: URLSessionTask) {
// Cleanup the task/requst map
taskToRequest.removeValue(forKey: task)
taskToRequest.lockedWrite { $0.removeValue(forKey: task) }
}

func sessionInvalidated(with error: Error?) {
// Notify the requests that the tasks have been invalidated
taskToRequest.values.forEach {
$0.cancel(PubNubError(.sessionInvalidated, router: $0.router, underlying: error))
taskToRequest.lockedRead {
$0.values.forEach {
$0.cancel(PubNubError(.sessionInvalidated, router: $0.router, underlying: error))
}
}

// Clean up the task dictionary
taskToRequest.removeAll()
taskToRequest.lockedWrite {
$0.removeAll()
}
}
}
31 changes: 16 additions & 15 deletions Sources/PubNub/Networking/Routers/PushRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,13 @@ struct RegisteredPushChannelsResponseDecoder: ResponseDecoder {
return .failure(PubNubError(.malformedResponseBody, response: response))
}

let pushListPayload = RegisteredPushChannelsPayloadResponse(channels: stringArray)

let decodedResponse = EndpointResponse<RegisteredPushChannelsPayloadResponse>(router: response.router,
request: response.request,
response: response.response,
data: response.data,
payload: pushListPayload)
let decodedResponse = EndpointResponse<RegisteredPushChannelsPayloadResponse>(
router: response.router,
request: response.request,
response: response.response,
data: response.data,
payload: RegisteredPushChannelsPayloadResponse(channels: stringArray)
)

return .success(decodedResponse)
} catch {
Expand All @@ -238,23 +238,24 @@ struct ModifyPushResponseDecoder: ResponseDecoder {
do {
let anyJSONPayload = try Constant.jsonDecoder.decode(AnyJSON.self, from: response.payload)

guard let anyArray = anyJSONPayload.arrayOptional,
anyArray.first as? Int != nil, anyArray.last as? String != nil
else {
guard let anyArray = anyJSONPayload.arrayOptional, anyArray.first is Int, anyArray.last is String else {
return .failure(PubNubError(.malformedResponseBody, response: response))
}

let endpoint = (response.router as? PushRouter)?.endpoint

let payload = ModifiedPushChannelsPayloadResponse(
added: endpoint?.addedChannels ?? [],
removed: endpoint?.removedChannels ?? []
)

let decodedResponse = EndpointResponse<ModifiedPushChannelsPayloadResponse>(router: response.router,
request: response.request,
response: response.response,
data: response.data,
payload: payload)
let decodedResponse = EndpointResponse<ModifiedPushChannelsPayloadResponse>(
router: response.router,
request: response.request,
response: response.response,
data: response.data,
payload: payload
)

return .success(decodedResponse)
} catch {
Expand Down

0 comments on commit 06ef868

Please sign in to comment.