7a9389330e
gcc/: * gcc.c (default_compilers): Add entry for ".go". * common.opt: Add -static-libgo as a driver option. * doc/install.texi (Configuration): Mention libgo as an option for --enable-shared. Mention go as an option for --enable-languages. * doc/invoke.texi (Overall Options): Mention .go as a file name suffix. Mention go as a -x option. * doc/frontends.texi (G++ and GCC): Mention Go as a supported language. * doc/sourcebuild.texi (Top Level): Mention libgo. * doc/standards.texi (Standards): Add section on Go language. Move references for other languages into their own section. * doc/contrib.texi (Contributors): Mention that I contributed the Go frontend. gcc/testsuite/: * lib/go.exp: New file. * lib/go-dg.exp: New file. * lib/go-torture.exp: New file. * lib/target-supports.exp (check_compile): Match // Go. From-SVN: r167407
190 lines
4.9 KiB
Go
190 lines
4.9 KiB
Go
// 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.
|