NIOHTTP1/HTTPServerPipelineHandler.swift:104: Fatal error: Unexpectedly received a response in state idle

Hey everyone,

i'm new with Swift and specially with SwiftNIO. I'm trying to create a http server with Lifecycle stuff. Now my biggest problem is the next error.

NIOHTTP1/HTTPServerPipelineHandler.swift:104: Fatal error: Unexpectedly received a response in state idle

I think something happens and the readChannel function is executed when it should not.

The server creation code:

class http_server_test{
    private let group: MultiThreadedEventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
    private var server: ServerBootstrap
    private let host = "127.0.0.1"
    private let port = 8080
    private var channel:Channel!
    var lifecycle : ServiceLifecycle = ServiceLifecycle(configuration: ServiceLifecycle.Configuration(label: "http", installBacktrace: true))

    init() {
        server = ServerBootstrap(group: group)
            .serverChannelOption(ChannelOptions.backlog, value: 256)
            .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)

        server        
            .childChannelInitializer { channel in
                channel.pipeline.configureHTTPServerPipeline().flatMap {
                    channel.pipeline.addHandlers([Handler(&self.lifecycle)])
                } 
            }        
            .childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
            .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
    }

    public func start() {
        print("Starting HTTP server...")
        do {
            channel = try server.bind(host: host, port: port).wait() 
            print("Server started and listening on \(channel.localAddress!)")
            try channel.closeFuture.wait()
        } catch  {
            print("ERROR")
        }
    }

    public func stop() {
        do {
            try group.syncShutdownGracefully()
            print("Server closed") 
        } catch {
            print("ERROR")
        }

    }
} 

My handler class code:

class Handler:ChannelInboundHandler {
    typealias InboundIn = HTTPServerRequestPart
    typealias OutboundOut = HTTPServerResponsePart
    let logger = Logger(label: "SubSystemLifecycle")
    let componentlifecycle: ComponentLifecycle = ComponentLifecycle(label: "SubSystem")
    let router:Router = Router()
    var isStarted = false
    var pendingProcess = false
    var context:ChannelHandlerContext? = nil  

    init(_ lifecycle: inout ServiceLifecycle) {
        lifecycle.register(self.componentlifecycle)
        componentlifecycle.start { error in       
            if let error = error {
                print("Lifecycle failed starting  ☠️: \(error)")
            } else {
                print("Lifecycle started successfully 🚀")
                self.isStarted = true
                if self.pendingProcess == true && self.context != nil { 
                   self.move(context:self.context!) 
                }
            }
        }
    }

    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        print("I'am the channelRead")
        router.setRequest(self.unwrapInboundIn(data))
        self.context = context
        if isStarted {
            move(context: context)
        }
        else {
            pendingProcess = true
        }
    }
    // more methods...
}

The Router class method move only returns a simple html to show, there is nothing of NIO there.

The issue here is that you're sending your HTML response immediately when you get a new connection, without waiting for a request. You need to wait for a request to come in on channelRead before you call self.move.

I think i'm doing that in Router.

func move(context: ChannelHandlerContext) -> String {
        switch request {
            case .head(let headers):
                print("Received headers: \(headers)")
                print(headers.uri)
                // HTML things....
            default:
                if let r = request {
                    return "Ignoring part: \(r)"
                } else {
                    return ""
                }
                
        } ```

Where do you write responses from? Where is the call to context.write?

In the Handler class i have a method move().

    func move(context: ChannelHandlerContext){
        let html = router.move(context: context)
        let responseHeaders = HTTPHeaders([("content-type", "text/html")])
        let responseData = context.channel.allocator.buffer(string: html)
        let responseHead = HTTPResponseHead(version: .init(major: 1, minor: 1), status: .ok, headers: responseHeaders)
        let responsePartHead = HTTPServerResponsePart.head(responseHead)
        context.write(self.wrapOutboundOut(responsePartHead), promise: nil)
        let responsePartBody = HTTPServerResponsePart.body(.byteBuffer(responseData))
        context.write(self.wrapOutboundOut(responsePartBody), promise: nil)
        let responsePartEnd = HTTPServerResponsePart.end(nil)
        context.writeAndFlush(self.wrapOutboundOut(responsePartEnd), promise: nil)
    }```

This seems to be able to call move multiple times: once for each part of the HTTP request. HTTP requests come in at least two parts: .head and .end, with the possibility for many more. You aren't keeping track of enough state to send only one response to a given request.

Generally, the right thing to do is to process a request when your receive .end, not .head.