@@ -96,7 +96,7 @@ import NIOHPACK
9696/// Operations on the state machine are performed by calling specific functions corresponding to
9797/// the operation that is about to occur.
9898struct HTTP2StreamStateMachine {
99- private enum State {
99+ fileprivate enum State {
100100 // TODO(cory): Can we remove the idle state? Streams shouldn't sit in idle for long periods
101101 // of time, they should immediately transition out, so can we avoid it entirely?
102102 /// In the idle state, the stream has not been opened by either peer.
@@ -191,7 +191,6 @@ struct HTTP2StreamStateMachine {
191191 case closed( reason: HTTP2ErrorCode ? )
192192 }
193193
194-
195194 /// The possible roles an endpoint may play in a given stream.
196195 enum StreamRole {
197196 /// A server. Servers initiate streams by pushing them.
@@ -384,9 +383,9 @@ extension HTTP2StreamStateMachine {
384383 // seems reasonable to me: specifically, if we have a stream to fail, fail it, otherwise treat
385384 // the error as connection scoped.)
386385 case . idle( . server, _, _) , . closed:
387- return . init( result: . connectionError( underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
386+ return . init( result: . connectionError( underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
388387 case . reservedRemote, . halfClosedLocalPeerIdle, . halfClosedLocalPeerActive:
389- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
388+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
390389 }
391390 } catch let error where error is NIOHTTP2Errors . ContentLengthViolated {
392391 return . init( result: . streamError( streamID: self . streamID, underlyingError: error, type: . protocolError) , effect: nil )
@@ -515,9 +514,9 @@ extension HTTP2StreamStateMachine {
515514 // seems reasonable to me: specifically, if we have a stream to fail, fail it, otherwise treat
516515 // the error as connection scoped.)
517516 case . idle( . client, _, _) , . closed:
518- return . init( result: . connectionError( underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
517+ return . init( result: . connectionError( underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
519518 case . reservedLocal, . halfClosedRemoteLocalIdle, . halfClosedRemoteLocalActive:
520- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
519+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
521520 }
522521 } catch let error where error is NIOHTTP2Errors . ContentLengthViolated {
523522 return . init( result: . streamError( streamID: self . streamID, underlyingError: error, type: . protocolError) , effect: nil )
@@ -587,7 +586,7 @@ extension HTTP2StreamStateMachine {
587586 // Sending a DATA frame outside any of these states is a stream error of type STREAM_CLOSED (RFC7540 § 6.1)
588587 case . idle, . halfOpenRemoteLocalIdle, . reservedLocal, . reservedRemote, . halfClosedLocalPeerIdle,
589588 . halfClosedLocalPeerActive, . halfClosedRemoteLocalIdle, . closed:
590- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . streamClosed) , effect: nil )
589+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . streamClosed) , effect: nil )
591590 }
592591 } catch let error where error is NIOHTTP2Errors . FlowControlViolation {
593592 return . init( result: . streamError( streamID: self . streamID, underlyingError: error, type: . flowControlError) , effect: nil )
@@ -658,7 +657,7 @@ extension HTTP2StreamStateMachine {
658657 // Receiving a DATA frame outside any of these states is a stream error of type STREAM_CLOSED (RFC7540 § 6.1)
659658 case . idle, . halfOpenLocalPeerIdle, . reservedLocal, . reservedRemote, . halfClosedLocalPeerIdle,
660659 . halfClosedRemoteLocalActive, . halfClosedRemoteLocalIdle, . closed:
661- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . streamClosed) , effect: nil )
660+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . streamClosed) , effect: nil )
662661 }
663662 } catch let error where error is NIOHTTP2Errors . FlowControlViolation {
664663 return . init( result: . streamError( streamID: self . streamID, underlyingError: error, type: . flowControlError) , effect: nil )
@@ -694,7 +693,7 @@ extension HTTP2StreamStateMachine {
694693 . fullyOpen( localRole: . client, localContentLength: _, remoteContentLength: _, localWindow: _, remoteWindow: _) ,
695694 . halfClosedRemoteLocalActive( localRole: . client, initiatedBy: _, localContentLength: _, localWindow: _) ,
696695 . halfClosedRemoteLocalActive( localRole: . server, initiatedBy: . server, localContentLength: _, localWindow: _) :
697- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
696+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
698697 }
699698 }
700699
@@ -721,7 +720,7 @@ extension HTTP2StreamStateMachine {
721720 . fullyOpen( localRole: . server, localContentLength: _, remoteContentLength: _, localWindow: _, remoteWindow: _) ,
722721 . halfClosedLocalPeerActive( localRole: . server, initiatedBy: _, remoteContentLength: _, remoteWindow: _) ,
723722 . halfClosedLocalPeerActive( localRole: . client, initiatedBy: . server, remoteContentLength: _, remoteWindow: _) :
724- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
723+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
725724 }
726725 }
727726
@@ -770,7 +769,7 @@ extension HTTP2StreamStateMachine {
770769 windowEffect = . windowSizeChange( . init( streamID: self . streamID, localStreamWindowSize: nil , remoteStreamWindowSize: Int ( remoteWindow) ) )
771770
772771 case . idle, . reservedLocal, . halfClosedRemoteLocalIdle, . halfClosedRemoteLocalActive, . closed:
773- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
772+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
774773 }
775774 } catch let error where error is NIOHTTP2Errors . InvalidFlowControlWindowSize {
776775 return . init( result: . streamError( streamID: self . streamID, underlyingError: error, type: . flowControlError) , effect: nil )
@@ -834,7 +833,7 @@ extension HTTP2StreamStateMachine {
834833 windowEffect = nil
835834
836835 case . idle, . reservedRemote, . closed:
837- return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
836+ return . init( result: . streamError( streamID: self . streamID, underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
838837 }
839838 } catch let error where error is NIOHTTP2Errors . InvalidFlowControlWindowSize {
840839 return . init( result: . streamError( streamID: self . streamID, underlyingError: error, type: . flowControlError) , effect: nil )
@@ -857,7 +856,7 @@ extension HTTP2StreamStateMachine {
857856 mutating func receiveRstStream( reason: HTTP2ErrorCode ) -> StateMachineResultWithStreamEffect {
858857 // We can receive RST_STREAM frames in any state but idle.
859858 if case . idle = self . state {
860- return . init( result: . connectionError( underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( ) , type: . protocolError) , effect: nil )
859+ return . init( result: . connectionError( underlyingError: NIOHTTP2Errors . BadStreamStateTransition ( from : NIOHTTP2StreamState . get ( self . state ) ) , type: . protocolError) , effect: nil )
861860 }
862861
863862 self . state = . closed( reason: reason)
@@ -1023,3 +1022,58 @@ private extension HPACKHeaders {
10231022 return self . first { $0. name == " :status " } ? . value. first == " 1 "
10241023 }
10251024}
1025+
1026+ /// A state of a `HTTP2StreamStateMachine`. This copy in effect mirrors `HTTP2StreamStateMachine.state` but without associated values.
1027+ public struct NIOHTTP2StreamState : Hashable , CustomStringConvertible {
1028+ private enum State {
1029+ case idle
1030+ case reservedRemote
1031+ case reservedLocal
1032+ case halfOpenLocalPeerIdle
1033+ case halfOpenRemoteLocalIdle
1034+ case fullyOpen
1035+ case halfClosedLocalPeerIdle
1036+ case halfClosedLocalPeerActive
1037+ case halfClosedRemoteLocalIdle
1038+ case halfClosedRemoteLocalActive
1039+ case closed
1040+ }
1041+
1042+ private var state : State
1043+
1044+ private init ( state: State ) {
1045+ self . state = state
1046+ }
1047+
1048+ public var description : String {
1049+ return " \( state) "
1050+ }
1051+
1052+ public static let idle = NIOHTTP2StreamState ( state: . idle)
1053+ public static let reservedRemote = NIOHTTP2StreamState ( state: . reservedRemote)
1054+ public static let reservedLocal = NIOHTTP2StreamState ( state: . reservedLocal)
1055+ public static let halfOpenLocalPeerIdle = NIOHTTP2StreamState ( state: . halfOpenLocalPeerIdle)
1056+ public static let halfOpenRemoteLocalIdle = NIOHTTP2StreamState ( state: . halfOpenRemoteLocalIdle)
1057+ public static let fullyOpen = NIOHTTP2StreamState ( state: . fullyOpen)
1058+ public static let halfClosedLocalPeerIdle = NIOHTTP2StreamState ( state: . halfClosedLocalPeerIdle)
1059+ public static let halfClosedLocalPeerActive = NIOHTTP2StreamState ( state: . halfClosedLocalPeerActive)
1060+ public static let halfClosedRemoteLocalIdle = NIOHTTP2StreamState ( state: . halfClosedRemoteLocalIdle)
1061+ public static let halfClosedRemoteLocalActive = NIOHTTP2StreamState ( state: . halfClosedRemoteLocalActive)
1062+ public static let closed = NIOHTTP2StreamState ( state: . closed)
1063+
1064+ fileprivate static func get( _ state: HTTP2StreamStateMachine . State ) -> NIOHTTP2StreamState {
1065+ switch state {
1066+ case . idle: return idle
1067+ case . reservedRemote: return reservedRemote
1068+ case . reservedLocal: return reservedLocal
1069+ case . halfOpenLocalPeerIdle: return halfOpenLocalPeerIdle
1070+ case . halfOpenRemoteLocalIdle: return halfOpenRemoteLocalIdle
1071+ case . fullyOpen: return fullyOpen
1072+ case . halfClosedLocalPeerIdle: return halfClosedLocalPeerIdle
1073+ case . halfClosedLocalPeerActive: return halfClosedLocalPeerActive
1074+ case . halfClosedRemoteLocalIdle: return halfClosedRemoteLocalIdle
1075+ case . halfClosedRemoteLocalActive: return halfClosedRemoteLocalActive
1076+ case . closed: return closed
1077+ }
1078+ }
1079+ }
0 commit comments