1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package websocket
6
7import (
8	"bufio"
9	"io"
10	"net"
11	"net/http"
12	"net/url"
13)
14
15// DialError is an error that occurs while dialling a websocket server.
16type DialError struct {
17	*Config
18	Err error
19}
20
21func (e *DialError) Error() string {
22	return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
23}
24
25// NewConfig creates a new WebSocket config for client connection.
26func NewConfig(server, origin string) (config *Config, err error) {
27	config = new(Config)
28	config.Version = ProtocolVersionHybi13
29	config.Location, err = url.ParseRequestURI(server)
30	if err != nil {
31		return
32	}
33	config.Origin, err = url.ParseRequestURI(origin)
34	if err != nil {
35		return
36	}
37	config.Header = http.Header(make(map[string][]string))
38	return
39}
40
41// NewClient creates a new WebSocket client connection over rwc.
42func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
43	br := bufio.NewReader(rwc)
44	bw := bufio.NewWriter(rwc)
45	err = hybiClientHandshake(config, br, bw)
46	if err != nil {
47		return
48	}
49	buf := bufio.NewReadWriter(br, bw)
50	ws = newHybiClientConn(config, buf, rwc)
51	return
52}
53
54// Dial opens a new client connection to a WebSocket.
55func Dial(url_, protocol, origin string) (ws *Conn, err error) {
56	config, err := NewConfig(url_, origin)
57	if err != nil {
58		return nil, err
59	}
60	if protocol != "" {
61		config.Protocol = []string{protocol}
62	}
63	return DialConfig(config)
64}
65
66var portMap = map[string]string{
67	"ws":  "80",
68	"wss": "443",
69}
70
71func parseAuthority(location *url.URL) string {
72	if _, ok := portMap[location.Scheme]; ok {
73		if _, _, err := net.SplitHostPort(location.Host); err != nil {
74			return net.JoinHostPort(location.Host, portMap[location.Scheme])
75		}
76	}
77	return location.Host
78}
79
80// DialConfig opens a new client connection to a WebSocket with a config.
81func DialConfig(config *Config) (ws *Conn, err error) {
82	var client net.Conn
83	if config.Location == nil {
84		return nil, &DialError{config, ErrBadWebSocketLocation}
85	}
86	if config.Origin == nil {
87		return nil, &DialError{config, ErrBadWebSocketOrigin}
88	}
89	dialer := config.Dialer
90	if dialer == nil {
91		dialer = &net.Dialer{}
92	}
93	client, err = dialWithDialer(dialer, config)
94	if err != nil {
95		goto Error
96	}
97	ws, err = NewClient(config, client)
98	if err != nil {
99		client.Close()
100		goto Error
101	}
102	return
103
104Error:
105	return nil, &DialError{config, err}
106}
107