1 /*
2 restinio
3 */
4
5 /*!
6 Test upgrade request.
7 */
8
9 #include <catch2/catch.hpp>
10
11 #include <restinio/all.hpp>
12 #include <restinio/websocket/websocket.hpp>
13 #include <restinio/utils/base64.hpp>
14 #include <restinio/utils/sha1.hpp>
15
16 #include <test/common/utest_logger.hpp>
17 #include <test/common/pub.hpp>
18 #include <test/websocket/common/pub.hpp>
19
20 #include <so_5/all.hpp>
21
22 namespace rws = restinio::websocket::basic;
23
24 using traits_t =
25 restinio::traits_t<
26 restinio::asio_timer_manager_t,
27 utest_logger_t >;
28
29 using http_server_t = restinio::http_server_t< traits_t >;
30
31 struct upgrade_request_t : public so_5::message_t
32 {
upgrade_request_tupgrade_request_t33 upgrade_request_t( restinio::request_handle_t req )
34 : m_req{ std::move( req ) }
35 {}
36
37 restinio::request_handle_t m_req;
38 };
39
40 struct msg_ws_message_t : public so_5::message_t
41 {
msg_ws_message_tmsg_ws_message_t42 msg_ws_message_t( rws::message_handle_t msg )
43 : m_msg{ msg }
44 {}
45
46 rws::message_handle_t m_msg;
47 };
48
49 struct server_started_t : public so_5::signal_t {};
50
51 //
52 // g_last_close_code
53 //
54
55 std::atomic< std::uint16_t > g_last_close_code{ 0 };
56 std::atomic< std::uint16_t > g_message_handled{ 0 };
57
58 //
59 // a_server_t
60 //
61
62 //! Agent running ws server logic.
63 class a_server_t
64 : public so_5::agent_t
65 {
66 using so_base_type_t = so_5::agent_t;
67
68 public:
a_server_t(context_t ctx,so_5::mchain_t server_started_mchain)69 a_server_t(
70 context_t ctx,
71 so_5::mchain_t server_started_mchain )
72 : so_base_type_t{ ctx }
73 , m_server_started_mchain( std::move(server_started_mchain) )
74 , m_http_server{
75 restinio::own_io_context(),
__anon6e6555c40102( )76 [this]( auto & settings ){
77 auto mbox = this->so_direct_mbox();
78 settings
79 .port( utest_default_port() )
80 .address( "127.0.0.1" )
81 .request_handler(
82 [mbox]( auto req ){
83 if( restinio::http_connection_header_t::upgrade ==
84 req->header().connection() )
85 {
86 ++g_message_handled;
87 so_5::send< upgrade_request_t >( mbox, std::move( req ) );
88
89 return restinio::request_accepted();
90 }
91
92 return restinio::request_rejected();
93 } );
94 } }
95 , m_other_thread{ m_http_server }
96 {
97 g_last_close_code = 0;
98 g_message_handled = 0;
99 }
100
101 virtual void
so_define_agent()102 so_define_agent() override
103 {
104 so_subscribe_self()
105 .event( &a_server_t::evt_upgrade_request )
106 .event( &a_server_t::evt_ws_message );
107 }
108
109 virtual void
so_evt_start()110 so_evt_start() override
111 {
112 m_other_thread.run();
113 so_5::send<server_started_t>( m_server_started_mchain );
114 }
115
116 virtual void
so_evt_finish()117 so_evt_finish() override
118 {
119 m_ws.reset();
120 m_other_thread.stop_and_join();
121 }
122
123 private:
124 void
evt_upgrade_request(const upgrade_request_t & msg)125 evt_upgrade_request( const upgrade_request_t & msg )
126 {
127 auto req = msg.m_req;
128
129 m_ws =
130 rws::upgrade< traits_t >(
131 *req,
132 rws::activation_t::immediate,
133 [mbox = so_direct_mbox()]( auto /* ws_handle*/, rws::message_handle_t m ){
134 so_5::send< msg_ws_message_t >( mbox, m );
135 } );
136 }
137
138 void
evt_ws_message(const msg_ws_message_t & msg)139 evt_ws_message( const msg_ws_message_t & msg )
140 {
141 if( m_ws )
142 {
143 auto & req = *(msg.m_msg);
144
145 if( rws::opcode_t::text_frame == req.opcode() ||
146 rws::opcode_t::binary_frame == req.opcode() )
147 {
148 if( req.payload() == "close" )
149 {
150 m_ws->send_message(
151 rws::final_frame,
152 rws::opcode_t::connection_close_frame,
153 rws::status_code_to_bin( rws::status_code_t::normal_closure ) );
154 }
155 else if( req.payload() == "shutdown" )
156 {
157 m_ws->shutdown();
158 m_ws.reset();
159 }
160 else if( req.payload() == "kill" )
161 {
162 m_ws->kill();
163 m_ws.reset();
164 }
165 else
166 {
167 auto resp = req;
168 m_ws->send_message( resp );
169 }
170 }
171 else if( rws::opcode_t::ping_frame == req.opcode() )
172 {
173 auto resp = req;
174 resp.set_opcode( rws::opcode_t::pong_frame );
175 m_ws->send_message( resp );
176 }
177 // else if( rws::opcode_t::pong_frame == req.opcode() )
178 // {
179 // // ?
180 // }
181 else if( rws::opcode_t::connection_close_frame == req.opcode() )
182 {
183 g_last_close_code = (std::uint16_t)rws::status_code_from_bin( req.payload() );
184 std::cout << "CLOSE FRAME: " << g_last_close_code << std::endl;
185 m_ws.reset();
186 }
187 }
188 }
189
190 const so_5::mchain_t m_server_started_mchain;
191 http_server_t m_http_server;
192 other_work_thread_for_server_t<http_server_t> m_other_thread;
193 rws::ws_handle_t m_ws;
194 };
195
196 const std::string upgrade_request{
197 "GET /chat HTTP/1.1\r\n"
198 "Host: 127.0.0.1\r\n"
199 "Upgrade: websocket\r\n"
200 "Connection: Upgrade\r\n"
201 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
202 "Sec-WebSocket-Protocol: chat\r\n"
203 "Sec-WebSocket-Version: 1\r\n"
204 "User-Agent: unit-test\r\n"
205 "\r\n" };
206
207 class sobj_t
208 {
209 so_5::wrapped_env_t m_sobj;
210
211 static void
init(so_5::environment_t & env)212 init( so_5::environment_t & env )
213 {
214 auto server_started_mchain = so_5::create_mchain(env);
215 const auto binder_maker = [](auto & env) {
216 #if !defined(SO_5_VERSION) || SO_5_VERSION < SO_5_VERSION_MAKE(6ull, 0ull, 0ull)
217 return so_5::disp::active_obj::create_private_disp(env)->binder();
218 #else
219 return so_5::disp::active_obj::make_dispatcher(env).binder();
220 #endif
221 };
222
223 // Launch server as separate coop.
224 env.introduce_coop(
225 binder_maker(env),
226 [&]( so_5::coop_t & coop ) {
227 coop.make_agent< a_server_t >(server_started_mchain);
228 } );
229 // Wait acknowledgement about successful server start.
230 so_5::receive(
231 from(server_started_mchain)
232 .handle_n(1u)
233 .empty_timeout(std::chrono::seconds(5)),
234 [](so_5::mhood_t<server_started_t>) {});
235 }
236
237 public :
238 sobj_t( const sobj_t & ) = delete;
239 sobj_t( sobj_t && ) = delete;
240
sobj_t()241 sobj_t()
242 {
243 init( m_sobj.environment() );
244 }
245
246 void
stop_and_join()247 stop_and_join()
248 {
249 m_sobj.stop();
250 m_sobj.join();
251 }
252 };
253
254 template < typename Socket >
255 void
fragmented_send(Socket & socket,void * buf,std::size_t n)256 fragmented_send( Socket & socket, void * buf, std::size_t n )
257 {
258 const auto * b = static_cast< std::uint8_t * >( buf );
259 while( n-- )
260 {
261 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( b++, 1 ) );
262 if( 0 < n )
263 std::this_thread::sleep_for( std::chrono::milliseconds( n ) );
264 }
265 }
266
267 TEST_CASE( "Simple echo" , "[ws_connection][echo][normal_close]" )
268 {
269 sobj_t sobj;
270
271 do_with_socket(
__anon6e6555c40702( auto & socket, auto & )272 [&]( auto & socket, auto & /*io_context*/ ){
273 REQUIRE_NOTHROW(
274 restinio::asio_ns::write(
275 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
276 );
277
278 std::array< std::uint8_t, 1024 > data;
279
280 std::size_t len{ 0 };
281 REQUIRE_NOTHROW(
282 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
283 );
284
285 std::vector< std::uint8_t > msg_frame =
286 { 0x81, 0x85, 0xAA,0xBB,0xCC,0xDD,
287 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o' };
288 SECTION( "simple msg_frame")
289 {
290 REQUIRE_NOTHROW(
291 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
292 );
293 }
294 SECTION( "fragmentated msg_frame")
295 {
296 REQUIRE_NOTHROW(
297 fragmented_send( socket, msg_frame.data(), msg_frame.size() )
298 );
299 }
300
301 REQUIRE_NOTHROW(
302 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
303 );
304
305 REQUIRE( 7 == len );
306 REQUIRE( 0x81 == data[ 0 ] );
307 REQUIRE( 0x05 == data[ 1 ] );
308 REQUIRE( 'H' == data[ 2 ] );
309 REQUIRE( 'e' == data[ 3 ] );
310 REQUIRE( 'l' == data[ 4 ] );
311 REQUIRE( 'l' == data[ 5 ] );
312 REQUIRE( 'o' == data[ 6 ] );
313
314 std::vector< std::uint8_t > close_frame =
315 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xe8 };
316
317 REQUIRE_NOTHROW(
318 restinio::asio_ns::write(
319 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
320 );
321
322 REQUIRE_NOTHROW(
323 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
324 );
325 REQUIRE( 4 == len );
326 REQUIRE( 0x88 == data[ 0 ] );
327 REQUIRE( 0x02 == data[ 1 ] );
328 REQUIRE( 0x03 == data[ 2 ] );
329 REQUIRE( 0xe8 == data[ 3 ] );
330
331 restinio::asio_ns::error_code ec;
332 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
333 REQUIRE( ec );
334 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
335
336 } );
337
338 sobj.stop_and_join();
339
340 REQUIRE( 1000 == g_last_close_code );
341 }
342
343 TEST_CASE( "Ping" , "[ws_connection][ping][normal_close]" )
344 {
345 sobj_t sobj;
346
347 do_with_socket(
__anon6e6555c40802( auto & socket, auto & )348 [&]( auto & socket, auto & /*io_context*/ ){
349 REQUIRE_NOTHROW(
350 restinio::asio_ns::write(
351 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
352 );
353
354 std::array< std::uint8_t, 1024 > data;
355
356 std::size_t len{ 0 };
357 REQUIRE_NOTHROW(
358 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
359 );
360
361 std::vector< std::uint8_t > msg_frame =
362 { 0x89, 0x84, 0x0A,0xB0,0x0C,0xD0,
363 0x0A ^ 'P', 0xB0 ^ 'i', 0x0C ^ 'n', 0xD0 ^ 'g' };
364
365 SECTION( "simple msg_frame")
366 {
367 REQUIRE_NOTHROW(
368 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
369 );
370 }
371 SECTION( "fragmentated msg_frame")
372 {
373 REQUIRE_NOTHROW(
374 fragmented_send( socket, msg_frame.data(), msg_frame.size() )
375 );
376 }
377
378 REQUIRE_NOTHROW(
379 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
380 );
381
382 REQUIRE( 6 == len );
383 REQUIRE( 0x8A == data[ 0 ] );
384 REQUIRE( 0x04 == data[ 1 ] );
385 REQUIRE( 'P' == data[ 2 ] );
386 REQUIRE( 'i' == data[ 3 ] );
387 REQUIRE( 'n' == data[ 4 ] );
388 REQUIRE( 'g' == data[ 5 ] );
389
390 std::vector< std::uint8_t > close_frame =
391 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xe8 };
392
393 REQUIRE_NOTHROW(
394 restinio::asio_ns::write(
395 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
396 );
397
398 REQUIRE_NOTHROW(
399 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
400 );
401 REQUIRE( 4 == len );
402 REQUIRE( 0x88 == data[ 0 ] );
403 REQUIRE( 0x02 == data[ 1 ] );
404 REQUIRE( 0x03 == data[ 2 ] );
405 REQUIRE( 0xe8 == data[ 3 ] );
406
407 restinio::asio_ns::error_code ec;
408 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
409 REQUIRE( ec );
410 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
411 } );
412
413 sobj.stop_and_join();
414
415 REQUIRE( 1000 == g_last_close_code );
416 }
417
418 TEST_CASE( "Close" , "[ws_connection][close][normal_close]" )
419 {
420 sobj_t sobj;
421
422 do_with_socket(
__anon6e6555c40902( auto & socket, auto & )423 [&]( auto & socket, auto & /*io_context*/ ){
424
425 REQUIRE_NOTHROW(
426 restinio::asio_ns::write(
427 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
428 );
429
430 std::array< std::uint8_t, 1024 > data;
431
432 std::size_t len{ 0 };
433 REQUIRE_NOTHROW(
434 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
435 );
436
437 std::vector< std::uint8_t > msg_frame =
438 { 0x81, 0x85, 0x0A,0xB0,0x0C,0xD0,
439 0x0A ^ 'c', 0xB0 ^ 'l', 0x0C ^ 'o', 0xD0 ^ 's', 0x0A ^ 'e' };
440
441 SECTION( "simple msg_frame")
442 {
443 REQUIRE_NOTHROW(
444 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
445 );
446 }
447 SECTION( "fragmentated msg_frame")
448 {
449 REQUIRE_NOTHROW(
450 fragmented_send( socket, msg_frame.data(), msg_frame.size() )
451 );
452 }
453
454 REQUIRE_NOTHROW(
455 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
456 );
457
458 REQUIRE( 4 == len );
459 REQUIRE( 0x88 == data[ 0 ] );
460 REQUIRE( 0x02 == data[ 1 ] );
461 REQUIRE( 0x03 == data[ 2 ] );
462 REQUIRE( 0xe8 == data[ 3 ] );
463
464 std::vector< std::uint8_t > close_frame =
465 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xe8 };
466
467 REQUIRE_NOTHROW(
468 restinio::asio_ns::write(
469 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
470 );
471
472 restinio::asio_ns::error_code ec;
473 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
474 REQUIRE( 0 == len );
475 REQUIRE( ec );
476 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
477 } );
478
479 sobj.stop_and_join();
480
481 // User initiates close.
482 REQUIRE( 0 == g_last_close_code );
483 }
484
485 TEST_CASE( "Shutdown" , "[ws_connection][shutdown][normal_close]" )
486 {
487 sobj_t sobj;
488
489 do_with_socket(
__anon6e6555c40a02( auto & socket, auto & )490 [&]( auto & socket, auto & /*io_context*/ ){
491 REQUIRE_NOTHROW(
492 restinio::asio_ns::write(
493 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
494 );
495
496 std::array< std::uint8_t, 1024 > data;
497
498 std::size_t len{ 0 };
499 REQUIRE_NOTHROW(
500 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
501 );
502
503 std::vector< std::uint8_t > msg_frame =
504 { 0x81, 0x88, 0x0A,0xB0,0x0C,0xD0,
505 0x0A ^ 's', 0xB0 ^ 'h', 0x0C ^ 'u', 0xD0 ^ 't',
506 0x0A ^ 'd', 0xB0 ^ 'o', 0x0C ^ 'w', 0xD0 ^ 'n' };
507
508 REQUIRE_NOTHROW(
509 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
510 );
511
512 REQUIRE_NOTHROW(
513 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
514 );
515
516 REQUIRE( 4 == len );
517 REQUIRE( 0x88 == data[ 0 ] );
518 REQUIRE( 0x02 == data[ 1 ] );
519 REQUIRE( 0x03 == data[ 2 ] );
520 REQUIRE( 0xe8 == data[ 3 ] );
521
522 std::vector< std::uint8_t > close_frame =
523 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xe8 };
524
525 SECTION( "simple close_frame")
526 {
527 REQUIRE_NOTHROW(
528 restinio::asio_ns::write(
529 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
530 );
531 }
532 SECTION( "fragmentated close_frame")
533 {
534 REQUIRE_NOTHROW(
535 fragmented_send( socket, close_frame.data(), close_frame.size() )
536 );
537 }
538
539 restinio::asio_ns::error_code ec;
540 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
541 REQUIRE( 0 == len );
542 REQUIRE( ec );
543 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
544 } );
545
546 sobj.stop_and_join();
547
548 // User initiates close via shutdown.
549 REQUIRE( 0 == g_last_close_code );
550 }
551
552 TEST_CASE( "Kill" , "[ws_connection][kill][abnormal_close]" )
553 {
554 sobj_t sobj;
555
556 do_with_socket(
__anon6e6555c40b02( auto & socket, auto & )557 [&]( auto & socket, auto & /*io_context*/ ){
558 REQUIRE_NOTHROW(
559 restinio::asio_ns::write(
560 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
561 );
562
563 std::array< std::uint8_t, 1024 > data;
564
565 std::size_t len{ 0 };
566 REQUIRE_NOTHROW(
567 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
568 );
569
570 std::vector< std::uint8_t > msg_frame =
571 { 0x81, 0x84, 0x0A,0xB0,0x0C,0xD0,
572 0x0A ^ 'k', 0xB0 ^ 'i', 0x0C ^ 'l', 0xD0 ^ 'l' };
573
574 SECTION( "simple msg_frame")
575 {
576 REQUIRE_NOTHROW(
577 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
578 );
579 }
580 SECTION( "fragmentated msg_frame")
581 {
582 REQUIRE_NOTHROW(
583 fragmented_send( socket, msg_frame.data(), msg_frame.size() )
584 );
585 }
586
587 restinio::asio_ns::error_code ec;
588 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
589 REQUIRE( 0 == len );
590 REQUIRE( ec );
591 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
592 } );
593
594 sobj.stop_and_join();
595
596 REQUIRE( 0 == g_last_close_code );
597 }
598
599 TEST_CASE( "Invalid header", "[ws_connection][error_close]" )
600 {
601 sobj_t sobj;
602
603 do_with_socket(
__anon6e6555c40c02( auto & socket, auto & )604 [&]( auto & socket, auto & /*io_context*/ ){
605 REQUIRE_NOTHROW(
606 restinio::asio_ns::write(
607 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
608 );
609
610 std::array< std::uint8_t, 1024 > data;
611
612 std::size_t len{ 0 };
613 REQUIRE_NOTHROW(
614 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
615 );
616
617 std::vector< std::uint8_t > msg_frame;
618 SECTION( "No mask" )
619 {
620 msg_frame.assign( { 0x81, 0x05, 'H', 'e', 'l', 'l', 'o'} );
621 }
622 SECTION( "No rsv1" )
623 {
624 msg_frame.assign(
625 { 0x81 + 0x40, 0x85, 0xAA,0xBB,0xCC,0xDD,
626 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
627 }
628 SECTION( "No rsv2" )
629 {
630 msg_frame.assign(
631 { 0x81 + 0x20, 0x85, 0xAA,0xBB,0xCC,0xDD,
632 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
633 }
634 SECTION( "No rsv3" )
635 {
636 msg_frame.assign(
637 { 0x81 + 0x10, 0x85, 0xAA,0xBB,0xCC,0xDD,
638 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
639 }
640
641 REQUIRE_NOTHROW(
642 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
643 );
644
645 // Validation would fail, close frame in return.
646 REQUIRE_NOTHROW(
647 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
648 );
649 REQUIRE( 4 == len );
650 REQUIRE( 0x88 == data[ 0 ] );
651 REQUIRE( 0x02 == data[ 1 ] );
652 REQUIRE( (1002 >> 8) == data[ 2 ] );
653 REQUIRE( (1002 & 0xFF) == data[ 3 ] );
654
655 restinio::asio_ns::error_code ec;
656 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
657 REQUIRE( ec );
658 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
659 } );
660
661 sobj.stop_and_join();
662
663 REQUIRE( 1002 == g_last_close_code );
664 }
665
666 TEST_CASE( "Invalid payload" , "[ws_connection][error_close]" )
667 {
668 sobj_t sobj;
669
670 do_with_socket(
__anon6e6555c40d02( auto & socket, auto & )671 [&]( auto & socket, auto & /*io_context*/ ){
672
673 REQUIRE_NOTHROW(
674 restinio::asio_ns::write(
675 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
676 );
677
678 std::array< std::uint8_t, 1024 > data;
679
680 std::size_t len{ 0 };
681 REQUIRE_NOTHROW(
682 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
683 );
684
685 std::vector< std::uint8_t > msg_frame =
686 { 0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 'H', 'e', 'l', 'l', 'o' };
687
688 SECTION( "simple msg_frame")
689 {
690 REQUIRE_NOTHROW(
691 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
692 );
693 }
694 SECTION( "fragmentated msg_frame")
695 {
696 REQUIRE_NOTHROW(
697 fragmented_send( socket, msg_frame.data(), msg_frame.size() )
698 );
699 }
700
701 // Validation would fail, so no data in return.
702 REQUIRE_NOTHROW(
703 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
704 );
705 REQUIRE( 4 == len );
706
707 // TODO: какие должны быть байты?
708 REQUIRE( 0x88 == data[ 0 ] );
709 REQUIRE( 0x02 == data[ 1 ] );
710 REQUIRE( (1007 >> 8) == data[ 2 ] );
711 REQUIRE( (1007 & 0xFF) == data[ 3 ] );
712
713 std::vector< std::uint8_t > close_frame =
714 { 0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xef };
715
716 REQUIRE_NOTHROW(
717 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
718 );
719
720 restinio::asio_ns::error_code ec;
721 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
722 REQUIRE( 0 == len );
723 REQUIRE( ec );
724 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
725 } );
726
727 sobj.stop_and_join();
728
729 REQUIRE( 1007 == g_last_close_code );
730 }
731
732 TEST_CASE( "Connection lost" , "[ws_connection][error_close][connection_lost]" )
733 {
734 sobj_t sobj;
735
736 do_with_socket(
__anon6e6555c40e02( auto & socket, auto & )737 [&]( auto & socket, auto & /*io_context*/ ){
738
739 REQUIRE_NOTHROW(
740 restinio::asio_ns::write(
741 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
742 );
743
744 std::array< std::uint8_t, 1024 > data;
745
746 std::size_t len{ 0 };
747 REQUIRE_NOTHROW(
748 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
749 );
750
751 std::vector< std::uint8_t > msg_frame =
752 { 0x81, 0x85, 0xAA,0xBB,0xCC,0xDD,
753 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o' };
754
755 SECTION( "reset before msg")
756 {
757 std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
758 // Do nothing.
759 }
760 SECTION( "after header first byte")
761 {
762 REQUIRE_NOTHROW(
763 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), 1 ) )
764 );
765 }
766 SECTION( "after header second byte")
767 {
768 REQUIRE_NOTHROW(
769 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), 2 ) )
770 );
771 }
772 SECTION( "in the middle of mask")
773 {
774 REQUIRE_NOTHROW(
775 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), 4 ) )
776 );
777 }
778 SECTION( "after mask before payload")
779 {
780 REQUIRE_NOTHROW(
781 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), 6 ) )
782 );
783 }
784 SECTION( "in the middle of the payload")
785 {
786 REQUIRE_NOTHROW(
787 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), 8 ) )
788 );
789 }
790
791 socket.close();
792
793 } );
794
795 // Give sobjectizer some time to run.
796 std::this_thread::sleep_for( std::chrono::milliseconds( 500 ) );
797 sobj.stop_and_join();
798
799
800 REQUIRE( 1006 == g_last_close_code );
801 }
802
803 TEST_CASE( "Invalid opcode" , "[ws_connection][error_close]" )
804 {
805 sobj_t sobj;
806
807 do_with_socket(
__anon6e6555c40f02( auto & socket, auto & )808 [&]( auto & socket, auto & /*io_context*/ ){
809 REQUIRE_NOTHROW(
810 restinio::asio_ns::write(
811 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
812 );
813
814 std::array< std::uint8_t, 1024 > data;
815
816 std::size_t len{ 0 };
817 REQUIRE_NOTHROW(
818 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
819 );
820
821 std::vector< std::uint8_t > msg_frame;
822 SECTION( "opcode 0x03" )
823 {
824 msg_frame.assign(
825 { 0x83, 0x85, 0xAA,0xBB,0xCC,0xDD,
826 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
827 }
828 SECTION( "opcode 0x04" )
829 {
830 msg_frame.assign(
831 { 0x84, 0x85, 0xAA,0xBB,0xCC,0xDD,
832 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
833 }
834 SECTION( "opcode 0x05" )
835 {
836 msg_frame.assign(
837 { 0x85, 0x85, 0xAA,0xBB,0xCC,0xDD,
838 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
839 }
840 SECTION( "opcode 0x06" )
841 {
842 msg_frame.assign(
843 { 0x86, 0x85, 0xAA,0xBB,0xCC,0xDD,
844 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
845 }
846 SECTION( "opcode 0x07" )
847 {
848 msg_frame.assign(
849 { 0x87, 0x85, 0xAA,0xBB,0xCC,0xDD,
850 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
851 }
852 SECTION( "opcode 0x0B" )
853 {
854 msg_frame.assign(
855 { 0x8B, 0x85, 0xAA,0xBB,0xCC,0xDD,
856 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
857 }
858 SECTION( "opcode 0x0C" )
859 {
860 msg_frame.assign(
861 { 0x8C, 0x85, 0xAA,0xBB,0xCC,0xDD,
862 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
863 }
864 SECTION( "opcode 0x0D" )
865 {
866 msg_frame.assign(
867 { 0x8D, 0x85, 0xAA,0xBB,0xCC,0xDD,
868 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
869 }
870 SECTION( "opcode 0x0E" )
871 {
872 msg_frame.assign(
873 { 0x8E, 0x85, 0xAA,0xBB,0xCC,0xDD,
874 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
875 }
876 SECTION( "opcode 0x0F" )
877 {
878 msg_frame.assign(
879 { 0x8F, 0x85, 0xAA,0xBB,0xCC,0xDD,
880 0xAA ^ 'H', 0xBB ^ 'e', 0xCC ^ 'l', 0xDD ^ 'l', 0xAA ^ 'o'} );
881 }
882
883 REQUIRE_NOTHROW(
884 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
885 );
886
887 // Validation would fail, close frame in return.
888 REQUIRE_NOTHROW(
889 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
890 );
891 REQUIRE( 4 == len );
892 REQUIRE( 0x88 == data[ 0 ] );
893 REQUIRE( 0x02 == data[ 1 ] );
894 REQUIRE( (1002 >> 8) == data[ 2 ] );
895 REQUIRE( (1002 & 0xFF) == data[ 3 ] );
896
897 restinio::asio_ns::error_code ec;
898 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
899 REQUIRE( ec );
900 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
901 } );
902
903 sobj.stop_and_join();
904
905 REQUIRE( 1002 == g_last_close_code );
906 }
907
908 TEST_CASE( "Invalid payload, close on first err 1" , "[ws_connection][echo][normal_close]" )
909 {
910 sobj_t sobj;
911
912 do_with_socket(
__anon6e6555c41002( auto & socket, auto & )913 [&]( auto & socket, auto & /*io_context*/ ){
914 REQUIRE_NOTHROW(
915 restinio::asio_ns::write(
916 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
917 );
918
919 std::array< std::uint8_t, 1024 > data;
920
921 std::size_t len{ 0 };
922 REQUIRE_NOTHROW(
923 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
924 );
925
926 std::vector< std::uint8_t > msg_frame =
927 { 0x81, 0x80 | 0x10, 0xAA,0xAA,0xAA,0xAA,
928 0xAA ^ '1', 0xAA ^ '2', 0xAA ^ '3', 0xAA ^ '4',
929 0xAA ^ '5', 0xAA ^ '6', 0xAA ^ '7', 0xAA ^ '8',
930 0xAA ^ '9', 0xAA ^ 'A', 0xAA ^ 'B', 0xAA ^ 'C',
931 0xAA ^ 'D', 0xAA ^ 'E', 0xAA ^ 'F', 0xAA ^ '0' };
932
933 std::size_t indx = 6;
934 SECTION( "pos 0")
935 {
936 indx += 0;
937 }
938 SECTION( "pos 2")
939 {
940 indx += 2;
941 }
942 SECTION( "pos 4")
943 {
944 indx += 4;
945 }
946 SECTION( "pos 8")
947 {
948 indx += 8;
949 }
950 SECTION( "pos 15")
951 {
952 indx += 15;
953 }
954 msg_frame[ indx ] = 0xFF ^ 0xAA;
955
956 REQUIRE_NOTHROW(
957 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), indx ) )
958 );
959
960 // Now error:
961 REQUIRE_NOTHROW(
962 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data() + indx, 1 ) )
963 );
964
965 REQUIRE_NOTHROW(
966 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
967 );
968 REQUIRE( 4 == len );
969 REQUIRE( 0x88 == data[ 0 ] );
970 REQUIRE( 0x02 == data[ 1 ] );
971 REQUIRE( 0x03 == data[ 2 ] );
972 REQUIRE( 0xef == data[ 3 ] );
973
974 if( indx != msg_frame.size() )
975 {
976 REQUIRE_NOTHROW(
977 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data() + indx + 1, msg_frame.size() - indx - 1 ) )
978 );
979 }
980
981 std::vector< std::uint8_t > close_frame =
982 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xef };
983
984 REQUIRE_NOTHROW(
985 restinio::asio_ns::write(
986 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
987 );
988
989 restinio::asio_ns::error_code ec;
990 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
991 REQUIRE( ec );
992 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
993 } );
994
995 sobj.stop_and_join();
996
997 REQUIRE( 1007 == g_last_close_code );
998 REQUIRE( 1 == g_message_handled );
999 }
1000
1001 TEST_CASE( "Invalid payload, close on first err 2", "[ws_connection][echo][normal_close]" )
1002 {
1003 sobj_t sobj;
1004
1005 do_with_socket(
__anon6e6555c41102( auto & socket, auto & )1006 [&]( auto & socket, auto & /*io_context*/ ){
1007 REQUIRE_NOTHROW(
1008 restinio::asio_ns::write(
1009 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
1010 );
1011
1012 std::array< std::uint8_t, 1024 > data;
1013
1014 std::size_t len{ 0 };
1015 REQUIRE_NOTHROW(
1016 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
1017 );
1018
1019 std::vector< std::uint8_t > msg_frame =
1020 { 0x81, 0x80 | 0x10, 0xAA,0xAA,0xAA,0xAA,
1021 0xAA ^ '1', 0xAA ^ '2', 0xAA ^ '3', 0xAA ^ '4',
1022 0xAA ^ '5', 0xAA ^ '6', 0xAA ^ '7', 0xAA ^ '8',
1023 0xAA ^ '9', 0xAA ^ 'A', 0xAA ^ 'B', 0xAA ^ 'C',
1024 0xAA ^ 'D', 0xAA ^ 'E', 0xAA ^ 'F', 0xAA ^ '0' };
1025
1026 std::size_t indx = 6;
1027 SECTION( "pos 0")
1028 {
1029 indx += 0;
1030 }
1031 SECTION( "pos 2")
1032 {
1033 indx += 2;
1034 }
1035 SECTION( "pos 4")
1036 {
1037 indx += 4;
1038 }
1039 SECTION( "pos 8")
1040 {
1041 indx += 8;
1042 }
1043 SECTION( "pos 15")
1044 {
1045 indx += 15;
1046 }
1047 unsigned char c = 0xFF ^ 0xAA;
1048
1049 REQUIRE_NOTHROW(
1050 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), indx ) )
1051 );
1052
1053 // Now error:
1054 REQUIRE_NOTHROW(
1055 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( &c, 1 ) )
1056 );
1057
1058 REQUIRE_NOTHROW(
1059 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
1060 );
1061 REQUIRE( 4 == len );
1062 REQUIRE( 0x88 == data[ 0 ] );
1063 REQUIRE( 0x02 == data[ 1 ] );
1064 REQUIRE( 0x03 == data[ 2 ] );
1065 REQUIRE( 0xef == data[ 3 ] );
1066
1067 if( indx != msg_frame.size() )
1068 {
1069 REQUIRE_NOTHROW(
1070 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data() + indx + 1, msg_frame.size() - indx - 1 ) )
1071 );
1072 }
1073
1074 // Send one more text-frame (valid one).
1075 REQUIRE_NOTHROW(
1076 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
1077 );
1078
1079 std::vector< std::uint8_t > close_frame =
1080 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xef };
1081
1082 REQUIRE_NOTHROW(
1083 restinio::asio_ns::write(
1084 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
1085 );
1086
1087 restinio::asio_ns::error_code ec;
1088 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
1089 REQUIRE( ec );
1090 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
1091 } );
1092
1093 sobj.stop_and_join();
1094
1095 REQUIRE( 1007 == g_last_close_code );
1096 REQUIRE( 1 == g_message_handled );
1097 }
1098
1099
1100 TEST_CASE( "Invalid payload, close on first err 3", "[ws_connection][echo][normal_close]" )
1101 {
1102 sobj_t sobj;
1103
1104 do_with_socket(
__anon6e6555c41202( auto & socket, auto & )1105 [&]( auto & socket, auto & /*io_context*/ ){
1106 REQUIRE_NOTHROW(
1107 restinio::asio_ns::write(
1108 socket, restinio::asio_ns::buffer( upgrade_request.data(), upgrade_request.size() ) )
1109 );
1110
1111 std::array< std::uint8_t, 1024 > data;
1112
1113 std::size_t len{ 0 };
1114 REQUIRE_NOTHROW(
1115 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
1116 );
1117
1118 std::vector< std::uint8_t > msg_frame =
1119 { 0x81, 0x80 | 0x10, 0xAA,0xAA,0xAA,0xAA,
1120 0xAA ^ '1', 0xAA ^ '2', 0xAA ^ '3', 0xAA ^ '4',
1121 0xAA ^ '5', 0xAA ^ '6', 0xAA ^ '7', 0xAA ^ '8',
1122 0xAA ^ '9', 0xAA ^ 'A', 0xAA ^ 'B', 0xAA ^ 'C',
1123 0xAA ^ 'D', 0xAA ^ 'E', 0xAA ^ 'F', 0xAA ^ '0' };
1124
1125 std::size_t indx = 6;
1126 SECTION( "pos 0")
1127 {
1128 indx += 0;
1129 }
1130 SECTION( "pos 2")
1131 {
1132 indx += 2;
1133 }
1134 SECTION( "pos 4")
1135 {
1136 indx += 4;
1137 }
1138 SECTION( "pos 8")
1139 {
1140 indx += 8;
1141 }
1142 SECTION( "pos 15")
1143 {
1144 indx += 15;
1145 }
1146 msg_frame[ indx ] = 0xFF ^ 0xAA;
1147
1148 REQUIRE_NOTHROW(
1149 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), indx ) )
1150 );
1151
1152 // Now error:
1153 REQUIRE_NOTHROW(
1154 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data() + indx, 1 ) )
1155 );
1156
1157 REQUIRE_NOTHROW(
1158 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ) )
1159 );
1160 REQUIRE( 4 == len );
1161 REQUIRE( 0x88 == data[ 0 ] );
1162 REQUIRE( 0x02 == data[ 1 ] );
1163 REQUIRE( 0x03 == data[ 2 ] );
1164 REQUIRE( 0xef == data[ 3 ] );
1165
1166 if( indx != msg_frame.size() )
1167 {
1168 REQUIRE_NOTHROW(
1169 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data() + indx + 1, msg_frame.size() - indx - 1 ) )
1170 );
1171 }
1172
1173 // Send one more text-frame (invalid one).
1174 REQUIRE_NOTHROW(
1175 restinio::asio_ns::write( socket, restinio::asio_ns::buffer( msg_frame.data(), msg_frame.size() ) )
1176 );
1177
1178 std::vector< std::uint8_t > close_frame =
1179 {0x88, 0x82, 0xFF,0xFF,0xFF,0xFF, 0xFF ^ 0x03, 0xFF ^ 0xef };
1180
1181 REQUIRE_NOTHROW(
1182 restinio::asio_ns::write(
1183 socket, restinio::asio_ns::buffer( close_frame.data(), close_frame.size() ) )
1184 );
1185
1186 restinio::asio_ns::error_code ec;
1187 len = socket.read_some( restinio::asio_ns::buffer( data.data(), data.size() ), ec );
1188 REQUIRE( ec );
1189 REQUIRE( restinio::asio_ns::error::eof == ec.value() );
1190 } );
1191
1192 sobj.stop_and_join();
1193
1194 REQUIRE( 1007 == g_last_close_code );
1195 REQUIRE( 1 == g_message_handled ); // close frame only.
1196 }
1197
1198