@@ -38,6 +38,7 @@ extension Transaction {
3838 case requestHeadSent
3939 case producing
4040 case paused( continuation: CheckedContinuation < Void , Error > ? )
41+ case endForwarded
4142 case finished
4243 }
4344
@@ -97,7 +98,8 @@ extension Transaction {
9798 bodyStreamContinuation: CheckedContinuation < Void , Error > ?
9899 )
99100
100- case failRequestStreamContinuation( CheckedContinuation < Void , Error > , Error )
101+ case failRequestStreamContinuation( CheckedContinuation < Void , Error > , Error , HTTPRequestExecutor )
102+ case cancelExecutor( HTTPRequestExecutor )
101103 }
102104
103105 mutating func fail( _ error: Error ) -> FailAction {
@@ -135,7 +137,7 @@ extension Transaction {
135137 bodyStreamContinuation: continuation
136138 )
137139
138- case . requestHeadSent, . finished, . producing, . paused( continuation: . none) :
140+ case . requestHeadSent, . endForwarded , . finished, . producing, . paused( continuation: . none) :
139141 self . state = . finished( error: error)
140142 return . failResponseHead(
141143 context. continuation,
@@ -156,12 +158,29 @@ extension Transaction {
156158 context. executor,
157159 bodyStreamContinuation: bodyStreamContinuation
158160 )
159- case . finished, . producing, . requestHeadSent:
161+ case . endForwarded , . finished, . producing, . requestHeadSent:
160162 return . failResponseStream( source, error, context. executor, bodyStreamContinuation: nil )
161163 }
162164
163- case . finished( error: _) ,
164- . executing( _, _, . finished) :
165+ case . executing( let context, let requestStreamState, . finished) :
166+ // an error occured after full response received, but before the full request was sent
167+ self . state = . finished( error: error)
168+ switch requestStreamState {
169+ case . paused( let bodyStreamContinuation) :
170+ if let bodyStreamContinuation {
171+ return . failRequestStreamContinuation(
172+ bodyStreamContinuation,
173+ error,
174+ context. executor
175+ )
176+ } else {
177+ return . cancelExecutor( context. executor)
178+ }
179+ case . endForwarded, . finished, . producing, . requestHeadSent:
180+ return . cancelExecutor( context. executor)
181+ }
182+
183+ case . finished( error: _) :
165184 return . none
166185 }
167186 }
@@ -232,7 +251,7 @@ extension Transaction {
232251 self . state = . executing( context, . producing, responseState)
233252 return . resumeStream( continuation)
234253
235- case . executing( _, . finished, _) :
254+ case . executing( _, . endForwarded , _ ) , . executing ( _ , . finished, _) :
236255 // the channels writability changed to writable after we have forwarded all the
237256 // request bytes. Can be ignored.
238257 return . none
@@ -254,6 +273,7 @@ extension Transaction {
254273 self . state = . executing( context, . paused( continuation: nil ) , responseSteam)
255274
256275 case . executing( _, . paused, _) ,
276+ . executing( _, . endForwarded, _) ,
257277 . executing( _, . finished, _) ,
258278 . finished:
259279 // the channels writability changed to paused after we have already forwarded all
@@ -298,7 +318,7 @@ extension Transaction {
298318 " A write continuation already exists, but we tried to set another one. Invalid state: \( self . state) "
299319 )
300320
301- case . finished, . executing( _, . finished, _) :
321+ case . finished, . executing( _, . endForwarded , _ ) , . executing ( _ , . finished, _) :
302322 return . fail
303323 }
304324 }
@@ -309,6 +329,7 @@ extension Transaction {
309329 . queued,
310330 . deadlineExceededWhileQueued,
311331 . executing( _, . requestHeadSent, _) ,
332+ . executing( _, . endForwarded, _) ,
312333 . executing( _, . finished, _) :
313334 preconditionFailure (
314335 " A request stream can only produce, if the request was started. Invalid state: \( self . state) "
@@ -343,6 +364,7 @@ extension Transaction {
343364 case . initialized,
344365 . queued,
345366 . deadlineExceededWhileQueued,
367+ . executing( _, . endForwarded, _) ,
346368 . executing( _, . finished, _) :
347369 preconditionFailure ( " Invalid state: \( self . state) " )
348370
@@ -355,17 +377,38 @@ extension Transaction {
355377 . executing( let context, . paused( continuation: . none) , let responseState) ,
356378 . executing( let context, . requestHeadSent, let responseState) :
357379
358- switch responseState {
359- case . finished:
360- // if the response stream has already finished before the request, we must succeed
361- // the final continuation.
362- self . state = . finished( error: nil )
363- return . forwardStreamFinished( context. executor)
380+ self . state = . executing( context, . endForwarded, responseState)
381+ return . forwardStreamFinished( context. executor)
364382
365- case . waitingForResponseHead, . streamingBody:
366- self . state = . executing( context, . finished, responseState)
367- return . forwardStreamFinished( context. executor)
368- }
383+ case . finished:
384+ return . none
385+ }
386+ }
387+
388+ enum RequestBodyStreamSentAction {
389+ case none
390+ case failure( Error )
391+ }
392+
393+ mutating func requestBodyStreamSent( ) -> RequestBodyStreamSentAction {
394+ switch self . state {
395+ case . initialized,
396+ . queued,
397+ . deadlineExceededWhileQueued,
398+ . executing( _, . requestHeadSent, _) ,
399+ . executing( _, . finished, _) ,
400+ . executing( _, . producing, _) ,
401+ . executing( _, . paused, _) :
402+ assertionFailure ( " Invalid state: \( self . state) " )
403+ return . failure( HTTPClientError . internalStateFailure ( ) )
404+
405+ case . executing( _, . endForwarded, . finished) :
406+ self . state = . finished( error: nil )
407+ return . none
408+
409+ case . executing( let context, . endForwarded, let responseState) :
410+ self . state = . executing( context, . finished, responseState)
411+ return . none
369412
370413 case . finished:
371414 return . none
@@ -482,7 +525,7 @@ extension Transaction {
482525 switch requestState {
483526 case . finished:
484527 self . state = . finished( error: nil )
485- case . paused, . producing, . requestHeadSent:
528+ case . paused, . producing, . requestHeadSent, . endForwarded :
486529 self . state = . executing( context, requestState, . finished)
487530 }
488531 return . finishResponseStream( source, finalBody: newChunks)
@@ -497,6 +540,15 @@ extension Transaction {
497540 }
498541 }
499542
543+ mutating func httpResponseStreamTerminated( ) -> FailAction {
544+ switch self . state {
545+ case . executing( _, _, . finished) , . finished:
546+ return . none
547+ default :
548+ return self . fail ( HTTPClientError . cancelled)
549+ }
550+ }
551+
500552 enum DeadlineExceededAction {
501553 case none
502554 case cancelSchedulerOnly( scheduler: HTTPRequestScheduler )
@@ -538,7 +590,7 @@ extension Transaction {
538590 executor: context. executor,
539591 bodyStreamContinuation: continuation
540592 )
541- case . requestHeadSent, . finished, . producing, . paused( continuation: . none) :
593+ case . requestHeadSent, . endForwarded , . finished, . producing, . paused( continuation: . none) :
542594 self . state = . finished( error: error)
543595 return . cancel(
544596 requestContinuation: context. continuation,
0 commit comments