With the router invocation moved to a DispatchQueue as in #78 we see a new failure in RegressionTests.testBadRequestFollowingGoodRequest
. A work-around for this failure was included in #78.
Initially, with the changes for #78, the test first started failing with a precondition failure at HTTPServerPipelineHandler.swift, line 108. This was fixed in swift-nio through this PR. After that, the same test starts failing with no activity on the client.
The test kicks off an HTTP server and makes a good request (GET /
) followed by a bad request to an invalid route (GET #/
). The first request has a normal response and the second one has an error response triggered from NIOHTTP1.HTTPServerProtocolErrorHandler
. We only check for a badRequest
response from the second request. With #78 we seem to be receiving neither of the responses at the client.
I printed some diagnostics as suggested by @weissi using a ChannelHandler. I added this to the server pipeline:
final class PrintEverythingHandler: ChannelDuplexHandler {
typealias OutboundIn = HTTPServerResponsePart
typealias InboundIn = HTTPServerRequestPart
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
print("Server <- \(self.unwrapInboundIn(data))")
ctx.fireChannelRead(data)
}
func write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
print("Server -> \(self.unwrapOutboundIn(data))")
ctx.write(data, promise: promise)
}
}
... and this to the client pipeline
final class PrintEverythingHandler: ChannelDuplexHandler {
typealias OutboundIn = HTTPClientRequestPart
typealias InboundIn = HTTPClientResponsePart
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
print("Client <- \(self.unwrapInboundIn(data))")
ctx.fireChannelRead(data)
}
func write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
print("Client -> \(self.unwrapOutboundIn(data))")
ctx.write(data, promise: promise)
}
}
Server <-
means "server received" and Server ->
means the "server sent". Client <-
and Client ->
have similar meanings.
Now, I see two types of intermittent problems running RegressionTests.testBadRequestFollowingGoodRequest
Problem 1
Client -> head(HTTPRequestHead { method: GET, uri: "/", version: HTTP/1.1, headers: [] })
Client -> end(nil)
Client -> head(HTTPRequestHead { method: GET, uri: "#/", version: HTTP/1.1, headers: [] })
Client -> end(nil)
Server <- head(HTTPRequestHead { method: GET, uri: "/", version: HTTP/1.1, headers: [] })
Server <- end(nil)
Server -> head(HTTPResponseHead { version: HTTP/1.1, status: notFound, headers: [("Date", "Sun, 26 Aug 2018 18:37:21 GMT"), ("Content-Type", "text/plain"), ("Connection", "Keep-Alive"), ("Keep-Alive", "timeout=60.0"), ("Content-Length", "14")] })
Server -> body(NIO.IOData.byteBuffer(ByteBuffer { readerIndex: 0, writerIndex: 14, readableBytes: 14, capacity: 16, slice: _ByteBufferSlice(upperBound: 16, _begin: 0), storage: 0x00007f6be0027400 (16 bytes)}))
Server -> end(nil)
So, the server sends a response to the first request, no error response to the second request and nothing is received at the client.
Problem 2
Client -> head(HTTPRequestHead { method: GET, uri: "/", version: HTTP/1.1, headers: [] })
Client -> end(nil)
Client -> head(HTTPRequestHead { method: GET, uri: "#/", version: HTTP/1.1, headers: [] })
Client -> end(nil)
Server <- head(HTTPRequestHead { method: GET, uri: "/", version: HTTP/1.1, headers: [] })
Server <- end(nil)
Here, the server send neither of the responses.
Expected output
Client -> head(HTTPRequestHead { method: GET, uri: "/", version: HTTP/1.1, headers: [] })
Client -> end(nil)
Client -> head(HTTPRequestHead { method: GET, uri: "#/", version: HTTP/1.1, headers: [] })
Client -> end(nil)
Server <- head(HTTPRequestHead { method: GET, uri: "/", version: HTTP/1.1, headers: [] })
Server <- end(nil)
Server -> head(HTTPResponseHead { version: HTTP/1.1, status: notFound, headers: [("Connection", "Keep-Alive"), ("Content-Type", "text/plain"), ("Date", "Sun, 26 Aug 2018 18:38:38 GMT"), ("Keep-Alive", "timeout=60.0"), ("Content-Length", "14")] })
Server -> body(NIO.IOData.byteBuffer(ByteBuffer { readerIndex: 0, writerIndex: 14, readableBytes: 14, capacity: 16, slice: _ByteBufferSlice(upperBound: 16, _begin: 0), storage: 0x00007f2a1c02d030 (16 bytes)}))
Server -> end(nil)
HTTPServerProtocolErrorHandler returning an error response
Client <- head(HTTPResponseHead { version: HTTP/1.1, status: notFound, headers: [("Connection", "Keep-Alive"), ("Content-Type", "text/plain"), ("Date", "Sun, 26 Aug 2018 18:38:38 GMT"), ("Keep-Alive", "timeout=60.0"), ("Content-Length", "14")] })
Client <- body(ByteBuffer { readerIndex: 0, writerIndex: 14, readableBytes: 14, capacity: 14, slice: _ByteBufferSlice(upperBound: 173, _begin: 159), storage: 0x00007f2a14031c60 (1024 bytes)})
Client <- end(nil)
Client <- head(HTTPResponseHead { version: HTTP/1.1, status: badRequest, headers: [("Connection", "close"), ("Content-Length", "0")] })
Client <- end(nil)
Here, the server sent a normal response followed by an error response and both of them were received on the client.
If the two requests are separated by a time gap, we always get the expected output. This is how we are currently working around the problem.