aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/websocket/hixie.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/websocket/hixie.go')
-rw-r--r--libgo/go/websocket/hixie.go696
1 files changed, 696 insertions, 0 deletions
diff --git a/libgo/go/websocket/hixie.go b/libgo/go/websocket/hixie.go
new file mode 100644
index 0000000..841ff3c
--- /dev/null
+++ b/libgo/go/websocket/hixie.go
@@ -0,0 +1,696 @@
+// Copyright 2009 The Go 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
+
+// This file implements a protocol of Hixie draft version 75 and 76
+// (draft 76 equals to hybi 00)
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/md5"
+ "encoding/binary"
+ "fmt"
+ "http"
+ "io"
+ "io/ioutil"
+ "os"
+ "rand"
+ "strconv"
+ "strings"
+ "url"
+)
+
+// An aray of characters to be randomly inserted to construct Sec-WebSocket-Key
+// value. It holds characters from ranges U+0021 to U+002F and U+003A to U+007E.
+// See Step 21 in Section 4.1 Opening handshake.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#page-22
+var secKeyRandomChars [0x30 - 0x21 + 0x7F - 0x3A]byte
+
+func init() {
+ i := 0
+ for ch := byte(0x21); ch < 0x30; ch++ {
+ secKeyRandomChars[i] = ch
+ i++
+ }
+ for ch := byte(0x3a); ch < 0x7F; ch++ {
+ secKeyRandomChars[i] = ch
+ i++
+ }
+}
+
+type byteReader interface {
+ ReadByte() (byte, os.Error)
+}
+
+// readHixieLength reads frame length for frame type 0x80-0xFF
+// as defined in Hixie draft.
+// See section 4.2 Data framing.
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-4.2
+func readHixieLength(r byteReader) (length int64, lengthFields []byte, err os.Error) {
+ for {
+ c, err := r.ReadByte()
+ if err != nil {
+ return 0, nil, err
+ }
+ lengthFields = append(lengthFields, c)
+ length = length*128 + int64(c&0x7f)
+ if c&0x80 == 0 {
+ break
+ }
+ }
+ return
+}
+
+// A hixieLengthFrameReader is a reader for frame type 0x80-0xFF
+// as defined in hixie draft.
+type hixieLengthFrameReader struct {
+ reader io.Reader
+ FrameType byte
+ Length int64
+ header *bytes.Buffer
+ length int
+}
+
+func (frame *hixieLengthFrameReader) Read(msg []byte) (n int, err os.Error) {
+ return frame.reader.Read(msg)
+}
+
+func (frame *hixieLengthFrameReader) PayloadType() byte {
+ if frame.FrameType == '\xff' && frame.Length == 0 {
+ return CloseFrame
+ }
+ return UnknownFrame
+}
+
+func (frame *hixieLengthFrameReader) HeaderReader() io.Reader {
+ if frame.header == nil {
+ return nil
+ }
+ if frame.header.Len() == 0 {
+ frame.header = nil
+ return nil
+ }
+ return frame.header
+}
+
+func (frame *hixieLengthFrameReader) TrailerReader() io.Reader { return nil }
+
+func (frame *hixieLengthFrameReader) Len() (n int) { return frame.length }
+
+// A HixieSentinelFrameReader is a reader for frame type 0x00-0x7F
+// as defined in hixie draft.
+type hixieSentinelFrameReader struct {
+ reader *bufio.Reader
+ FrameType byte
+ header *bytes.Buffer
+ data []byte
+ seenTrailer bool
+ trailer *bytes.Buffer
+}
+
+func (frame *hixieSentinelFrameReader) Read(msg []byte) (n int, err os.Error) {
+ if len(frame.data) == 0 {
+ if frame.seenTrailer {
+ return 0, os.EOF
+ }
+ frame.data, err = frame.reader.ReadSlice('\xff')
+ if err == nil {
+ frame.seenTrailer = true
+ frame.data = frame.data[:len(frame.data)-1] // trim \xff
+ frame.trailer = bytes.NewBuffer([]byte{0xff})
+ }
+ }
+ n = copy(msg, frame.data)
+ frame.data = frame.data[n:]
+ return n, err
+}
+
+func (frame *hixieSentinelFrameReader) PayloadType() byte {
+ if frame.FrameType == 0 {
+ return TextFrame
+ }
+ return UnknownFrame
+}
+
+func (frame *hixieSentinelFrameReader) HeaderReader() io.Reader {
+ if frame.header == nil {
+ return nil
+ }
+ if frame.header.Len() == 0 {
+ frame.header = nil
+ return nil
+ }
+ return frame.header
+}
+
+func (frame *hixieSentinelFrameReader) TrailerReader() io.Reader {
+ if frame.trailer == nil {
+ return nil
+ }
+ if frame.trailer.Len() == 0 {
+ frame.trailer = nil
+ return nil
+ }
+ return frame.trailer
+}
+
+func (frame *hixieSentinelFrameReader) Len() int { return -1 }
+
+// A HixieFrameReaderFactory creates new frame reader based on its frame type.
+type hixieFrameReaderFactory struct {
+ *bufio.Reader
+}
+
+func (buf hixieFrameReaderFactory) NewFrameReader() (r frameReader, err os.Error) {
+ var header []byte
+ var b byte
+ b, err = buf.ReadByte()
+ if err != nil {
+ return
+ }
+ header = append(header, b)
+ if b&0x80 == 0x80 {
+ length, lengthFields, err := readHixieLength(buf.Reader)
+ if err != nil {
+ return nil, err
+ }
+ if length == 0 {
+ return nil, os.EOF
+ }
+ header = append(header, lengthFields...)
+ return &hixieLengthFrameReader{
+ reader: io.LimitReader(buf.Reader, length),
+ FrameType: b,
+ Length: length,
+ header: bytes.NewBuffer(header)}, err
+ }
+ return &hixieSentinelFrameReader{
+ reader: buf.Reader,
+ FrameType: b,
+ header: bytes.NewBuffer(header)}, err
+}
+
+type hixiFrameWriter struct {
+ writer *bufio.Writer
+}
+
+func (frame *hixiFrameWriter) Write(msg []byte) (n int, err os.Error) {
+ frame.writer.WriteByte(0)
+ frame.writer.Write(msg)
+ frame.writer.WriteByte(0xff)
+ err = frame.writer.Flush()
+ return len(msg), err
+}
+
+func (frame *hixiFrameWriter) Close() os.Error { return nil }
+
+type hixiFrameWriterFactory struct {
+ *bufio.Writer
+}
+
+func (buf hixiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err os.Error) {
+ if payloadType != TextFrame {
+ return nil, ErrNotSupported
+ }
+ return &hixiFrameWriter{writer: buf.Writer}, nil
+}
+
+type hixiFrameHandler struct {
+ conn *Conn
+}
+
+func (handler *hixiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err os.Error) {
+ if header := frame.HeaderReader(); header != nil {
+ io.Copy(ioutil.Discard, header)
+ }
+ if frame.PayloadType() != TextFrame {
+ io.Copy(ioutil.Discard, frame)
+ return nil, nil
+ }
+ return frame, nil
+}
+
+func (handler *hixiFrameHandler) WriteClose(_ int) (err os.Error) {
+ handler.conn.wio.Lock()
+ defer handler.conn.wio.Unlock()
+ closingFrame := []byte{'\xff', '\x00'}
+ handler.conn.buf.Write(closingFrame)
+ return handler.conn.buf.Flush()
+}
+
+// newHixiConn creates a new WebSocket connection speaking hixie draft protocol.
+func newHixieConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
+ if buf == nil {
+ br := bufio.NewReader(rwc)
+ bw := bufio.NewWriter(rwc)
+ buf = bufio.NewReadWriter(br, bw)
+ }
+ ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
+ frameReaderFactory: hixieFrameReaderFactory{buf.Reader},
+ frameWriterFactory: hixiFrameWriterFactory{buf.Writer},
+ PayloadType: TextFrame}
+ ws.frameHandler = &hixiFrameHandler{ws}
+ return ws
+}
+
+// getChallengeResponse computes the expected response from the
+// challenge as described in section 5.1 Opening Handshake steps 42 to
+// 43 of http://www.whatwg.org/specs/web-socket-protocol/
+func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte, err os.Error) {
+ // 41. Let /challenge/ be the concatenation of /number_1/, expressed
+ // a big-endian 32 bit integer, /number_2/, expressed in a big-
+ // endian 32 bit integer, and the eight bytes of /key_3/ in the
+ // order they were sent to the wire.
+ challenge := make([]byte, 16)
+ binary.BigEndian.PutUint32(challenge[0:], number1)
+ binary.BigEndian.PutUint32(challenge[4:], number2)
+ copy(challenge[8:], key3)
+
+ // 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big-
+ // endian 128 bit string.
+ h := md5.New()
+ if _, err = h.Write(challenge); err != nil {
+ return
+ }
+ expected = h.Sum()
+ return
+}
+
+// Generates handshake key as described in 4.1 Opening handshake step 16 to 22.
+// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+func generateKeyNumber() (key string, number uint32) {
+ // 16. Let /spaces_n/ be a random integer from 1 to 12 inclusive.
+ spaces := rand.Intn(12) + 1
+
+ // 17. Let /max_n/ be the largest integer not greater than
+ // 4,294,967,295 divided by /spaces_n/
+ max := int(4294967295 / uint32(spaces))
+
+ // 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive.
+ number = uint32(rand.Intn(max + 1))
+
+ // 19. Let /product_n/ be the result of multiplying /number_n/ and
+ // /spaces_n/ together.
+ product := number * uint32(spaces)
+
+ // 20. Let /key_n/ be a string consisting of /product_n/, expressed
+ // in base ten using the numerals in the range U+0030 DIGIT ZERO (0)
+ // to U+0039 DIGIT NINE (9).
+ key = fmt.Sprintf("%d", product)
+
+ // 21. Insert between one and twelve random characters from the ranges
+ // U+0021 to U+002F and U+003A to U+007E into /key_n/ at random
+ // positions.
+ n := rand.Intn(12) + 1
+ for i := 0; i < n; i++ {
+ pos := rand.Intn(len(key)) + 1
+ ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))]
+ key = key[0:pos] + string(ch) + key[pos:]
+ }
+
+ // 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random
+ // positions other than the start or end of the string.
+ for i := 0; i < spaces; i++ {
+ pos := rand.Intn(len(key)-1) + 1
+ key = key[0:pos] + " " + key[pos:]
+ }
+
+ return
+}
+
+// Generates handshake key_3 as described in 4.1 Opening handshake step 26.
+// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+func generateKey3() (key []byte) {
+ // 26. Let /key3/ be a string consisting of eight random bytes (or
+ // equivalently, a random 64 bit integer encoded in big-endian order).
+ key = make([]byte, 8)
+ for i := 0; i < 8; i++ {
+ key[i] = byte(rand.Intn(256))
+ }
+ return
+}
+
+// Cilent handhake described in (soon obsolete)
+// draft-ietf-hybi-thewebsocket-protocol-00
+// (draft-hixie-thewebsocket-protocol-76)
+func hixie76ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
+ switch config.Version {
+ case ProtocolVersionHixie76, ProtocolVersionHybi00:
+ default:
+ panic("wrong protocol version.")
+ }
+ // 4.1. Opening handshake.
+ // Step 5. send a request line.
+ bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
+
+ // Step 6-14. push request headers in fields.
+ fields := []string{
+ "Upgrade: WebSocket\r\n",
+ "Connection: Upgrade\r\n",
+ "Host: " + config.Location.Host + "\r\n",
+ "Origin: " + config.Origin.String() + "\r\n",
+ }
+ if len(config.Protocol) > 0 {
+ if len(config.Protocol) != 1 {
+ return ErrBadWebSocketProtocol
+ }
+ fields = append(fields, "Sec-WebSocket-Protocol: "+config.Protocol[0]+"\r\n")
+ }
+ // TODO(ukai): Step 15. send cookie if any.
+
+ // Step 16-23. generate keys and push Sec-WebSocket-Key<n> in fields.
+ key1, number1 := generateKeyNumber()
+ key2, number2 := generateKeyNumber()
+ if config.handshakeData != nil {
+ key1 = config.handshakeData["key1"]
+ n, err := strconv.Atoui(config.handshakeData["number1"])
+ if err != nil {
+ panic(err)
+ }
+ number1 = uint32(n)
+ key2 = config.handshakeData["key2"]
+ n, err = strconv.Atoui(config.handshakeData["number2"])
+ if err != nil {
+ panic(err)
+ }
+ number2 = uint32(n)
+ }
+ fields = append(fields, "Sec-WebSocket-Key1: "+key1+"\r\n")
+ fields = append(fields, "Sec-WebSocket-Key2: "+key2+"\r\n")
+
+ // Step 24. shuffle fields and send them out.
+ for i := 1; i < len(fields); i++ {
+ j := rand.Intn(i)
+ fields[i], fields[j] = fields[j], fields[i]
+ }
+ for i := 0; i < len(fields); i++ {
+ bw.WriteString(fields[i])
+ }
+ // Step 25. send CRLF.
+ bw.WriteString("\r\n")
+
+ // Step 26. generate 8 bytes random key.
+ key3 := generateKey3()
+ if config.handshakeData != nil {
+ key3 = []byte(config.handshakeData["key3"])
+ }
+ // Step 27. send it out.
+ bw.Write(key3)
+ if err = bw.Flush(); err != nil {
+ return
+ }
+
+ // Step 28-29, 32-40. read response from server.
+ resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
+ if err != nil {
+ return err
+ }
+ // Step 30. check response code is 101.
+ if resp.StatusCode != 101 {
+ return ErrBadStatus
+ }
+
+ // Step 41. check websocket headers.
+ if resp.Header.Get("Upgrade") != "WebSocket" ||
+ strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
+ return ErrBadUpgrade
+ }
+
+ if resp.Header.Get("Sec-Websocket-Origin") != config.Origin.String() {
+ return ErrBadWebSocketOrigin
+ }
+
+ if resp.Header.Get("Sec-Websocket-Location") != config.Location.String() {
+ return ErrBadWebSocketLocation
+ }
+
+ if len(config.Protocol) > 0 && resp.Header.Get("Sec-Websocket-Protocol") != config.Protocol[0] {
+ return ErrBadWebSocketProtocol
+ }
+
+ // Step 42-43. get expected data from challenge data.
+ expected, err := getChallengeResponse(number1, number2, key3)
+ if err != nil {
+ return err
+ }
+
+ // Step 44. read 16 bytes from server.
+ reply := make([]byte, 16)
+ if _, err = io.ReadFull(br, reply); err != nil {
+ return err
+ }
+
+ // Step 45. check the reply equals to expected data.
+ if !bytes.Equal(expected, reply) {
+ return ErrChallengeResponse
+ }
+ // WebSocket connection is established.
+ return
+}
+
+// Client Handshake described in (soon obsolete)
+// draft-hixie-thewebsocket-protocol-75.
+func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
+ if config.Version != ProtocolVersionHixie75 {
+ panic("wrong protocol version.")
+ }
+ bw.WriteString("GET " + config.Location.RawPath + " HTTP/1.1\r\n")
+ bw.WriteString("Upgrade: WebSocket\r\n")
+ bw.WriteString("Connection: Upgrade\r\n")
+ bw.WriteString("Host: " + config.Location.Host + "\r\n")
+ bw.WriteString("Origin: " + config.Origin.String() + "\r\n")
+ if len(config.Protocol) > 0 {
+ if len(config.Protocol) != 1 {
+ return ErrBadWebSocketProtocol
+ }
+ bw.WriteString("WebSocket-Protocol: " + config.Protocol[0] + "\r\n")
+ }
+ bw.WriteString("\r\n")
+ bw.Flush()
+ resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
+ if err != nil {
+ return
+ }
+ if resp.Status != "101 Web Socket Protocol Handshake" {
+ return ErrBadStatus
+ }
+ if resp.Header.Get("Upgrade") != "WebSocket" ||
+ resp.Header.Get("Connection") != "Upgrade" {
+ return ErrBadUpgrade
+ }
+ if resp.Header.Get("Websocket-Origin") != config.Origin.String() {
+ return ErrBadWebSocketOrigin
+ }
+ if resp.Header.Get("Websocket-Location") != config.Location.String() {
+ return ErrBadWebSocketLocation
+ }
+ if len(config.Protocol) > 0 && resp.Header.Get("Websocket-Protocol") != config.Protocol[0] {
+ return ErrBadWebSocketProtocol
+ }
+ return
+}
+
+// newHixieClientConn returns new WebSocket connection speaking hixie draft protocol.
+func newHixieClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
+ return newHixieConn(config, buf, rwc, nil)
+}
+
+// Gets key number from Sec-WebSocket-Key<n>: field as described
+// in 5.2 Sending the server's opening handshake, 4.
+func getKeyNumber(s string) (r uint32) {
+ // 4. Let /key-number_n/ be the digits (characters in the range
+ // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/,
+ // interpreted as a base ten integer, ignoring all other characters
+ // in /key_n/.
+ r = 0
+ for i := 0; i < len(s); i++ {
+ if s[i] >= '0' && s[i] <= '9' {
+ r = r*10 + uint32(s[i]) - '0'
+ }
+ }
+ return
+}
+
+// A Hixie76ServerHandshaker performs a server handshake using
+// hixie draft 76 protocol.
+type hixie76ServerHandshaker struct {
+ *Config
+ challengeResponse []byte
+}
+
+func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) {
+ c.Version = ProtocolVersionHybi00
+ if req.Method != "GET" {
+ return http.StatusMethodNotAllowed, ErrBadRequestMethod
+ }
+ // HTTP version can be safely ignored.
+
+ if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
+ strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
+ return http.StatusBadRequest, ErrNotWebSocket
+ }
+
+ // TODO(ukai): check Host
+ c.Origin, err = url.ParseRequest(req.Header.Get("Origin"))
+ if err != nil {
+ return http.StatusBadRequest, err
+ }
+
+ key1 := req.Header.Get("Sec-Websocket-Key1")
+ if key1 == "" {
+ return http.StatusBadRequest, ErrChallengeResponse
+ }
+ key2 := req.Header.Get("Sec-Websocket-Key2")
+ if key2 == "" {
+ return http.StatusBadRequest, ErrChallengeResponse
+ }
+ key3 := make([]byte, 8)
+ if _, err := io.ReadFull(buf, key3); err != nil {
+ return http.StatusBadRequest, ErrChallengeResponse
+ }
+
+ var scheme string
+ if req.TLS != nil {
+ scheme = "wss"
+ } else {
+ scheme = "ws"
+ }
+ c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
+ if err != nil {
+ return http.StatusBadRequest, err
+ }
+
+ // Step 4. get key number in Sec-WebSocket-Key<n> fields.
+ keyNumber1 := getKeyNumber(key1)
+ keyNumber2 := getKeyNumber(key2)
+
+ // Step 5. get number of spaces in Sec-WebSocket-Key<n> fields.
+ space1 := uint32(strings.Count(key1, " "))
+ space2 := uint32(strings.Count(key2, " "))
+ if space1 == 0 || space2 == 0 {
+ return http.StatusBadRequest, ErrChallengeResponse
+ }
+
+ // Step 6. key number must be an integral multiple of spaces.
+ if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 {
+ return http.StatusBadRequest, ErrChallengeResponse
+ }
+
+ // Step 7. let part be key number divided by spaces.
+ part1 := keyNumber1 / space1
+ part2 := keyNumber2 / space2
+
+ // Step 8. let challenge be concatenation of part1, part2 and key3.
+ // Step 9. get MD5 fingerprint of challenge.
+ c.challengeResponse, err = getChallengeResponse(part1, part2, key3)
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
+ protocols := strings.Split(protocol, ",")
+ for i := 0; i < len(protocols); i++ {
+ c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
+ }
+
+ return http.StatusSwitchingProtocols, nil
+}
+
+func (c *hixie76ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err os.Error) {
+ if len(c.Protocol) > 0 {
+ if len(c.Protocol) != 1 {
+ return ErrBadWebSocketProtocol
+ }
+ }
+
+ // Step 10. send response status line.
+ buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n")
+ // Step 11. send response headers.
+ buf.WriteString("Upgrade: WebSocket\r\n")
+ buf.WriteString("Connection: Upgrade\r\n")
+ buf.WriteString("Sec-WebSocket-Origin: " + c.Origin.String() + "\r\n")
+ buf.WriteString("Sec-WebSocket-Location: " + c.Location.String() + "\r\n")
+ if len(c.Protocol) > 0 {
+ buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
+ }
+ // Step 12. send CRLF.
+ buf.WriteString("\r\n")
+ // Step 13. send response data.
+ buf.Write(c.challengeResponse)
+ return buf.Flush()
+}
+
+func (c *hixie76ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) {
+ return newHixieServerConn(c.Config, buf, rwc, request)
+}
+
+// A hixie75ServerHandshaker performs a server handshake using
+// hixie draft 75 protocol.
+type hixie75ServerHandshaker struct {
+ *Config
+}
+
+func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) {
+ c.Version = ProtocolVersionHixie75
+ if req.Method != "GET" || req.Proto != "HTTP/1.1" {
+ return http.StatusMethodNotAllowed, ErrBadRequestMethod
+ }
+ if req.Header.Get("Upgrade") != "WebSocket" {
+ return http.StatusBadRequest, ErrNotWebSocket
+ }
+ if req.Header.Get("Connection") != "Upgrade" {
+ return http.StatusBadRequest, ErrNotWebSocket
+ }
+ c.Origin, err = url.ParseRequest(strings.TrimSpace(req.Header.Get("Origin")))
+ if err != nil {
+ return http.StatusBadRequest, err
+ }
+
+ var scheme string
+ if req.TLS != nil {
+ scheme = "wss"
+ } else {
+ scheme = "ws"
+ }
+ c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath)
+ if err != nil {
+ return http.StatusBadRequest, err
+ }
+ protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol"))
+ protocols := strings.Split(protocol, ",")
+ for i := 0; i < len(protocols); i++ {
+ c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
+ }
+
+ return http.StatusSwitchingProtocols, nil
+}
+
+func (c *hixie75ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err os.Error) {
+ if len(c.Protocol) > 0 {
+ if len(c.Protocol) != 1 {
+ return ErrBadWebSocketProtocol
+ }
+ }
+
+ buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
+ buf.WriteString("Upgrade: WebSocket\r\n")
+ buf.WriteString("Connection: Upgrade\r\n")
+ buf.WriteString("WebSocket-Origin: " + c.Origin.String() + "\r\n")
+ buf.WriteString("WebSocket-Location: " + c.Location.String() + "\r\n")
+ if len(c.Protocol) > 0 {
+ buf.WriteString("WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
+ }
+ buf.WriteString("\r\n")
+ return buf.Flush()
+}
+
+func (c *hixie75ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) {
+ return newHixieServerConn(c.Config, buf, rwc, request)
+}
+
+// newHixieServerConn returns a new WebSocket connection speaking hixie draft protocol.
+func newHixieServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
+ return newHixieConn(config, buf, rwc, request)
+}