|  | // 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" | 
|  | "io" | 
|  | "net" | 
|  | "net/http" | 
|  | "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.ParseRequestURI(server) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | config.Origin, err = url.ParseRequestURI(origin) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | config.Header = http.Header(make(map[string][]string)) | 
|  | 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) | 
|  | err = hybiClientHandshake(config, br, bw) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | buf := bufio.NewReadWriter(br, bw) | 
|  | ws = newHybiClientConn(config, buf, rwc) | 
|  | return | 
|  | } | 
|  |  | 
|  | // Dial opens a new client connection to a WebSocket. | 
|  | func Dial(url_, protocol, origin string) (ws *Conn, err error) { | 
|  | config, err := NewConfig(url_, origin) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if protocol != "" { | 
|  | config.Protocol = []string{protocol} | 
|  | } | 
|  | return DialConfig(config) | 
|  | } | 
|  |  | 
|  | var portMap = map[string]string{ | 
|  | "ws":  "80", | 
|  | "wss": "443", | 
|  | } | 
|  |  | 
|  | func parseAuthority(location *url.URL) string { | 
|  | if _, ok := portMap[location.Scheme]; ok { | 
|  | if _, _, err := net.SplitHostPort(location.Host); err != nil { | 
|  | return net.JoinHostPort(location.Host, portMap[location.Scheme]) | 
|  | } | 
|  | } | 
|  | return location.Host | 
|  | } | 
|  |  | 
|  | // 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} | 
|  | } | 
|  | dialer := config.Dialer | 
|  | if dialer == nil { | 
|  | dialer = &net.Dialer{} | 
|  | } | 
|  | client, err = dialWithDialer(dialer, config) | 
|  | if err != nil { | 
|  | goto Error | 
|  | } | 
|  | ws, err = NewClient(config, client) | 
|  | if err != nil { | 
|  | client.Close() | 
|  | goto Error | 
|  | } | 
|  | return | 
|  |  | 
|  | Error: | 
|  | return nil, &DialError{config, err} | 
|  | } |