blob: 20d1e1e38eb226f84e3b4890d87c0db22eb8ef4b [file] [log] [blame]
Russ Cox3294cb52012-01-25 15:31:30 -05001// 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 "crypto/tls"
10 "io"
11 "net"
Fumitoshi Ukai0005f0a2013-05-12 13:50:10 +090012 "net/http"
Russ Cox3294cb52012-01-25 15:31:30 -050013 "net/url"
14)
15
16// DialError is an error that occurs while dialling a websocket server.
17type DialError struct {
18 *Config
19 Err error
20}
21
22func (e *DialError) Error() string {
23 return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
24}
25
26// NewConfig creates a new WebSocket config for client connection.
27func NewConfig(server, origin string) (config *Config, err error) {
28 config = new(Config)
29 config.Version = ProtocolVersionHybi13
David Symonds359f9342012-02-18 12:49:41 +110030 config.Location, err = url.ParseRequestURI(server)
Russ Cox3294cb52012-01-25 15:31:30 -050031 if err != nil {
32 return
33 }
David Symonds359f9342012-02-18 12:49:41 +110034 config.Origin, err = url.ParseRequestURI(origin)
Russ Cox3294cb52012-01-25 15:31:30 -050035 if err != nil {
36 return
37 }
Fumitoshi Ukai0005f0a2013-05-12 13:50:10 +090038 config.Header = http.Header(make(map[string][]string))
Russ Cox3294cb52012-01-25 15:31:30 -050039 return
40}
41
42// NewClient creates a new WebSocket client connection over rwc.
43func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
44 br := bufio.NewReader(rwc)
45 bw := bufio.NewWriter(rwc)
Scott Ferguson38c17ad2013-09-19 11:03:51 +100046 err = hybiClientHandshake(config, br, bw)
Russ Cox3294cb52012-01-25 15:31:30 -050047 if err != nil {
48 return
49 }
50 buf := bufio.NewReadWriter(br, bw)
Scott Ferguson38c17ad2013-09-19 11:03:51 +100051 ws = newHybiClientConn(config, buf, rwc)
Russ Cox3294cb52012-01-25 15:31:30 -050052 return
53}
54
Francisco Souza3b94eae2012-09-06 15:22:57 +100055// Dial opens a new client connection to a WebSocket.
Russ Cox3294cb52012-01-25 15:31:30 -050056func Dial(url_, protocol, origin string) (ws *Conn, err error) {
57 config, err := NewConfig(url_, origin)
58 if err != nil {
59 return nil, err
60 }
Fumitoshi Ukai5a654032012-10-10 07:45:07 +090061 if protocol != "" {
62 config.Protocol = []string{protocol}
63 }
Russ Cox3294cb52012-01-25 15:31:30 -050064 return DialConfig(config)
65}
66
Norberto Lopes5058c782014-12-30 18:02:23 +000067var portMap = map[string]string{
68 "ws": "80",
69 "wss": "443",
70}
71
72func parseAuthority(location *url.URL) string {
73 if _, ok := portMap[location.Scheme]; ok {
74 if _, _, err := net.SplitHostPort(location.Host); err != nil {
75 return net.JoinHostPort(location.Host, portMap[location.Scheme])
76 }
77 }
78 return location.Host
79}
80
Russ Cox3294cb52012-01-25 15:31:30 -050081// DialConfig opens a new client connection to a WebSocket with a config.
82func DialConfig(config *Config) (ws *Conn, err error) {
83 var client net.Conn
84 if config.Location == nil {
85 return nil, &DialError{config, ErrBadWebSocketLocation}
86 }
87 if config.Origin == nil {
88 return nil, &DialError{config, ErrBadWebSocketOrigin}
89 }
90 switch config.Location.Scheme {
91 case "ws":
Norberto Lopes5058c782014-12-30 18:02:23 +000092 client, err = net.Dial("tcp", parseAuthority(config.Location))
Russ Cox3294cb52012-01-25 15:31:30 -050093
94 case "wss":
Norberto Lopes5058c782014-12-30 18:02:23 +000095 client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
Russ Cox3294cb52012-01-25 15:31:30 -050096
97 default:
98 err = ErrBadScheme
99 }
100 if err != nil {
101 goto Error
102 }
103
104 ws, err = NewClient(config, client)
105 if err != nil {
Mikio Hara13b09082015-02-20 10:57:31 +0900106 client.Close()
Russ Cox3294cb52012-01-25 15:31:30 -0500107 goto Error
108 }
109 return
110
111Error:
112 return nil, &DialError{config, err}
113}