mutating func sendGoaway()

in Sources/NIOHTTP2/ConnectionStateMachine/ConnectionStateMachine.swift [1259:1329]


    mutating func sendGoaway(lastStreamID: HTTP2StreamID) -> StateMachineResultWithEffect {
        // GOAWAY frames are some of the most subtle frames in HTTP/2, they cause a number of state transitions all at once.
        // In particular, the value of lastStreamID heavily affects the state transitions we perform here.
        // In this case, all streams initiated by us that have stream IDs higher than lastStreamID will be closed, effective
        // immediately. If this leaves us with zero streams, the connection is fullyQuiesced. Otherwise, we are quiescing.
        switch self.state {
        case .prefaceSent(var state):
            return self.avoidingStateMachineCoW { newState in
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                let newStateData = QuiescingPrefaceSentState(fromPrefaceSent: state, lastStreamID: lastStreamID)
                newState = .quiescingPrefaceSent(newStateData)
                return result
            }

        case .active(var state):
            return self.avoidingStateMachineCoW { newState in
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                let newStateData = LocallyQuiescedState(fromActive: state, lastRemoteStreamID: lastStreamID)
                newState = .locallyQuiesced(newStateData)
                newState.closeIfNeeded(newStateData)
                return result
            }

        case .locallyQuiesced(var state):
            return self.avoidingStateMachineCoW { newState in
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                newState = .locallyQuiesced(state)
                newState.closeIfNeeded(state)
                return result
            }

        case .remotelyQuiesced(var state):
            return self.avoidingStateMachineCoW { newState in
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                let newStateData = BothQuiescingState(fromRemotelyQuiesced: state, lastRemoteStreamID: lastStreamID)
                newState = .bothQuiescing(newStateData)
                newState.closeIfNeeded(newStateData)
                return result
            }

        case .bothQuiescing(var state):
            return self.avoidingStateMachineCoW { newState in
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                newState = .bothQuiescing(state)
                newState.closeIfNeeded(state)
                return result
            }

        case .quiescingPrefaceSent(var state):
            return self.avoidingStateMachineCoW { newState in
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                newState = .quiescingPrefaceSent(state)
                return result
            }

        case .idle, .prefaceReceived, .quiescingPrefaceReceived:
            // We're waiting for the preface.
            return .init(result: .connectionError(underlyingError: NIOHTTP2Errors.missingPreface(), type: .protocolError), effect: nil)

        case .fullyQuiesced(var state):
            return self.avoidingStateMachineCoW { newState in
                // We allow duplicate GOAWAY here, so long as it ratchets downwards.
                let result = state.sendGoAwayFrame(lastStreamID: lastStreamID)
                newState = .fullyQuiesced(state)
                return result
            }

        case .modifying:
            preconditionFailure("Must not be left in modifying state")
        }
    }