2009-11-29 15:22:44 -07:00
|
|
|
// 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 (
|
2009-12-15 16:41:46 -07:00
|
|
|
"bufio"
|
|
|
|
"http"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"os"
|
2009-11-29 15:22:44 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type ProtocolError struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
os.ErrorString
|
2009-11-29 15:22:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2009-12-15 16:41:46 -07:00
|
|
|
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"}
|
2009-11-29 15:22:44 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// newClient creates a new Web Socket client connection.
|
|
|
|
func newClient(resourceName, host, origin, location, protocol string, rwc io.ReadWriteCloser) (ws *Conn, err os.Error) {
|
2009-12-15 16:41:46 -07:00
|
|
|
br := bufio.NewReader(rwc)
|
|
|
|
bw := bufio.NewWriter(rwc)
|
|
|
|
err = handshake(resourceName, host, origin, location, protocol, br, bw)
|
2009-11-29 15:22:44 -07:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
buf := bufio.NewReadWriter(br, bw)
|
|
|
|
ws = newConn(origin, location, protocol, buf, rwc)
|
|
|
|
return
|
2009-11-29 15:22:44 -07:00
|
|
|
}
|
|
|
|
|
2010-01-27 21:38:32 -07:00
|
|
|
/*
|
|
|
|
Dial opens a new client connection to a Web Socket.
|
|
|
|
A trivial example client is:
|
2009-11-29 15:22:44 -07:00
|
|
|
|
2010-01-27 21:38:32 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"websocket"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
ws, err := websocket.Dial("ws://localhost/ws", "", "http://localhost/");
|
|
|
|
if err != nil {
|
|
|
|
panic("Dial: ", err.String())
|
|
|
|
}
|
2010-03-01 17:18:22 -07:00
|
|
|
if _, err := ws.Write([]byte("hello, world!\n")); err != nil {
|
2010-01-27 21:38:32 -07:00
|
|
|
panic("Write: ", err.String())
|
|
|
|
}
|
|
|
|
var msg = make([]byte, 512);
|
|
|
|
if n, err := ws.Read(msg); err != nil {
|
|
|
|
panic("Read: ", err.String())
|
|
|
|
}
|
|
|
|
// use msg[0:n]
|
|
|
|
}
|
|
|
|
*/
|
2009-11-29 15:22:44 -07:00
|
|
|
func Dial(url, protocol, origin string) (ws *Conn, err os.Error) {
|
2009-12-15 16:41:46 -07:00
|
|
|
parsedUrl, err := http.ParseURL(url)
|
2009-11-29 15:22:44 -07:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
client, err := net.Dial("tcp", "", parsedUrl.Host)
|
2009-11-29 15:22:44 -07:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return newClient(parsedUrl.Path, parsedUrl.Host, origin, url, protocol, client)
|
2009-11-29 15:22:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
|
2009-12-15 16:41:46 -07:00
|
|
|
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")
|
2009-11-29 15:22:44 -07:00
|
|
|
if protocol != "" {
|
|
|
|
bw.WriteString("WebSocket-Protocol: " + protocol + "\r\n")
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
bw.WriteString("\r\n")
|
|
|
|
bw.Flush()
|
2010-01-18 22:46:59 -07:00
|
|
|
resp, err := http.ReadResponse(br, "GET")
|
2009-11-29 15:22:44 -07:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if resp.Status != "101 Web Socket Protocol Handshake" {
|
|
|
|
return ErrBadStatus
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
upgrade, found := resp.Header["Upgrade"]
|
2009-11-29 15:22:44 -07:00
|
|
|
if !found {
|
|
|
|
return ErrNoUpgrade
|
|
|
|
}
|
|
|
|
if upgrade != "WebSocket" {
|
|
|
|
return ErrBadUpgrade
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
connection, found := resp.Header["Connection"]
|
2009-11-29 15:22:44 -07:00
|
|
|
if !found || connection != "Upgrade" {
|
|
|
|
return ErrBadUpgrade
|
|
|
|
}
|
|
|
|
|
2009-12-15 16:41:46 -07:00
|
|
|
ws_origin, found := resp.Header["Websocket-Origin"]
|
2009-11-29 15:22:44 -07:00
|
|
|
if !found {
|
|
|
|
return ErrNoWebSocketOrigin
|
|
|
|
}
|
|
|
|
if ws_origin != origin {
|
|
|
|
return ErrBadWebSocketOrigin
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
ws_location, found := resp.Header["Websocket-Location"]
|
2009-11-29 15:22:44 -07:00
|
|
|
if !found {
|
|
|
|
return ErrNoWebSocketLocation
|
|
|
|
}
|
|
|
|
if ws_location != location {
|
|
|
|
return ErrBadWebSocketLocation
|
|
|
|
}
|
|
|
|
if protocol != "" {
|
2009-12-15 16:41:46 -07:00
|
|
|
ws_protocol, found := resp.Header["Websocket-Protocol"]
|
2009-11-29 15:22:44 -07:00
|
|
|
if !found {
|
|
|
|
return ErrNoWebSocketProtocol
|
|
|
|
}
|
|
|
|
if ws_protocol != protocol {
|
|
|
|
return ErrBadWebSocketProtocol
|
|
|
|
}
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return
|
2009-11-29 15:22:44 -07:00
|
|
|
}
|