@@ -207,23 +207,27 @@ future<websocket_parser::consumption_result_t> websocket_parser::operator()(
207
207
}
208
208
if (_state == parsing_state::flags_and_payload_data) {
209
209
if (_buffer.length () + data.size () >= 2 ) {
210
- if (_buffer.length () < 2 ) {
211
- size_t hlen = _buffer.length ();
212
- _buffer.append (data.get (), 2 - hlen);
213
- data.trim_front (2 - hlen);
214
- _header = std::make_unique<frame_header>(_buffer.data ());
215
- _buffer = {};
216
-
217
- // https://datatracker.ietf.org/doc/html/rfc6455#section-5.1
218
- // We must close the connection if data isn't masked.
219
- if ((!_header->masked ) ||
220
- // RSVX must be 0
221
- (_header->rsv1 | _header->rsv2 | _header->rsv3 ) ||
222
- // Opcode must be known.
223
- (!_header->is_opcode_known ())) {
224
- _cstate = connection_state::error;
225
- return websocket_parser::stop (std::move (data));
226
- }
210
+ // _buffer.length() is less than 2 when entering this if body due to how
211
+ // the rest of code is structured. The else branch will never increase
212
+ // _buffer.length() to >=2 and other paths to this condition will always
213
+ // have buffer cleared.
214
+ assert (_buffer.length () < 2 );
215
+
216
+ size_t hlen = _buffer.length ();
217
+ _buffer.append (data.get (), 2 - hlen);
218
+ data.trim_front (2 - hlen);
219
+ _header = std::make_unique<frame_header>(_buffer.data ());
220
+ _buffer = {};
221
+
222
+ // https://datatracker.ietf.org/doc/html/rfc6455#section-5.1
223
+ // We must close the connection if data isn't masked.
224
+ if ((!_header->masked ) ||
225
+ // RSVX must be 0
226
+ (_header->rsv1 | _header->rsv2 | _header->rsv3 ) ||
227
+ // Opcode must be known.
228
+ (!_header->is_opcode_known ())) {
229
+ _cstate = connection_state::error;
230
+ return websocket_parser::stop (std::move (data));
227
231
}
228
232
_state = parsing_state::payload_length_and_mask;
229
233
} else {
@@ -238,35 +242,54 @@ future<websocket_parser::consumption_result_t> websocket_parser::operator()(
238
242
size_t hlen = _buffer.length ();
239
243
_buffer.append (data.get (), required_bytes - hlen);
240
244
data.trim_front (required_bytes - hlen);
241
-
242
- _payload_length = _header->length ;
243
- char const *input = _buffer.data ();
244
- if (_header->length == 126 ) {
245
- _payload_length = consume_be<uint16_t >(input);
246
- } else if (_header->length == 127 ) {
247
- _payload_length = consume_be<uint64_t >(input);
248
- }
249
-
250
- _masking_key = consume_be<uint32_t >(input);
251
- _buffer = {};
252
245
}
246
+ _payload_length = _header->length ;
247
+ char const *input = _buffer.data ();
248
+ if (_header->length == 126 ) {
249
+ _payload_length = consume_be<uint16_t >(input);
250
+ } else if (_header->length == 127 ) {
251
+ _payload_length = consume_be<uint64_t >(input);
252
+ }
253
+
254
+ _masking_key = consume_be<uint32_t >(input);
255
+ _buffer = {};
253
256
_state = parsing_state::payload;
254
257
} else {
255
258
_buffer.append (data.get (), data.size ());
256
259
return websocket_parser::dont_stop ();
257
260
}
258
261
}
259
262
if (_state == parsing_state::payload) {
260
- if (_payload_length > data.size ()) {
261
- _payload_length -= data.size ();
262
- remove_mask (data, data.size ());
263
- _result = std::move (data);
264
- return websocket_parser::stop (buff_t (0 ));
263
+ if (data.size () < remaining_payload_length ()) {
264
+ // data has insufficient data to complete the frame - consume data.size() bytes
265
+ if (_result.empty ()) {
266
+ _result = temporary_buffer<char >(remaining_payload_length ());
267
+ _consumed_payload_length = 0 ;
268
+ }
269
+ std::copy (data.begin (), data.end (), _result.get_write () + _consumed_payload_length);
270
+ _consumed_payload_length += data.size ();
271
+ return websocket_parser::dont_stop ();
265
272
} else {
266
- _result = data.clone ();
273
+ // data has sufficient data to complete the frame - consume remaining_payload_length()
274
+ auto consumed_bytes = remaining_payload_length ();
275
+ if (_result.empty ()) {
276
+ // Try to avoid memory copies in case when network packets contain one or more full
277
+ // websocket frames.
278
+ if (consumed_bytes == data.size ()) {
279
+ _result = std::move (data);
280
+ data = temporary_buffer<char >(0 );
281
+ } else {
282
+ _result = data.share ();
283
+ _result.trim (consumed_bytes);
284
+ data.trim_front (consumed_bytes);
285
+ }
286
+ } else {
287
+ std::copy (data.begin (), data.begin () + consumed_bytes,
288
+ _result.get_write () + _consumed_payload_length);
289
+ data.trim_front (consumed_bytes);
290
+ }
267
291
remove_mask (_result, _payload_length);
268
- data.trim_front (_payload_length);
269
- _payload_length = 0 ;
292
+ _consumed_payload_length = 0 ;
270
293
_state = parsing_state::flags_and_payload_data;
271
294
return websocket_parser::stop (std::move (data));
272
295
}
0 commit comments