package ws

import (
	
	
	
	
	
	

	
)

const (
	crlf          = "\r\n"
	colonAndSpace = ": "
	commaAndSpace = ", "
)

const (
	textHeadUpgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n"
)

var (
	textHeadBadRequest          = statusText(http.StatusBadRequest)
	textHeadInternalServerError = statusText(http.StatusInternalServerError)
	textHeadUpgradeRequired     = statusText(http.StatusUpgradeRequired)

	textTailErrHandshakeBadProtocol   = errorText(ErrHandshakeBadProtocol)
	textTailErrHandshakeBadMethod     = errorText(ErrHandshakeBadMethod)
	textTailErrHandshakeBadHost       = errorText(ErrHandshakeBadHost)
	textTailErrHandshakeBadUpgrade    = errorText(ErrHandshakeBadUpgrade)
	textTailErrHandshakeBadConnection = errorText(ErrHandshakeBadConnection)
	textTailErrHandshakeBadSecAccept  = errorText(ErrHandshakeBadSecAccept)
	textTailErrHandshakeBadSecKey     = errorText(ErrHandshakeBadSecKey)
	textTailErrHandshakeBadSecVersion = errorText(ErrHandshakeBadSecVersion)
	textTailErrUpgradeRequired        = errorText(ErrHandshakeUpgradeRequired)
)

const (
	// Every new header must be added to TestHeaderNames test.
	headerHost          = "Host"
	headerUpgrade       = "Upgrade"
	headerConnection    = "Connection"
	headerSecVersion    = "Sec-WebSocket-Version"
	headerSecProtocol   = "Sec-WebSocket-Protocol"
	headerSecExtensions = "Sec-WebSocket-Extensions"
	headerSecKey        = "Sec-WebSocket-Key"
	headerSecAccept     = "Sec-WebSocket-Accept"

	headerHostCanonical          = headerHost
	headerUpgradeCanonical       = headerUpgrade
	headerConnectionCanonical    = headerConnection
	headerSecVersionCanonical    = "Sec-Websocket-Version"
	headerSecProtocolCanonical   = "Sec-Websocket-Protocol"
	headerSecExtensionsCanonical = "Sec-Websocket-Extensions"
	headerSecKeyCanonical        = "Sec-Websocket-Key"
	headerSecAcceptCanonical     = "Sec-Websocket-Accept"
)

var (
	specHeaderValueUpgrade         = []byte("websocket")
	specHeaderValueConnection      = []byte("Upgrade")
	specHeaderValueConnectionLower = []byte("upgrade")
	specHeaderValueSecVersion      = []byte("13")
)

var (
	httpVersion1_0    = []byte("HTTP/1.0")
	httpVersion1_1    = []byte("HTTP/1.1")
	httpVersionPrefix = []byte("HTTP/")
)

type httpRequestLine struct {
	method, uri  []byte
	major, minor int
}

type httpResponseLine struct {
	major, minor int
	status       int
	reason       []byte
}

// httpParseRequestLine parses http request line like "GET / HTTP/1.0".
func httpParseRequestLine( []byte) ( httpRequestLine,  error) {
	var  []byte
	.method, .uri,  = bsplit3(, ' ')

	var  bool
	.major, .minor,  = httpParseVersion()
	if ! {
		 = ErrMalformedRequest
	}
	return , 
}

func httpParseResponseLine( []byte) ( httpResponseLine,  error) {
	var (
		  []byte
		 []byte
	)
	, , .reason = bsplit3(, ' ')

	var  bool
	.major, .minor,  = httpParseVersion()
	if ! {
		return , ErrMalformedResponse
	}

	var  error
	.status,  = asciiToInt()
	if  != nil {
		return , ErrMalformedResponse
	}

	return , nil
}

// httpParseVersion parses major and minor version of HTTP protocol. It returns
// parsed values and true if parse is ok.
func httpParseVersion( []byte) (,  int,  bool) {
	switch {
	case bytes.Equal(, httpVersion1_0):
		return 1, 0, true
	case bytes.Equal(, httpVersion1_1):
		return 1, 1, true
	case len() < 8:
		return 0, 0, false
	case !bytes.Equal([:5], httpVersionPrefix):
		return 0, 0, false
	}

	 = [5:]

	 := bytes.IndexByte(, '.')
	if  == -1 {
		return 0, 0, false
	}
	var  error
	,  = asciiToInt([:])
	if  != nil {
		return , 0, false
	}
	,  = asciiToInt([+1:])
	if  != nil {
		return , , false
	}

	return , , true
}

// httpParseHeaderLine parses HTTP header as key-value pair. It returns parsed
// values and true if parse is ok.
func httpParseHeaderLine( []byte) (,  []byte,  bool) {
	 := bytes.IndexByte(, ':')
	if  == -1 {
		return nil, nil, false
	}

	 = btrim([:])
	// TODO(gobwas): maybe use just lower here?
	canonicalizeHeaderKey()

	 = btrim([+1:])

	return , , true
}

