browser wasm

This commit is contained in:
Jorijn van der Graaf 2026-05-19 02:53:50 +02:00
commit e8630528af
24 changed files with 2490 additions and 100 deletions

View file

@ -0,0 +1,152 @@
/*
Crafter®.Network
Copyright (C) 2026 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
module Crafter.Network:WebTransport_impl;
import :WebTransport;
import :ClientQUIC;
import :HTTP3;
import Crafter.Thread;
import std;
using namespace Crafter;
struct WebTransportSession::Impl {
// Non-owning. The session is constructed and owned by ListenerHTTP, and
// ListenerHTTP keeps the underlying ClientQUIC alive in its PeerState
// for the duration of the connection — so this pointer is valid as
// long as the session is.
ClientQUIC* connection = nullptr;
// The HTTP/3 extended-CONNECT bidi stream the session was upgraded on.
// Stays open for the session's lifetime. Phase 1 closes it with a bare
// FIN; later phases will emit a CLOSE_WEBTRANSPORT_SESSION capsule.
QUICStream connectStream;
std::mutex mtx;
std::function<void(QUICStream)> onStream;
std::deque<QUICStream> pendingStreams;
std::function<void(std::vector<char>)> onDatagram; // Phase 2
bool closed = false;
};
WebTransportSession::WebTransportSession()
: impl(std::make_unique<Impl>())
{}
WebTransportSession::WebTransportSession(WebTransportSession&&) noexcept = default;
WebTransportSession& WebTransportSession::operator=(WebTransportSession&&) noexcept = default;
WebTransportSession::~WebTransportSession() {
if (impl) Close();
}
QUICStream WebTransportSession::OpenStream(bool unidirectional) {
if (!impl || impl->closed || !impl->connection) {
throw QUICClosedException();
}
QUICStream stream = impl->connection->OpenStream(unidirectional);
auto prefix = unidirectional
? HTTP3::BuildWtUnidiPrefix(sessionId)
: HTTP3::BuildWtBidiPrefix(sessionId);
// Write the WT_STREAM (bidi) or stream-type (unidi) prefix as the
// first send on the stream. The peer reads it to associate this
// stream with our session before treating the rest as opaque payload.
stream.SendSync(prefix.data(), static_cast<std::uint32_t>(prefix.size()), /*finish=*/false);
return stream;
}
void WebTransportSession::OnStream(std::function<void(QUICStream)> callback) {
std::deque<QUICStream> drained;
{
std::lock_guard lk(impl->mtx);
impl->onStream = callback;
drained.swap(impl->pendingStreams);
}
// Dispatch any streams that arrived before the handler was installed.
// Each goes to the ThreadPool so user code runs off the demuxer thread.
for (auto& s : drained) {
auto* shared = new QUICStream(std::move(s));
ThreadPool::Enqueue([callback, shared]{
callback(std::move(*shared));
delete shared;
});
}
}
void WebTransportSession::OnDatagram(std::function<void(std::vector<char>)> callback) {
// Phase 1 stub. Phase 2 will plumb QUIC datagrams through here after
// demuxing on quarter_session_id.
if (impl) impl->onDatagram = std::move(callback);
}
void WebTransportSession::SendDatagram(const void*, std::uint32_t) {
// Phase 1 stub — would prepend quarter_session_id varint and call
// connection->SendDatagram. Drops silently for now.
}
void WebTransportSession::Close() {
if (!impl || impl->closed) return;
impl->closed = true;
try {
// Empty FIN on the CONNECT stream. Chrome / Firefox both treat
// peer-FIN of the CONNECT stream as session-close.
impl->connectStream.SendSync(nullptr, 0, /*finish=*/true);
} catch (...) {
// Connection may already be gone — that's fine.
}
}
// ─── Internal ListenerHTTP-facing helpers ───────────────────────────────
//
// Declared (not exported) in the interface partition so ListenerHTTP_impl
// can call them; defined here. Friendship in WebTransportSession gives
// them access to the private Impl.
namespace Crafter {
void WebTransportInitialise(WebTransportSession& session,
ClientQUIC* connection,
QUICStream connectStream,
std::uint64_t sessionId,
std::string path) {
session.impl->connection = connection;
session.impl->connectStream = std::move(connectStream);
session.sessionId = sessionId;
session.path = std::move(path);
}
void WebTransportDeliverStream(WebTransportSession& session, QUICStream stream) {
std::function<void(QUICStream)> cb;
{
std::lock_guard lk(session.impl->mtx);
cb = session.impl->onStream;
if (!cb) {
session.impl->pendingStreams.push_back(std::move(stream));
return;
}
}
auto* shared = new QUICStream(std::move(stream));
ThreadPool::Enqueue([cb, shared]{
cb(std::move(*shared));
delete shared;
});
}
}