@@ -31,6 +31,7 @@ extension RequestBag {
31
31
fileprivate enum State {
32
32
case initialized
33
33
case queued( HTTPRequestScheduler )
34
+ case canceledWhileQueued( CancelationReason )
34
35
case executing( HTTPRequestExecutor , RequestStreamState , ResponseStreamState )
35
36
case finished( error: Error ? )
36
37
case redirected( HTTPRequestExecutor , Int , HTTPResponseHead , URL )
@@ -95,7 +96,7 @@ extension RequestBag.StateMachine {
95
96
case . initialized, . queued:
96
97
self . state = . executing( executor, . initialized, . initialized)
97
98
return true
98
- case . finished( error: . some) :
99
+ case . finished( error: . some) , . canceledWhileQueued :
99
100
return false
100
101
case . executing, . redirected, . finished( error: . none) , . modifying:
101
102
preconditionFailure ( " Invalid state: \( self . state) " )
@@ -110,7 +111,7 @@ extension RequestBag.StateMachine {
110
111
111
112
mutating func resumeRequestBodyStream( ) -> ResumeProducingAction {
112
113
switch self . state {
113
- case . initialized, . queued:
114
+ case . initialized, . queued, . canceledWhileQueued :
114
115
preconditionFailure ( " A request stream can only be resumed, if the request was started " )
115
116
116
117
case . executing( let executor, . initialized, . initialized) :
@@ -150,7 +151,7 @@ extension RequestBag.StateMachine {
150
151
151
152
mutating func pauseRequestBodyStream( ) {
152
153
switch self . state {
153
- case . initialized, . queued:
154
+ case . initialized, . queued, . canceledWhileQueued :
154
155
preconditionFailure ( " A request stream can only be paused, if the request was started " )
155
156
case . executing( let executor, let requestState, let responseState) :
156
157
switch requestState {
@@ -185,7 +186,7 @@ extension RequestBag.StateMachine {
185
186
186
187
mutating func writeNextRequestPart( _ part: IOData , taskEventLoop: EventLoop ) -> WriteAction {
187
188
switch self . state {
188
- case . initialized, . queued:
189
+ case . initialized, . queued, . canceledWhileQueued :
189
190
preconditionFailure ( " Invalid state: \( self . state) " )
190
191
case . executing( let executor, let requestState, let responseState) :
191
192
switch requestState {
@@ -231,7 +232,7 @@ extension RequestBag.StateMachine {
231
232
232
233
mutating func finishRequestBodyStream( _ result: Result < Void , Error > ) -> FinishAction {
233
234
switch self . state {
234
- case . initialized, . queued:
235
+ case . initialized, . queued, . canceledWhileQueued :
235
236
preconditionFailure ( " Invalid state: \( self . state) " )
236
237
case . executing( let executor, let requestState, let responseState) :
237
238
switch requestState {
@@ -282,7 +283,7 @@ extension RequestBag.StateMachine {
282
283
/// - Returns: Whether the response should be forwarded to the delegate. Will be `false` if the request follows a redirect.
283
284
mutating func receiveResponseHead( _ head: HTTPResponseHead ) -> ReceiveResponseHeadAction {
284
285
switch self . state {
285
- case . initialized, . queued:
286
+ case . initialized, . queued, . canceledWhileQueued :
286
287
preconditionFailure ( " How can we receive a response, if the request hasn't started yet. " )
287
288
case . executing( let executor, let requestState, let responseState) :
288
289
guard case . initialized = responseState else {
@@ -328,7 +329,7 @@ extension RequestBag.StateMachine {
328
329
329
330
mutating func receiveResponseBodyParts( _ buffer: CircularBuffer < ByteBuffer > ) -> ReceiveResponseBodyAction {
330
331
switch self . state {
331
- case . initialized, . queued:
332
+ case . initialized, . queued, . canceledWhileQueued :
332
333
preconditionFailure ( " How can we receive a response body part, if the request hasn't started yet. " )
333
334
case . executing( _, _, . initialized) :
334
335
preconditionFailure ( " If we receive a response body, we must have received a head before " )
@@ -385,7 +386,7 @@ extension RequestBag.StateMachine {
385
386
386
387
mutating func succeedRequest( _ newChunks: CircularBuffer < ByteBuffer > ? ) -> ReceiveResponseEndAction {
387
388
switch self . state {
388
- case . initialized, . queued:
389
+ case . initialized, . queued, . canceledWhileQueued :
389
390
preconditionFailure ( " How can we receive a response body part, if the request hasn't started yet. " )
390
391
case . executing( _, _, . initialized) :
391
392
preconditionFailure ( " If we receive a response body, we must have received a head before " )
@@ -447,7 +448,7 @@ extension RequestBag.StateMachine {
447
448
448
449
private mutating func failWithConsumptionError( _ error: Error ) -> ConsumeAction {
449
450
switch self . state {
450
- case . initialized, . queued:
451
+ case . initialized, . queued, . canceledWhileQueued :
451
452
preconditionFailure ( " Invalid state: \( self . state) " )
452
453
case . executing( _, _, . initialized) :
453
454
preconditionFailure ( " Invalid state: Must have received response head, before this method is called for the first time " )
@@ -482,7 +483,7 @@ extension RequestBag.StateMachine {
482
483
483
484
private mutating func consumeMoreBodyData( ) -> ConsumeAction {
484
485
switch self . state {
485
- case . initialized, . queued:
486
+ case . initialized, . queued, . canceledWhileQueued :
486
487
preconditionFailure ( " Invalid state: \( self . state) " )
487
488
488
489
case . executing( _, _, . initialized) :
@@ -531,9 +532,24 @@ extension RequestBag.StateMachine {
531
532
preconditionFailure ( )
532
533
}
533
534
}
535
+
536
+ enum CancelAction {
537
+ case cancelScheduler( HTTPRequestScheduler ? )
538
+ case fail( FailAction )
539
+ }
540
+
541
+ mutating func cancel( _ reason: CancelationReason ) -> CancelAction {
542
+ switch self . state {
543
+ case . queued( let queuer) where reason == . deadlineExceeded:
544
+ self . state = . canceledWhileQueued( reason)
545
+ return . cancelScheduler( queuer)
546
+ default :
547
+ return . fail( self . fail ( reason. error) )
548
+ }
549
+ }
534
550
535
551
enum FailAction {
536
- case failTask( HTTPRequestScheduler ? , HTTPRequestExecutor ? )
552
+ case failTask( Error , HTTPRequestScheduler ? , HTTPRequestExecutor ? )
537
553
case cancelExecutor( HTTPRequestExecutor )
538
554
case none
539
555
}
@@ -542,31 +558,39 @@ extension RequestBag.StateMachine {
542
558
switch self . state {
543
559
case . initialized:
544
560
self . state = . finished( error: error)
545
- return . failTask( nil , nil )
561
+ return . failTask( error , nil , nil )
546
562
case . queued( let queuer) :
547
563
self . state = . finished( error: error)
548
- return . failTask( queuer, nil )
564
+ return . failTask( error , queuer, nil )
549
565
case . executing( let executor, let requestState, . buffering( _, next: . eof) ) :
550
566
self . state = . executing( executor, requestState, . buffering( . init( ) , next: . error( error) ) )
551
567
return . cancelExecutor( executor)
552
568
case . executing( let executor, _, . buffering( _, next: . askExecutorForMore) ) :
553
569
self . state = . finished( error: error)
554
- return . failTask( nil , executor)
570
+ return . failTask( error , nil , executor)
555
571
case . executing( let executor, _, . buffering( _, next: . error( _) ) ) :
556
572
// this would override another error, let's keep the first one
557
573
return . cancelExecutor( executor)
558
574
case . executing( let executor, _, . initialized) :
559
575
self . state = . finished( error: error)
560
- return . failTask( nil , executor)
576
+ return . failTask( error , nil , executor)
561
577
case . executing( let executor, _, . waitingForRemote) :
562
578
self . state = . finished( error: error)
563
- return . failTask( nil , executor)
579
+ return . failTask( error , nil , executor)
564
580
case . redirected:
565
581
self . state = . finished( error: error)
566
- return . failTask( nil , nil )
582
+ return . failTask( error , nil , nil )
567
583
case . finished( . none) :
568
584
// An error occurred after the request has finished. Ignore...
569
585
return . none
586
+ case . canceledWhileQueued( let reason) :
587
+ // if we just get a `HTTPClientError.cancelled` we can use the orignal cancelation reason
588
+ // to give a more descriptive error to the user.
589
+ if ( error as? HTTPClientError ) == . cancelled {
590
+ return . failTask( reason. error, nil , nil )
591
+ }
592
+ // otherwise we already had an intermidate connection error which we should present to the user instead
593
+ return . failTask( error, nil , nil )
570
594
case . finished( . some( _) ) :
571
595
// this might happen, if the stream consumer has failed... let's just drop the data
572
596
return . none
0 commit comments