//go:build !js
// +build !js

package websocket

import (
	
	
	
	
	
	
	
	
	
	
	
	

	
)

// AcceptOptions represents Accept's options.
type AcceptOptions struct {
	// Subprotocols lists the WebSocket subprotocols that Accept will negotiate with the client.
	// The empty subprotocol will always be negotiated as per RFC 6455. If you would like to
	// reject it, close the connection when c.Subprotocol() == "".
	Subprotocols []string

	// InsecureSkipVerify is used to disable Accept's origin verification behaviour.
	//
	// You probably want to use OriginPatterns instead.
	InsecureSkipVerify bool

	// OriginPatterns lists the host patterns for authorized origins.
	// The request host is always authorized.
	// Use this to enable cross origin WebSockets.
	//
	// i.e javascript running on example.com wants to access a WebSocket server at chat.example.com.
	// In such a case, example.com is the origin and chat.example.com is the request host.
	// One would set this field to []string{"example.com"} to authorize example.com to connect.
	//
	// Each pattern is matched case insensitively against the request origin host
	// with filepath.Match.
	// See https://golang.org/pkg/path/filepath/#Match
	//
	// Please ensure you understand the ramifications of enabling this.
	// If used incorrectly your WebSocket server will be open to CSRF attacks.
	//
	// Do not use * as a pattern to allow any origin, prefer to use InsecureSkipVerify instead
	// to bring attention to the danger of such a setting.
	OriginPatterns []string

	// CompressionMode controls the compression mode.
	// Defaults to CompressionDisabled.
	//
	// See docs on CompressionMode for details.
	CompressionMode CompressionMode

	// CompressionThreshold controls the minimum size of a message before compression is applied.
	//
	// Defaults to 512 bytes for CompressionNoContextTakeover and 128 bytes
	// for CompressionContextTakeover.
	CompressionThreshold int
}

func ( *AcceptOptions) () *AcceptOptions {
	var  AcceptOptions
	if  != nil {
		 = *
	}
	return &
}

// Accept accepts a WebSocket handshake from a client and upgrades the
// the connection to a WebSocket.
//
// Accept will not allow cross origin requests by default.
// See the InsecureSkipVerify and OriginPatterns options to allow cross origin requests.
//
// Accept will write a response to w on all errors.
func ( http.ResponseWriter,  *http.Request,  *AcceptOptions) (*Conn, error) {
	return accept(, , )
}

