aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/net/websocket/message.v
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/net/websocket/message.v
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/vlib/net/websocket/message.v')
-rw-r--r--v_windows/v/vlib/net/websocket/message.v295
1 files changed, 295 insertions, 0 deletions
diff --git a/v_windows/v/vlib/net/websocket/message.v b/v_windows/v/vlib/net/websocket/message.v
new file mode 100644
index 0000000..4c57232
--- /dev/null
+++ b/v_windows/v/vlib/net/websocket/message.v
@@ -0,0 +1,295 @@
+module websocket
+
+import encoding.utf8
+
+const (
+ header_len_offset = 2 // offset for lengthpart of websocket header
+ buffer_size = 256 // default buffer size
+ extended_payload16_end_byte = 4 // header length with 16-bit extended payload
+ extended_payload64_end_byte = 10 // header length with 64-bit extended payload
+)
+
+// Fragment represents a websocket data fragment
+struct Fragment {
+ data []byte // included data payload data in a fragment
+ opcode OPCode // interpretation of the payload data
+}
+
+// Frame represents a data frame header
+struct Frame {
+mut:
+ // length of the websocket header part
+ header_len int = 2
+ // size of total frame
+ frame_size int = 2
+ fin bool // true if final fragment of message
+ rsv1 bool // reserved for future use in websocket RFC
+ rsv2 bool // reserved for future use in websocket RFC
+ rsv3 bool // reserved for future use in websocket RFC
+ opcode OPCode // interpretation of the payload data
+ has_mask bool // true if the payload data is masked
+ payload_len int // payload length
+ masking_key [4]byte // all frames from client to server is masked with this key
+}
+
+const (
+ invalid_close_codes = [999, 1004, 1005, 1006, 1014, 1015, 1016, 1100, 2000, 2999, 5000, 65536]
+)
+
+// validate_client validates client frame rules from RFC6455
+pub fn (mut ws Client) validate_frame(frame &Frame) ? {
+ if frame.rsv1 || frame.rsv2 || frame.rsv3 {
+ ws.close(1002, 'rsv cannot be other than 0, not negotiated') ?
+ return error('rsv cannot be other than 0, not negotiated')
+ }
+ if (int(frame.opcode) >= 3 && int(frame.opcode) <= 7)
+ || (int(frame.opcode) >= 11 && int(frame.opcode) <= 15) {
+ ws.close(1002, 'use of reserved opcode') ?
+ return error('use of reserved opcode')
+ }
+ if frame.has_mask && !ws.is_server {
+ // server should never send masked frames
+ // to client, close connection
+ ws.close(1002, 'client got masked frame') ?
+ return error('client sent masked frame')
+ }
+ if is_control_frame(frame.opcode) {
+ if !frame.fin {
+ ws.close(1002, 'control message must not be fragmented') ?
+ return error('unexpected control frame with no fin')
+ }
+ if frame.payload_len > 125 {
+ ws.close(1002, 'control frames must not exceed 125 bytes') ?
+ return error('unexpected control frame payload length')
+ }
+ }
+ if frame.fin == false && ws.fragments.len == 0 && frame.opcode == .continuation {
+ err_msg := 'unexecpected continuation, there are no frames to continue, $frame'
+ ws.close(1002, err_msg) ?
+ return error(err_msg)
+ }
+}
+
+// is_control_frame returns true if the frame is a control frame
+fn is_control_frame(opcode OPCode) bool {
+ return opcode !in [.text_frame, .binary_frame, .continuation]
+}
+
+// is_data_frame returns true if the frame is a control frame
+fn is_data_frame(opcode OPCode) bool {
+ return opcode in [.text_frame, .binary_frame]
+}
+
+// read_payload reads the message payload from the socket
+fn (mut ws Client) read_payload(frame &Frame) ?[]byte {
+ if frame.payload_len == 0 {
+ return []byte{}
+ }
+ mut buffer := []byte{cap: frame.payload_len}
+ mut read_buf := [1]byte{}
+ mut bytes_read := 0
+ for bytes_read < frame.payload_len {
+ len := ws.socket_read_ptr(&read_buf[0], 1) ?
+ if len != 1 {
+ return error('expected read all message, got zero')
+ }
+ bytes_read += len
+ buffer << read_buf[0]
+ }
+ if bytes_read != frame.payload_len {
+ return error('failed to read payload')
+ }
+ if frame.has_mask {
+ for i in 0 .. frame.payload_len {
+ buffer[i] ^= frame.masking_key[i % 4] & 0xff
+ }
+ }
+ return buffer
+}
+
+// validate_utf_8 validates payload for valid utf8 encoding
+// - Future implementation needs to support fail fast utf errors for strict autobahn conformance
+fn (mut ws Client) validate_utf_8(opcode OPCode, payload []byte) ? {
+ if opcode in [.text_frame, .close] && !utf8.validate(payload.data, payload.len) {
+ ws.logger.error('malformed utf8 payload, payload len: ($payload.len)')
+ ws.send_error_event('Recieved malformed utf8.')
+ ws.close(1007, 'malformed utf8 payload') ?
+ return error('malformed utf8 payload')
+ }
+}
+
+// read_next_message reads 1 to n frames to compose a message
+pub fn (mut ws Client) read_next_message() ?Message {
+ for {
+ frame := ws.parse_frame_header() ?
+ ws.validate_frame(&frame) ?
+ frame_payload := ws.read_payload(&frame) ?
+ if is_control_frame(frame.opcode) {
+ // Control frames can interject other frames
+ // and need to be returned immediately
+ msg := Message{
+ opcode: OPCode(frame.opcode)
+ payload: frame_payload.clone()
+ }
+ unsafe { frame_payload.free() }
+ return msg
+ }
+ // if the message is fragmented we just put it on fragments
+ // a fragment is allowed to have zero size payload
+ if !frame.fin {
+ ws.fragments << &Fragment{
+ data: frame_payload.clone()
+ opcode: frame.opcode
+ }
+ unsafe { frame_payload.free() }
+ continue
+ }
+ if ws.fragments.len == 0 {
+ ws.validate_utf_8(frame.opcode, frame_payload) or {
+ ws.logger.error('UTF8 validation error: $err, len of payload($frame_payload.len)')
+ ws.send_error_event('UTF8 validation error: $err, len of payload($frame_payload.len)')
+ return err
+ }
+ msg := Message{
+ opcode: OPCode(frame.opcode)
+ payload: frame_payload.clone()
+ }
+ unsafe { frame_payload.free() }
+ return msg
+ }
+ defer {
+ ws.fragments = []
+ }
+ if is_data_frame(frame.opcode) {
+ ws.close(0, '') ?
+ return error('Unexpected frame opcode')
+ }
+ payload := ws.payload_from_fragments(frame_payload) ?
+ opcode := ws.opcode_from_fragments()
+ ws.validate_utf_8(opcode, payload) ?
+ msg := Message{
+ opcode: opcode
+ payload: payload.clone()
+ }
+ unsafe {
+ frame_payload.free()
+ payload.free()
+ }
+ return msg
+ }
+ return none
+}
+
+// payload_from_fragments returs the whole paylaod from fragmented message
+fn (ws Client) payload_from_fragments(fin_payload []byte) ?[]byte {
+ mut total_size := 0
+ for f in ws.fragments {
+ if f.data.len > 0 {
+ total_size += f.data.len
+ }
+ }
+ total_size += fin_payload.len
+ if total_size == 0 {
+ return []byte{}
+ }
+ mut total_buffer := []byte{cap: total_size}
+ for f in ws.fragments {
+ if f.data.len > 0 {
+ total_buffer << f.data
+ }
+ }
+ total_buffer << fin_payload
+ return total_buffer
+}
+
+// opcode_from_fragments returns the opcode for message from the first fragment sent
+fn (ws Client) opcode_from_fragments() OPCode {
+ return OPCode(ws.fragments[0].opcode)
+}
+
+// parse_frame_header parses next message by decoding the incoming frames
+pub fn (mut ws Client) parse_frame_header() ?Frame {
+ mut buffer := [256]byte{}
+ mut bytes_read := 0
+ mut frame := Frame{}
+ mut rbuff := [1]byte{}
+ mut mask_end_byte := 0
+ for ws.state == .open {
+ read_bytes := ws.socket_read_ptr(&rbuff[0], 1) ?
+ if read_bytes == 0 {
+ // this is probably a timeout or close
+ continue
+ }
+ buffer[bytes_read] = rbuff[0]
+ bytes_read++
+ // parses the first two header bytes to get basic frame information
+ if bytes_read == u64(websocket.header_len_offset) {
+ frame.fin = (buffer[0] & 0x80) == 0x80
+ frame.rsv1 = (buffer[0] & 0x40) == 0x40
+ frame.rsv2 = (buffer[0] & 0x20) == 0x20
+ frame.rsv3 = (buffer[0] & 0x10) == 0x10
+ frame.opcode = OPCode(int(buffer[0] & 0x7F))
+ frame.has_mask = (buffer[1] & 0x80) == 0x80
+ frame.payload_len = buffer[1] & 0x7F
+ // if has mask set the byte postition where mask ends
+ if frame.has_mask {
+ mask_end_byte = if frame.payload_len < 126 {
+ websocket.header_len_offset + 4
+ } else if frame.payload_len == 126 {
+ websocket.header_len_offset + 6
+ } else if frame.payload_len == 127 {
+ websocket.header_len_offset + 12
+ } else {
+ 0
+ } // impossible
+ }
+ frame.payload_len = frame.payload_len
+ frame.frame_size = frame.header_len + frame.payload_len
+ if !frame.has_mask && frame.payload_len < 126 {
+ break
+ }
+ }
+ if frame.payload_len == 126 && bytes_read == u64(websocket.extended_payload16_end_byte) {
+ frame.header_len += 2
+ frame.payload_len = 0
+ frame.payload_len |= buffer[2] << 8
+ frame.payload_len |= buffer[3]
+ frame.frame_size = frame.header_len + frame.payload_len
+ if !frame.has_mask {
+ break
+ }
+ }
+ if frame.payload_len == 127 && bytes_read == u64(websocket.extended_payload64_end_byte) {
+ frame.header_len += 8
+ // these shift operators needs 64 bit on clang with -prod flag
+ mut payload_len := u64(0)
+ payload_len |= u64(buffer[2]) << 56
+ payload_len |= u64(buffer[3]) << 48
+ payload_len |= u64(buffer[4]) << 40
+ payload_len |= u64(buffer[5]) << 32
+ payload_len |= u64(buffer[6]) << 24
+ payload_len |= u64(buffer[7]) << 16
+ payload_len |= u64(buffer[8]) << 8
+ payload_len |= u64(buffer[9])
+ frame.payload_len = int(payload_len)
+ if !frame.has_mask {
+ break
+ }
+ }
+ if frame.has_mask && bytes_read == mask_end_byte {
+ frame.masking_key[0] = buffer[mask_end_byte - 4]
+ frame.masking_key[1] = buffer[mask_end_byte - 3]
+ frame.masking_key[2] = buffer[mask_end_byte - 2]
+ frame.masking_key[3] = buffer[mask_end_byte - 1]
+ break
+ }
+ }
+ return frame
+}
+
+// unmask_sequence unmask any given sequence
+fn (f Frame) unmask_sequence(mut buffer []byte) {
+ for i in 0 .. buffer.len {
+ buffer[i] ^= f.masking_key[i % 4] & 0xff
+ }
+}