@@ -170,8 +170,11 @@ public class CRTClientEngine: HTTPClient {
170170 self . logger. debug ( " Using HTTP/1.1 connection " )
171171 let crtRequest = try request. toHttpRequest ( )
172172 return try await withCheckedThrowingContinuation { ( continuation: StreamContinuation ) in
173- let requestOptions = makeHttpRequestStreamOptions ( request: crtRequest,
174- continuation: continuation)
173+ let wrappedContinuation = ContinuationWrapper ( continuation)
174+ let requestOptions = makeHttpRequestStreamOptions (
175+ request: crtRequest,
176+ continuation: wrappedContinuation
177+ )
175178 do {
176179 let stream = try connection. makeRequest ( requestOptions: requestOptions)
177180 try stream. activate ( )
@@ -217,28 +220,34 @@ public class CRTClientEngine: HTTPClient {
217220 }
218221 }
219222 } catch {
220- continuation. resume ( throwing: error)
223+ logger. error ( error. localizedDescription)
224+ wrappedContinuation. safeResume ( error: error)
221225 }
222226 }
223227 }
224228 } catch {
225- continuation. resume ( throwing: error)
229+ logger. error ( error. localizedDescription)
230+ wrappedContinuation. safeResume ( error: error)
226231 }
227232 }
228233 case . version_2:
229234 self . logger. debug ( " Using HTTP/2 connection " )
230235 let crtRequest = try request. toHttp2Request ( )
231236 return try await withCheckedThrowingContinuation { ( continuation: StreamContinuation ) in
232- let requestOptions = makeHttpRequestStreamOptions ( request: crtRequest,
233- continuation: continuation,
234- http2ManualDataWrites: true )
237+ let wrappedContinuation = ContinuationWrapper ( continuation)
238+ let requestOptions = makeHttpRequestStreamOptions (
239+ request: crtRequest,
240+ continuation: wrappedContinuation,
241+ http2ManualDataWrites: true
242+ )
235243 let stream : HTTP2Stream
236244 do {
237245 // swiftlint:disable:next force_cast
238246 stream = try connection. makeRequest ( requestOptions: requestOptions) as! HTTP2Stream
239247 try stream. activate ( )
240248 } catch {
241- continuation. resume ( throwing: error)
249+ logger. error ( error. localizedDescription)
250+ wrappedContinuation. safeResume ( error: error)
242251 return
243252 }
244253
@@ -267,29 +276,29 @@ public class CRTClientEngine: HTTPClient {
267276 let crtRequest = try request. toHttp2Request ( )
268277
269278 return try await withCheckedThrowingContinuation { ( continuation: StreamContinuation ) in
279+ let wrappedContinuation = ContinuationWrapper ( continuation)
270280 let requestOptions = makeHttpRequestStreamOptions (
271281 request: crtRequest,
272- continuation: continuation ,
282+ continuation: wrappedContinuation ,
273283 http2ManualDataWrites: true
274284 )
275- Task {
285+ Task { [ logger ] in
276286 let stream : HTTP2Stream
277287 do {
278288 stream = try await connectionMgr. acquireStream ( requestOptions: requestOptions)
279289 } catch {
280- continuation. resume ( throwing: error)
290+ logger. error ( error. localizedDescription)
291+ wrappedContinuation. safeResume ( error: error)
281292 return
282293 }
283294
284295 // At this point, continuation is resumed when the initial headers are received
285296 // it is now safe to write the body
286297 // writing is done in a separate task to avoid blocking the continuation
287- Task { [ logger] in
288- do {
289- try await stream. write ( body: request. body)
290- } catch {
291- logger. error ( error. localizedDescription)
292- }
298+ do {
299+ try await stream. write ( body: request. body)
300+ } catch {
301+ logger. error ( error. localizedDescription)
293302 }
294303 }
295304 }
@@ -298,7 +307,7 @@ public class CRTClientEngine: HTTPClient {
298307 /// Creates a `HTTPRequestOptions` object that can be used to make a HTTP request
299308 /// - Parameters:
300309 /// - request: The `HTTPRequestBase` object that contains the request information
301- /// - continuation: The continuation that will be resumed when the request is complete
310+ /// - continuation: The wrapped continuation that will be resumed when the request is complete
302311 /// - http2ManualDataWrites: Whether or not the request is using HTTP/2 manual data writes, defaults to `false`
303312 /// If set to false, HTTP/2 manual data writes will be disabled and result in a runtime error on writing on the
304313 /// HTTP/2 stream
@@ -308,7 +317,7 @@ public class CRTClientEngine: HTTPClient {
308317 /// - Returns: A `HTTPRequestOptions` object that can be used to make a HTTP request
309318 private func makeHttpRequestStreamOptions(
310319 request: HTTPRequestBase ,
311- continuation: StreamContinuation ,
320+ continuation: ContinuationWrapper ,
312321 http2ManualDataWrites: Bool = false
313322 ) -> HTTPRequestOptions {
314323 let response = HttpResponse ( )
@@ -330,7 +339,7 @@ public class CRTClientEngine: HTTPClient {
330339 // resume the continuation as soon as we have all the initial headers
331340 // this allows callers to start reading the response as it comes in
332341 // instead of waiting for the entire response to be received
333- continuation. resume ( returning : response)
342+ continuation. safeResume ( response : response)
334343 } onIncomingBody: { bodyChunk in
335344 self . logger. debug ( " Body chunk received " )
336345 do {
@@ -348,7 +357,7 @@ public class CRTClientEngine: HTTPClient {
348357 response. statusCode = makeStatusCode ( statusCode)
349358 case . failure( let error) :
350359 self . logger. error ( " Response encountered an error: \( error) " )
351- continuation. resume ( throwing : error)
360+ continuation. safeResume ( error : error)
352361 }
353362
354363 // closing the stream is required to signal to the caller that the response is complete
@@ -361,4 +370,22 @@ public class CRTClientEngine: HTTPClient {
361370 response. body = . stream( stream)
362371 return requestOptions
363372 }
373+
374+ class ContinuationWrapper {
375+ private var continuation : StreamContinuation ?
376+
377+ public init ( _ continuation: StreamContinuation ) {
378+ self . continuation = continuation
379+ }
380+
381+ public func safeResume( response: HttpResponse ) {
382+ continuation? . resume ( returning: response)
383+ self . continuation = nil
384+ }
385+
386+ public func safeResume( error: Error ) {
387+ continuation? . resume ( throwing: error)
388+ self . continuation = nil
389+ }
390+ }
364391}
0 commit comments