@@ -139,30 +139,13 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
139139 // MARK: Run Actions
140140
141141 private func run( _ action: HTTPRequestStateMachine . Action , context: ChannelHandlerContext ) {
142- // NOTE: We can bang the request in the following actions, since the `HTTPRequestStateMachine`
143- // ensures, that actions that require a request are only called, if the request is
144- // still present. The request is only nilled as a response to a state machine action
145- // (.failRequest or .succeedRequest).
146-
147142 switch action {
148143 case . sendRequestHead( let head, let startBody) :
149- if startBody {
150- context. writeAndFlush ( self . wrapOutboundOut ( . head( head) ) , promise: nil )
151- self . request!. requestHeadSent ( )
152- self . request!. resumeRequestBodyStream ( )
153- } else {
154- context. write ( self . wrapOutboundOut ( . head( head) ) , promise: nil )
155- context. write ( self . wrapOutboundOut ( . end( nil ) ) , promise: nil )
156- context. flush ( )
157-
158- self . request!. requestHeadSent ( )
159-
160- if let timeoutAction = self . idleReadTimeoutStateMachine? . requestEndSent ( ) {
161- self . runTimeoutAction ( timeoutAction, context: context)
162- }
163- }
144+ self . sendRequestHead ( head, startBody: startBody, context: context)
164145
165146 case . pauseRequestBodyStream:
147+ // We can force unwrap the request here, as we have just validated in the state machine,
148+ // that the request is neither failed nor finished yet
166149 self . request!. pauseRequestBodyStream ( )
167150
168151 case . sendBodyPart( let data) :
@@ -182,18 +165,29 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
182165 break
183166
184167 case . resumeRequestBodyStream:
168+ // We can force unwrap the request here, as we have just validated in the state machine,
169+ // that the request is neither failed nor finished yet
185170 self . request!. resumeRequestBodyStream ( )
186171
187172 case . forwardResponseHead( let head, pauseRequestBodyStream: let pauseRequestBodyStream) :
173+ // We can force unwrap the request here, as we have just validated in the state machine,
174+ // that the request is neither failed nor finished yet
188175 self . request!. receiveResponseHead ( head)
189- if pauseRequestBodyStream {
190- self . request!. pauseRequestBodyStream ( )
176+ if pauseRequestBodyStream, let request = self . request {
177+ // The above response head forward might lead the request to mark itself as
178+ // cancelled, which in turn might pop the request of the handler. For this reason we
179+ // must check if the request is still present here.
180+ request. pauseRequestBodyStream ( )
191181 }
192182
193183 case . forwardResponseBodyParts( let parts) :
184+ // We can force unwrap the request here, as we have just validated in the state machine,
185+ // that the request is neither failed nor finished yet
194186 self . request!. receiveResponseBodyParts ( parts)
195187
196188 case . failRequest( let error, _) :
189+ // We can force unwrap the request here, as we have just validated in the state machine,
190+ // that the request object is still present.
197191 self . request!. fail ( error)
198192 self . request = nil
199193 self . runTimeoutAction ( . clearIdleReadTimeoutTimer, context: context)
@@ -204,13 +198,42 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
204198 self . runFinalAction ( . close, context: context)
205199
206200 case . succeedRequest( let finalAction, let finalParts) :
201+ // We can force unwrap the request here, as we have just validated in the state machine,
202+ // that the request object is still present.
207203 self . request!. succeedRequest ( finalParts)
208204 self . request = nil
209205 self . runTimeoutAction ( . clearIdleReadTimeoutTimer, context: context)
210206 self . runFinalAction ( finalAction, context: context)
211207 }
212208 }
213209
210+ private func sendRequestHead( _ head: HTTPRequestHead , startBody: Bool , context: ChannelHandlerContext ) {
211+ if startBody {
212+ context. writeAndFlush ( self . wrapOutboundOut ( . head( head) ) , promise: nil )
213+
214+ // The above write might trigger an error, which may lead to a call to `errorCaught`,
215+ // which in turn, may fail the request and pop it from the handler. For this reason
216+ // we must check if the request is still present here.
217+ guard let request = self . request else { return }
218+ request. requestHeadSent ( )
219+ request. resumeRequestBodyStream ( )
220+ } else {
221+ context. write ( self . wrapOutboundOut ( . head( head) ) , promise: nil )
222+ context. write ( self . wrapOutboundOut ( . end( nil ) ) , promise: nil )
223+ context. flush ( )
224+
225+ // The above write might trigger an error, which may lead to a call to `errorCaught`,
226+ // which in turn, may fail the request and pop it from the handler. For this reason
227+ // we must check if the request is still present here.
228+ guard let request = self . request else { return }
229+ request. requestHeadSent ( )
230+
231+ if let timeoutAction = self . idleReadTimeoutStateMachine? . requestEndSent ( ) {
232+ self . runTimeoutAction ( timeoutAction, context: context)
233+ }
234+ }
235+ }
236+
214237 private func runFinalAction( _ action: HTTPRequestStateMachine . Action . FinalStreamAction , context: ChannelHandlerContext ) {
215238 switch action {
216239 case . close:
0 commit comments