-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.go
114 lines (102 loc) · 2.31 KB
/
client.go
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
package main
import (
"bufio"
"crypto/tls"
"errors"
"net"
"net/http"
"net/url"
urlpkg "net/url"
"strings"
)
type Client struct {
URL *url.URL
Header http.Header
Dialer *net.Dialer
Config *tls.Config
Response *http.Response
Conn net.Conn
}
func NewClient(url string) (*Client, error) {
u, err := urlpkg.Parse(url)
if err != nil {
return nil, err
}
nonce, err := genNonce()
if err != nil {
return nil, err
}
header := http.Header{}
header.Set("Connection", "Upgrade")
header.Set("Upgrade", "websocket")
header.Set("Origin", "http://"+u.Host)
header.Set("Host", u.Host)
header.Set("Sec-WebSocket-Version", "13")
header.Set("Sec-WebSocket-Key", nonce)
return &Client{
URL: u,
Header: header,
}, nil
}
func (cli *Client) Connect() error {
var err error
if cli.Dialer == nil {
cli.Dialer = &net.Dialer{}
}
switch cli.URL.Scheme {
case "ws":
cli.Conn, err = cli.Dialer.Dial("tcp", cli.URL.Host)
case "wss":
cli.Conn, err = tls.DialWithDialer(cli.Dialer, "tcp", cli.URL.Host, cli.Config)
}
if err != nil {
return err
}
bw := bufio.NewWriter(cli.Conn)
bw.WriteString("GET " + cli.URL.Path + " HTTP/1.1\r\n")
cli.Header.Write(bw)
bw.WriteString("\r\n")
err = bw.Flush()
if err != nil {
cli.Close()
return err
}
br := bufio.NewReader(cli.Conn)
cli.Response, err = http.ReadResponse(br, &http.Request{Method: "GET"})
if err != nil {
cli.Close()
return err
}
if cli.Response.StatusCode != 101 {
cli.Close()
return errors.New("bad status")
}
if strings.ToLower(cli.Response.Header.Get("Connection")) != "upgrade" ||
strings.ToLower(cli.Response.Header.Get("Upgrade")) != "websocket" {
cli.Close()
return errors.New("bad upgrade")
}
nonceAccept, err := genNonceAccept(cli.Header.Get("Sec-WebSocket-Key"))
if err != nil {
cli.Close()
return err
}
if cli.Response.Header.Get("Sec-Websocket-Accept") != string(nonceAccept) {
cli.Close()
return errors.New("mismatch challenge/response")
}
return nil
}
func (cli *Client) WriteFrame(opcode byte, content []byte) error {
return nil //writeFrame(cli.Conn, opcode, content)
}
func (cli *Client) ReadFrame() (byte, []byte, error) {
return 0, nil, nil //readFrame(cli.Conn)
}
func (cli *Client) Close() error {
if cli.Conn == nil {
return nil
}
cli.WriteFrame(CloseMessage, []byte{0x03, 0xe8})
return cli.Conn.Close()
}