/src/libzmq/src/ws_encoder.cpp
Line | Count | Source |
1 | | /* SPDX-License-Identifier: MPL-2.0 */ |
2 | | |
3 | | #include "precompiled.hpp" |
4 | | #include "ws_protocol.hpp" |
5 | | #include "ws_encoder.hpp" |
6 | | #include "msg.hpp" |
7 | | #include "likely.hpp" |
8 | | #include "wire.hpp" |
9 | | #include "random.hpp" |
10 | | |
11 | | #include <limits.h> |
12 | | |
13 | | zmq::ws_encoder_t::ws_encoder_t (size_t bufsize_, bool must_mask_) : |
14 | 0 | encoder_base_t<ws_encoder_t> (bufsize_), _must_mask (must_mask_) |
15 | 0 | { |
16 | | // Write 0 bytes to the batch and go to message_ready state. |
17 | 0 | next_step (NULL, 0, &ws_encoder_t::message_ready, true); |
18 | 0 | _masked_msg.init (); |
19 | 0 | } |
20 | | |
21 | | zmq::ws_encoder_t::~ws_encoder_t () |
22 | 0 | { |
23 | 0 | _masked_msg.close (); |
24 | 0 | } |
25 | | |
26 | | void zmq::ws_encoder_t::message_ready () |
27 | 0 | { |
28 | 0 | int offset = 0; |
29 | |
|
30 | 0 | _is_binary = false; |
31 | |
|
32 | 0 | if (in_progress ()->is_ping ()) |
33 | 0 | _tmp_buf[offset++] = 0x80 | zmq::ws_protocol_t::opcode_ping; |
34 | 0 | else if (in_progress ()->is_pong ()) |
35 | 0 | _tmp_buf[offset++] = 0x80 | zmq::ws_protocol_t::opcode_pong; |
36 | 0 | else if (in_progress ()->is_close_cmd ()) |
37 | 0 | _tmp_buf[offset++] = 0x80 | zmq::ws_protocol_t::opcode_close; |
38 | 0 | else { |
39 | 0 | _tmp_buf[offset++] = 0x82; // Final | binary |
40 | 0 | _is_binary = true; |
41 | 0 | } |
42 | |
|
43 | 0 | _tmp_buf[offset] = _must_mask ? 0x80 : 0x00; |
44 | |
|
45 | 0 | size_t size = in_progress ()->size (); |
46 | 0 | if (_is_binary) |
47 | 0 | size++; |
48 | | // TODO: create an opcode for subscribe/cancel |
49 | 0 | if (in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) |
50 | 0 | size++; |
51 | |
|
52 | 0 | if (size <= 125) |
53 | 0 | _tmp_buf[offset++] |= static_cast<unsigned char> (size & 127); |
54 | 0 | else if (size <= 0xFFFF) { |
55 | 0 | _tmp_buf[offset++] |= 126; |
56 | 0 | _tmp_buf[offset++] = static_cast<unsigned char> ((size >> 8) & 0xFF); |
57 | 0 | _tmp_buf[offset++] = static_cast<unsigned char> (size & 0xFF); |
58 | 0 | } else { |
59 | 0 | _tmp_buf[offset++] |= 127; |
60 | 0 | put_uint64 (_tmp_buf + offset, size); |
61 | 0 | offset += 8; |
62 | 0 | } |
63 | |
|
64 | 0 | if (_must_mask) { |
65 | 0 | const uint32_t random = generate_random (); |
66 | 0 | put_uint32 (_tmp_buf + offset, random); |
67 | 0 | put_uint32 (_mask, random); |
68 | 0 | offset += 4; |
69 | 0 | } |
70 | |
|
71 | 0 | int mask_index = 0; |
72 | 0 | if (_is_binary) { |
73 | | // Encode flags. |
74 | 0 | unsigned char protocol_flags = 0; |
75 | 0 | if (in_progress ()->flags () & msg_t::more) |
76 | 0 | protocol_flags |= ws_protocol_t::more_flag; |
77 | 0 | if (in_progress ()->flags () & msg_t::command) |
78 | 0 | protocol_flags |= ws_protocol_t::command_flag; |
79 | |
|
80 | 0 | _tmp_buf[offset++] = |
81 | 0 | _must_mask ? protocol_flags ^ _mask[mask_index++] : protocol_flags; |
82 | 0 | } |
83 | | |
84 | | // Encode the subscribe/cancel byte. |
85 | | // TODO: remove once there is an opcode for subscribe/cancel |
86 | 0 | if (in_progress ()->is_subscribe ()) |
87 | 0 | _tmp_buf[offset++] = _must_mask ? 1 ^ _mask[mask_index++] : 1; |
88 | 0 | else if (in_progress ()->is_cancel ()) |
89 | 0 | _tmp_buf[offset++] = _must_mask ? 0 ^ _mask[mask_index++] : 0; |
90 | |
|
91 | 0 | next_step (_tmp_buf, offset, &ws_encoder_t::size_ready, false); |
92 | 0 | } |
93 | | |
94 | | void zmq::ws_encoder_t::size_ready () |
95 | 0 | { |
96 | 0 | if (_must_mask) { |
97 | 0 | assert (in_progress () != &_masked_msg); |
98 | 0 | const size_t size = in_progress ()->size (); |
99 | |
|
100 | 0 | unsigned char *src = |
101 | 0 | static_cast<unsigned char *> (in_progress ()->data ()); |
102 | 0 | unsigned char *dest = src; |
103 | | |
104 | | // If msg is shared or data is constant we cannot mask in-place, allocate a new msg for it |
105 | 0 | if (in_progress ()->flags () & msg_t::shared |
106 | 0 | || in_progress ()->is_cmsg ()) { |
107 | 0 | _masked_msg.close (); |
108 | 0 | _masked_msg.init_size (size); |
109 | 0 | dest = static_cast<unsigned char *> (_masked_msg.data ()); |
110 | 0 | } |
111 | |
|
112 | 0 | int mask_index = 0; |
113 | 0 | if (_is_binary) |
114 | 0 | ++mask_index; |
115 | | // TODO: remove once there is an opcode for subscribe/cancel |
116 | 0 | if (in_progress ()->is_subscribe () || in_progress ()->is_cancel ()) |
117 | 0 | ++mask_index; |
118 | 0 | for (size_t i = 0; i < size; ++i, mask_index++) |
119 | 0 | dest[i] = src[i] ^ _mask[mask_index % 4]; |
120 | |
|
121 | 0 | next_step (dest, size, &ws_encoder_t::message_ready, true); |
122 | 0 | } else { |
123 | 0 | next_step (in_progress ()->data (), in_progress ()->size (), |
124 | 0 | &ws_encoder_t::message_ready, true); |
125 | 0 | } |
126 | 0 | } |