@@ -111,27 +111,29 @@ internal final class HTTPBin {
111
111
return channel. pipeline. addHandler ( try ! NIOSSLServerHandler ( context: context) , position: . first)
112
112
}
113
113
114
- init ( ssl: Bool = false , simulateProxy: HTTPProxySimulator . Option ? = nil , channelPromise: EventLoopPromise < Channel > ? = nil ) {
114
+ init ( ssl: Bool = false , simulateProxy: HTTPProxySimulator . Option ? = nil , channelPromise: EventLoopPromise < Channel > ? = nil , connectionDelay : TimeAmount = . nanoseconds ( 0 ) , maxChannelAge : TimeAmount ? = nil ) {
115
115
self . serverChannel = try ! ServerBootstrap ( group: self . group)
116
116
. serverChannelOption ( ChannelOptions . socket ( SocketOptionLevel ( SOL_SOCKET) , SO_REUSEADDR) , value: 1 )
117
117
. childChannelOption ( ChannelOptions . socket ( IPPROTO_TCP, TCP_NODELAY) , value: 1 )
118
118
. childChannelInitializer { channel in
119
- channel. pipeline. configureHTTPServerPipeline ( withPipeliningAssistance: true , withErrorHandling: true ) . flatMap {
120
- if let simulateProxy = simulateProxy {
121
- let responseEncoder = HTTPResponseEncoder ( )
122
- let requestDecoder = ByteToMessageHandler ( HTTPRequestDecoder ( leftOverBytesStrategy: . forwardBytes) )
123
-
124
- return channel. pipeline. addHandlers ( [ responseEncoder, requestDecoder, HTTPProxySimulator ( option: simulateProxy, encoder: responseEncoder, decoder: requestDecoder) ] , position: . first)
125
- } else {
126
- return channel. eventLoop. makeSucceededFuture ( ( ) )
127
- }
128
- } . flatMap {
129
- if ssl {
130
- return HTTPBin . configureTLS ( channel: channel) . flatMap {
131
- channel. pipeline. addHandler ( HttpBinHandler ( channelPromise: channelPromise) )
119
+ channel. eventLoop. scheduleTask ( in: connectionDelay) { } . futureResult. flatMap {
120
+ channel. pipeline. configureHTTPServerPipeline ( withPipeliningAssistance: true , withErrorHandling: true ) . flatMap {
121
+ if let simulateProxy = simulateProxy {
122
+ let responseEncoder = HTTPResponseEncoder ( )
123
+ let requestDecoder = ByteToMessageHandler ( HTTPRequestDecoder ( leftOverBytesStrategy: . forwardBytes) )
124
+
125
+ return channel. pipeline. addHandlers ( [ responseEncoder, requestDecoder, HTTPProxySimulator ( option: simulateProxy, encoder: responseEncoder, decoder: requestDecoder) ] , position: . first)
126
+ } else {
127
+ return channel. eventLoop. makeSucceededFuture ( ( ) )
128
+ }
129
+ } . flatMap {
130
+ if ssl {
131
+ return HTTPBin . configureTLS ( channel: channel) . flatMap {
132
+ channel. pipeline. addHandler ( HttpBinHandler ( channelPromise: channelPromise) )
133
+ }
134
+ } else {
135
+ return channel. pipeline. addHandler ( HttpBinHandler ( channelPromise: channelPromise) )
132
136
}
133
- } else {
134
- return channel. pipeline. addHandler ( HttpBinHandler ( channelPromise: channelPromise) )
135
137
}
136
138
}
137
139
} . bind ( host: " 127.0.0.1 " , port: 0 ) . wait ( )
@@ -229,14 +231,53 @@ internal final class HttpBinHandler: ChannelInboundHandler {
229
231
230
232
let channelPromise : EventLoopPromise < Channel > ?
231
233
var resps = CircularBuffer < HTTPResponseBuilder > ( )
232
-
233
- init ( channelPromise: EventLoopPromise < Channel > ? = nil ) {
234
+ var closeAfterResponse = false
235
+ var delay : TimeAmount = . seconds( 0 )
236
+ let creationDate = Date ( )
237
+ let maxChannelAge : TimeAmount ?
238
+ var shouldClose = false
239
+ var isServingRequest = false
240
+
241
+ init ( channelPromise: EventLoopPromise < Channel > ? = nil , maxChannelAge: TimeAmount ? = nil ) {
234
242
self . channelPromise = channelPromise
243
+ self . maxChannelAge = maxChannelAge
244
+ }
245
+
246
+ func handlerAdded( context: ChannelHandlerContext ) {
247
+ if let maxChannelAge = self . maxChannelAge {
248
+ context. eventLoop. scheduleTask ( in: maxChannelAge) {
249
+ if !self . isServingRequest {
250
+ context. close ( promise: nil )
251
+ } else {
252
+ self . shouldClose = true
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ func parseAndSetOptions( from head: HTTPRequestHead ) {
259
+ if let delay = head. headers [ " X-internal-delay " ] . first {
260
+ if let milliseconds = TimeAmount . Value ( delay) {
261
+ self . delay = TimeAmount . milliseconds ( milliseconds)
262
+ } else {
263
+ assertionFailure ( " Invalid interval format " )
264
+ }
265
+ } else {
266
+ self . delay = . nanoseconds( 0 )
267
+ }
268
+
269
+ if let connection = head. headers [ " Connection " ] . first {
270
+ self . closeAfterResponse = ( connection == " close " )
271
+ } else {
272
+ self . closeAfterResponse = false
273
+ }
235
274
}
236
275
237
276
func channelRead( context: ChannelHandlerContext , data: NIOAny ) {
277
+ self . isServingRequest = true
238
278
switch self . unwrapInboundIn ( data) {
239
279
case . head( let req) :
280
+ self . parseAndSetOptions ( from: req)
240
281
let url = URL ( string: req. uri) !
241
282
switch url. path {
242
283
case " /ok " :
@@ -333,7 +374,19 @@ internal final class HttpBinHandler: ChannelInboundHandler {
333
374
responseBody. writeBytes ( serialized)
334
375
context. write ( wrapOutboundOut ( . body( . byteBuffer( responseBody) ) ) , promise: nil )
335
376
}
336
- context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) , promise: nil )
377
+ context. eventLoop. scheduleTask ( in: self . delay) {
378
+ context. writeAndFlush ( self . wrapOutboundOut ( . end( nil ) ) ) . whenComplete { result in
379
+ self . isServingRequest = false
380
+ switch result {
381
+ case . success:
382
+ if self . closeAfterResponse || self . shouldClose {
383
+ context. close ( promise: nil )
384
+ }
385
+ case . failure( let error) :
386
+ assertionFailure ( " \( error) " )
387
+ }
388
+ }
389
+ }
337
390
}
338
391
}
339
392
0 commit comments