@@ -2,6 +2,7 @@ package websocket_test
22
33import (
44 "bytes"
5+ "errors"
56 "testing"
67
78 "github.com/mccutchen/websocket"
@@ -51,3 +52,183 @@ func TestMaxFrameSize(t *testing.T) {
5152 assert .Error (t , err , websocket .ErrFrameTooLarge )
5253 assert .Equal (t , serverFrame , nil , "expected nil frame on error" )
5354}
55+
56+ func TestRSV (t * testing.T ) {
57+ // We don't currently support any extensions, so RSV bits are not allowed.
58+ // But we still need to be able properly parse and marshal them.
59+ const (
60+ finBit = 0b1000_0000
61+ txtOpcodeBit = 0b0000_0001
62+ rsv1bit = 0b0100_0000
63+ rsv2bit = 0b0010_0000
64+ rsv3bit = 0b0001_0000
65+ )
66+ testCases := map [string ]struct {
67+ rawBytes []byte
68+ wantRSV1 bool
69+ wantRSV2 bool
70+ wantRSV3 bool
71+ }{
72+ "no RSV bits set" : {
73+ rawBytes : []byte {0x81 , 0x00 },
74+ },
75+ "RSV1 set" : {
76+ rawBytes : []byte {finBit | rsv1bit | txtOpcodeBit , 0x00 },
77+ wantRSV1 : true ,
78+ },
79+ "RSV2 set" : {
80+ rawBytes : []byte {finBit | rsv2bit | txtOpcodeBit , 0x00 },
81+ wantRSV2 : true ,
82+ },
83+ "RSV3 set" : {
84+ rawBytes : []byte {finBit | rsv3bit | txtOpcodeBit , 0x00 },
85+ wantRSV3 : true ,
86+ },
87+ "all RSV bits set" : {
88+ rawBytes : []byte {finBit | rsv1bit | rsv2bit | rsv3bit | txtOpcodeBit , 0x00 },
89+ wantRSV1 : true ,
90+ wantRSV2 : true ,
91+ wantRSV3 : true ,
92+ },
93+ }
94+ for name , tc := range testCases {
95+ tc := tc
96+ t .Run (name , func (t * testing.T ) {
97+ t .Parallel ()
98+ buf := bytes .NewReader (tc .rawBytes )
99+ frame := mustReadFrame (t , buf , len (tc .rawBytes ))
100+ assert .Equal (t , frame .RSV1 (), tc .wantRSV1 , "incorrect RSV1" )
101+ assert .Equal (t , frame .RSV2 (), tc .wantRSV2 , "incorrect RSV2" )
102+ assert .Equal (t , frame .RSV3 (), tc .wantRSV3 , "incorrect RSV3" )
103+ })
104+ }
105+ }
106+
107+ func TestExampleFramesFromRFC (t * testing.T ) {
108+ // This tests every example provided in RFC 6455 section 5.7:
109+ // https://datatracker.ietf.org/doc/html/rfc6455#section-5.7
110+ testCases := map [string ]struct {
111+ rawBytes []byte
112+ wantFrame * websocket.Frame
113+ }{
114+ "single-frame unmasked text" : {
115+ rawBytes : []byte {0x81 , 0x05 , 0x48 , 0x65 , 0x6c , 0x6c , 0x6f },
116+ wantFrame : & websocket.Frame {
117+ Fin : true ,
118+ Opcode : websocket .OpcodeText ,
119+ Payload : []byte ("Hello" ),
120+ Masked : false ,
121+ },
122+ },
123+ "single-frame masked text" : {
124+ rawBytes : []byte {0x81 , 0x85 , 0x37 , 0xfa , 0x21 , 0x3d , 0x7f , 0x9f , 0x4d , 0x51 , 0x58 },
125+ wantFrame : & websocket.Frame {
126+ Fin : true ,
127+ Opcode : websocket .OpcodeText ,
128+ Payload : []byte ("Hello" ),
129+ Masked : true ,
130+ },
131+ },
132+ "fragmented unmasked text part 1" : {
133+ rawBytes : []byte {0x01 , 0x03 , 0x48 , 0x65 , 0x6c },
134+ wantFrame : & websocket.Frame {
135+ Fin : false ,
136+ Opcode : websocket .OpcodeText ,
137+ Payload : []byte ("Hel" ),
138+ },
139+ },
140+ "fragmented unmasked text part 2" : {
141+ rawBytes : []byte {0x80 , 0x02 , 0x6c , 0x6f },
142+ wantFrame : & websocket.Frame {
143+ Fin : true ,
144+ Opcode : websocket .OpcodeContinuation ,
145+ Payload : []byte ("lo" ),
146+ },
147+ },
148+ "unmasked ping" : {
149+ rawBytes : []byte {
150+ 0x89 , 0x05 , 0x48 , 0x65 , 0x6c , 0x6c , 0x6f ,
151+ },
152+ wantFrame : & websocket.Frame {
153+ Fin : true ,
154+ Opcode : websocket .OpcodePing ,
155+ Payload : []byte ("Hello" ),
156+ },
157+ },
158+ "masked ping response" : {
159+ rawBytes : []byte {0x8a , 0x85 , 0x37 , 0xfa , 0x21 , 0x3d , 0x7f , 0x9f , 0x4d , 0x51 , 0x58 },
160+ wantFrame : & websocket.Frame {
161+ Fin : true ,
162+ Opcode : websocket .OpcodePong ,
163+ Payload : []byte ("Hello" ),
164+ Masked : true ,
165+ },
166+ },
167+ "256 bytes binary message" : {
168+ rawBytes : append (
169+ []byte {0x82 , 0x7E , 0x01 , 0x00 },
170+ make ([]byte , 256 )... ,
171+ ),
172+ wantFrame : & websocket.Frame {
173+ Fin : true ,
174+ Opcode : websocket .OpcodeBinary ,
175+ Payload : make ([]byte , 256 ),
176+ },
177+ },
178+ "64KiB binary message" : {
179+ rawBytes : append (
180+ []byte {0x82 , 0x7F , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x00 },
181+ make ([]byte , 65536 )... ,
182+ ),
183+ wantFrame : & websocket.Frame {
184+ Fin : true ,
185+ Opcode : websocket .OpcodeBinary ,
186+ Payload : make ([]byte , 65536 ),
187+ },
188+ },
189+ }
190+
191+ for name , tc := range testCases {
192+ tc := tc
193+ t .Run (name , func (t * testing.T ) {
194+ t .Parallel ()
195+ buf := bytes .NewReader (tc .rawBytes )
196+ got := mustReadFrame (t , buf , len (tc .rawBytes ))
197+ assert .DeepEqual (t , got , tc .wantFrame , "frames do not match" )
198+ })
199+ }
200+ }
201+
202+ func TestIncompleteFrames (t * testing.T ) {
203+ testCases := map [string ]struct {
204+ rawBytes []byte
205+ wantErr error
206+ }{
207+ "2-byte extended payload can't be read" : {
208+ rawBytes : []byte {0x82 , 0x7E },
209+ wantErr : errors .New ("error reading 2-byte extended payload length: EOF" ),
210+ },
211+ "8-byte extended payload can't be read" : {
212+ rawBytes : []byte {0x82 , 0x7F },
213+ wantErr : errors .New ("error reading 8-byte extended payload length: EOF" ),
214+ },
215+ "mask can't be read" : {
216+ rawBytes : []byte {0x81 , 0x85 },
217+ wantErr : errors .New ("error reading mask key: EOF" ),
218+ },
219+ "payload can't be read" : {
220+ rawBytes : []byte {0x81 , 0x05 },
221+ wantErr : errors .New ("error reading 5 byte payload: EOF" ),
222+ },
223+ }
224+
225+ for name , tc := range testCases {
226+ tc := tc
227+ t .Run (name , func (t * testing.T ) {
228+ t .Parallel ()
229+ buf := bytes .NewReader (tc .rawBytes )
230+ _ , err := websocket .ReadFrame (buf , 70000 )
231+ assert .Error (t , err , tc .wantErr )
232+ })
233+ }
234+ }
0 commit comments