v2 / vlib / net / websocket / handshake.v
164 lines · 157 sloc · 5.18 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1module websocket
2
3import encoding.base64
4import strings
5
6// handshake manages the websocket handshake process
7fn (mut ws Client) handshake() ! {
8 nonce := get_nonce(ws.nonce_size)
9 seckey := base64.encode_str(nonce)
10 mut sb := strings.new_builder(1024)
11 defer {
12 unsafe { sb.free() }
13 }
14 sb.write_string('GET ')
15 sb.write_string(ws.uri.resource)
16 sb.write_string(ws.uri.querystring)
17 sb.write_string(' HTTP/1.1\r\nHost: ')
18 sb.write_string(ws.uri.hostname)
19 sb.write_string(':')
20 sb.write_string(ws.uri.port)
21 sb.write_string('\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n')
22 sb.write_string('Sec-WebSocket-Key: ')
23 sb.write_string(seckey)
24 sb.write_string('\r\nSec-WebSocket-Version: 13')
25 for key in ws.header.keys() {
26 val := ws.header.custom_values(key).join(',')
27 sb.write_string('\r\n${key}: ${val}')
28 }
29 sb.write_string('\r\n\r\n')
30 handshake := sb.str()
31 handshake_bytes := handshake.bytes()
32 ws.debug_log('sending handshake: ${handshake}')
33 ws.socket_write(handshake_bytes)!
34 ws.read_handshake(seckey)!
35}
36
37// handle_server_handshake manages websocket server handshake process
38fn (mut s Server) handle_server_handshake(mut c Client) !(string, &ServerClient) {
39 msg := c.read_handshake_str()!
40 handshake_response, client := s.parse_client_handshake(msg, mut c)!
41 return handshake_response, client
42}
43
44// parse_client_handshake parses result from handshake process
45fn (mut s Server) parse_client_handshake(client_handshake string, mut c Client) !(string, &ServerClient) {
46 s.logger.debug('server-> client handshake:\n${client_handshake}')
47 lines := client_handshake.split_into_lines()
48 get_tokens := lines[0].split(' ')
49 if get_tokens.len < 3 {
50 return error_with_code('unexpected get operation, ${get_tokens}', 1)
51 }
52 if get_tokens[0].trim_space() != 'GET' {
53 return error_with_code("unexpected request '${get_tokens[0]}', expected 'GET'", 2)
54 }
55 if get_tokens[2].trim_space() != 'HTTP/1.1' {
56 return error_with_code("unexpected request ${get_tokens}, expected 'HTTP/1.1'", 3)
57 }
58 mut seckey := ''
59 mut flags := []Flag{}
60 mut key := ''
61 for i in 1 .. lines.len {
62 if lines[i].len <= 0 || lines[i] == '\r\n' {
63 continue
64 }
65 keys := lines[i].split(':').map(it.trim_space())
66 match keys[0].to_lower() {
67 'upgrade' {
68 flags << .has_upgrade
69 }
70 'connection' {
71 flags << .has_connection
72 }
73 'sec-websocket-key' {
74 key = keys[1]
75 s.logger.debug('server-> got key: ${key}')
76 seckey = create_key_challenge_response(key)!
77 s.logger.debug('server-> challenge: ${seckey}, response: ${key}')
78 flags << .has_accept
79 }
80 else {
81 // we ignore other headers like protocol for now
82 }
83 }
84 }
85 if flags.len < 3 {
86 return error_with_code('invalid client handshake, ${client_handshake}', 4)
87 }
88 server_handshake := 'HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${seckey}\r\n\r\n'
89 server_client := &ServerClient{
90 resource_name: get_tokens[1]
91 client_key: key
92 client: unsafe { c }
93 server: unsafe { s }
94 }
95 return server_handshake, server_client
96}
97
98// read_handshake_str returns the handshake response
99fn (mut ws Client) read_handshake_str() !string {
100 mut total_bytes_read := 0
101 mut msg := [1024]u8{}
102 mut buffer := [1]u8{}
103 for total_bytes_read < 1024 {
104 bytes_read := ws.socket_read_ptr(&buffer[0], 1)!
105 if bytes_read == 0 {
106 return error_with_code('unexpected no response from handshake', 5)
107 }
108 msg[total_bytes_read] = buffer[0]
109 total_bytes_read++
110 if total_bytes_read > 5 && msg[total_bytes_read - 1] == `\n`
111 && msg[total_bytes_read - 2] == `\r` && msg[total_bytes_read - 3] == `\n`
112 && msg[total_bytes_read - 4] == `\r` {
113 break
114 }
115 }
116 res := msg[..total_bytes_read].bytestr()
117 return res
118}
119
120// read_handshake reads the handshake result and check if valid
121fn (mut ws Client) read_handshake(seckey string) ! {
122 mut msg := ws.read_handshake_str()!
123 ws.check_handshake_response(msg, seckey)!
124}
125
126// check_handshake_response checks the response from handshake and returns
127// the response and secure key provided by the websocket client
128fn (mut ws Client) check_handshake_response(handshake_response string, seckey string) ! {
129 ws.debug_log('handshake response:\n${handshake_response}')
130 lines := handshake_response.split_into_lines()
131 header := lines[0]
132 if !header.starts_with('HTTP/1.1 101') && !header.starts_with('HTTP/1.0 101') {
133 return error_with_code('handshake_handler: invalid HTTP status response code, ${header}', 6)
134 }
135 for i in 1 .. lines.len {
136 if lines[i].len <= 0 || lines[i] == '\r\n' {
137 continue
138 }
139 keys := lines[i].split(':').map(it.trim_space())
140 match keys[0].to_lower() {
141 'upgrade' {
142 ws.flags << .has_upgrade
143 }
144 'connection' {
145 ws.flags << .has_connection
146 }
147 'sec-websocket-accept' {
148 ws.debug_log('seckey: ${seckey}')
149 challenge := create_key_challenge_response(seckey)!
150 ws.debug_log('challenge: ${challenge}, response: ${keys[1]}')
151 if keys[1].trim_space() != challenge {
152 return error_with_code('handshake_handler: Sec-WebSocket-Accept header does not match computed sha1/base64 response.',
153 7)
154 }
155 ws.flags << .has_accept
156 }
157 else {}
158 }
159 }
160 if ws.flags.len < 3 {
161 ws.close(1002, 'invalid websocket HTTP headers')!
162 return error_with_code('invalid websocket HTTP headers', 8)
163 }
164}
165