@@ -24,9 +24,29 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
24
24
25
25
private let eventLoop : EventLoop
26
26
27
+ private var state : HTTPRequestStateMachine = . init( isChannelWritable: false ) {
28
+ didSet {
29
+ self . eventLoop. assertInEventLoop ( )
30
+ }
31
+ }
32
+
33
+ /// while we are in a channel pipeline, this context can be used.
27
34
private var channelContext : ChannelHandlerContext ?
28
- private var state : HTTPRequestStateMachine = . init( isChannelWritable: false )
29
- private var request : HTTPExecutableRequest ?
35
+
36
+ private var request : HTTPExecutableRequest ? {
37
+ didSet {
38
+ if let newRequest = self . request {
39
+ if let idleReadTimeout = newRequest. idleReadTimeout {
40
+ self . idleReadTimeoutStateMachine = . init( timeAmount: idleReadTimeout)
41
+ }
42
+ } else {
43
+ self . idleReadTimeoutStateMachine = nil
44
+ }
45
+ }
46
+ }
47
+
48
+ private var idleReadTimeoutStateMachine : IdleReadStateMachine ?
49
+ private var idleReadTimeoutTimer : Scheduled < Void > ?
30
50
31
51
init ( eventLoop: EventLoop ) {
32
52
self . eventLoop = eventLoop
@@ -40,10 +60,12 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
40
60
self . run ( action, context: context)
41
61
}
42
62
43
- func handlerRemoved( ) {
63
+ func handlerRemoved( context : ChannelHandlerContext ) {
44
64
self . channelContext = nil
45
65
}
46
66
67
+ // MARK: Channel Inbound Handler
68
+
47
69
func channelActive( context: ChannelHandlerContext ) {
48
70
let action = self . state. writabilityChanged ( writable: context. channel. isWritable)
49
71
self . run ( action, context: context)
@@ -54,15 +76,34 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
54
76
self . run ( action, context: context)
55
77
}
56
78
57
- func handlerRemoved( context: ChannelHandlerContext ) {
58
- self . channelContext = nil
59
- }
60
-
61
79
func channelWritabilityChanged( context: ChannelHandlerContext ) {
62
80
let action = self . state. writabilityChanged ( writable: context. channel. isWritable)
63
81
self . run ( action, context: context)
64
82
}
65
83
84
+ func channelRead( context: ChannelHandlerContext , data: NIOAny ) {
85
+ let httpPart = self . unwrapInboundIn ( data)
86
+
87
+ if let timeoutAction = self . idleReadTimeoutStateMachine? . channelRead ( httpPart) {
88
+ self . runTimeoutAction ( timeoutAction, context: context)
89
+ }
90
+
91
+ let action = self . state. channelRead ( self . unwrapInboundIn ( data) )
92
+ self . run ( action, context: context)
93
+ }
94
+
95
+ func channelReadComplete( context: ChannelHandlerContext ) {
96
+ let action = self . state. channelReadComplete ( )
97
+ self . run ( action, context: context)
98
+ }
99
+
100
+ func errorCaught( context: ChannelHandlerContext , error: Error ) {
101
+ let action = self . state. errorHappened ( error)
102
+ self . run ( action, context: context)
103
+ }
104
+
105
+ // MARK: Channel Outbound Handler
106
+
66
107
func write( context: ChannelHandlerContext , data: NIOAny , promise: EventLoopPromise < Void > ? ) {
67
108
let request = self . unwrapOutboundIn ( data)
68
109
self . request = request
@@ -79,19 +120,11 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
79
120
self . run ( action, context: context)
80
121
}
81
122
82
- func channelRead( context: ChannelHandlerContext , data: NIOAny ) {
83
- let action = self . state. channelRead ( self . unwrapInboundIn ( data) )
84
- self . run ( action, context: context)
85
- }
86
-
87
- func errorCaught( context: ChannelHandlerContext , error: Error ) {
88
- let action = self . state. errorHappened ( error)
89
- self . run ( action, context: context)
90
- }
123
+ // MARK: - Private Methods -
91
124
92
- // MARK: - Run Actions
125
+ // MARK: Run Actions
93
126
94
- func run( _ action: HTTPRequestStateMachine . Action , context: ChannelHandlerContext ) {
127
+ private func run( _ action: HTTPRequestStateMachine . Action , context: ChannelHandlerContext ) {
95
128
switch action {
96
129
case . sendRequestHead( let head, let startBody) :
97
130
if startBody {
@@ -102,7 +135,12 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
102
135
context. write ( self . wrapOutboundOut ( . head( head) ) , promise: nil )
103
136
context. write ( self . wrapOutboundOut ( . end( nil ) ) , promise: nil )
104
137
context. flush ( )
138
+
105
139
self . request!. requestHeadSent ( )
140
+
141
+ if let timeoutAction = self . idleReadTimeoutStateMachine? . requestEndSent ( ) {
142
+ self . runTimeoutAction ( timeoutAction, context: context)
143
+ }
106
144
}
107
145
108
146
case . pauseRequestBodyStream:
@@ -114,6 +152,10 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
114
152
case . sendRequestEnd:
115
153
context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) , promise: nil )
116
154
155
+ if let timeoutAction = self . idleReadTimeoutStateMachine? . requestEndSent ( ) {
156
+ self . runTimeoutAction ( timeoutAction, context: context)
157
+ }
158
+
117
159
case . read:
118
160
context. read ( )
119
161
@@ -144,19 +186,52 @@ class HTTP2ClientRequestHandler: ChannelDuplexHandler {
144
186
}
145
187
}
146
188
147
- // MARK: - Private Methods -
148
-
149
189
private func runFinalAction( _ action: HTTPRequestStateMachine . Action . FinalStreamAction , context: ChannelHandlerContext ) {
150
190
switch action {
151
191
case . close:
152
192
context. close ( promise: nil )
193
+
153
194
case . sendRequestEnd:
154
195
context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) , promise: nil )
196
+
155
197
case . none:
156
198
break
157
199
}
158
200
}
159
201
202
+ private func runTimeoutAction( _ action: IdleReadStateMachine . Action , context: ChannelHandlerContext ) {
203
+ switch action {
204
+ case . startIdleReadTimeoutTimer( let timeAmount) :
205
+ assert ( self . idleReadTimeoutTimer == nil , " Expected there is no timeout timer so far. " )
206
+
207
+ self . idleReadTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
208
+ let action = self . state. idleReadTimeoutTriggered ( )
209
+ self . run ( action, context: context)
210
+ }
211
+
212
+ case . resetIdleReadTimeoutTimer( let timeAmount) :
213
+ if let oldTimer = self . idleReadTimeoutTimer {
214
+ oldTimer. cancel ( )
215
+ }
216
+
217
+ self . idleReadTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
218
+ let action = self . state. idleReadTimeoutTriggered ( )
219
+ self . run ( action, context: context)
220
+ }
221
+
222
+ case . clearIdleReadTimeoutTimer:
223
+ if let oldTimer = self . idleReadTimeoutTimer {
224
+ self . idleReadTimeoutTimer = nil
225
+ oldTimer. cancel ( )
226
+ }
227
+
228
+ case . none:
229
+ break
230
+ }
231
+ }
232
+
233
+ // MARK: Private HTTPRequestExecutor
234
+
160
235
private func writeRequestBodyPart0( _ data: IOData , request: HTTPExecutableRequest ) {
161
236
guard self . request === request, let context = self . channelContext else {
162
237
// Because the HTTPExecutingRequest may run in a different thread to our eventLoop,
0 commit comments