@@ -24,7 +24,10 @@ const (
2424 StatusUnsupportedData
2525 _ // 1004 is reserved.
2626 StatusNoStatusRcvd
27- StatusAbnormalClosure
27+ // statusAbnormalClosure is unexported because it isn't necessary, at least until WASM.
28+ // The error returned will indicate whether the connection was closed or not or what happened.
29+ // It only makes sense for browser clients.
30+ statusAbnormalClosure
2831 StatusInvalidFramePayloadData
2932 StatusPolicyViolation
3033 StatusMessageTooBig
@@ -33,7 +36,10 @@ const (
3336 StatusServiceRestart
3437 StatusTryAgainLater
3538 StatusBadGateway
36- StatusTLSHandshake
39+ // statusTLSHandshake is unexported because we just return
40+ // handshake error in dial. We do not return a conn
41+ // so there is nothing to use this on. At least until WASM.
42+ statusTLSHandshake
3743)
3844
3945// CloseError represents an error from a WebSocket close frame.
@@ -43,68 +49,63 @@ type CloseError struct {
4349 Reason string
4450}
4551
46- func (e CloseError ) Error () string {
47- return fmt .Sprintf ("WebSocket closed with status = %v and reason = %q" , e .Code , e .Reason )
52+ func (ce CloseError ) Error () string {
53+ return fmt .Sprintf ("WebSocket closed with status = %v and reason = %q" , ce .Code , ce .Reason )
4854}
4955
50- func parseClosePayload (p []byte ) (code StatusCode , reason string , err error ) {
56+ func parseClosePayload (p []byte ) (CloseError , error ) {
5157 if len (p ) < 2 {
52- return 0 , "" , fmt .Errorf ("close payload too small, cannot even contain the 2 byte status code" )
58+ return CloseError {} , fmt .Errorf ("close payload too small, cannot even contain the 2 byte status code" )
5359 }
5460
55- code = StatusCode (binary .BigEndian .Uint16 (p ))
56- reason = string (p [2 :])
61+ ce := CloseError {
62+ Code : StatusCode (binary .BigEndian .Uint16 (p )),
63+ Reason : string (p [2 :]),
64+ }
5765
58- if ! utf8 .ValidString (reason ) {
59- return 0 , "" , xerrors .Errorf ("invalid utf-8: %q" , reason )
66+ if ! utf8 .ValidString (ce . Reason ) {
67+ return CloseError {}, xerrors .Errorf ("invalid utf-8: %q" , ce . Reason )
6068 }
61- if ! validCloseCode ( code ) {
62- return 0 , "" , xerrors .Errorf ("invalid code %v" , code )
69+ if ! validWireCloseCode ( ce . Code ) {
70+ return CloseError {}, xerrors .Errorf ("invalid code %v" , ce . Code )
6371 }
6472
65- return code , reason , nil
73+ return ce , nil
6674}
6775
6876// See http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
6977// and https://tools.ietf.org/html/rfc6455#section-7.4.1
70- var validReceivedCloseCodes = map [StatusCode ]bool {
71- StatusNormalClosure : true ,
72- StatusGoingAway : true ,
73- StatusProtocolError : true ,
74- StatusUnsupportedData : true ,
75- StatusNoStatusRcvd : false ,
76- // TODO use
77- StatusAbnormalClosure : false ,
78- StatusInvalidFramePayloadData : true ,
79- StatusPolicyViolation : true ,
80- StatusMessageTooBig : true ,
81- StatusMandatoryExtension : true ,
82- StatusInternalError : true ,
83- StatusServiceRestart : true ,
84- StatusTryAgainLater : true ,
85- StatusTLSHandshake : false ,
86- }
78+ func validWireCloseCode (code StatusCode ) bool {
79+ if code >= StatusNormalClosure && code <= statusTLSHandshake {
80+ switch code {
81+ case 1004 , StatusNoStatusRcvd , statusAbnormalClosure , statusTLSHandshake :
82+ return false
83+ default :
84+ return true
85+ }
86+ }
87+ if code >= 3000 && code <= 4999 {
88+ return true
89+ }
8790
88- func validCloseCode (code StatusCode ) bool {
89- return validReceivedCloseCodes [code ] || (code >= 3000 && code <= 4999 )
91+ return false
9092}
9193
9294const maxControlFramePayload = 125
9395
94- // TODO make method on CloseError
95- func closePayload (code StatusCode , reason string ) ([]byte , error ) {
96- if len (reason ) > maxControlFramePayload - 2 {
97- return nil , xerrors .Errorf ("reason string max is %v but got %q with length %v" , maxControlFramePayload - 2 , reason , len (reason ))
96+ func (ce CloseError ) bytes () ([]byte , error ) {
97+ if len (ce .Reason ) > maxControlFramePayload - 2 {
98+ return nil , xerrors .Errorf ("reason string max is %v but got %q with length %v" , maxControlFramePayload - 2 , ce .Reason , len (ce .Reason ))
9899 }
99- if bits .Len (uint (code )) > 16 {
100+ if bits .Len (uint (ce . Code )) > 16 {
100101 return nil , errors .New ("status code is larger than 2 bytes" )
101102 }
102- if ! validCloseCode ( code ) {
103- return nil , fmt .Errorf ("status code %v cannot be set" , code )
103+ if ! validWireCloseCode ( ce . Code ) {
104+ return nil , fmt .Errorf ("status code %v cannot be set" , ce . Code )
104105 }
105106
106- buf := make ([]byte , 2 + len (reason ))
107- binary .BigEndian .PutUint16 (buf [:], uint16 (code ))
108- copy (buf [2 :], reason )
107+ buf := make ([]byte , 2 + len (ce . Reason ))
108+ binary .BigEndian .PutUint16 (buf [:], uint16 (ce . Code ))
109+ copy (buf [2 :], ce . Reason )
109110 return buf , nil
110111}
0 commit comments