blob: c81f4f440fc9945c9cd752f252c750059a439279 [file] [log] [blame]
// 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
}