mutating func receiveWindowUpdate()

in Sources/NIOHTTP2/StreamStateMachine.swift [785:847]


    mutating func receiveWindowUpdate(windowIncrement: UInt32) -> StateMachineResultWithStreamEffect {
        let windowEffect: StreamStateChange?

        do {
            // RFC 7540 does not limit the states in which WINDOW_UDPATE frames can be received. For this reason we need to be
            // fairly conservative about applying limits. In essence, we allow receiving WINDOW_UPDATE frames in all but the
            // following states:
            //
            // - idle, because the stream hasn't been created yet so the stream ID is invalid
            // - reservedRemote, because we will never be able to send data so it's silly to manipulate our flow control window
            // - closed, because the entire stream is closed now
            //
            // Note that, unlike with sending, we allow receiving window update frames when we are half-closed. This is because
            // it is possible that those frames may have been in flight when we were closing the stream, and so we shouldn't cause
            // the stream to explode simply for that reason. In this case, we just ignore the data.
            switch self.state {
            case .reservedLocal(localWindow: var localWindow):
                try localWindow.windowUpdate(by: windowIncrement)
                self.state = .reservedLocal(localWindow: localWindow)
                windowEffect = .windowSizeChange(.init(streamID: self.streamID, localStreamWindowSize: Int(localWindow), remoteStreamWindowSize: nil))

            case .halfOpenLocalPeerIdle(localWindow: var localWindow, localContentLength: let localContentLength, remoteWindow: let remoteWindow):
                try localWindow.windowUpdate(by: windowIncrement)
                self.state = .halfOpenLocalPeerIdle(localWindow: localWindow, localContentLength: localContentLength, remoteWindow: remoteWindow)
                windowEffect = .windowSizeChange(.init(streamID: self.streamID, localStreamWindowSize: Int(localWindow), remoteStreamWindowSize: Int(remoteWindow)))

            case .halfOpenRemoteLocalIdle(localWindow: var localWindow, remoteContentLength: let remoteContentLength, remoteWindow: let remoteWindow):
                try localWindow.windowUpdate(by: windowIncrement)
                self.state = .halfOpenRemoteLocalIdle(localWindow: localWindow, remoteContentLength: remoteContentLength, remoteWindow: remoteWindow)
                windowEffect = .windowSizeChange(.init(streamID: self.streamID, localStreamWindowSize: Int(localWindow), remoteStreamWindowSize: Int(remoteWindow)))

            case .fullyOpen(localRole: let localRole, localContentLength: let localContentLength, remoteContentLength: let remoteContentLength, localWindow: var localWindow, remoteWindow: let remoteWindow):
                try localWindow.windowUpdate(by: windowIncrement)
                self.state = .fullyOpen(localRole: localRole, localContentLength: localContentLength, remoteContentLength: remoteContentLength, localWindow: localWindow, remoteWindow: remoteWindow)
                windowEffect = .windowSizeChange(.init(streamID: self.streamID, localStreamWindowSize: Int(localWindow), remoteStreamWindowSize: Int(remoteWindow)))

            case .halfClosedRemoteLocalIdle(localWindow: var localWindow):
                try localWindow.windowUpdate(by: windowIncrement)
                self.state = .halfClosedRemoteLocalIdle(localWindow: localWindow)
                windowEffect = .windowSizeChange(.init(streamID: self.streamID, localStreamWindowSize: Int(localWindow), remoteStreamWindowSize: nil))

            case .halfClosedRemoteLocalActive(localRole: let localRole, initiatedBy: let initiatedBy, localContentLength: let localContentLength, localWindow: var localWindow):
                try localWindow.windowUpdate(by: windowIncrement)
                self.state = .halfClosedRemoteLocalActive(localRole: localRole, initiatedBy: initiatedBy, localContentLength: localContentLength, localWindow: localWindow)
                windowEffect = .windowSizeChange(.init(streamID: self.streamID, localStreamWindowSize: Int(localWindow), remoteStreamWindowSize: nil))

            case .halfClosedLocalPeerIdle, .halfClosedLocalPeerActive:
                // No-op, see above
                windowEffect = nil

            case .idle, .reservedRemote, .closed:
                return .init(result: .streamError(streamID: self.streamID, underlyingError: NIOHTTP2Errors.badStreamStateTransition(from: NIOHTTP2StreamState.get(self.state)), type: .protocolError), effect: nil)
            }
        } catch let error where error is NIOHTTP2Errors.InvalidFlowControlWindowSize {
            return .init(result: .streamError(streamID: self.streamID, underlyingError: error, type: .flowControlError), effect: nil)
        } catch let error where error is NIOHTTP2Errors.InvalidWindowIncrementSize {
            return .init(result: .streamError(streamID: self.streamID, underlyingError: error, type: .protocolError), effect: nil)
        } catch {
            preconditionFailure("Unexpected error: \(error)")
        }

        return .init(result: .succeed, effect: windowEffect)
    }