diff --git a/docs/build.md b/docs/build.md index 7e97b976..e9cd9464 100644 --- a/docs/build.md +++ b/docs/build.md @@ -52,6 +52,11 @@ Options for building: * `-DUSE_WS=1` will build the ws interactive command line tool * `-DUSE_TEST=1` will build the unittest +The default CTest suite uses loopback servers and in-memory TLS sessions; it does +not require Internet access. TLS server tests generate a local CA and verify the +client connection against it. The legacy external HTTP client tests in +`test/IXHttpClientTest.cpp` are not part of the default suite. + If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies. It is also possible to externally include the project, so that everything is fetched over the wire when you build like so: diff --git a/ixwebsocket/IXSocketMbedTLS.cpp b/ixwebsocket/IXSocketMbedTLS.cpp index 0e7dbdf1..1020bc11 100644 --- a/ixwebsocket/IXSocketMbedTLS.cpp +++ b/ixwebsocket/IXSocketMbedTLS.cpp @@ -14,6 +14,7 @@ #include "IXNetSystem.h" #include "IXSocket.h" #include "IXSocketConnect.h" +#include #include #include @@ -367,6 +368,7 @@ namespace ix mbedtls_x509_crt_free(&_cert); mbedtls_pk_free(&_pkey); _mbedtlsInitialized = false; + _sendBuffer.clear(); // PSA crypto state is process-global; it is released in ix::uninitNetSystem(), // not per socket. @@ -377,21 +379,41 @@ namespace ix { std::lock_guard lock(_mutex); - std::ptrdiff_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte); - - if (res > 0) + if (nbyte != 0 && buf == nullptr) + { + Socket::setErrno(EINVAL); + return -1; + } + if (_sendBuffer.empty()) + { + if (nbyte == 0) return 0; + // Retain at most one TLS record, not the caller's growing send queue. + const size_t size = (std::min)(nbyte, size_t(16384)); + _sendBuffer.assign(buf, buf + size); + } + else if (nbyte < _sendBuffer.size()) { - return res; + Socket::setErrno(EINVAL); + return -1; } - else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) + + // WANT_READ/WRITE must be retried with the same pointer and length. + // The caller may append to or reallocate its queue between calls. + const int res = mbedtls_ssl_write(&_ssl, _sendBuffer.data(), _sendBuffer.size()); + if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) { Socket::setErrno(EWOULDBLOCK); return -1; } - else + + _sendBuffer.clear(); + if (res < 0) { + // A previous retry may have left the socket's wait status set. + Socket::setErrno(EIO); return -1; } + return res; } std::ptrdiff_t SocketMbedTLS::recv(void* buf, size_t nbyte) diff --git a/ixwebsocket/IXSocketMbedTLS.h b/ixwebsocket/IXSocketMbedTLS.h index d53ea95b..053a45d8 100644 --- a/ixwebsocket/IXSocketMbedTLS.h +++ b/ixwebsocket/IXSocketMbedTLS.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace ix { @@ -44,6 +45,7 @@ namespace ix virtual std::ptrdiff_t recv(void* buffer, size_t length) final; private: + friend struct SocketMbedTLSWriteTestAccess; mbedtls_ssl_context _ssl; mbedtls_ssl_config _conf; #if MBEDTLS_VERSION_MAJOR < 4 @@ -54,6 +56,7 @@ namespace ix mbedtls_x509_crt _cert; mbedtls_pk_context _pkey; + std::vector _sendBuffer; std::mutex _mutex; SocketTLSOptions _tlsOptions; bool _mbedtlsInitialized = false; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2337d845..750adb4c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,6 +52,7 @@ endif() if (USE_TLS AND USE_MBED_TLS) list(APPEND TEST_TARGET_NAMES IXSocketMbedTLSPSATest + IXSocketMbedTLSWriteTest ) endif() @@ -85,6 +86,10 @@ target_compile_definitions(ixwebsocket_test PRIVATE ${TEST_PROGRAMS_DEFINITIONS} target_link_libraries(ixwebsocket_test Catch2) target_link_libraries(ixwebsocket_test ixwebsocket) +if (WIN32 AND USE_TLS AND USE_MBED_TLS) + # Required by Mbed TLS builds that use the Windows CNG entropy source. + target_link_libraries(ixwebsocket_test bcrypt) +endif() foreach(TEST_TARGET_NAME ${TEST_TARGET_NAMES}) add_executable(${TEST_TARGET_NAME} diff --git a/test/IXDNSLookupTest.cpp b/test/IXDNSLookupTest.cpp index 8f6c88e1..6696e4b9 100644 --- a/test/IXDNSLookupTest.cpp +++ b/test/IXDNSLookupTest.cpp @@ -14,9 +14,9 @@ using namespace ix; TEST_CASE("dns", "[net]") { - SECTION("Test resolving a known hostname") + SECTION("Test resolving a loopback address") { - auto dnsLookup = std::make_shared("www.google.com", 80); + auto dnsLookup = std::make_shared("127.0.0.1", 80); std::string errMsg; auto res = dnsLookup->resolve(errMsg, [] { return false; }); @@ -24,9 +24,9 @@ TEST_CASE("dns", "[net]") REQUIRE(res != nullptr); } - SECTION("Test resolving a non-existing hostname") + SECTION("Test resolving an invalid IPv6 literal") { - auto dnsLookup = std::make_shared("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); + auto dnsLookup = std::make_shared("::invalid", 80); std::string errMsg; auto res = dnsLookup->resolve(errMsg, [] { return false; }); @@ -34,9 +34,9 @@ TEST_CASE("dns", "[net]") REQUIRE(res == nullptr); } - SECTION("Test resolving a good hostname, with cancellation") + SECTION("Test resolving a loopback address, with cancellation") { - auto dnsLookup = std::make_shared("www.google.com", 80, 1); + auto dnsLookup = std::make_shared("127.0.0.1", 80, 1); std::string errMsg; // The callback returning true means we are requesting cancellation diff --git a/test/IXHttpServerTest.cpp b/test/IXHttpServerTest.cpp index a5110663..8c208cb9 100644 --- a/test/IXHttpServerTest.cpp +++ b/test/IXHttpServerTest.cpp @@ -59,8 +59,13 @@ TEST_CASE("http server", "[httpd]") REQUIRE(response->errorCode == HttpErrorCode::Ok); REQUIRE(response->statusCode == 200); +#ifdef IXWEBSOCKET_USE_ZLIB REQUIRE(response->headers["Accept-Encoding"] == "gzip"); REQUIRE(response->headers["Content-Encoding"] == "gzip"); +#else + REQUIRE(response->headers["Accept-Encoding"].empty()); + REQUIRE(response->headers["Content-Encoding"].empty()); +#endif server.stop(); } @@ -121,7 +126,8 @@ TEST_CASE("http server redirection", "[httpd_redirect]") { int port = getFreePort(); ix::HttpServer server(port, "127.0.0.1"); - server.makeRedirectServer("http://example.com"); + const std::string targetUrl("http://127.0.0.1:1/not-followed"); + server.makeRedirectServer(targetUrl); auto res = server.listen(); REQUIRE(res.first); @@ -163,16 +169,29 @@ TEST_CASE("http server redirection", "[httpd_redirect]") REQUIRE(response->errorCode == HttpErrorCode::Ok); REQUIRE(response->statusCode == 301); - REQUIRE(response->headers["Location"] == "http://example.com"); + REQUIRE(response->headers["Location"] == targetUrl); server.stop(); } SECTION("Connect to a local HTTP server, with redirection enabled, but we do follow redirects") { + ix::HttpServer target(getFreePort(), "127.0.0.1"); + const std::string expectedBody("local redirect target"); + target.setOnConnectionCallback( + [expectedBody](HttpRequestPtr request, std::shared_ptr) -> HttpResponsePtr { + if (request->uri != "/redirect-target") + return std::make_shared(404, "NOT FOUND"); + return std::make_shared( + 200, "OK", HttpErrorCode::Ok, WebSocketHttpHeaders(), expectedBody); + }); + REQUIRE(target.listen().first); + target.start(); + int port = getFreePort(); ix::HttpServer server(port, "127.0.0.1"); - server.makeRedirectServer("http://www.google.com"); + server.makeRedirectServer("http://127.0.0.1:" + std::to_string(target.getPort()) + + "/redirect-target"); auto res = server.listen(); REQUIRE(res.first); @@ -214,7 +233,9 @@ TEST_CASE("http server redirection", "[httpd_redirect]") REQUIRE(response->errorCode == HttpErrorCode::Ok); REQUIRE(response->statusCode == 200); + REQUIRE(response->body == expectedBody); server.stop(); + target.stop(); } } diff --git a/test/IXSocketConnectTest.cpp b/test/IXSocketConnectTest.cpp index b00886d0..36fd68d1 100644 --- a/test/IXSocketConnectTest.cpp +++ b/test/IXSocketConnectTest.cpp @@ -26,11 +26,10 @@ TEST_CASE("socket_connect", "[net]") REQUIRE(fd != -1); } - SECTION("Test connecting to a non-existing hostname") + SECTION("Test connecting to an invalid IPv6 literal") { std::string errMsg; - std::string hostname("12313lhjlkjhopiupoijlkasdckljqwehrlkqjwehraospidcuaposidcasdc"); - int fd = SocketConnect::connect(hostname, 80, errMsg, [] { return false; }); + int fd = SocketConnect::connect("::invalid", 80, errMsg, [] { return false; }); std::cerr << "Error message: " << errMsg << std::endl; REQUIRE(fd == -1); } diff --git a/test/IXSocketMbedTLSWriteTest.cpp b/test/IXSocketMbedTLSWriteTest.cpp new file mode 100644 index 00000000..dee1f515 --- /dev/null +++ b/test/IXSocketMbedTLSWriteTest.cpp @@ -0,0 +1,336 @@ +/* + * IXSocketMbedTLSWriteTest.cpp + * + * Exercise the real TLS adapter with deterministic, in-memory backpressure. + */ + +#ifdef IXWEBSOCKET_USE_MBED_TLS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if MBEDTLS_VERSION_MAJOR < 4 +#include +#include +#endif +#include +#include +#include +#include + +#include + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) && \ + defined(MBEDTLS_PSA_CRYPTO_C) +#include + +namespace ix +{ + struct SocketMbedTLSWriteTestAccess + { + static void install(SocketMbedTLS& socket, mbedtls_ssl_context& session) + { + socket.initMBedTLS(); + std::swap(socket._ssl, session); + } + + static int recordSize(const SocketMbedTLS& socket) + { + return mbedtls_ssl_get_max_out_record_payload(&socket._ssl); + } + }; +} + +namespace +{ + struct MemoryPeer + { + mbedtls_ssl_context ssl; + mbedtls_ssl_config config; + MemoryPeer* other = nullptr; + std::deque incoming; + int writesBeforeBlocking = -1; + int blockedResult = MBEDTLS_ERR_SSL_WANT_WRITE; + bool failWrites = false; + + MemoryPeer() + { + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&config); + } + + ~MemoryPeer() + { + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&config); + } + + MemoryPeer(const MemoryPeer&) = delete; + MemoryPeer& operator=(const MemoryPeer&) = delete; + + static int random(void*, unsigned char* output, size_t size) + { + return psa_generate_random(output, size) == PSA_SUCCESS ? 0 : -1; + } + + static int send(void* context, const unsigned char* data, size_t size) + { + auto& peer = *static_cast(context); + if (peer.failWrites) return MBEDTLS_ERR_NET_SEND_FAILED; + if (peer.writesBeforeBlocking == 0) return peer.blockedResult; + if (peer.writesBeforeBlocking > 0) --peer.writesBeforeBlocking; + peer.other->incoming.insert(peer.other->incoming.end(), data, data + size); + return static_cast(size); + } + + static int receive(void* context, unsigned char* data, size_t size) + { + auto& peer = *static_cast(context); + if (peer.incoming.empty()) return MBEDTLS_ERR_SSL_WANT_READ; + const size_t count = (std::min)(size, peer.incoming.size()); + for (size_t i = 0; i < count; ++i) + { + data[i] = peer.incoming.front(); + peer.incoming.pop_front(); + } + return static_cast(count); + } + + void configure(bool server) + { + const int* supported = mbedtls_ssl_list_ciphersuites(); + while (*supported && *supported != MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256) + ++supported; + if (!*supported) SKIP("TLS 1.2 PSK AES-GCM ciphersuite is not enabled"); + REQUIRE(psa_crypto_init() == PSA_SUCCESS); + REQUIRE(mbedtls_ssl_config_defaults(&config, + server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) == 0); +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + mbedtls_ssl_conf_min_tls_version(&config, MBEDTLS_SSL_VERSION_TLS1_2); + mbedtls_ssl_conf_max_tls_version(&config, MBEDTLS_SSL_VERSION_TLS1_2); +#else + mbedtls_ssl_conf_min_version(&config, 3, 3); + mbedtls_ssl_conf_max_version(&config, 3, 3); +#endif +#if MBEDTLS_VERSION_MAJOR < 4 + mbedtls_ssl_conf_rng(&config, random, nullptr); +#endif + static const int suites[] = {MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, 0}; + mbedtls_ssl_conf_ciphersuites(&config, suites); + const unsigned char key[] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + const unsigned char identity[] = "memory-write-test"; + REQUIRE(mbedtls_ssl_conf_psk(&config, key, sizeof(key), identity, + sizeof(identity) - 1) == 0); + REQUIRE(mbedtls_ssl_setup(&ssl, &config) == 0); + mbedtls_ssl_set_bio(&ssl, this, send, receive, nullptr); + } + }; + + struct MemoryConnection + { + ix::SocketMbedTLS& socket; + MemoryPeer client; + MemoryPeer server; + + explicit MemoryConnection(ix::SocketMbedTLS& socket) + : socket(socket) + { + client.other = &server; + server.other = &client; + client.configure(false); + server.configure(true); + bool clientReady = false; + bool serverReady = false; + for (int attempt = 0; attempt < 100 && !(clientReady && serverReady); ++attempt) + { + if (!clientReady) clientReady = handshake(client.ssl); + if (!serverReady) serverReady = handshake(server.ssl); + } + REQUIRE(clientReady); + REQUIRE(serverReady); + ix::SocketMbedTLSWriteTestAccess::install(socket, client.ssl); + } + + ~MemoryConnection() + { + socket.close(); + } + + static bool handshake(mbedtls_ssl_context& ssl) + { + const int result = mbedtls_ssl_handshake(&ssl); + REQUIRE((result == 0 || result == MBEDTLS_ERR_SSL_WANT_READ || + result == MBEDTLS_ERR_SSL_WANT_WRITE)); + return result == 0; + } + + std::vector received() + { + std::vector output; + unsigned char buffer[16384]; + for (int attempt = 0; attempt < 100; ++attempt) + { + const int count = mbedtls_ssl_read(&server.ssl, buffer, sizeof(buffer)); + if (count == MBEDTLS_ERR_SSL_WANT_READ) return output; + REQUIRE(count > 0); + output.insert(output.end(), buffer, buffer + count); + } + FAIL("TLS read budget exhausted"); + return output; + } + }; + + std::vector message(unsigned char seed, size_t size = 32776) + { + std::vector data(size); + for (size_t i = 0; i < data.size(); ++i) + data[i] = static_cast(seed + i * 31 + i / 251); + return data; + } + + std::vector pendingMessage(const ix::SocketMbedTLS& socket, unsigned char seed) + { + const int size = ix::SocketMbedTLSWriteTestAccess::recordSize(socket); + REQUIRE(size > 8); + return message(seed, static_cast(size) * 2 + 8); + } + + void drain(ix::SocketMbedTLS& socket, std::vector& queue) + { + for (int attempt = 0; attempt < 100 && !queue.empty(); ++attempt) + { + const auto count = socket.send(reinterpret_cast(queue.data()), queue.size()); + REQUIRE(count > 0); + REQUIRE(static_cast(count) <= queue.size()); + queue.erase(queue.begin(), queue.begin() + count); + } + REQUIRE(queue.empty()); + } + + void blockTail(ix::SocketMbedTLS& socket, MemoryConnection& connection, + std::vector& queue, int blockedResult) + { + connection.client.writesBeforeBlocking = 2; + connection.client.blockedResult = blockedResult; + for (int attempt = 0; attempt < 10; ++attempt) + { + const auto count = socket.send(reinterpret_cast(queue.data()), queue.size()); + if (count < 0) + { + REQUIRE(ix::Socket::isWaitNeeded()); + REQUIRE(queue.size() == 8); + REQUIRE(socket.send(reinterpret_cast(queue.data()), queue.size()) == -1); + return; + } + REQUIRE(count > 0); + REQUIRE(static_cast(count) <= queue.size()); + queue.erase(queue.begin(), queue.begin() + count); + } + FAIL("Did not reach the pending tail write"); + } + + void growsWhilePending(int blockedResult) + { + ix::SocketMbedTLS socket(ix::SocketTLSOptions{}); + MemoryConnection connection(socket); + auto expected = pendingMessage(socket, 17); + auto queue = expected; + blockTail(socket, connection, queue, blockedResult); + const auto next = message(93); + expected.insert(expected.end(), next.begin(), next.end()); + { + // A new allocation also releases the caller's original buffer. + auto replacement = queue; + replacement.insert(replacement.end(), next.begin(), next.end()); + queue.swap(replacement); + } + REQUIRE(socket.send(reinterpret_cast(queue.data()), queue.size()) == -1); + connection.client.writesBeforeBlocking = -1; + drain(socket, queue); + const auto actual = connection.received(); + REQUIRE(actual.size() == expected.size()); + REQUIRE(actual == expected); + socket.close(); + } +} + +TEST_CASE("MbedTLS send delivers large buffers without backpressure", "[socket_mbedtls]") +{ + ix::SocketMbedTLS socket(ix::SocketTLSOptions{}); + MemoryConnection connection(socket); + const auto expected = message(17); + auto queue = expected; + drain(socket, queue); + REQUIRE(connection.received() == expected); + socket.close(); +} + +TEST_CASE("MbedTLS send preserves data when the queue grows after WANT_WRITE", "[socket_mbedtls]") +{ + growsWhilePending(MBEDTLS_ERR_SSL_WANT_WRITE); +} + +TEST_CASE("MbedTLS send preserves data when the queue grows after WANT_READ", "[socket_mbedtls]") +{ + growsWhilePending(MBEDTLS_ERR_SSL_WANT_READ); +} + +TEST_CASE("MbedTLS send rejects a retry shorter than its pending write", "[socket_mbedtls]") +{ + ix::SocketMbedTLS socket(ix::SocketTLSOptions{}); + MemoryConnection connection(socket); + const auto expected = pendingMessage(socket, 17); + auto queue = expected; + blockTail(socket, connection, queue, MBEDTLS_ERR_SSL_WANT_WRITE); + connection.client.writesBeforeBlocking = -1; + REQUIRE(socket.send(reinterpret_cast(queue.data()), queue.size() - 1) == -1); + REQUIRE_FALSE(ix::Socket::isWaitNeeded()); + drain(socket, queue); + REQUIRE(connection.received() == expected); + socket.close(); +} + +TEST_CASE("MbedTLS close discards a pending write before a new connection", "[socket_mbedtls]") +{ + ix::SocketMbedTLS socket(ix::SocketTLSOptions{}); + { + MemoryConnection connection(socket); + auto queue = pendingMessage(socket, 17); + blockTail(socket, connection, queue, MBEDTLS_ERR_SSL_WANT_WRITE); + socket.close(); + } + MemoryConnection connection(socket); + const auto expected = message(93); + auto queue = expected; + drain(socket, queue); + REQUIRE(connection.received() == expected); + socket.close(); +} + +TEST_CASE("MbedTLS fatal send errors do not retain the previous wait status", "[socket_mbedtls]") +{ + ix::SocketMbedTLS socket(ix::SocketTLSOptions{}); + MemoryConnection connection(socket); + auto queue = pendingMessage(socket, 17); + blockTail(socket, connection, queue, MBEDTLS_ERR_SSL_WANT_WRITE); + connection.client.failWrites = true; + REQUIRE(socket.send(reinterpret_cast(queue.data()), queue.size()) == -1); + REQUIRE_FALSE(ix::Socket::isWaitNeeded()); + socket.close(); +} + +#else +TEST_CASE("MbedTLS memory write tests require TLS 1.2 PSK and PSA crypto", "[socket_mbedtls]") +{ + SKIP("MbedTLS configuration does not enable TLS 1.2 PSK and PSA crypto"); +} +#endif +#endif diff --git a/test/IXSocketTest.cpp b/test/IXSocketTest.cpp index 2507965d..f5a09db5 100644 --- a/test/IXSocketTest.cpp +++ b/test/IXSocketTest.cpp @@ -76,28 +76,4 @@ TEST_CASE("socket", "[socket]") testSocket(host, port, request, socket, expectedStatus, timeoutSecs); } - -#if defined(IXWEBSOCKET_USE_TLS) - SECTION("Connect to google HTTPS server over port 443. Send GET request without header. Should " - "return 200") - { - std::string errMsg; - bool tls = true; - SocketTLSOptions tlsOptions; -#ifdef IXWEBSOCKET_USE_NETWORK_FRAMEWORK - // Exercise native system trust; the vendored CA bundle dates from 2014. - tlsOptions.caFile = "SYSTEM"; -#else - tlsOptions.caFile = "cacert.pem"; -#endif - std::shared_ptr socket = createSocket(tls, -1, errMsg, tlsOptions); - std::string host("www.google.com"); - int port = 443; - std::string request("GET / HTTP/1.1\r\n\r\n"); - int expectedStatus = 200; - int timeoutSecs = 3; - - testSocket(host, port, request, socket, expectedStatus, timeoutSecs); - } -#endif } diff --git a/test/IXWebSocketTestConnectionDisconnection.cpp b/test/IXWebSocketTestConnectionDisconnection.cpp index 6ed5f21e..e12d2fda 100644 --- a/test/IXWebSocketTestConnectionDisconnection.cpp +++ b/test/IXWebSocketTestConnectionDisconnection.cpp @@ -13,13 +13,6 @@ using namespace ix; -namespace -{ - const std::string WEBSOCKET_DOT_ORG_URL("wss://echo.websocket.org"); - const std::string GOOGLE_URL("wss://google.com"); - const std::string UNKNOWN_URL("wss://asdcasdcaasdcasdcasdcasdcasdcasdcasassdd.com"); -} // namespace - namespace { class IXWebSocketTestConnectionDisconnection @@ -47,10 +40,6 @@ namespace { _webSocket.setUrl(url); - SocketTLSOptions tlsOptions; - tlsOptions.caFile = "cacert.pem"; - _webSocket.setTLSOptions(tlsOptions); - std::stringstream ss; log(std::string("Connecting to url: ") + url); @@ -114,11 +103,11 @@ TEST_CASE("websocket_connections", "[websocket]") { IXWebSocketTestConnectionDisconnection test; - test.start(GOOGLE_URL); + test.start("ws://127.0.0.1:" + std::to_string(getFreePort()) + "/"); ix::msleep(1000); test.stop(); - test.start(UNKNOWN_URL); + test.start("ws:///"); ix::msleep(1000); test.stop(); } @@ -126,13 +115,16 @@ TEST_CASE("websocket_connections", "[websocket]") SECTION("Try to connect and disconnect with different timing, not enough time to succesfully " "connect") { + ix::WebSocketServer server(getFreePort(), "127.0.0.1"); + REQUIRE(startWebSocketEchoServer(server)); + const std::string url("ws://127.0.0.1:" + std::to_string(server.getPort()) + "/"); IXWebSocketTestConnectionDisconnection test; log(std::string("50 Runs")); for (int i = 0; i < 50; ++i) { log(std::string("Run: ") + std::to_string(i)); - test.start(WEBSOCKET_DOT_ORG_URL); + test.start(url); log(std::string("Sleeping")); ix::msleep(i); @@ -147,13 +139,16 @@ TEST_CASE("websocket_connections", "[websocket]") SECTION("Try to connect and disconnect with different timing, from not enough time to " "successfull connect") { + ix::WebSocketServer server(getFreePort(), "127.0.0.1"); + REQUIRE(startWebSocketEchoServer(server)); + const std::string url("ws://127.0.0.1:" + std::to_string(server.getPort()) + "/"); IXWebSocketTestConnectionDisconnection test; log(std::string("20 Runs")); for (int i = 0; i < 20; ++i) { log(std::string("Run: ") + std::to_string(i)); - test.start(WEBSOCKET_DOT_ORG_URL); + test.start(url); log(std::string("Sleeping")); ix::msleep(i * 50);