// httpGetHeader is the same as textproto.MIMEHeader.Get, except the thing,
// that key is already canonical. This helps to increase performance.
func httpGetHeader( http.Header,  string) string {
	if  == nil {
		return ""
	}
	 := []
	if len() == 0 {
		return ""
	}
	return [0]
}

// The request MAY include a header field with the name
// |Sec-WebSocket-Protocol|.  If present, this value indicates one or more
// comma-separated subprotocol the client wishes to speak, ordered by
// preference.  The elements that comprise this value MUST be non-empty strings
// with characters in the range U+0021 to U+007E not including separator
// characters as defined in [RFC2616] and MUST all be unique strings.  The ABNF
// for the value of this header field is 1#token, where the definitions of
// constructs and rules are as given in [RFC2616].
func strSelectProtocol( string,  func(string) bool) ( string,  bool) {
	 = httphead.ScanTokens(strToBytes(), func( []byte) bool {
		if (btsToString()) {
			 = string()
			return false
		}
		return true
	})
	return , 
}

func btsSelectProtocol( []byte,  func([]byte) bool) ( string,  bool) {
	var  []byte
	 = httphead.ScanTokens(, func( []byte) bool {
		if () {
			 = 
			return false
		}
		return true
	})
	if  &&  != nil {
		return string(), true
	}
	return , 
}

func btsSelectExtensions( []byte,  []httphead.Option,  func(httphead.Option) bool) ([]httphead.Option, bool) {
	 := httphead.OptionSelector{
		Flags: httphead.SelectCopy,
		Check: ,
	}
	return .Select(, )
}

func negotiateMaybe( httphead.Option,  []httphead.Option,  func(httphead.Option) (httphead.Option, error)) ([]httphead.Option, error) {
	if .Size() == 0 {
		return , nil
	}
	,  := ()
	if  != nil {
		return nil, 
	}
	if .Size() > 0 {
		 = append(, )
	}
	return , nil
}

func negotiateExtensions(
	 []byte,  []httphead.Option,
	 func(httphead.Option) (httphead.Option, error),
) ( []httphead.Option,  error) {
	 := -1
	var  httphead.Option
	 := httphead.ScanOptions(, func( int, , ,  []byte) httphead.Control {
		if  !=  {
			,  = negotiateMaybe(, , )
			if  != nil {
				return httphead.ControlBreak
			}
			 = 
			 = httphead.Option{Name: }
		}
		if  != nil {
			.Parameters.Set(, )
		}
		return httphead.ControlContinue
	})
	if ! {
		return nil, ErrMalformedRequest
	}
	return negotiateMaybe(, , )
}

func httpWriteHeader( *bufio.Writer, ,  string) {
	httpWriteHeaderKey(, )
	.WriteString()
	.WriteString(crlf)
}

func httpWriteHeaderBts( *bufio.Writer,  string,  []byte) {
	httpWriteHeaderKey(, )
	.Write()
	.WriteString(crlf)
}

func httpWriteHeaderKey( *bufio.Writer,  string) {
	.WriteString()
	.WriteString(colonAndSpace)
}

func httpWriteUpgradeRequest(
	 *bufio.Writer,
	 *url.URL,
	 []byte,
	 []string,
	 []httphead.Option,
	 HandshakeHeader,
) {
	.WriteString("GET ")
	.WriteString(.RequestURI())
	.WriteString(" HTTP/1.1\r\n")

	httpWriteHeader(, headerHost, .Host)

	httpWriteHeaderBts(, headerUpgrade, specHeaderValueUpgrade)
	httpWriteHeaderBts(, headerConnection, specHeaderValueConnection)
	httpWriteHeaderBts(, headerSecVersion, specHeaderValueSecVersion)

	// NOTE: write nonce bytes as a string to prevent heap allocation –
	// WriteString() copy given string into its inner buffer, unlike Write()
	// which may write p directly to the underlying io.Writer – which in turn
	// will lead to p escape.
	httpWriteHeader(, headerSecKey, btsToString())

	if len() > 0 {
		httpWriteHeaderKey(, headerSecProtocol)
		for ,  := range  {
			if  > 0 {
				.WriteString(commaAndSpace)
			}
			.WriteString()
		}
		.WriteString(crlf)
	}

	if len() > 0 {
		httpWriteHeaderKey(, headerSecExtensions)
		httphead.WriteOptions(, )
		.WriteString(crlf)
	}

	if  != nil {
		.WriteTo()
	}

	.WriteString(crlf)
}

func httpWriteResponseUpgrade( *bufio.Writer,  []byte,  Handshake,  HandshakeHeaderFunc) {
	.WriteString(textHeadUpgrade)

	httpWriteHeaderKey(, headerSecAccept)
	writeAccept(, )
	.WriteString(crlf)

	if .Protocol != "" {
		httpWriteHeader(, headerSecProtocol, .Protocol)
	}
	if len(.Extensions) > 0 {
		httpWriteHeaderKey(, headerSecExtensions)
		httphead.WriteOptions(, .Extensions)
		.WriteString(crlf)
	}
	if  != nil {
		()
	}

	.WriteString(crlf)
}

