diff options
Diffstat (limited to 'src/pkg/websocket/client.go')
| -rw-r--r-- | src/pkg/websocket/client.go | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/pkg/websocket/client.go b/src/pkg/websocket/client.go new file mode 100644 index 000000000..bedaec02f --- /dev/null +++ b/src/pkg/websocket/client.go @@ -0,0 +1,136 @@ +// 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 + +import ( + "bufio"; + "http"; + "io"; + "net"; + "os"; +) + +type ProtocolError struct { + os.ErrorString; +} + +var ( + ErrBadStatus = &ProtocolError{"bad status"}; + ErrNoUpgrade = &ProtocolError{"no upgrade"}; + ErrBadUpgrade = &ProtocolError{"bad upgrade"}; + ErrNoWebSocketOrigin = &ProtocolError{"no WebSocket-Origin"}; + ErrBadWebSocketOrigin = &ProtocolError{"bad WebSocket-Origin"}; + ErrNoWebSocketLocation = &ProtocolError{"no WebSocket-Location"}; + ErrBadWebSocketLocation = &ProtocolError{"bad WebSocket-Location"}; + ErrNoWebSocketProtocol = &ProtocolError{"no WebSocket-Protocol"}; + ErrBadWebSocketProtocol = &ProtocolError{"bad WebSocket-Protocol"}; +) + +// newClient creates a new Web Socket client connection. +func newClient(resourceName, host, origin, location, protocol string, rwc io.ReadWriteCloser) (ws *Conn, err os.Error) { + br := bufio.NewReader(rwc); + bw := bufio.NewWriter(rwc); + err = handshake(resourceName, host, origin, location, protocol, br, bw); + if err != nil { + return + } + buf := bufio.NewReadWriter(br, bw); + ws = newConn(origin, location, protocol, buf, rwc); + return; +} + +// Dial opens new Web Socket client connection. +// +// A trivial example client is: +// +// package main +// +// import ( +// "websocket" +// "strings" +// ) +// +// func main() { +// ws, err := websocket.Dial("ws://localhost/ws", "", "http://localhost/"); +// if err != nil { +// panic("Dial: ", err.String()) +// } +// if _, err := ws.Write(strings.Bytes("hello, world!\n")); err != nil { +// panic("Write: ", err.String()) +// } +// var msg = make([]byte, 512); +// if n, err := ws.Read(msg); err != nil { +// panic("Read: ", err.String()) +// } +// // msg[0:n] +// } + +func Dial(url, protocol, origin string) (ws *Conn, err os.Error) { + parsedUrl, err := http.ParseURL(url); + if err != nil { + return + } + client, err := net.Dial("tcp", "", parsedUrl.Host); + if err != nil { + return + } + return newClient(parsedUrl.Path, parsedUrl.Host, origin, url, protocol, client); +} + +func handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) { + bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n"); + bw.WriteString("Upgrade: WebSocket\r\n"); + bw.WriteString("Connection: Upgrade\r\n"); + bw.WriteString("Host: " + host + "\r\n"); + bw.WriteString("Origin: " + origin + "\r\n"); + if protocol != "" { + bw.WriteString("WebSocket-Protocol: " + protocol + "\r\n") + } + bw.WriteString("\r\n"); + bw.Flush(); + resp, err := http.ReadResponse(br); + if err != nil { + return + } + if resp.Status != "101 Web Socket Protocol Handshake" { + return ErrBadStatus + } + upgrade, found := resp.Header["Upgrade"]; + if !found { + return ErrNoUpgrade + } + if upgrade != "WebSocket" { + return ErrBadUpgrade + } + connection, found := resp.Header["Connection"]; + if !found || connection != "Upgrade" { + return ErrBadUpgrade + } + + ws_origin, found := resp.Header["Websocket-Origin"]; + if !found { + return ErrNoWebSocketOrigin + } + if ws_origin != origin { + return ErrBadWebSocketOrigin + } + ws_location, found := resp.Header["Websocket-Location"]; + if !found { + return ErrNoWebSocketLocation + } + if ws_location != location { + return ErrBadWebSocketLocation + } + if protocol != "" { + ws_protocol, found := resp.Header["Websocket-Protocol"]; + if !found { + return ErrNoWebSocketProtocol + } + if ws_protocol != protocol { + return ErrBadWebSocketProtocol + } + } + return; +} |
