// 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; }