func httpWriteResponseError( *bufio.Writer,  error,  int,  HandshakeHeaderFunc) {
	switch  {
	case http.StatusBadRequest:
		.WriteString(textHeadBadRequest)
	case http.StatusInternalServerError:
		.WriteString(textHeadInternalServerError)
	case http.StatusUpgradeRequired:
		.WriteString(textHeadUpgradeRequired)
	default:
		writeStatusText(, )
	}

	// Write custom headers.
	if  != nil {
		()
	}

	switch  {
	case ErrHandshakeBadProtocol:
		.WriteString(textTailErrHandshakeBadProtocol)
	case ErrHandshakeBadMethod:
		.WriteString(textTailErrHandshakeBadMethod)
	case ErrHandshakeBadHost:
		.WriteString(textTailErrHandshakeBadHost)
	case ErrHandshakeBadUpgrade:
		.WriteString(textTailErrHandshakeBadUpgrade)
	case ErrHandshakeBadConnection:
		.WriteString(textTailErrHandshakeBadConnection)
	case ErrHandshakeBadSecAccept:
		.WriteString(textTailErrHandshakeBadSecAccept)
	case ErrHandshakeBadSecKey:
		.WriteString(textTailErrHandshakeBadSecKey)
	case ErrHandshakeBadSecVersion:
		.WriteString(textTailErrHandshakeBadSecVersion)
	case ErrHandshakeUpgradeRequired:
		.WriteString(textTailErrUpgradeRequired)
	case nil:
		.WriteString(crlf)
	default:
		writeErrorText(, )
	}
}

func writeStatusText( *bufio.Writer,  int) {
	.WriteString("HTTP/1.1 ")
	.WriteString(strconv.Itoa())
	.WriteByte(' ')
	.WriteString(http.StatusText())
	.WriteString(crlf)
	.WriteString("Content-Type: text/plain; charset=utf-8")
	.WriteString(crlf)
}

func writeErrorText( *bufio.Writer,  error) {
	 := .Error()
	.WriteString("Content-Length: ")
	.WriteString(strconv.Itoa(len()))
	.WriteString(crlf)
	.WriteString(crlf)
	.WriteString()
}

// httpError is like the http.Error with WebSocket context exception.
func httpError( http.ResponseWriter,  string,  int) {
	.Header().Set("Content-Type", "text/plain; charset=utf-8")
	.Header().Set("Content-Length", strconv.Itoa(len()))
	.WriteHeader()
	.Write([]byte())
}

// statusText is a non-performant status text generator.
// NOTE: Used only to generate constants.
func statusText( int) string {
	var  bytes.Buffer
	 := bufio.NewWriter(&)
	writeStatusText(, )
	.Flush()
	return .String()
}

// errorText is a non-performant error text generator.
// NOTE: Used only to generate constants.
func errorText( error) string {
	var  bytes.Buffer
	 := bufio.NewWriter(&)
	writeErrorText(, )
	.Flush()
	return .String()
}

// HandshakeHeader is the interface that writes both upgrade request or
// response headers into a given io.Writer.
type HandshakeHeader interface {
	io.WriterTo
}

// HandshakeHeaderString is an adapter to allow the use of headers represented
// by ordinary string as HandshakeHeader.
type HandshakeHeaderString string

// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
func ( HandshakeHeaderString) ( io.Writer) (int64, error) {
	,  := io.WriteString(, string())
	return int64(), 
}

// HandshakeHeaderBytes is an adapter to allow the use of headers represented
// by ordinary slice of bytes as HandshakeHeader.
type HandshakeHeaderBytes []byte

// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
func ( HandshakeHeaderBytes) ( io.Writer) (int64, error) {
	,  := .Write()
	return int64(), 
}

// HandshakeHeaderFunc is an adapter to allow the use of headers represented by
// ordinary function as HandshakeHeader.
type HandshakeHeaderFunc func(io.Writer) (int64, error)

// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
func ( HandshakeHeaderFunc) ( io.Writer) (int64, error) {
	return ()
}

// HandshakeHeaderHTTP is an adapter to allow the use of http.Header as
// HandshakeHeader.
type HandshakeHeaderHTTP http.Header

// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
func ( HandshakeHeaderHTTP) ( io.Writer) (int64, error) {
	 := writer{w: }
	 := http.Header().Write(&)
	return .n, 
}

type writer struct {
	n int64
	w io.Writer
}

func ( *writer) ( string) (int, error) {
	,  := io.WriteString(.w, )
	.n += int64()
	return , 
}

func ( *writer) ( []byte) (int, error) {
	,  := .w.Write()
	.n += int64()
	return , 
}