good morning!!!!

Skip to content
Snippets Groups Projects
statuscode.go 2.97 KiB
Newer Older
package websocket
Anmol Sethi's avatar
Anmol Sethi committed

import (
	"encoding/binary"
	"fmt"
Anmol Sethi's avatar
Anmol Sethi committed

	"golang.org/x/xerrors"
Anmol Sethi's avatar
Anmol Sethi committed
)

// StatusCode represents a WebSocket status code.
// https://tools.ietf.org/html/rfc6455#section-7.4
Anmol Sethi's avatar
Anmol Sethi committed
type StatusCode int
//go:generate go run golang.org/x/tools/cmd/stringer -type=StatusCode
Anmol Sethi's avatar
Anmol Sethi committed

// These codes were retrieved from:
// https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
const (
	StatusNormalClosure StatusCode = 1000 + iota
	StatusGoingAway
	StatusProtocolError
	StatusUnsupportedData
	_ // 1004 is reserved.
	StatusNoStatusRcvd
	// statusAbnormalClosure is unexported because it isn't necessary, at least until WASM.
	// The error returned will indicate whether the connection was closed or not or what happened.
	// It only makes sense for browser clients.
	statusAbnormalClosure
Anmol Sethi's avatar
Anmol Sethi committed
	StatusInvalidFramePayloadData
	StatusPolicyViolation
	StatusMessageTooBig
	StatusMandatoryExtension
	StatusInternalError
	StatusServiceRestart
	StatusTryAgainLater
	StatusBadGateway
	// statusTLSHandshake is unexported because we just return
	// handshake error in dial. We do not return a conn
	// so there is nothing to use this on. At least until WASM.
	statusTLSHandshake
Anmol Sethi's avatar
Anmol Sethi committed
)

// CloseError represents a WebSocket close frame.
// It is returned by Conn's methods when the Connection is closed with a WebSocket close frame.
Anmol Sethi's avatar
Anmol Sethi committed
// You will need to use https://golang.org/x/xerrors to check for this error.
Anmol Sethi's avatar
Anmol Sethi committed
type CloseError struct {
	Code   StatusCode
	Reason string
}

func (ce CloseError) Error() string {
Anmol Sethi's avatar
Anmol Sethi committed
	return fmt.Sprintf("status = %v and reason = %q", ce.Code, ce.Reason)
Anmol Sethi's avatar
Anmol Sethi committed
}

func parseClosePayload(p []byte) (CloseError, error) {
	if len(p) == 0 {
		return CloseError{
			Code: StatusNoStatusRcvd,
		}, nil
	}

Anmol Sethi's avatar
Anmol Sethi committed
	if len(p) < 2 {
Anmol Sethi's avatar
Anmol Sethi committed
		return CloseError{}, xerrors.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p)
Anmol Sethi's avatar
Anmol Sethi committed
	}

	ce := CloseError{
		Code:   StatusCode(binary.BigEndian.Uint16(p)),
		Reason: string(p[2:]),
	}
Anmol Sethi's avatar
Anmol Sethi committed

	if !validWireCloseCode(ce.Code) {
		return CloseError{}, xerrors.Errorf("invalid status code %v", ce.Code)
	return ce, nil
Anmol Sethi's avatar
Anmol Sethi committed
}

// See http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
// and https://tools.ietf.org/html/rfc6455#section-7.4.1
func validWireCloseCode(code StatusCode) bool {
Anmol Sethi's avatar
Anmol Sethi committed
	switch code {
	case 1004, StatusNoStatusRcvd, statusAbnormalClosure, statusTLSHandshake:
		return false
	}

	if code >= StatusNormalClosure && code <= StatusBadGateway {
		return true
	}
	if code >= 3000 && code <= 4999 {
		return true
	}
	return false
Anmol Sethi's avatar
Anmol Sethi committed
const maxControlFramePayload = 125

func (ce CloseError) bytes() ([]byte, error) {
	if len(ce.Reason) > maxControlFramePayload-2 {
		return nil, xerrors.Errorf("reason string max is %v but got %q with length %v", maxControlFramePayload-2, ce.Reason, len(ce.Reason))
Anmol Sethi's avatar
Anmol Sethi committed
	}
	if !validWireCloseCode(ce.Code) {
		return nil, xerrors.Errorf("status code %v cannot be set", ce.Code)
Anmol Sethi's avatar
Anmol Sethi committed
	}

	buf := make([]byte, 2+len(ce.Reason))
	binary.BigEndian.PutUint16(buf, uint16(ce.Code))
	copy(buf[2:], ce.Reason)
Anmol Sethi's avatar
Anmol Sethi committed
	return buf, nil
}