aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/websocket/client.go
blob: 5dfd824e6e5b9a110f0ae5fd20872b7f2ee81672 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// 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"
	"crypto/tls"
	"io"
	"net"
	"net/url"
)

// DialError is an error that occurs while dialling a websocket server.
type DialError struct {
	*Config
	Err error
}

func (e *DialError) Error() string {
	return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
}

// NewConfig creates a new WebSocket config for client connection.
func NewConfig(server, origin string) (config *Config, err error) {
	config = new(Config)
	config.Version = ProtocolVersionHybi13
	config.Location, err = url.ParseRequest(server)
	if err != nil {
		return
	}
	config.Origin, err = url.ParseRequest(origin)
	if err != nil {
		return
	}
	return
}

// NewClient creates a new WebSocket client connection over rwc.
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
	br := bufio.NewReader(rwc)
	bw := bufio.NewWriter(rwc)
	switch config.Version {
	case ProtocolVersionHixie75:
		err = hixie75ClientHandshake(config, br, bw)
	case ProtocolVersionHixie76, ProtocolVersionHybi00:
		err = hixie76ClientHandshake(config, br, bw)
	case ProtocolVersionHybi08, ProtocolVersionHybi13:
		err = hybiClientHandshake(config, br, bw)
	default:
		err = ErrBadProtocolVersion
	}
	if err != nil {
		return
	}
	buf := bufio.NewReadWriter(br, bw)
	switch config.Version {
	case ProtocolVersionHixie75, ProtocolVersionHixie76, ProtocolVersionHybi00:
		ws = newHixieClientConn(config, buf, rwc)
	case ProtocolVersionHybi08, ProtocolVersionHybi13:
		ws = newHybiClientConn(config, buf, rwc)
	}
	return
}

/*
Dial opens a new client connection to a WebSocket.

A trivial example client:

	package main

	import (
		"http"
		"log"
		"strings"
		"websocket"
	)

	func main() {
		origin := "http://localhost/"
		url := "ws://localhost/ws" 
		ws, err := websocket.Dial(url, "", origin)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := ws.Write([]byte("hello, world!\n")); err != nil {
			log.Fatal(err)
		}
		var msg = make([]byte, 512);
		if n, err := ws.Read(msg); err != nil {
			log.Fatal(err)
		}
		// use msg[0:n]
	}
*/
func Dial(url_, protocol, origin string) (ws *Conn, err error) {
	config, err := NewConfig(url_, origin)
	if err != nil {
		return nil, err
	}
	return DialConfig(config)
}

// DialConfig opens a new client connection to a WebSocket with a config.
func DialConfig(config *Config) (ws *Conn, err error) {
	var client net.Conn
	if config.Location == nil {
		return nil, &DialError{config, ErrBadWebSocketLocation}
	}
	if config.Origin == nil {
		return nil, &DialError{config, ErrBadWebSocketOrigin}
	}
	switch config.Location.Scheme {
	case "ws":
		client, err = net.Dial("tcp", config.Location.Host)

	case "wss":
		client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig)

	default:
		err = ErrBadScheme
	}
	if err != nil {
		goto Error
	}

	ws, err = NewClient(config, client)
	if err != nil {
		goto Error
	}
	return

Error:
	return nil, &DialError{config, err}
}