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

Code before:
        // Note that because of the state transition in closeOutputSent() above we likely won't actually
        // forward any further reads to the user, unless they belong to a request currently streaming in.
        // Any reads past that point will be dropped in channelRead().
        if shouldRead && self.readPending {
            self.readPending = false
            context.read()
        }
    }

    /// - Returns: True if an error was sent, ie the caller should not continue
    private func handleConnectionStateAction(
            context: ChannelHandlerContext,
            action: ConnectionStateAction,
            promise: EventLoopPromise<Void>?
    ) -> Bool {
        switch action {
        case .warnPreconditionViolated(let message):
            self.preconditionFailed(message)
            let error = ConnectionStateError.preconditionViolated(message: message)
            self.deliverOneError(context: context, error: error)
            promise?.fail(error)
            return true
        case .forceCloseConnection:
            let message = "The connection has been forcefully closed because further IO was attempted after a precondition was violated"
            let error = ConnectionStateError.preconditionViolated(message: message)
            self.deliverOneError(context: context, error: error)
            promise?.fail(error)
            self.close(context: context, mode: .all, promise: nil)
            return true
        case .none:
            return false
        }
    }

    /// A response has been sent: we can now start passing reads through
    /// again if there are no further pending requests, and send any read()
    /// call we may have swallowed.
    private func startReading(context: ChannelHandlerContext) {
        if self.readPending && self.state != .responseEndPending {
            switch self.lifecycleState {
            case .quiescingLastRequestEndReceived, .quiescingCompleted:
                // No more reads in these states.
                ()
            case .acceptingEvents, .quiescingWaitingForRequestEnd:
                self.readPending = false
                context.read()
            }
        }
    }

    /// A response has been sent: deliver all pending requests and

Review comment (suggestion):
We must not deliver this error: it risks an infinite recursion. It's fine for us to just silently close.

Code after:
        // cannot be expected to act on them any longer.
        //
        // Side note: it's important that we drop these. If we don't, handlerRemoved will deliver them all.
        // While it's fair to immediately pipeline a channel where the user chose to remove the HTTPPipelineHandler,
        // it's deeply unfair to do so to a user that didn't choose to do that, where it happened to them only because
        // the channel closed.
        //
        // We set keepingCapacity to avoid this reallocating a buffer, as we'll just free it shortly anyway.
        self.eventBuffer.removeAll(keepingCapacity: true)
        context.fireChannelInactive()
    }

    public func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
        var shouldRead = false

        if mode == .output {
            // We need to do special handling here. If the server is closing output they don't intend to write anymore.
            // That means we want to drop anything up to the end of the in-flight request.
            self.dropAllButInFlightRequest()
            self.state.closeOutputSent()

            // If there's a read pending, we should deliver it after we forward the close on.
            shouldRead = self.readPending
        }

        context.close(mode: mode, promise: promise)

        // Double-check readPending here in case something weird happened.
        //
        // Note that because of the state transition in closeOutputSent() above we likely won't actually
        // forward any further reads to the user, unless they belong to a request currently streaming in.
        // Any reads past that point will be dropped in channelRead().
        if shouldRead && self.readPending {
            self.readPending = false
            context.read()
        }
    }

    /// - Returns: True if an error was sent, ie the caller should not continue
    private func handleConnectionStateAction(
            context: ChannelHandlerContext,
            action: ConnectionStateAction,
            promise: EventLoopPromise<Void>?
    ) -> Bool {
        switch action {
        case .warnPreconditionViolated(let message):
            self.assertionFailed(message)
            let error = ConnectionStateError.preconditionViolated(message: message)
            self.deliverOneError(context: context, error: error)
            promise?.fail(error)
            return true