You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
2.4 KiB

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