1package yamux
2
3import (
4	"encoding/binary"
5	"fmt"
6)
7
8var (
9	// ErrInvalidVersion means we received a frame with an
10	// invalid version
11	ErrInvalidVersion = fmt.Errorf("invalid protocol version")
12
13	// ErrInvalidMsgType means we received a frame with an
14	// invalid message type
15	ErrInvalidMsgType = fmt.Errorf("invalid msg type")
16
17	// ErrSessionShutdown is used if there is a shutdown during
18	// an operation
19	ErrSessionShutdown = fmt.Errorf("session shutdown")
20
21	// ErrStreamsExhausted is returned if we have no more
22	// stream ids to issue
23	ErrStreamsExhausted = fmt.Errorf("streams exhausted")
24
25	// ErrDuplicateStream is used if a duplicate stream is
26	// opened inbound
27	ErrDuplicateStream = fmt.Errorf("duplicate stream initiated")
28
29	// ErrReceiveWindowExceeded indicates the window was exceeded
30	ErrRecvWindowExceeded = fmt.Errorf("recv window exceeded")
31
32	// ErrTimeout is used when we reach an IO deadline
33	ErrTimeout = fmt.Errorf("i/o deadline reached")
34
35	// ErrStreamClosed is returned when using a closed stream
36	ErrStreamClosed = fmt.Errorf("stream closed")
37
38	// ErrUnexpectedFlag is set when we get an unexpected flag
39	ErrUnexpectedFlag = fmt.Errorf("unexpected flag")
40
41	// ErrRemoteGoAway is used when we get a go away from the other side
42	ErrRemoteGoAway = fmt.Errorf("remote end is not accepting connections")
43
44	// ErrConnectionReset is sent if a stream is reset. This can happen
45	// if the backlog is exceeded, or if there was a remote GoAway.
46	ErrConnectionReset = fmt.Errorf("connection reset")
47
48	// ErrConnectionWriteTimeout indicates that we hit the "safety valve"
49	// timeout writing to the underlying stream connection.
50	ErrConnectionWriteTimeout = fmt.Errorf("connection write timeout")
51
52	// ErrKeepAliveTimeout is sent if a missed keepalive caused the stream close
53	ErrKeepAliveTimeout = fmt.Errorf("keepalive timeout")
54)
55
56const (
57	// protoVersion is the only version we support
58	protoVersion uint8 = 0
59)
60
61const (
62	// Data is used for data frames. They are followed
63	// by length bytes worth of payload.
64	typeData uint8 = iota
65
66	// WindowUpdate is used to change the window of
67	// a given stream. The length indicates the delta
68	// update to the window.
69	typeWindowUpdate
70
71	// Ping is sent as a keep-alive or to measure
72	// the RTT. The StreamID and Length value are echoed
73	// back in the response.
74	typePing
75
76	// GoAway is sent to terminate a session. The StreamID
77	// should be 0 and the length is an error code.
78	typeGoAway
79)
80
81const (
82	// SYN is sent to signal a new stream. May
83	// be sent with a data payload
84	flagSYN uint16 = 1 << iota
85
86	// ACK is sent to acknowledge a new stream. May
87	// be sent with a data payload
88	flagACK
89
90	// FIN is sent to half-close the given stream.
91	// May be sent with a data payload.
92	flagFIN
93
94	// RST is used to hard close a given stream.
95	flagRST
96)
97
98const (
99	// initialStreamWindow is the initial stream window size
100	initialStreamWindow uint32 = 256 * 1024
101)
102
103const (
104	// goAwayNormal is sent on a normal termination
105	goAwayNormal uint32 = iota
106
107	// goAwayProtoErr sent on a protocol error
108	goAwayProtoErr
109
110	// goAwayInternalErr sent on an internal error
111	goAwayInternalErr
112)
113
114const (
115	sizeOfVersion  = 1
116	sizeOfType     = 1
117	sizeOfFlags    = 2
118	sizeOfStreamID = 4
119	sizeOfLength   = 4
120	headerSize     = sizeOfVersion + sizeOfType + sizeOfFlags +
121		sizeOfStreamID + sizeOfLength
122)
123
124type header []byte
125
126func (h header) Version() uint8 {
127	return h[0]
128}
129
130func (h header) MsgType() uint8 {
131	return h[1]
132}
133
134func (h header) Flags() uint16 {
135	return binary.BigEndian.Uint16(h[2:4])
136}
137
138func (h header) StreamID() uint32 {
139	return binary.BigEndian.Uint32(h[4:8])
140}
141
142func (h header) Length() uint32 {
143	return binary.BigEndian.Uint32(h[8:12])
144}
145
146func (h header) String() string {
147	return fmt.Sprintf("Vsn:%d Type:%d Flags:%d StreamID:%d Length:%d",
148		h.Version(), h.MsgType(), h.Flags(), h.StreamID(), h.Length())
149}
150
151func (h header) encode(msgType uint8, flags uint16, streamID uint32, length uint32) {
152	h[0] = protoVersion
153	h[1] = msgType
154	binary.BigEndian.PutUint16(h[2:4], flags)
155	binary.BigEndian.PutUint32(h[4:8], streamID)
156	binary.BigEndian.PutUint32(h[8:12], length)
157}
158