- --no-quic leaves QUIC, HTTP/3 and WebTransport out of the build, and with
them msquic: a consumer that only needs TCP builds no msquic. The flag
isn't passed on to dependencies.
- --local takes Crafter.Thread from the sibling checkout, as the other
Crafter projects do.
- ClientTCP gains SetNoDelay, SetReceiveTimeout, a connect timeout and
SocketTimeoutException.
- TCP receives return the bytes that arrived:
- RecieveSync(n) returned the whole buffer when fewer had come;
- RecieveUntilCloseSync truncated what it had read;
- a signal (EINTR) during a receive threw where it should wait on;
- RecieveSync(size, buffer) returns the count and throws like the others;
- a receive of no bytes returns at once: recv's 0 for it isn't a close.
- Every TCP *Async receive takes an onError(std::exception_ptr): an
exception escaping a ThreadPool task ended the program. An empty onError
drops the error.
- QUIC streams:
- async receives deliver an empty chunk on close, as the browser build does;
- RecieveUntilFullAsync gives what arrived on a stream that closes early;
- RecieveUntilFullSync takes all it asked for or nothing, under one hold
of the lock, so what did arrive stays for the next receive and no
other receive can take bytes from among it;
- SendAsync's callback hears whether the send went out (bool sent): native,
browser, the JS bridge and the example.
- msquic is pinned to v2.6.3 and built against the system OpenSSL
(QUIC_TLS_LIB=openssl, 3.5 or newer). Its bundled quictls read the system's
openssl.cnf, and the Flatpak runtime's (a PKCS#11 engine, with
config_diagnostics on) made it fail to initialise. The README says to use
--no-quic where the distro's OpenSSL is older.
- Tests now register on any Linux triple: they never ran under the Flatpak's
x86_64-unknown-linux-gnu. New: ShouldBuildWithoutQUIC, ShouldConfigureTCP,
ShouldRecieveTCP, ShouldReportClosedQUICStream.
Breaking: SendAsync's callback takes a bool, the TCP *Async receives take
an onError, and RecieveSync(size, buffer) returns the count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.2 KiB
C++
62 lines
2.2 KiB
C++
//SPDX-License-Identifier: LGPL-3.0-only
|
|
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
// A build with --no-quic leaves out the QUIC, HTTP/3 and WebTransport
|
|
// partitions, and msquic with them, for consumers that only need TCP or
|
|
// HTTP/1.1: building msquic means cloning it with its submodules and running
|
|
// its CMake build. This test is only built that way. It checks that msquic is
|
|
// not in the process and that TCP and HTTP/1.1 still work.
|
|
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <cstdlib>
|
|
|
|
import Crafter.Network;
|
|
import std;
|
|
using namespace Crafter;
|
|
|
|
namespace {
|
|
|
|
int failures = 0;
|
|
|
|
void Check(bool ok, std::string_view what) {
|
|
std::println("{} {}", ok ? "PASS" : "FAIL", what);
|
|
if (!ok) ++failures;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
std::ifstream maps("/proc/self/maps");
|
|
const std::string mapped((std::istreambuf_iterator<char>(maps)), std::istreambuf_iterator<char>());
|
|
Check(!mapped.empty() && mapped.find("msquic") == std::string::npos, "libmsquic is not loaded");
|
|
|
|
const int listener = socket(AF_INET, SOCK_STREAM, 0);
|
|
sockaddr_in address {};
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof(address));
|
|
listen(listener, 1);
|
|
socklen_t length = sizeof(address);
|
|
getsockname(listener, reinterpret_cast<sockaddr*>(&address), &length);
|
|
|
|
ClientTCP client("127.0.0.1", ntohs(address.sin_port));
|
|
const int peer = accept(listener, nullptr, nullptr);
|
|
client.Send("ping", 4);
|
|
char echoed[4] {};
|
|
recv(peer, echoed, sizeof(echoed), MSG_WAITALL);
|
|
Check(std::string_view(echoed, 4) == "ping", "a ClientTCP still connects and sends over loopback");
|
|
|
|
// HTTP/1.1 stays in this build.
|
|
HTTP1::MessageParser parser(HTTP1::MessageKind::Request);
|
|
const std::string_view wire = "GET / HTTP/1.1\r\nHost: h\r\n\r\n";
|
|
parser.Feed(wire.data(), wire.size());
|
|
Check(parser.Complete() && parser.TakeRequest().path == "/", "the HTTP/1.1 parser is still there");
|
|
|
|
close(peer);
|
|
close(listener);
|
|
std::println("{}", failures == 0 ? "all checks passed" : "check(s) failed");
|
|
return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|