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)
}