Repository: apple/swift-nio
File: Sources/NIOHTTP1/NIOTypedHTTPServerUpgradeHandler.swift (Swift)

Code before:
        requestHead: HTTPRequestHead,
        responseHeaders: HTTPHeaders,
        proto: String
    ) {
        // Before we finish the upgrade we have to remove the HTTPDecoder and any other non-Encoder HTTP
        // handlers from the pipeline, to prevent them parsing any more data. We'll buffer the data until
        // that completes.
        // While there are a lot of Futures involved here it's quite possible that all of this code will
        // actually complete synchronously: we just want to program for the possibility that it won't.
        // Once that's done, we send the upgrade response, then remove the HTTP encoder, then call the
        // internal handler, then call the user code, and then finally when the user code is done we do
        // our final cleanup steps, namely we replay the received data we buffered in the meantime and
        // then remove ourselves from the pipeline.
        let channel = context.channel
        let pipeline = context.pipeline

        self.removeExtraHandlers(pipeline: pipeline)
            .assumeIsolated()
            .flatMap {
                self.sendUpgradeResponse(context: context, responseHeaders: responseHeaders)
            }.flatMap {
                pipeline.syncOperations.removeHandler(self.httpEncoder)
            }.flatMap { () -> EventLoopFuture<UpgradeResult> in
                upgrader.upgrade(channel: channel, upgradeRequest: requestHead)
            }.nonisolated().hop(to: context.eventLoop)
            .assumeIsolated()
            .whenComplete { result in
                self.upgradingHandlerCompleted(context: context, result, requestHeadAndProtocol: (requestHead, proto))
            }
    }

    /// Sends the 101 Switching Protocols response for the pipeline.
    private func sendUpgradeResponse(
        context: ChannelHandlerContext,
        responseHeaders: HTTPHeaders
    ) -> EventLoopFuture<Void> {
        var response = HTTPResponseHead(version: .http1_1, status: .switchingProtocols)
        response.headers = responseHeaders
        return context.writeAndFlush(wrapOutboundOut(HTTPServerResponsePart.head(response)))
    }

    /// Builds the initial mandatory HTTP headers for HTTP upgrade responses.
    private func buildUpgradeHeaders(`protocol`: String) -> HTTPHeaders {
        HTTPHeaders([("connection", "upgrade"), ("upgrade", `protocol`)])
    }

    /// Removes any extra HTTP-related handlers from the channel pipeline.
    private func removeExtraHandlers(pipeline: ChannelPipeline) -> EventLoopFuture<Void> {
        guard self.extraHTTPHandlers.count > 0 else {
            return pipeline.eventLoop.makeSucceededFuture(())
        }

Review comment (suggestion):
`nonisolated().hop(to:).assumeIsolated()` shouldn't do anything meaningful here. We were already isolated.

Code after:
        responseHeaders: HTTPHeaders,
        proto: String
    ) {
        // Before we finish the upgrade we have to remove the HTTPDecoder and any other non-Encoder HTTP
        // handlers from the pipeline, to prevent them parsing any more data. We'll buffer the data until
        // that completes.
        // While there are a lot of Futures involved here it's quite possible that all of this code will
        // actually complete synchronously: we just want to program for the possibility that it won't.
        // Once that's done, we send the upgrade response, then remove the HTTP encoder, then call the
        // internal handler, then call the user code, and then finally when the user code is done we do
        // our final cleanup steps, namely we replay the received data we buffered in the meantime and
        // then remove ourselves from the pipeline.
        let channel = context.channel
        let pipeline = context.pipeline

        self.removeExtraHandlers(pipeline: pipeline)
            .assumeIsolated()
            .flatMap {
                self.sendUpgradeResponse(context: context, responseHeaders: responseHeaders)
            }.flatMap {
                pipeline.syncOperations.removeHandler(self.httpEncoder)
            }.flatMap { () -> EventLoopFuture<UpgradeResult> in
                upgrader.upgrade(channel: channel, upgradeRequest: requestHead)
            }
            .whenComplete { result in
                self.upgradingHandlerCompleted(context: context, result, requestHeadAndProtocol: (requestHead, proto))
            }
    }

    /// Sends the 101 Switching Protocols response for the pipeline.
    private func sendUpgradeResponse(
        context: ChannelHandlerContext,
        responseHeaders: HTTPHeaders
    ) -> EventLoopFuture<Void> {
        var response = HTTPResponseHead(version: .http1_1, status: .switchingProtocols)
        response.headers = responseHeaders
        return context.writeAndFlush(wrapOutboundOut(HTTPServerResponsePart.head(response)))
    }

    /// Builds the initial mandatory HTTP headers for HTTP upgrade responses.
    private func buildUpgradeHeaders(`protocol`: String) -> HTTPHeaders {
        HTTPHeaders([("connection", "upgrade"), ("upgrade", `protocol`)])
    }

    /// Removes any extra HTTP-related handlers from the channel pipeline.
    private func removeExtraHandlers(pipeline: ChannelPipeline) -> EventLoopFuture<Void> {
        guard self.extraHTTPHandlers.count > 0 else {
            return pipeline.eventLoop.makeSucceededFuture(())
        }

        return .andAllSucceed(