@@ -28,30 +28,48 @@ struct HTTP1ConnectionStateMachine {
28
28
29
29
enum Action {
30
30
/// A action to execute, when we consider a request "done".
31
- enum FinalStreamAction {
31
+ enum FinalSuccessfulStreamAction {
32
32
/// Close the connection
33
33
case close
34
34
/// If the server has replied, with a status of 200...300 before all data was sent, a request is considered succeeded,
35
35
/// as soon as we wrote the request end onto the wire.
36
- case sendRequestEnd
36
+ ///
37
+ /// The promise is an optional write promise.
38
+ case sendRequestEnd( EventLoopPromise < Void > ? )
37
39
/// Inform an observer that the connection has become idle
38
40
case informConnectionIsIdle
41
+ }
42
+
43
+ /// A action to execute, when we consider a request "done".
44
+ enum FinalFailedStreamAction {
45
+ /// Close the connection
46
+ ///
47
+ /// The promise is an optional write promise.
48
+ case close( EventLoopPromise < Void > ? )
49
+ /// Inform an observer that the connection has become idle
50
+ ///
51
+ /// The promise is an optional write promise.
52
+ case informConnectionIsIdle( EventLoopPromise < Void > ? )
53
+ /// Fail the write promise
54
+ case failWritePromise( EventLoopPromise < Void > ? )
39
55
/// Do nothing.
40
56
case none
41
57
}
42
58
43
59
case sendRequestHead( HTTPRequestHead , startBody: Bool )
44
- case sendBodyPart( IOData )
45
- case sendRequestEnd
60
+ case sendBodyPart( IOData , EventLoopPromise < Void > ? )
61
+ case sendRequestEnd( EventLoopPromise < Void > ? )
62
+ case failSendBodyPart( Error , EventLoopPromise < Void > ? )
63
+ case failSendStreamFinished( Error , EventLoopPromise < Void > ? )
46
64
47
65
case pauseRequestBodyStream
48
66
case resumeRequestBodyStream
49
67
50
68
case forwardResponseHead( HTTPResponseHead , pauseRequestBodyStream: Bool )
51
69
case forwardResponseBodyParts( CircularBuffer < ByteBuffer > )
52
70
53
- case failRequest( Error , FinalStreamAction )
54
- case succeedRequest( FinalStreamAction , CircularBuffer < ByteBuffer > )
71
+ case failRequest( Error , FinalFailedStreamAction )
72
+ case succeedRequest( FinalSuccessfulStreamAction , CircularBuffer < ByteBuffer > )
55
73
56
74
case read
57
75
case close
@@ -173,7 +191,7 @@ struct HTTP1ConnectionStateMachine {
173
191
// as closed.
174
192
//
175
193
// TODO: AHC should support a fast rescheduling mechanism here.
176
- return . failRequest( HTTPClientError . remoteConnectionClosed, . none )
194
+ return . failRequest( HTTPClientError . remoteConnectionClosed, . failWritePromise ( nil ) )
177
195
178
196
case . idle:
179
197
var requestStateMachine = HTTPRequestStateMachine ( isChannelWritable: self . isChannelWritable)
@@ -189,25 +207,25 @@ struct HTTP1ConnectionStateMachine {
189
207
}
190
208
}
191
209
192
- mutating func requestStreamPartReceived( _ part: IOData ) -> Action {
210
+ mutating func requestStreamPartReceived( _ part: IOData , promise : EventLoopPromise < Void > ? ) -> Action {
193
211
guard case . inRequest( var requestStateMachine, let close) = self . state else {
194
212
preconditionFailure ( " Invalid state: \( self . state) " )
195
213
}
196
214
197
215
return self . avoidingStateMachineCoW { state -> Action in
198
- let action = requestStateMachine. requestStreamPartReceived ( part)
216
+ let action = requestStateMachine. requestStreamPartReceived ( part, promise : promise )
199
217
state = . inRequest( requestStateMachine, close: close)
200
218
return state. modify ( with: action)
201
219
}
202
220
}
203
221
204
- mutating func requestStreamFinished( ) -> Action {
222
+ mutating func requestStreamFinished( promise : EventLoopPromise < Void > ? ) -> Action {
205
223
guard case . inRequest( var requestStateMachine, let close) = self . state else {
206
224
preconditionFailure ( " Invalid state: \( self . state) " )
207
225
}
208
226
209
227
return self . avoidingStateMachineCoW { state -> Action in
210
- let action = requestStateMachine. requestStreamFinished ( )
228
+ let action = requestStateMachine. requestStreamFinished ( promise : promise )
211
229
state = . inRequest( requestStateMachine, close: close)
212
230
return state. modify ( with: action)
213
231
}
@@ -377,10 +395,10 @@ extension HTTP1ConnectionStateMachine.State {
377
395
return . pauseRequestBodyStream
378
396
case . resumeRequestBodyStream:
379
397
return . resumeRequestBodyStream
380
- case . sendBodyPart( let part) :
381
- return . sendBodyPart( part)
382
- case . sendRequestEnd:
383
- return . sendRequestEnd
398
+ case . sendBodyPart( let part, let writePromise ) :
399
+ return . sendBodyPart( part, writePromise )
400
+ case . sendRequestEnd( let writePromise ) :
401
+ return . sendRequestEnd( writePromise )
384
402
case . forwardResponseHead( let head, let pauseRequestBodyStream) :
385
403
return . forwardResponseHead( head, pauseRequestBodyStream: pauseRequestBodyStream)
386
404
case . forwardResponseBodyParts( let parts) :
@@ -390,41 +408,57 @@ extension HTTP1ConnectionStateMachine.State {
390
408
preconditionFailure ( " Invalid state: \( self ) " )
391
409
}
392
410
393
- let newFinalAction : HTTP1ConnectionStateMachine . Action . FinalStreamAction
411
+ let newFinalAction : HTTP1ConnectionStateMachine . Action . FinalSuccessfulStreamAction
394
412
switch finalAction {
395
413
case . close:
396
414
self = . closing
397
415
newFinalAction = . close
398
- case . sendRequestEnd:
399
- newFinalAction = . sendRequestEnd
416
+ case . sendRequestEnd( let writePromise ) :
417
+ newFinalAction = . sendRequestEnd( writePromise )
400
418
case . none:
401
419
self = . idle
402
420
newFinalAction = close ? . close : . informConnectionIsIdle
403
421
}
404
422
return . succeedRequest( newFinalAction, finalParts)
405
423
406
424
case . failRequest( let error, let finalAction) :
407
- switch self {
408
- case . initialized:
425
+ switch ( self , finalAction ) {
426
+ case ( . initialized, _ ) :
409
427
preconditionFailure ( " Invalid state: \( self ) " )
410
- case . idle:
428
+
429
+ case ( . idle, _) :
411
430
preconditionFailure ( " How can we fail a task, if we are idle " )
412
- case . inRequest( _, close: let close) :
413
- if close || finalAction == . close {
414
- self = . closing
415
- return . failRequest( error, . close)
416
- } else {
417
- self = . idle
418
- return . failRequest( error, . informConnectionIsIdle)
419
- }
420
431
421
- case . closing:
432
+ // If we are either in .inRequest(_, close: true) or the final action is .close
433
+ // we have to fail the request with .close()
434
+ case ( . inRequest( _, let close) , . none) where close:
435
+ self = . closing
436
+ return . failRequest( error, . close( nil ) )
437
+
438
+ case ( . inRequest( _, _) , . close( let writePromise) ) :
439
+ self = . closing
440
+ return . failRequest( error, . close( writePromise) )
441
+
442
+ // otherwise we fail with .informConnectionIsIdle
443
+ case ( . inRequest( _, _) , . none) :
444
+ self = . idle
445
+ return . failRequest( error, . informConnectionIsIdle( nil ) )
446
+
447
+ case ( . closing, . close( let writePromise) ) :
448
+ return . failRequest( error, . failWritePromise( writePromise) )
449
+
450
+ case ( . closing, . none) :
422
451
return . failRequest( error, . none)
423
- case . closed:
452
+
453
+ case ( . closed, . close( let writePromise) ) :
454
+ // this state can be reached, if the connection was unexpectedly closed by remote
455
+ return . failRequest( error, . failWritePromise( writePromise) )
456
+
457
+ case ( . closed, . none) :
424
458
// this state can be reached, if the connection was unexpectedly closed by remote
425
459
return . failRequest( error, . none)
426
460
427
- case . modifying:
461
+ case ( . modifying, _ ) :
428
462
preconditionFailure ( " Invalid state: \( self ) " )
429
463
}
430
464
@@ -433,6 +467,12 @@ extension HTTP1ConnectionStateMachine.State {
433
467
434
468
case . wait:
435
469
return . wait
470
+
471
+ case . failSendBodyPart( let error, let writePromise) :
472
+ return . failSendBodyPart( error, writePromise)
473
+
474
+ case . failSendStreamFinished( let error, let writePromise) :
475
+ return . failSendStreamFinished( error, writePromise)
436
476
}
437
477
}
438
478
}
0 commit comments