diff --git a/implementations/Crafter.Network-ListenerHTTP1.cpp b/implementations/Crafter.Network-ListenerHTTP1.cpp index 7cd0460..e4d29cd 100644 --- a/implementations/Crafter.Network-ListenerHTTP1.cpp +++ b/implementations/Crafter.Network-ListenerHTTP1.cpp @@ -288,7 +288,15 @@ ListenerHTTP1::ListenerHTTP1(ListenerHTTP1&& other) noexcept } ListenerHTTP1::~ListenerHTTP1() { - if (impl) Stop(); + // Stop() joins threads and touches sockets, so it can throw. Letting that + // out of a destructor ends the process — and the case where it matters is + // exactly the unhappy one: a second listener failing to bind unwinds past + // a live first listener, so a throw here replaces a reportable error with + // a terminate. + try { + if (impl) Stop(); + } catch (...) { + } } void ListenerHTTP1::Listen() { @@ -372,7 +380,10 @@ ListenerAsyncHTTP1::ListenerAsyncHTTP1(std::uint16_t port, {} ListenerAsyncHTTP1::~ListenerAsyncHTTP1() { - Stop(); + try { + Stop(); + } catch (...) { + } } void ListenerAsyncHTTP1::Stop() { diff --git a/tests/ShouldInteropCurlHTTPS1/main.cpp b/tests/ShouldInteropCurlHTTPS1/main.cpp index 40125bb..2182b36 100644 --- a/tests/ShouldInteropCurlHTTPS1/main.cpp +++ b/tests/ShouldInteropCurlHTTPS1/main.cpp @@ -132,13 +132,13 @@ namespace { return CreateResponseHTTP("200", request.scheme); }; - ListenerAsyncHTTP1 listener(8097, std::move(routes), + ListenerAsyncHTTP1 listener(8111, std::move(routes), TLSServerCredentials{ .selfSigned = true }); - Check(WaitForPort(8097, std::chrono::seconds(2)), "the HTTPS listener came up"); + Check(WaitForPort(8111, std::chrono::seconds(2)), "the HTTPS listener came up"); // --cacert, not --insecure: curl does the full chain and hostname // check, so this is a real verification of what we present. - const std::string base = "https://localhost:8097"; + const std::string base = "https://localhost:8111"; const std::string curl = "curl -sS --http1.1 --cacert '" + files.certificate.string() + "' "; Check(Run(curl + base + "/hello") == "Hello curl!", "curl GET over TLS"); @@ -203,13 +203,13 @@ namespace { + files.privateKey.string() + "')\n" "handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory='" + root.string() + "')\n" - "server = http.server.HTTPServer(('127.0.0.1', 8098), handler)\n" + "server = http.server.HTTPServer(('127.0.0.1', 8112), handler)\n" "server.socket = context.wrap_socket(server.socket, server_side=True)\n" "server.serve_forever()\n"; Child server({"python3", "-c", script}); Check(server.Started(), "python3 TLS http.server was spawned"); - if (!WaitForPort(8098, std::chrono::seconds(10))) { + if (!WaitForPort(8112, std::chrono::seconds(10))) { std::println("skipping the python half: the TLS http.server never came up"); return; } @@ -218,22 +218,22 @@ namespace { // rather than the caPem blob the self-contained test uses. TLSClientCredentials credentials; credentials.caPath = files.certificate.string(); - ClientHTTP1 client("localhost", 8098, credentials); + ClientHTTP1 client("localhost", 8112, credentials); - HTTPResponse response = client.Send(CreateRequestHTTP("GET", "/hello.txt", "localhost:8098")); + HTTPResponse response = client.Send(CreateRequestHTTP("GET", "/hello.txt", "localhost:8112")); Check(response.status == "200", "python TLS GET status"); Check(response.body == content, "python TLS GET body"); Check(!client.Connected(), "an HTTP/1.0 response closes the TLS connection"); - HTTPResponse listing = client.Send(CreateRequestHTTP("GET", "/", "localhost:8098")); + HTTPResponse listing = client.Send(CreateRequestHTTP("GET", "/", "localhost:8112")); Check(listing.status == "200", "python TLS directory listing status"); Check(listing.body.find("hello.txt") != std::string::npos, "python TLS directory listing body"); - HTTPResponse missing = client.Send(CreateRequestHTTP("GET", "/nothing-here", "localhost:8098")); + HTTPResponse missing = client.Send(CreateRequestHTTP("GET", "/nothing-here", "localhost:8112")); Check(missing.status == "404", "python TLS 404"); - HTTPResponse head = client.Send(CreateRequestHTTP("HEAD", "/hello.txt", "localhost:8098")); + HTTPResponse head = client.Send(CreateRequestHTTP("HEAD", "/hello.txt", "localhost:8112")); Check(head.status == "200", "python TLS HEAD status"); Check(head.body.empty(), "python TLS HEAD has no body"); } diff --git a/tests/ShouldRequireClientCertificateHTTPS1/main.cpp b/tests/ShouldRequireClientCertificateHTTPS1/main.cpp index b4db348..cf56821 100644 --- a/tests/ShouldRequireClientCertificateHTTPS1/main.cpp +++ b/tests/ShouldRequireClientCertificateHTTPS1/main.cpp @@ -84,13 +84,13 @@ namespace { // The :TLS layer without any HTTP on top — the case for anything else that // owns a connected socket and wants a record layer over it. void RawStreamAgainstListener() { - ListenerAsyncHTTP1 listener(8099, Routes(), TLSServerCredentials{ .selfSigned = true }); + ListenerAsyncHTTP1 listener(8113, Routes(), TLSServerCredentials{ .selfSigned = true }); TLSClientCredentials credentials; credentials.caPem = GetSelfSignedCertificatePem().certificate; auto context = TLSContext::Client(credentials); - ClientTCP socket("localhost", 8099); + ClientTCP socket("localhost", 8113); std::unique_ptr stream = TLSStream::Connect(socket.socketid, context, "localhost", std::chrono::seconds(5)); @@ -143,7 +143,7 @@ namespace { server.selfSigned = true; server.requireClientCertificate = true; server.clientCaPath = identity.authority.string(); - ListenerAsyncHTTP1 listener(8100, Routes(), server); + ListenerAsyncHTTP1 listener(8114, Routes(), server); // A client with a certificate the listener's CA vouches for. { @@ -151,7 +151,7 @@ namespace { credentials.caPem = GetSelfSignedCertificatePem().certificate; credentials.certPath = identity.certificate.string(); credentials.keyPath = identity.privateKey.string(); - ClientHTTP1 client("localhost", 8100, credentials); + ClientHTTP1 client("localhost", 8114, credentials); HTTPResponse response = client.Send(CreateRequestHTTP("GET", "/", "localhost")); Check(response.status == "200", "a client with a trusted certificate is served"); @@ -167,7 +167,7 @@ namespace { { TLSClientCredentials credentials; credentials.caPem = GetSelfSignedCertificatePem().certificate; - ClientHTTP1 anonymous("localhost", 8100, credentials); + ClientHTTP1 anonymous("localhost", 8114, credentials); bool refused = false; try { anonymous.Send(CreateRequestHTTP("GET", "/", "localhost")); diff --git a/tests/ShouldSendRecieveHTTPS1/main.cpp b/tests/ShouldSendRecieveHTTPS1/main.cpp index 6e9a629..fd0e907 100644 --- a/tests/ShouldSendRecieveHTTPS1/main.cpp +++ b/tests/ShouldSendRecieveHTTPS1/main.cpp @@ -49,12 +49,12 @@ int main() { // The listener mints an ephemeral certificate; the client is handed // that same certificate as a trust anchor, so this exercises real // chain *and* hostname verification rather than skipping both. - ListenerAsyncHTTP1 listener(8095, Routes(), TLSServerCredentials{ .selfSigned = true }); + ListenerAsyncHTTP1 listener(8110, Routes(), TLSServerCredentials{ .selfSigned = true }); Check(listener.listener.Secure(), "the listener reports itself as https"); TLSClientCredentials credentials; credentials.caPem = GetSelfSignedCertificatePem().certificate; - ClientHTTP1 client("localhost", 8095, credentials); + ClientHTTP1 client("localhost", 8110, credentials); Check(client.Secure(), "the client reports itself as https"); HTTPResponse hello = client.Send(CreateRequestHTTP("GET", "/", "localhost")); @@ -110,7 +110,7 @@ int main() { // Default credentials: system trust store only, so a self-signed // certificate must be rejected rather than quietly accepted. { - ClientHTTP1 strict("localhost", 8095, TLSClientCredentials{}); + ClientHTTP1 strict("localhost", 8110, TLSClientCredentials{}); bool rejected = false; try { strict.Send(CreateRequestHTTP("GET", "/", "localhost")); @@ -128,7 +128,7 @@ int main() { TLSClientCredentials mismatched; mismatched.caPem = GetSelfSignedCertificatePem().certificate; mismatched.serverName = "not-localhost.invalid"; - ClientHTTP1 wrongName("localhost", 8095, mismatched); + ClientHTTP1 wrongName("localhost", 8110, mismatched); bool rejected = false; try { wrongName.Send(CreateRequestHTTP("GET", "/", "localhost")); @@ -141,7 +141,7 @@ int main() { // insecureNoServerValidation is the dev escape hatch; it has to work, // because the alternative is people shipping their own worse one. { - ClientHTTP1 insecure("localhost", 8095, + ClientHTTP1 insecure("localhost", 8110, TLSClientCredentials{ .insecureNoServerValidation = true }); HTTPResponse response = insecure.Send(CreateRequestHTTP("GET", "/", "localhost")); Check(response.body == "Hello World!", "insecureNoServerValidation talks to the same server"); @@ -154,7 +154,7 @@ int main() { { const std::uint64_t before = listener.listener.HandshakeFailureCount(); try { - ClientHTTP1 plaintext("localhost", 8095); + ClientHTTP1 plaintext("localhost", 8110); plaintext.Send(CreateRequestHTTP("GET", "/", "localhost")); } catch (const std::exception&) { // Expected: the listener drops it without answering.