11#include " catch2/catch_all.hpp"
22
33#include " crow.h"
4+ #include < cstddef>
45
56using namespace std ;
67using namespace crow ;
@@ -649,4 +650,100 @@ TEST_CASE("mirror_websocket_subprotocols", "[websocket]")
649650 }
650651
651652 app.stop ();
652- }
653+ }
654+
655+ TEST_CASE (" multithreaded_websockets_open_close" , " [websocket]" )
656+ {
657+ static std::string http_message =
658+ " GET /ws HTTP/1.1\r\n "
659+ " Connection: keep-alive, Upgrade\r\n "
660+ " upgrade: websocket\r\n "
661+ " Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n "
662+ " Sec-WebSocket-Version: 13\r\n "
663+ " Host: localhost\r\n "
664+ " \r\n " ;
665+
666+ CROW_LOG_INFO << " Setting up app!\n " ;
667+ SimpleApp app;
668+
669+ CROW_WEBSOCKET_ROUTE (app, " /ws" )
670+ .onaccept ([&](const crow::request& req, void **) {
671+ CROW_LOG_INFO << " Accepted websocket with URL " << req.url ;
672+ return true ;
673+ })
674+ .onopen ([&](websocket::connection&) {
675+ CROW_LOG_INFO << " Connected websocket" ;
676+ })
677+ .onmessage ([&](websocket::connection& conn, const std::string& message, bool ) {
678+ CROW_LOG_INFO << " Message is \" " << message << ' \" ' ;
679+ if (message == " quit-default" )
680+ conn.close ();
681+ else if (message == " quit-custom" )
682+ conn.close (" custom" , crow::websocket::StartStatusCodesForPrivateUse + 10u );
683+ })
684+ .onclose ([&](websocket::connection& conn, const std::string&, uint16_t ) {
685+ // There should just be one connection
686+ CHECK_FALSE (conn.get_remote_ip ().empty ());
687+ CROW_LOG_INFO << " Closing websocket" ;
688+ });
689+
690+ app.validate ();
691+
692+ CROW_LOG_WARNING << " Starting app!\n " ;
693+ auto _ = app.bindaddr (LOCALHOST_ADDRESS).port (45453 ).run_async ();
694+ app.wait_for_server_start ();
695+ CROW_LOG_WARNING << " App started!\n " ;
696+ asio::io_context ic;
697+
698+ const auto thread_function = [&]()
699+ {
700+ asio::ip::tcp::socket c (ic);
701+ c.connect (asio::ip::tcp::endpoint (
702+ asio::ip::make_address (LOCALHOST_ADDRESS), 45453 ));
703+
704+ CROW_LOG_WARNING << " Connected!\n " ;
705+
706+ char buf[2048 ];
707+
708+ // ----------Handshake----------
709+ {
710+ std::fill_n (buf, 2048 , 0 );
711+ c.send (asio::buffer (http_message));
712+
713+ c.receive (asio::buffer (buf, 2048 ));
714+ std::this_thread::sleep_for (std::chrono::milliseconds (5 ));
715+ }
716+
717+ // ----------Close websocket----------
718+ std::fill_n (buf, 2048 , 0 );
719+ // Close message with, len = 2, status code = 1000
720+ char close_message[5 ](" \x88\x02\x03\xE8 " );
721+ c.send (asio::buffer (close_message, 4 ));
722+ c.receive (asio::buffer (buf, 2048 ));
723+ std::this_thread::sleep_for (std::chrono::milliseconds (5 ));
724+ CHECK ((int )(unsigned char )buf[0 ] == 0x88 );
725+ CHECK ((int )(unsigned char )buf[1 ] == 0x02 );
726+ CHECK ((int )(unsigned char )buf[2 ] == 0x03 );
727+ CHECK ((int )(unsigned char )buf[3 ] == 0xE8 );
728+ };
729+
730+ const auto multiple_run_thread_function = [&](const std::size_t count){
731+ for (std::size_t i = 0 ; i < count; ++i) {
732+ thread_function ();
733+ }
734+ };
735+
736+ constexpr std::size_t threads_count = 3 ;
737+ constexpr std::size_t thread_run = 10 ;
738+
739+ std::vector<std::thread> threads;
740+ for (std::size_t i = 0 ; i < threads_count; ++i) {
741+ threads.emplace_back (multiple_run_thread_function, thread_run);
742+ }
743+ for (std::thread& thread : threads) {
744+ thread.join ();
745+ }
746+
747+ CROW_LOG_WARNING << " Stopping app!\n " ;
748+ app.stop ();
749+ }
0 commit comments