func ( http.ResponseWriter,  *http.Request,  *AcceptOptions) ( *Conn,  error) {
	defer errd.Wrap(&, "failed to accept WebSocket connection")

	,  := verifyClientRequest(, )
	if  != nil {
		http.Error(, .Error(), )
		return nil, 
	}

	 = .cloneWithDefaults()
	if !.InsecureSkipVerify {
		 = authenticateOrigin(, .OriginPatterns)
		if  != nil {
			if errors.Is(, filepath.ErrBadPattern) {
				log.Printf("websocket: %v", )
				 = errors.New(http.StatusText(http.StatusForbidden))
			}
			http.Error(, .Error(), http.StatusForbidden)
			return nil, 
		}
	}

	,  := .(http.Hijacker)
	if ! {
		 = errors.New("http.ResponseWriter does not implement http.Hijacker")
		http.Error(, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
		return nil, 
	}

	.Header().Set("Upgrade", "websocket")
	.Header().Set("Connection", "Upgrade")

	 := .Header.Get("Sec-WebSocket-Key")
	.Header().Set("Sec-WebSocket-Accept", secWebSocketAccept())

	 := selectSubprotocol(, .Subprotocols)
	if  != "" {
		.Header().Set("Sec-WebSocket-Protocol", )
	}

	,  := selectDeflate(websocketExtensions(.Header), .CompressionMode)
	if  {
		.Header().Set("Sec-WebSocket-Extensions", .String())
	}

	.WriteHeader(http.StatusSwitchingProtocols)
	// See https://github.com/nhooyr/websocket/issues/166
	if ,  := .(interface {
		()
	});  {
		.()
	}

	, ,  := .Hijack()
	if  != nil {
		 = fmt.Errorf("failed to hijack connection: %w", )
		http.Error(, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return nil, 
	}

	// https://github.com/golang/go/issues/32314
	,  := .Reader.Peek(.Reader.Buffered())
	.Reader.Reset(io.MultiReader(bytes.NewReader(), ))

	return newConn(connConfig{
		subprotocol:    .Header().Get("Sec-WebSocket-Protocol"),
		rwc:            ,
		client:         false,
		copts:          ,
		flateThreshold: .CompressionThreshold,

		br: .Reader,
		bw: .Writer,
	}), nil
}

func ( http.ResponseWriter,  *http.Request) ( int,  error) {
	if !.ProtoAtLeast(1, 1) {
		return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: handshake request must be at least HTTP/1.1: %q", .Proto)
	}

	if !headerContainsTokenIgnoreCase(.Header, "Connection", "Upgrade") {
		.Header().Set("Connection", "Upgrade")
		.Header().Set("Upgrade", "websocket")
		return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", .Header.Get("Connection"))
	}

	if !headerContainsTokenIgnoreCase(.Header, "Upgrade", "websocket") {
		.Header().Set("Connection", "Upgrade")
		.Header().Set("Upgrade", "websocket")
		return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", .Header.Get("Upgrade"))
	}

	if .Method != "GET" {
		return http.StatusMethodNotAllowed, fmt.Errorf("WebSocket protocol violation: handshake request method is not GET but %q", .Method)
	}

	if .Header.Get("Sec-WebSocket-Version") != "13" {
		.Header().Set("Sec-WebSocket-Version", "13")
		return http.StatusBadRequest, fmt.Errorf("unsupported WebSocket protocol version (only 13 is supported): %q", .Header.Get("Sec-WebSocket-Version"))
	}

	 := .Header.Values("Sec-WebSocket-Key")
	if len() == 0 {
		return http.StatusBadRequest, errors.New("WebSocket protocol violation: missing Sec-WebSocket-Key")
	}

	if len() > 1 {
		return http.StatusBadRequest, errors.New("WebSocket protocol violation: multiple Sec-WebSocket-Key headers")
	}

	// The RFC states to remove any leading or trailing whitespace.
	 := strings.TrimSpace([0])
	if ,  := base64.StdEncoding.DecodeString();  != nil || len() != 16 {
		return http.StatusBadRequest, fmt.Errorf("WebSocket protocol violation: invalid Sec-WebSocket-Key %q, must be a 16 byte base64 encoded string", )
	}

	return 0, nil
}

func ( *http.Request,  []string) error {
	 := .Header.Get("Origin")
	if  == "" {
		return nil
	}

	,  := url.Parse()
	if  != nil {
		return fmt.Errorf("failed to parse Origin header %q: %w", , )
	}

	if strings.EqualFold(.Host, .Host) {
		return nil
	}

	for ,  := range  {
		,  := match(, .Host)
		if  != nil {
			return fmt.Errorf("failed to parse filepath pattern %q: %w", , )
		}
		if  {
			return nil
		}
	}
	if .Host == "" {
		return fmt.Errorf("request Origin %q is not a valid URL with a host", )
	}
	return fmt.Errorf("request Origin %q is not authorized for Host %q", .Host, .Host)
}

func (,  string) (bool, error) {
	return filepath.Match(strings.ToLower(), strings.ToLower())
}

func ( *http.Request,  []string) string {
	 := headerTokens(.Header, "Sec-WebSocket-Protocol")
	for ,  := range  {
		for ,  := range  {
			if strings.EqualFold(, ) {
				return 
			}
		}
	}
	return ""
}

func ( []websocketExtension,  CompressionMode) (*compressionOptions, bool) {
	if  == CompressionDisabled {
		return nil, false
	}
	for ,  := range  {
		switch .name {
		// We used to implement x-webkit-deflate-frame too for Safari but Safari has bugs...
		// See https://github.com/nhooyr/websocket/issues/218
		case "permessage-deflate":
			,  := acceptDeflate(, )
			if  {
				return , true
			}
		}
	}
	return nil, false
}

func ( websocketExtension,  CompressionMode) (*compressionOptions, bool) {
	 := .opts()
	for ,  := range .params {
		switch  {
		case "client_no_context_takeover":
			.clientNoContextTakeover = true
			continue
		case "server_no_context_takeover":
			.serverNoContextTakeover = true
			continue
		case "client_max_window_bits",
			"server_max_window_bits=15":
			continue
		}

		if strings.HasPrefix(, "client_max_window_bits=") {
			// We can't adjust the deflate window, but decoding with a larger window is acceptable.
			continue
		}
		return nil, false
	}
	return , true
}

func ( http.Header, ,  string) bool {
	for ,  := range headerTokens(, ) {
		if strings.EqualFold(, ) {
			return true
		}
	}
	return false
}

type websocketExtension struct {
	name   string
	params []string
}

func ( http.Header) []websocketExtension {
	var  []websocketExtension
	 := headerTokens(, "Sec-WebSocket-Extensions")
	for ,  := range  {
		if  == "" {
			continue
		}

		 := strings.Split(, ";")
		for  := range  {
			[] = strings.TrimSpace([])
		}

		 := websocketExtension{
			name:   [0],
			params: [1:],
		}

		 = append(, )
	}
	return 
}

func ( http.Header,  string) []string {
	 = textproto.CanonicalMIMEHeaderKey()
	var  []string
	for ,  := range [] {
		 = strings.TrimSpace()
		for ,  := range strings.Split(, ",") {
			 = strings.TrimSpace()
			 = append(, )
		}
	}
	return 
}

var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")

func ( string) string {
	 := sha1.New()
	.Write([]byte())
	.Write(keyGUID)

	return base64.StdEncoding.EncodeToString(.Sum(nil))
}