text: // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package websocket
  
  import (
  	"crypto/rand"
  	"crypto/sha1"
  	"encoding/base64"
  	"io"
  	"net/http"
  	"strings"
  	"unicode/utf8"
  )
  
  var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
  
  func computeAcceptKey(challengeKey string) string {
  	h := sha1.New()
  	h.Write([]byte(challengeKey))
  	h.Write(keyGUID)
  	return base64.StdEncoding.EncodeToString(h.Sum(nil))
  }
  
  func generateChallengeKey() (string, error) {
  	p := make([]byte, 16)
  	if _, err := io.ReadFull(rand.Reader, p); err != nil {
  		return "", err
  	}
  	return base64.StdEncoding.EncodeToString(p), nil
  }
  
  // Token octets per RFC 2616.
  var isTokenOctet = [256]bool{
  	'!':  true,
  	'#':  true,
  	'$':  true,
  	'%':  true,
  	'&':  true,
  	'\'': true,
  	'*':  true,
  	'+':  true,
  	'-':  true,
  	'.':  true,
  	'0':  true,
  	'1':  true,
  	'2':  true,
  	'3':  true,
  	'4':  true,
  	'5':  true,
  	'6':  true,
  	'7':  true,
  	'8':  true,
  	'9':  true,
  	'A':  true,
  	'B':  true,
  	'C':  true,
  	'D':  true,
  	'E':  true,
  	'F':  true,
  	'G':  true,
  	'H':  true,
  	'I':  true,
  	'J':  true,
  	'K':  true,
  	'L':  true,
  	'M':  true,
  	'N':  true,
  	'O':  true,
  	'P':  true,
  	'Q':  true,
  	'R':  true,
  	'S':  true,
  	'T':  true,
  	'U':  true,
  	'W':  true,
  	'V':  true,
  	'X':  true,
  	'Y':  true,
  	'Z':  true,
  	'^':  true,
  	'_':  true,
  	'`':  true,
  	'a':  true,
  	'b':  true,
  	'c':  true,
  	'd':  true,
  	'e':  true,
  	'f':  true,
  	'g':  true,
  	'h':  true,
  	'i':  true,
  	'j':  true,
  	'k':  true,
  	'l':  true,
  	'm':  true,
  	'n':  true,
  	'o':  true,
  	'p':  true,
  	'q':  true,
  	'r':  true,
  	's':  true,
  	't':  true,
  	'u':  true,
  	'v':  true,
  	'w':  true,
  	'x':  true,
  	'y':  true,
  	'z':  true,
  	'|':  true,
  	'~':  true,
  }
  
  // skipSpace returns a slice of the string s with all leading RFC 2616 linear
  // whitespace removed.
  func skipSpace(s string) (rest string) {
  	i := 0
  	for ; i < len(s); i++ {
  		if b := s[i]; b != ' ' && b != '\t' {
  			break
  		}
  	}
  	return s[i:]
  }
  
  // nextToken returns the leading RFC 2616 token of s and the string following
  // the token.
  func nextToken(s string) (token, rest string) {
  	i := 0
  	for ; i < len(s); i++ {
  		if !isTokenOctet[s[i]] {
  			break
  		}
  	}
  	return s[:i], s[i:]
  }
  
  // nextTokenOrQuoted returns the leading token or quoted string per RFC 2616
  // and the string following the token or quoted string.
  func nextTokenOrQuoted(s string) (value string, rest string) {
  	if !strings.HasPrefix(s, "\"") {
  		return nextToken(s)
  	}
  	s = s[1:]
  	for i := 0; i < len(s); i++ {
  		switch s[i] {
  		case '"':
  			return s[:i], s[i+1:]
  		case '\\':
  			p := make([]byte, len(s)-1)
  			j := copy(p, s[:i])
  			escape := true
  			for i = i + 1; i < len(s); i++ {
  				b := s[i]
  				switch {
  				case escape:
  					escape = false
  					p[j] = b
  					j++
  				case b == '\\':
  					escape = true
  				case b == '"':
  					return string(p[:j]), s[i+1:]
  				default:
  					p[j] = b
  					j++
  				}
  			}
  			return "", ""
  		}
  	}
  	return "", ""
  }
  
  // equalASCIIFold returns true if s is equal to t with ASCII case folding as
  // defined in RFC 4790.
  func equalASCIIFold(s, t string) bool {
  	for s != "" && t != "" {
  		sr, size := utf8.DecodeRuneInString(s)
  		s = s[size:]
  		tr, size := utf8.DecodeRuneInString(t)
  		t = t[size:]
  		if sr == tr {
  			continue
  		}
  		if 'A' <= sr && sr <= 'Z' {
  			sr = sr + 'a' - 'A'
  		}
  		if 'A' <= tr && tr <= 'Z' {
  			tr = tr + 'a' - 'A'
  		}
  		if sr != tr {
  			return false
  		}
  	}
  	return s == t
  }
  
  // tokenListContainsValue returns true if the 1#token header with the given
  // name contains a token equal to value with ASCII case folding.
  func tokenListContainsValue(header http.Header, name string, value string) bool {
  headers:
  	for _, s := range header[name] {
  		for {
  			var t string
  			t, s = nextToken(skipSpace(s))
  			if t == "" {
  				continue headers
  			}
  			s = skipSpace(s)
  			if s != "" && s[0] != ',' {
  				continue headers
  			}
  			if equalASCIIFold(t, value) {
  				return true
  			}
  			if s == "" {
  				continue headers
  			}
  			s = s[1:]
  		}
  	}
  	return false
  }
  
  // parseExtensions parses WebSocket extensions from a header.
  func parseExtensions(header http.Header) []map[string]string {
  	// From RFC 6455:
  	//
  	//  Sec-WebSocket-Extensions = extension-list
  	//  extension-list = 1#extension
  	//  extension = extension-token *( ";" extension-param )
  	//  extension-token = registered-token
  	//  registered-token = token
  	//  extension-param = token [ "=" (token | quoted-string) ]
  	//     ;When using the quoted-string syntax variant, the value
  	//     ;after quoted-string unescaping MUST conform to the
  	//     ;'token' ABNF.
  
  	var result []map[string]string
  headers:
  	for _, s := range header["Sec-Websocket-Extensions"] {
  		for {
  			var t string
  			t, s = nextToken(skipSpace(s))
  			if t == "" {
  				continue headers
  			}
  			ext := map[string]string{"": t}
  			for {
  				s = skipSpace(s)
  				if !strings.HasPrefix(s, ";") {
  					break
  				}
  				var k string
  				k, s = nextToken(skipSpace(s[1:]))
  				if k == "" {
  					continue headers
  				}
  				s = skipSpace(s)
  				var v string
  				if strings.HasPrefix(s, "=") {
  					v, s = nextTokenOrQuoted(skipSpace(s[1:]))
  					s = skipSpace(s)
  				}
  				if s != "" && s[0] != ',' && s[0] != ';' {
  					continue headers
  				}
  				ext[k] = v
  			}
  			if s != "" && s[0] != ',' {
  				continue headers
  			}
  			result = append(result, ext)
  			if s == "" {
  				continue headers
  			}
  			s = s[1:]
  		}
  	}
  	return result
  }
  