summaryrefslogtreecommitdiff
path: root/libgo/go/websocket/websocket.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/websocket/websocket.go')
-rw-r--r--libgo/go/websocket/websocket.go189
1 files changed, 189 insertions, 0 deletions
diff --git a/libgo/go/websocket/websocket.go b/libgo/go/websocket/websocket.go
new file mode 100644
index 0000000000..d5996abe1a
--- /dev/null
+++ b/libgo/go/websocket/websocket.go
@@ -0,0 +1,189 @@
+// 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.
+
+// The websocket package implements a client and server for the Web Socket protocol.
+// The protocol is defined at http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
+package websocket
+
+// TODO(ukai):
+// better logging.
+
+import (
+ "bufio"
+ "crypto/md5"
+ "encoding/binary"
+ "io"
+ "net"
+ "os"
+)
+
+// WebSocketAddr is an implementation of net.Addr for Web Sockets.
+type WebSocketAddr string
+
+// Network returns the network type for a Web Socket, "websocket".
+func (addr WebSocketAddr) Network() string { return "websocket" }
+
+// String returns the network address for a Web Socket.
+func (addr WebSocketAddr) String() string { return string(addr) }
+
+const (
+ stateFrameByte = iota
+ stateFrameLength
+ stateFrameData
+ stateFrameTextData
+)
+
+// Conn is a channel to communicate to a Web Socket.
+// It implements the net.Conn interface.
+type Conn struct {
+ // The origin URI for the Web Socket.
+ Origin string
+ // The location URI for the Web Socket.
+ Location string
+ // The subprotocol for the Web Socket.
+ Protocol string
+
+ buf *bufio.ReadWriter
+ rwc io.ReadWriteCloser
+
+ // It holds text data in previous Read() that failed with small buffer.
+ data []byte
+ reading bool
+}
+
+// newConn creates a new Web Socket.
+func newConn(origin, location, protocol string, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
+ if buf == nil {
+ br := bufio.NewReader(rwc)
+ bw := bufio.NewWriter(rwc)
+ buf = bufio.NewReadWriter(br, bw)
+ }
+ ws := &Conn{Origin: origin, Location: location, Protocol: protocol, buf: buf, rwc: rwc}
+ return ws
+}
+
+// Read implements the io.Reader interface for a Conn.
+func (ws *Conn) Read(msg []byte) (n int, err os.Error) {
+Frame:
+ for !ws.reading && len(ws.data) == 0 {
+ // Beginning of frame, possibly.
+ b, err := ws.buf.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ if b&0x80 == 0x80 {
+ // Skip length frame.
+ length := 0
+ for {
+ c, err := ws.buf.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ length = length*128 + int(c&0x7f)
+ if c&0x80 == 0 {
+ break
+ }
+ }
+ for length > 0 {
+ _, err := ws.buf.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ }
+ continue Frame
+ }
+ // In text mode
+ if b != 0 {
+ // Skip this frame
+ for {
+ c, err := ws.buf.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ if c == '\xff' {
+ break
+ }
+ }
+ continue Frame
+ }
+ ws.reading = true
+ }
+ if len(ws.data) == 0 {
+ ws.data, err = ws.buf.ReadSlice('\xff')
+ if err == nil {
+ ws.reading = false
+ ws.data = ws.data[:len(ws.data)-1] // trim \xff
+ }
+ }
+ n = copy(msg, ws.data)
+ ws.data = ws.data[n:]
+ return n, err
+}
+
+// Write implements the io.Writer interface for a Conn.
+func (ws *Conn) Write(msg []byte) (n int, err os.Error) {
+ ws.buf.WriteByte(0)
+ ws.buf.Write(msg)
+ ws.buf.WriteByte(0xff)
+ err = ws.buf.Flush()
+ return len(msg), err
+}
+
+// Close implements the io.Closer interface for a Conn.
+func (ws *Conn) Close() os.Error { return ws.rwc.Close() }
+
+// LocalAddr returns the WebSocket Origin for the connection.
+func (ws *Conn) LocalAddr() net.Addr { return WebSocketAddr(ws.Origin) }
+
+// RemoteAddr returns the WebSocket locations for the connection.
+func (ws *Conn) RemoteAddr() net.Addr { return WebSocketAddr(ws.Location) }
+
+// SetTimeout sets the connection's network timeout in nanoseconds.
+func (ws *Conn) SetTimeout(nsec int64) os.Error {
+ if conn, ok := ws.rwc.(net.Conn); ok {
+ return conn.SetTimeout(nsec)
+ }
+ return os.EINVAL
+}
+
+// SetReadTimeout sets the connection's network read timeout in nanoseconds.
+func (ws *Conn) SetReadTimeout(nsec int64) os.Error {
+ if conn, ok := ws.rwc.(net.Conn); ok {
+ return conn.SetReadTimeout(nsec)
+ }
+ return os.EINVAL
+}
+
+// SetWritetTimeout sets the connection's network write timeout in nanoseconds.
+func (ws *Conn) SetWriteTimeout(nsec int64) os.Error {
+ if conn, ok := ws.rwc.(net.Conn); ok {
+ return conn.SetWriteTimeout(nsec)
+ }
+ return os.EINVAL
+}
+
+// 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
+}
+
+var _ net.Conn = (*Conn)(nil) // compile-time check that *Conn implements net.Conn.