| 1 | module websocket |
| 2 | |
| 3 | import encoding.utf8 |
| 4 | |
| 5 | const header_len_offset = 2 // offset for lengthpart of websocket header |
| 6 | |
| 7 | const buffer_size = 256 // default buffer size |
| 8 | |
| 9 | const extended_payload16_end_byte = 4 // header length with 16-bit extended payload |
| 10 | |
| 11 | const extended_payload64_end_byte = 10 |
| 12 | |
| 13 | // Fragment represents a websocket data fragment |
| 14 | struct Fragment { |
| 15 | data []u8 // included data payload data in a fragment |
| 16 | opcode OPCode // interpretation of the payload data |
| 17 | } |
| 18 | |
| 19 | // Frame represents a data frame header |
| 20 | struct Frame { |
| 21 | mut: |
| 22 | // length of the websocket header part |
| 23 | header_len int = 2 |
| 24 | // size of total frame |
| 25 | frame_size int = 2 |
| 26 | fin bool // true if final fragment of message |
| 27 | rsv1 bool // reserved for future use in websocket RFC |
| 28 | rsv2 bool // reserved for future use in websocket RFC |
| 29 | rsv3 bool // reserved for future use in websocket RFC |
| 30 | opcode OPCode // interpretation of the payload data |
| 31 | has_mask bool // true if the payload data is masked |
| 32 | payload_len int // payload length |
| 33 | masking_key [4]u8 // all frames from client to server is masked with this key |
| 34 | } |
| 35 | |
| 36 | const invalid_close_codes = [999, 1004, 1005, 1006, 1014, 1015, 1016, 1100, 2000, 2999, 5000, 65536] |
| 37 | |
| 38 | // validate_client validates client frame rules from RFC6455 |
| 39 | pub fn (mut ws Client) validate_frame(frame &Frame) ! { |
| 40 | if frame.rsv1 || frame.rsv2 || frame.rsv3 { |
| 41 | ws.close(1002, 'rsv cannot be other than 0, not negotiated')! |
| 42 | return error('rsv cannot be other than 0, not negotiated') |
| 43 | } |
| 44 | if (int(frame.opcode) >= 3 && int(frame.opcode) <= 7) |
| 45 | || (int(frame.opcode) >= 11 && int(frame.opcode) <= 15) { |
| 46 | ws.close(1002, 'use of reserved opcode')! |
| 47 | return error('use of reserved opcode') |
| 48 | } |
| 49 | if frame.has_mask && !ws.is_server { |
| 50 | // server should never send masked frames |
| 51 | // to client, close connection |
| 52 | ws.close(1002, 'client got masked frame')! |
| 53 | return error('client sent masked frame') |
| 54 | } |
| 55 | if is_control_frame(frame.opcode) { |
| 56 | if !frame.fin { |
| 57 | ws.close(1002, 'control message must not be fragmented')! |
| 58 | return error('unexpected control frame with no fin') |
| 59 | } |
| 60 | if frame.payload_len > 125 { |
| 61 | ws.close(1002, 'control frames must not exceed 125 bytes')! |
| 62 | return error('unexpected control frame payload length') |
| 63 | } |
| 64 | } |
| 65 | if frame.fin == false && ws.fragments.len == 0 && frame.opcode == .continuation { |
| 66 | err_msg := 'unexecpected continuation, there are no frames to continue, ${frame}' |
| 67 | ws.close(1002, err_msg)! |
| 68 | return error(err_msg) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // is_control_frame returns true if the frame is a control frame |
| 73 | fn is_control_frame(opcode OPCode) bool { |
| 74 | return opcode !in [.text_frame, .binary_frame, .continuation] |
| 75 | } |
| 76 | |
| 77 | // is_data_frame returns true if the frame is a control frame |
| 78 | fn is_data_frame(opcode OPCode) bool { |
| 79 | return opcode in [.text_frame, .binary_frame] |
| 80 | } |
| 81 | |
| 82 | // read_payload reads the message payload from the socket |
| 83 | fn (mut ws Client) read_payload(frame &Frame) ![]u8 { |
| 84 | if frame.payload_len == 0 { |
| 85 | return []u8{} |
| 86 | } |
| 87 | mut buffer := []u8{cap: frame.payload_len} |
| 88 | mut read_buf := [1]u8{} |
| 89 | mut bytes_read := 0 |
| 90 | for bytes_read < frame.payload_len { |
| 91 | len := ws.socket_read_ptr(&read_buf[0], 1)! |
| 92 | if len != 1 { |
| 93 | return error('expected read all message, got zero') |
| 94 | } |
| 95 | bytes_read += len |
| 96 | buffer << read_buf[0] |
| 97 | } |
| 98 | if bytes_read != frame.payload_len { |
| 99 | return error('failed to read payload') |
| 100 | } |
| 101 | if frame.has_mask { |
| 102 | for i in 0 .. frame.payload_len { |
| 103 | buffer[i] ^= frame.masking_key[i % 4] & 0xff |
| 104 | } |
| 105 | } |
| 106 | return buffer |
| 107 | } |
| 108 | |
| 109 | // validate_utf_8 validates payload for valid utf8 encoding |
| 110 | // - Future implementation needs to support fail fast utf errors for strict autobahn conformance |
| 111 | fn (mut ws Client) validate_utf_8(opcode OPCode, payload []u8) ! { |
| 112 | if opcode in [.text_frame, .close] && !utf8.validate(payload.data, payload.len) { |
| 113 | ws.logger.error('malformed utf8 payload, payload len: (${payload.len})') |
| 114 | ws.send_error_event('Received malformed utf8.') |
| 115 | ws.close(1007, 'malformed utf8 payload')! |
| 116 | return error('malformed utf8 payload') |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // read_next_message reads 1 to n frames to compose a message |
| 121 | pub fn (mut ws Client) read_next_message() !Message { |
| 122 | for { |
| 123 | frame := ws.parse_frame_header()! |
| 124 | ws.validate_frame(&frame)! |
| 125 | frame_payload := ws.read_payload(&frame)! |
| 126 | if is_control_frame(frame.opcode) { |
| 127 | // Control frames can interject other frames |
| 128 | // and need to be returned immediately |
| 129 | msg := Message{ |
| 130 | opcode: OPCode(frame.opcode) |
| 131 | payload: frame_payload.clone() |
| 132 | } |
| 133 | unsafe { frame_payload.free() } |
| 134 | return msg |
| 135 | } |
| 136 | // if the message is fragmented we just put it on fragments |
| 137 | // a fragment is allowed to have zero size payload |
| 138 | if !frame.fin { |
| 139 | ws.fragments << &Fragment{ |
| 140 | data: frame_payload.clone() |
| 141 | opcode: frame.opcode |
| 142 | } |
| 143 | unsafe { frame_payload.free() } |
| 144 | continue |
| 145 | } |
| 146 | if ws.fragments.len == 0 { |
| 147 | ws.validate_utf_8(frame.opcode, frame_payload) or { |
| 148 | ws.logger.error('UTF8 validation error: ${err}, len of payload(${frame_payload.len})') |
| 149 | ws.send_error_event('UTF8 validation error: ${err}, len of payload(${frame_payload.len})') |
| 150 | return err |
| 151 | } |
| 152 | msg := Message{ |
| 153 | opcode: OPCode(frame.opcode) |
| 154 | payload: frame_payload.clone() |
| 155 | } |
| 156 | unsafe { frame_payload.free() } |
| 157 | return msg |
| 158 | } |
| 159 | defer { |
| 160 | ws.fragments = [] |
| 161 | } |
| 162 | if is_data_frame(frame.opcode) { |
| 163 | ws.close(0, '')! |
| 164 | return error('Unexpected frame opcode') |
| 165 | } |
| 166 | payload := ws.payload_from_fragments(frame_payload)! |
| 167 | opcode := ws.opcode_from_fragments() |
| 168 | ws.validate_utf_8(opcode, payload)! |
| 169 | msg := Message{ |
| 170 | opcode: opcode |
| 171 | payload: payload.clone() |
| 172 | } |
| 173 | unsafe { |
| 174 | frame_payload.free() |
| 175 | payload.free() |
| 176 | } |
| 177 | return msg |
| 178 | } |
| 179 | return error('none') |
| 180 | } |
| 181 | |
| 182 | // payload_from_fragments returns the whole payload from fragmented message |
| 183 | fn (ws &Client) payload_from_fragments(fin_payload []u8) ![]u8 { |
| 184 | mut total_size := 0 |
| 185 | for f in ws.fragments { |
| 186 | if f.data.len > 0 { |
| 187 | total_size += f.data.len |
| 188 | } |
| 189 | } |
| 190 | total_size += fin_payload.len |
| 191 | if total_size == 0 { |
| 192 | return []u8{} |
| 193 | } |
| 194 | mut total_buffer := []u8{cap: total_size} |
| 195 | for f in ws.fragments { |
| 196 | if f.data.len > 0 { |
| 197 | total_buffer << f.data |
| 198 | } |
| 199 | } |
| 200 | total_buffer << fin_payload |
| 201 | return total_buffer |
| 202 | } |
| 203 | |
| 204 | // opcode_from_fragments returns the opcode for message from the first fragment sent |
| 205 | fn (ws &Client) opcode_from_fragments() OPCode { |
| 206 | return OPCode(ws.fragments[0].opcode) |
| 207 | } |
| 208 | |
| 209 | // parse_frame_header parses next message by decoding the incoming frames |
| 210 | pub fn (mut ws Client) parse_frame_header() !Frame { |
| 211 | mut buffer := [256]u8{} |
| 212 | mut bytes_read := 0 |
| 213 | mut frame := Frame{} |
| 214 | mut rbuff := [1]u8{} |
| 215 | mut mask_end_byte := 0 |
| 216 | for ws.get_state() == .open { |
| 217 | read_bytes := ws.socket_read_ptr(&rbuff[0], 1)! |
| 218 | if read_bytes == 0 { |
| 219 | return error('websocket peer closed connection') |
| 220 | } |
| 221 | buffer[bytes_read] = rbuff[0] |
| 222 | bytes_read++ |
| 223 | // parses the first two header bytes to get basic frame information |
| 224 | if bytes_read == header_len_offset { |
| 225 | frame.fin = (buffer[0] & 0x80) == 0x80 |
| 226 | frame.rsv1 = (buffer[0] & 0x40) == 0x40 |
| 227 | frame.rsv2 = (buffer[0] & 0x20) == 0x20 |
| 228 | frame.rsv3 = (buffer[0] & 0x10) == 0x10 |
| 229 | frame.opcode = unsafe { OPCode(int(buffer[0] & 0x7F)) } |
| 230 | frame.has_mask = (buffer[1] & 0x80) == 0x80 |
| 231 | frame.payload_len = buffer[1] & 0x7F |
| 232 | // if the frame has a mask, set the byte position where the mask ends |
| 233 | if frame.has_mask { |
| 234 | mask_end_byte = if frame.payload_len < 126 { |
| 235 | header_len_offset + 4 |
| 236 | } else if frame.payload_len == 126 { |
| 237 | header_len_offset + 6 |
| 238 | } else if frame.payload_len == 127 { |
| 239 | header_len_offset + 12 |
| 240 | } else { |
| 241 | 0 |
| 242 | } // impossible |
| 243 | } |
| 244 | frame.payload_len = frame.payload_len |
| 245 | frame.frame_size = frame.header_len + frame.payload_len |
| 246 | if !frame.has_mask && frame.payload_len < 126 { |
| 247 | break |
| 248 | } |
| 249 | } |
| 250 | if frame.payload_len == 126 && bytes_read == extended_payload16_end_byte { |
| 251 | frame.header_len += 2 |
| 252 | frame.payload_len = 0 |
| 253 | frame.payload_len |= int(u32(buffer[2]) << 8) |
| 254 | frame.payload_len |= int(buffer[3]) |
| 255 | frame.frame_size = frame.header_len + frame.payload_len |
| 256 | if !frame.has_mask { |
| 257 | break |
| 258 | } |
| 259 | } |
| 260 | if frame.payload_len == 127 && bytes_read == extended_payload64_end_byte { |
| 261 | frame.header_len += 8 |
| 262 | // these shift operators needs 64 bit on clang with -prod flag |
| 263 | mut payload_len := u64(0) |
| 264 | payload_len |= u64(buffer[2]) << 56 |
| 265 | payload_len |= u64(buffer[3]) << 48 |
| 266 | payload_len |= u64(buffer[4]) << 40 |
| 267 | payload_len |= u64(buffer[5]) << 32 |
| 268 | payload_len |= u64(buffer[6]) << 24 |
| 269 | payload_len |= u64(buffer[7]) << 16 |
| 270 | payload_len |= u64(buffer[8]) << 8 |
| 271 | payload_len |= u64(buffer[9]) |
| 272 | frame.payload_len = int(payload_len) |
| 273 | if !frame.has_mask { |
| 274 | break |
| 275 | } |
| 276 | } |
| 277 | if frame.has_mask && bytes_read == mask_end_byte { |
| 278 | frame.masking_key[0] = buffer[mask_end_byte - 4] |
| 279 | frame.masking_key[1] = buffer[mask_end_byte - 3] |
| 280 | frame.masking_key[2] = buffer[mask_end_byte - 2] |
| 281 | frame.masking_key[3] = buffer[mask_end_byte - 1] |
| 282 | break |
| 283 | } |
| 284 | } |
| 285 | return frame |
| 286 | } |
| 287 | |
| 288 | // unmask_sequence unmask any given sequence |
| 289 | fn (f &Frame) unmask_sequence(mut buffer []u8) { |
| 290 | for i in 0 .. buffer.len { |
| 291 | buffer[i] ^= f.masking_key[i % 4] & 0xff |
| 292 | } |
| 293 | } |
| 294 | |