summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/webtransport
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /netwerk/protocol/webtransport
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/protocol/webtransport')
-rw-r--r--netwerk/protocol/webtransport/WebTransportLog.h21
-rw-r--r--netwerk/protocol/webtransport/WebTransportSessionProxy.cpp954
-rw-r--r--netwerk/protocol/webtransport/WebTransportSessionProxy.h183
-rw-r--r--netwerk/protocol/webtransport/WebTransportStreamProxy.cpp277
-rw-r--r--netwerk/protocol/webtransport/WebTransportStreamProxy.h77
-rw-r--r--netwerk/protocol/webtransport/moz.build34
-rw-r--r--netwerk/protocol/webtransport/nsIWebTransport.idl103
-rw-r--r--netwerk/protocol/webtransport/nsIWebTransportStream.idl78
8 files changed, 1727 insertions, 0 deletions
diff --git a/netwerk/protocol/webtransport/WebTransportLog.h b/netwerk/protocol/webtransport/WebTransportLog.h
new file mode 100644
index 0000000000..99f211b893
--- /dev/null
+++ b/netwerk/protocol/webtransport/WebTransportLog.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WebTransportLog_h
+#define WebTransportLog_h
+
+#include "mozilla/Logging.h"
+#include "mozilla/net/NeckoChild.h"
+
+namespace mozilla::net {
+extern LazyLogModule webTransportLog;
+} // namespace mozilla::net
+
+#undef LOG
+#define LOG(args) \
+ MOZ_LOG(mozilla::net::webTransportLog, mozilla::LogLevel::Debug, args)
+
+#endif
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
new file mode 100644
index 0000000000..1df46995b0
--- /dev/null
+++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
@@ -0,0 +1,954 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebTransportLog.h"
+#include "Http3WebTransportSession.h"
+#include "Http3WebTransportStream.h"
+#include "WebTransportSessionProxy.h"
+#include "WebTransportStreamProxy.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIHttpChannel.h"
+#include "nsIRequest.h"
+#include "nsNetUtil.h"
+#include "nsProxyRelease.h"
+#include "nsSocketTransportService2.h"
+#include "mozilla/Logging.h"
+#include "mozilla/StaticPrefs_network.h"
+
+namespace mozilla::net {
+
+LazyLogModule webTransportLog("nsWebTransport");
+
+NS_IMPL_ISUPPORTS(WebTransportSessionProxy, WebTransportSessionEventListener,
+ nsIWebTransport, nsIRedirectResultListener, nsIStreamListener,
+ nsIChannelEventSink, nsIInterfaceRequestor);
+
+WebTransportSessionProxy::WebTransportSessionProxy()
+ : mMutex("WebTransportSessionProxy::mMutex") {
+ LOG(("WebTransportSessionProxy constructor"));
+}
+
+WebTransportSessionProxy::~WebTransportSessionProxy() {
+ if (OnSocketThread()) {
+ return;
+ }
+
+ MutexAutoLock lock(mMutex);
+ if ((mState != WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED) &&
+ (mState != WebTransportSessionProxyState::ACTIVE) &&
+ (mState != WebTransportSessionProxyState::SESSION_CLOSE_PENDING)) {
+ return;
+ }
+
+ MOZ_ASSERT(mState != WebTransportSessionProxyState::SESSION_CLOSE_PENDING,
+ "We can not be in the SESSION_CLOSE_PENDING state in destructor, "
+ "because should e an runnable that holds reference to this"
+ "object.");
+
+ Unused << gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::ProxyHttp3WebTransportSessionRelease",
+ [self{std::move(mWebTransportSession)}]() {}));
+}
+
+//-----------------------------------------------------------------------------
+// WebTransportSessionProxy::nsIWebTransport
+//-----------------------------------------------------------------------------
+
+nsresult WebTransportSessionProxy::AsyncConnect(
+ nsIURI* aURI, nsIPrincipal* aPrincipal, uint32_t aSecurityFlags,
+ WebTransportSessionEventListener* aListener) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ LOG(("WebTransportSessionProxy::AsyncConnect"));
+ {
+ MutexAutoLock lock(mMutex);
+ mListener = aListener;
+ }
+ nsSecurityFlags flags = nsILoadInfo::SEC_COOKIES_OMIT | aSecurityFlags;
+ nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
+ nsIRequest::LOAD_BYPASS_CACHE |
+ nsIRequest::INHIBIT_CACHING;
+ nsresult rv = NS_NewChannel(getter_AddRefs(mChannel), aURI, aPrincipal, flags,
+ nsContentPolicyType::TYPE_OTHER,
+ /* aCookieJarSettings */ nullptr,
+ /* aPerformanceStorage */ nullptr,
+ /* aLoadGroup */ nullptr,
+ /* aCallbacks */ this, loadFlags);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // configure HTTP specific stuff
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
+ if (!httpChannel) {
+ mChannel = nullptr;
+ return NS_ERROR_ABORT;
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+ ChangeState(WebTransportSessionProxyState::NEGOTIATING);
+ }
+
+ rv = mChannel->AsyncOpen(this);
+ if (NS_FAILED(rv)) {
+ MutexAutoLock lock(mMutex);
+ mChannel = nullptr;
+ mListener = nullptr;
+ ChangeState(WebTransportSessionProxyState::DONE);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::GetStats() { return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+WebTransportSessionProxy::CloseSession(uint32_t status,
+ const nsACString& reason) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mMutex);
+ mCloseStatus = status;
+ mReason = reason;
+ mListener = nullptr;
+ mPendingEvents.Clear();
+ switch (mState) {
+ case WebTransportSessionProxyState::INIT:
+ case WebTransportSessionProxyState::DONE:
+ return NS_ERROR_NOT_INITIALIZED;
+ case WebTransportSessionProxyState::NEGOTIATING:
+ mChannel->Cancel(NS_ERROR_ABORT);
+ mChannel = nullptr;
+ ChangeState(WebTransportSessionProxyState::DONE);
+ break;
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ mChannel->Cancel(NS_ERROR_ABORT);
+ mChannel = nullptr;
+ ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING);
+ CloseSessionInternal();
+ break;
+ case WebTransportSessionProxyState::ACTIVE:
+ ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING);
+ CloseSessionInternal();
+ break;
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ ChangeState(WebTransportSessionProxyState::DONE);
+ break;
+ case SESSION_CLOSE_PENDING:
+ break;
+ }
+ return NS_OK;
+}
+
+void WebTransportSessionProxy::CloseSessionInternal() {
+ if (!OnSocketThread()) {
+ mMutex.AssertCurrentThreadOwns();
+ RefPtr<WebTransportSessionProxy> self(this);
+ Unused << gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::CallCloseWebTransportSession",
+ [self{std::move(self)}]() { self->CloseSessionInternal(); }));
+ return;
+ }
+
+ RefPtr<Http3WebTransportSession> wt;
+ uint32_t closeStatus = 0;
+ nsCString reason;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState == WebTransportSessionProxyState::SESSION_CLOSE_PENDING) {
+ MOZ_ASSERT(mWebTransportSession);
+ wt = mWebTransportSession;
+ mWebTransportSession = nullptr;
+ closeStatus = mCloseStatus;
+ reason = mReason;
+ ChangeState(WebTransportSessionProxyState::DONE);
+ } else {
+ MOZ_ASSERT(mState == WebTransportSessionProxyState::DONE);
+ }
+ }
+ if (wt) {
+ wt->CloseSession(closeStatus, reason);
+ }
+}
+
+class WebTransportStreamCallbackWrapper final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebTransportStreamCallbackWrapper)
+
+ explicit WebTransportStreamCallbackWrapper(
+ nsIWebTransportStreamCallback* aCallback, bool aBidi)
+ : mCallback(aCallback), mTarget(GetCurrentEventTarget()), mBidi(aBidi) {}
+
+ void CallOnError(nsresult aError) {
+ if (!mTarget->IsOnCurrentThread()) {
+ RefPtr<WebTransportStreamCallbackWrapper> self(this);
+ Unused << mTarget->Dispatch(NS_NewRunnableFunction(
+ "WebTransportStreamCallbackWrapper::CallOnError",
+ [self{std::move(self)}, error{aError}]() {
+ self->CallOnError(error);
+ }));
+ return;
+ }
+
+ LOG(("WebTransportStreamCallbackWrapper::OnError aError=0x%" PRIx32,
+ static_cast<uint32_t>(aError)));
+ Unused << mCallback->OnError(nsIWebTransport::INVALID_STATE_ERROR);
+ }
+
+ void CallOnStreamReady(WebTransportStreamProxy* aStream) {
+ if (!mTarget->IsOnCurrentThread()) {
+ RefPtr<WebTransportStreamCallbackWrapper> self(this);
+ RefPtr<WebTransportStreamProxy> stream = aStream;
+ Unused << mTarget->Dispatch(NS_NewRunnableFunction(
+ "WebTransportStreamCallbackWrapper::CallOnStreamReady",
+ [self{std::move(self)}, stream{std::move(stream)}]() {
+ self->CallOnStreamReady(stream);
+ }));
+ return;
+ }
+
+ if (mBidi) {
+ Unused << mCallback->OnBidirectionalStreamReady(aStream);
+ return;
+ }
+
+ Unused << mCallback->OnUnidirectionalStreamReady(aStream);
+ }
+
+ private:
+ virtual ~WebTransportStreamCallbackWrapper() {
+ NS_ProxyRelease(
+ "WebTransportStreamCallbackWrapper::~WebTransportStreamCallbackWrapper",
+ mTarget, mCallback.forget());
+ }
+
+ nsCOMPtr<nsIWebTransportStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ bool mBidi = false;
+};
+
+void WebTransportSessionProxy::CreateStreamInternal(
+ WebTransportStreamCallbackWrapper* aCallback, bool aBidi) {
+ if (!OnSocketThread()) {
+ RefPtr<WebTransportSessionProxy> self(this);
+ RefPtr<WebTransportStreamCallbackWrapper> wrapper(aCallback);
+ Unused << gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::CreateStreamInternal",
+ [self{std::move(self)}, wrapper{std::move(wrapper)}, bidi(aBidi)]() {
+ self->CreateStreamInternal(wrapper, bidi);
+ }));
+ return;
+ }
+
+ RefPtr<WebTransportStreamCallbackWrapper> wrapper(aCallback);
+ auto callback =
+ [wrapper{std::move(wrapper)}](
+ Result<RefPtr<Http3WebTransportStream>, nsresult>&& aResult) {
+ if (aResult.isErr()) {
+ wrapper->CallOnError(aResult.unwrapErr());
+ return;
+ }
+
+ RefPtr<Http3WebTransportStream> stream = aResult.unwrap();
+ RefPtr<WebTransportStreamProxy> streamProxy =
+ new WebTransportStreamProxy(stream);
+ wrapper->CallOnStreamReady(streamProxy);
+ };
+
+ RefPtr<Http3WebTransportSession> session;
+ {
+ MutexAutoLock lock(mMutex);
+ session = mWebTransportSession;
+ }
+
+ if (!session) {
+ MOZ_ASSERT(false, "This should not happen");
+ callback(Err(NS_ERROR_UNEXPECTED));
+ return;
+ }
+
+ if (aBidi) {
+ session->CreateOutgoingBidirectionalStream(std::move(callback));
+ } else {
+ session->CreateOutgoingUnidirectionalStream(std::move(callback));
+ }
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::CreateOutgoingUnidirectionalStream(
+ nsIWebTransportStreamCallback* callback) {
+ if (!callback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE ||
+ !mWebTransportSession) {
+ nsCOMPtr<nsIWebTransportStreamCallback> cb(callback);
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::CreateOutgoingUnidirectionalStream",
+ [cb{std::move(cb)}]() {
+ cb->OnError(nsIWebTransport::INVALID_STATE_ERROR);
+ }));
+ return NS_OK;
+ }
+ }
+
+ RefPtr<WebTransportStreamCallbackWrapper> wrapper =
+ new WebTransportStreamCallbackWrapper(callback, false);
+ CreateStreamInternal(wrapper, false);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::CreateOutgoingBidirectionalStream(
+ nsIWebTransportStreamCallback* callback) {
+ if (!callback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE ||
+ !mWebTransportSession) {
+ nsCOMPtr<nsIWebTransportStreamCallback> cb(callback);
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::CreateOutgoingBidirectionalStream",
+ [cb{std::move(cb)}]() {
+ cb->OnError(nsIWebTransport::INVALID_STATE_ERROR);
+ }));
+ return NS_OK;
+ }
+ }
+
+ RefPtr<WebTransportStreamCallbackWrapper> wrapper =
+ new WebTransportStreamCallbackWrapper(callback, true);
+ CreateStreamInternal(wrapper, true);
+ return NS_OK;
+}
+
+void WebTransportSessionProxy::SendDatagramInternal(
+ const RefPtr<Http3WebTransportSession>& aSession, nsTArray<uint8_t>&& aData,
+ uint64_t aTrackingId) {
+ MOZ_ASSERT(OnSocketThread());
+
+ aSession->SendDatagram(std::move(aData), aTrackingId);
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::SendDatagram(const nsTArray<uint8_t>& aData,
+ uint64_t aTrackingId) {
+ RefPtr<Http3WebTransportSession> session;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE ||
+ !mWebTransportSession) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ session = mWebTransportSession;
+ }
+
+ nsTArray<uint8_t> copied;
+ copied.Assign(aData);
+ if (!OnSocketThread()) {
+ return gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::SendDatagramInternal",
+ [self = RefPtr{this}, session{std::move(session)},
+ data{std::move(copied)}, trackingId(aTrackingId)]() mutable {
+ self->SendDatagramInternal(session, std::move(data), trackingId);
+ }));
+ }
+
+ SendDatagramInternal(session, std::move(copied), aTrackingId);
+ return NS_OK;
+}
+
+void WebTransportSessionProxy::GetMaxDatagramSizeInternal(
+ const RefPtr<Http3WebTransportSession>& aSession) {
+ MOZ_ASSERT(OnSocketThread());
+
+ aSession->GetMaxDatagramSize();
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::GetMaxDatagramSize() {
+ RefPtr<Http3WebTransportSession> session;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE ||
+ !mWebTransportSession) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ session = mWebTransportSession;
+ }
+
+ if (!OnSocketThread()) {
+ return gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::GetMaxDatagramSizeInternal",
+ [self = RefPtr{this}, session{std::move(session)}]() {
+ self->GetMaxDatagramSizeInternal(session);
+ }));
+ }
+
+ GetMaxDatagramSizeInternal(session);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// WebTransportSessionProxy::nsIStreamListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnStartRequest(nsIRequest* aRequest) {
+ MOZ_ASSERT(NS_IsMainThread());
+ LOG(("WebTransportSessionProxy::OnStartRequest\n"));
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ nsAutoCString reason;
+ uint32_t closeStatus = 0;
+ {
+ MutexAutoLock lock(mMutex);
+ switch (mState) {
+ case WebTransportSessionProxyState::INIT:
+ case WebTransportSessionProxyState::DONE:
+ case WebTransportSessionProxyState::ACTIVE:
+ case WebTransportSessionProxyState::SESSION_CLOSE_PENDING:
+ MOZ_ASSERT(false, "OnStartRequest cannot be called in this state.");
+ break;
+ case WebTransportSessionProxyState::NEGOTIATING:
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ listener = mListener;
+ mListener = nullptr;
+ mChannel = nullptr;
+ reason = mReason;
+ closeStatus = mCloseStatus;
+ ChangeState(WebTransportSessionProxyState::DONE);
+ break;
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: {
+ uint32_t status;
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
+ if (!httpChannel ||
+ NS_FAILED(httpChannel->GetResponseStatus(&status)) ||
+ status != 200) {
+ listener = mListener;
+ mListener = nullptr;
+ mChannel = nullptr;
+ mReason = ""_ns;
+ reason = ""_ns;
+ mCloseStatus =
+ 0; // TODO: find a better error. Currently error code 0 is used
+ ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING);
+ CloseSessionInternal(); // TODO: find a better error. Currently error
+ // code 0 is used.
+ }
+ // The success cases will be handled in OnStopRequest.
+ } break;
+ }
+ }
+ if (listener) {
+ listener->OnSessionClosed(closeStatus, reason);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aStream,
+ uint64_t aOffset, uint32_t aCount) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(
+ false, "WebTransportSessionProxy::OnDataAvailable should not be called");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnStopRequest(nsIRequest* aRequest,
+ nsresult aStatus) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mChannel = nullptr;
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ nsAutoCString reason;
+ uint32_t closeStatus = 0;
+ uint64_t sessionId;
+ bool succeeded = false;
+ nsTArray<std::function<void()>> pendingEvents;
+ {
+ MutexAutoLock lock(mMutex);
+ switch (mState) {
+ case WebTransportSessionProxyState::INIT:
+ case WebTransportSessionProxyState::ACTIVE:
+ case WebTransportSessionProxyState::NEGOTIATING:
+ MOZ_ASSERT(false, "OnStotRequest cannot be called in this state.");
+ break;
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ reason = mReason;
+ closeStatus = mCloseStatus;
+ listener = mListener;
+ mListener = nullptr;
+ ChangeState(WebTransportSessionProxyState::DONE);
+ break;
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ if (NS_FAILED(aStatus)) {
+ listener = mListener;
+ mListener = nullptr;
+ mReason = ""_ns;
+ reason = ""_ns;
+ mCloseStatus = 0;
+ ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING);
+ CloseSessionInternal(); // TODO: find a better error. Currently error
+ // code 0 is used.
+ } else {
+ succeeded = true;
+ sessionId = mSessionId;
+ listener = mListener;
+ ChangeState(WebTransportSessionProxyState::ACTIVE);
+ pendingEvents = std::move(mPendingEvents);
+ }
+ break;
+ case WebTransportSessionProxyState::SESSION_CLOSE_PENDING:
+ case WebTransportSessionProxyState::DONE:
+ break;
+ }
+ }
+ if (listener) {
+ if (succeeded) {
+ listener->OnSessionReady(sessionId);
+ if (!pendingEvents.IsEmpty()) {
+ for (const auto& event : pendingEvents) {
+ event();
+ }
+ }
+ } else {
+ listener->OnSessionClosed(closeStatus,
+ reason); // TODO: find a better error.
+ // Currently error code 0 is used.
+ }
+ }
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// WebTransportSessionProxy::nsIChannelEventSink
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+WebTransportSessionProxy::AsyncOnChannelRedirect(
+ nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
+ nsIAsyncVerifyRedirectCallback* callback) {
+ // Currently implementation we do not reach this part of the code
+ // as location headers are not forwarded by the http3 stack to the applicaion.
+ // Hence, the channel is aborted due to the location header check in
+ // nsHttpChannel::AsyncProcessRedirection This comment must be removed after
+ // the following neqo bug is resolved
+ // https://github.com/mozilla/neqo/issues/1364
+ if (!StaticPrefs::network_webtransport_redirect_enabled()) {
+ LOG(("Channel Redirects are disabled for WebTransport sessions"));
+ return NS_ERROR_ABORT;
+ }
+
+ nsCOMPtr<nsIURI> newURI;
+ nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aNewChannel->GetURI(getter_AddRefs(newURI));
+ if (NS_FAILED(rv)) {
+ callback->OnRedirectVerifyCallback(rv);
+ return NS_OK;
+ }
+
+ // abort the request if redirecting to insecure context
+ if (!newURI->SchemeIs("https")) {
+ callback->OnRedirectVerifyCallback(NS_ERROR_ABORT);
+ return NS_OK;
+ }
+
+ // Assign to mChannel after we get notification about success of the
+ // redirect in OnRedirectResult.
+ mRedirectChannel = aNewChannel;
+
+ callback->OnRedirectVerifyCallback(NS_OK);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// WebTransportSessionProxy::nsIRedirectResultListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnRedirectResult(nsresult aStatus) {
+ if (NS_SUCCEEDED(aStatus) && mRedirectChannel) {
+ mChannel = mRedirectChannel;
+ }
+
+ mRedirectChannel = nullptr;
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// WebTransportSessionProxy::nsIInterfaceRequestor
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+WebTransportSessionProxy::GetInterface(const nsIID& aIID, void** aResult) {
+ if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+ NS_ADDREF_THIS();
+ *aResult = static_cast<nsIChannelEventSink*>(this);
+ return NS_OK;
+ }
+
+ if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
+ NS_ADDREF_THIS();
+ *aResult = static_cast<nsIRedirectResultListener*>(this);
+ return NS_OK;
+ }
+
+ return NS_ERROR_NO_INTERFACE;
+}
+
+//-----------------------------------------------------------------------------
+// WebTransportSessionProxy::WebTransportSessionEventListener
+//-----------------------------------------------------------------------------
+
+// This function is called when the Http3WebTransportSession is ready. After
+// this call WebTransportSessionProxy is responsible for the
+// Http3WebTransportSession, i.e. it is responsible for closing it.
+// The listener of the WebTransportSessionProxy will be informed during
+// OnStopRequest call.
+NS_IMETHODIMP
+WebTransportSessionProxy::OnSessionReadyInternal(
+ Http3WebTransportSession* aSession) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ LOG(("WebTransportSessionProxy::OnSessionReadyInternal"));
+ MutexAutoLock lock(mMutex);
+ switch (mState) {
+ case WebTransportSessionProxyState::INIT:
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ case WebTransportSessionProxyState::ACTIVE:
+ case WebTransportSessionProxyState::SESSION_CLOSE_PENDING:
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ MOZ_ASSERT(false,
+ "OnSessionReadyInternal cannot be called in this state.");
+ return NS_ERROR_ABORT;
+ case WebTransportSessionProxyState::NEGOTIATING:
+ mWebTransportSession = aSession;
+ mSessionId = aSession->StreamId();
+ ChangeState(WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED);
+ break;
+ case WebTransportSessionProxyState::DONE:
+ // The session has been canceled. We do not need to set
+ // mWebTransportSession.
+ break;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnIncomingStreamAvailableInternal(
+ Http3WebTransportStream* aStream) {
+ if (!NS_IsMainThread()) {
+ RefPtr<WebTransportSessionProxy> self(this);
+ RefPtr<Http3WebTransportStream> stream = aStream;
+ Unused << NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::OnIncomingStreamAvailableInternal",
+ [self{std::move(self)}, stream{std::move(stream)}]() {
+ self->OnIncomingStreamAvailableInternal(stream);
+ }));
+ return NS_OK;
+ }
+
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ {
+ MutexAutoLock lock(mMutex);
+ LOG(
+ ("WebTransportSessionProxy::OnIncomingStreamAvailableInternal %p "
+ "mState=%d mListener=%p",
+ this, mState, mListener.get()));
+ switch (mState) {
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ // OnSessionReady is not called yet, so we need to wait.
+ mPendingEvents.AppendElement(
+ [self = RefPtr{this}, stream = RefPtr{aStream}]() {
+ self->OnIncomingStreamAvailableInternal(stream);
+ });
+ break;
+ case WebTransportSessionProxyState::ACTIVE:
+ listener = mListener;
+ break;
+ default:
+ return NS_ERROR_ABORT;
+ }
+ }
+
+ if (!listener) {
+ return NS_OK;
+ }
+
+ RefPtr<WebTransportStreamProxy> streamProxy =
+ new WebTransportStreamProxy(aStream);
+ if (aStream->StreamType() == WebTransportStreamType::BiDi) {
+ Unused << listener->OnIncomingBidirectionalStreamAvailable(streamProxy);
+ } else {
+ Unused << listener->OnIncomingUnidirectionalStreamAvailable(streamProxy);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnIncomingBidirectionalStreamAvailable(
+ nsIWebTransportBidirectionalStream* aStream) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnIncomingUnidirectionalStreamAvailable(
+ nsIWebTransportReceiveStream* aStream) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnSessionReady(uint64_t ready) {
+ MOZ_ASSERT(false, "Should not b called");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnSessionClosed(uint32_t status,
+ const nsACString& reason) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ LOG(("WebTransportSessionProxy::OnSessionClosed"));
+ MutexAutoLock lock(mMutex);
+ switch (mState) {
+ case WebTransportSessionProxyState::INIT:
+ case WebTransportSessionProxyState::NEGOTIATING:
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ MOZ_ASSERT(false, "OnSessionClosed cannot be called in this state.");
+ return NS_ERROR_ABORT;
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ case WebTransportSessionProxyState::ACTIVE: {
+ mCloseStatus = status;
+ mReason = reason;
+ mWebTransportSession = nullptr;
+ ChangeState(WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING);
+ RefPtr<WebTransportSessionProxy> self(this);
+ Unused << NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::CallOnSessionClose",
+ [self{std::move(self)}]() { self->CallOnSessionClosed(); }));
+ } break;
+ case WebTransportSessionProxyState::SESSION_CLOSE_PENDING:
+ ChangeState(WebTransportSessionProxyState::DONE);
+ break;
+ case WebTransportSessionProxyState::DONE:
+ // The session has been canceled. We do not need to set
+ // mWebTransportSession.
+ break;
+ }
+ return NS_OK;
+}
+
+void WebTransportSessionProxy::CallOnSessionClosed() {
+ MOZ_ASSERT(NS_IsMainThread(), "not on socket thread");
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ nsAutoCString reason;
+ uint32_t closeStatus = 0;
+ {
+ MutexAutoLock lock(mMutex);
+ switch (mState) {
+ case WebTransportSessionProxyState::INIT:
+ case WebTransportSessionProxyState::NEGOTIATING:
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ case WebTransportSessionProxyState::ACTIVE:
+ case WebTransportSessionProxyState::SESSION_CLOSE_PENDING:
+ MOZ_ASSERT(false,
+ "CallOnSessionClosed cannot be called in this state.");
+ break;
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ listener = mListener;
+ mListener = nullptr;
+ reason = mReason;
+ closeStatus = mCloseStatus;
+ ChangeState(WebTransportSessionProxyState::DONE);
+ break;
+ case WebTransportSessionProxyState::DONE:
+ break;
+ }
+ }
+ if (listener) {
+ listener->OnSessionClosed(closeStatus, reason);
+ }
+}
+
+void WebTransportSessionProxy::ChangeState(
+ WebTransportSessionProxyState newState) {
+ mMutex.AssertCurrentThreadOwns();
+ LOG(("WebTransportSessionProxy::ChangeState %d -> %d [this=%p]", mState,
+ newState, this));
+ switch (newState) {
+ case WebTransportSessionProxyState::INIT:
+ MOZ_ASSERT(false, "Cannot change into INIT sate.");
+ break;
+ case WebTransportSessionProxyState::NEGOTIATING:
+ MOZ_ASSERT(mState == WebTransportSessionProxyState::INIT,
+ "Only from INIT can be change into NEGOTIATING");
+ MOZ_ASSERT(mChannel);
+ MOZ_ASSERT(mListener);
+ break;
+ case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED:
+ MOZ_ASSERT(
+ mState == WebTransportSessionProxyState::NEGOTIATING,
+ "Only from NEGOTIATING can be change into NEGOTIATING_SUCCEEDED");
+ MOZ_ASSERT(mChannel);
+ MOZ_ASSERT(mWebTransportSession);
+ MOZ_ASSERT(mListener);
+ break;
+ case WebTransportSessionProxyState::ACTIVE:
+ MOZ_ASSERT(mState == WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED,
+ "Only from NEGOTIATING_SUCCEEDED can be change into ACTIVE");
+ MOZ_ASSERT(!mChannel);
+ MOZ_ASSERT(mWebTransportSession);
+ MOZ_ASSERT(mListener);
+ break;
+ case WebTransportSessionProxyState::SESSION_CLOSE_PENDING:
+ MOZ_ASSERT(
+ (mState == WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED) ||
+ (mState == WebTransportSessionProxyState::ACTIVE),
+ "Only from NEGOTIATING_SUCCEEDED and ACTIVE can be change into"
+ " SESSION_CLOSE_PENDING");
+ MOZ_ASSERT(!mChannel);
+ MOZ_ASSERT(mWebTransportSession);
+ MOZ_ASSERT(!mListener);
+ break;
+ case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING:
+ MOZ_ASSERT(
+ (mState == WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED) ||
+ (mState == WebTransportSessionProxyState::ACTIVE),
+ "Only from NEGOTIATING_SUCCEEDED and ACTIVE can be change into"
+ " CLOSE_CALLBACK_PENDING");
+ MOZ_ASSERT(!mWebTransportSession);
+ MOZ_ASSERT(mListener);
+ break;
+ case WebTransportSessionProxyState::DONE:
+ MOZ_ASSERT(
+ (mState == WebTransportSessionProxyState::NEGOTIATING) ||
+ (mState ==
+ WebTransportSessionProxyState::SESSION_CLOSE_PENDING) ||
+ (mState == WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING),
+ "Only from NEGOTIATING, SESSION_CLOSE_PENDING and "
+ "CLOSE_CALLBACK_PENDING can be change into DONE");
+ MOZ_ASSERT(!mChannel);
+ MOZ_ASSERT(!mWebTransportSession);
+ MOZ_ASSERT(!mListener);
+ break;
+ }
+ mState = newState;
+}
+
+void WebTransportSessionProxy::NotifyDatagramReceived(
+ nsTArray<uint8_t>&& aData) {
+ // TODO: this should be on the target thread, but the target thread is main
+ // thread for now.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) {
+ return;
+ }
+ listener = mListener;
+ }
+
+ listener->OnDatagramReceived(aData);
+}
+
+NS_IMETHODIMP WebTransportSessionProxy::OnDatagramReceivedInternal(
+ nsTArray<uint8_t>&& aData) {
+ MOZ_ASSERT(OnSocketThread());
+
+ if (!NS_IsMainThread()) {
+ return NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::OnDatagramReceived",
+ [self = RefPtr{this}, data{std::move(aData)}]() mutable {
+ self->NotifyDatagramReceived(std::move(data));
+ }));
+ }
+
+ NotifyDatagramReceived(std::move(aData));
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportSessionProxy::OnDatagramReceived(
+ const nsTArray<uint8_t>& aData) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void WebTransportSessionProxy::OnMaxDatagramSizeInternal(uint64_t aSize) {
+ // TODO: this should be on the target thread, but the target thread is main
+ // thread for now.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) {
+ return;
+ }
+ listener = mListener;
+ }
+
+ listener->OnMaxDatagramSize(aSize);
+}
+
+NS_IMETHODIMP WebTransportSessionProxy::OnMaxDatagramSize(uint64_t aSize) {
+ MOZ_ASSERT(OnSocketThread());
+
+ if (!NS_IsMainThread()) {
+ return NS_DispatchToMainThread(
+ NS_NewRunnableFunction("WebTransportSessionProxy::OnMaxDatagramSize",
+ [self = RefPtr{this}, size(aSize)] {
+ self->OnMaxDatagramSizeInternal(size);
+ }));
+ }
+
+ OnMaxDatagramSizeInternal(aSize);
+ return NS_OK;
+}
+
+void WebTransportSessionProxy::OnOutgoingDatagramOutComeInternal(
+ uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) {
+ // TODO: this should be on the target thread, but the target thread is main
+ // thread for now.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<WebTransportSessionEventListener> listener;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) {
+ return;
+ }
+ listener = mListener;
+ }
+
+ listener->OnOutgoingDatagramOutCome(aId, aOutCome);
+}
+
+NS_IMETHODIMP
+WebTransportSessionProxy::OnOutgoingDatagramOutCome(
+ uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) {
+ MOZ_ASSERT(OnSocketThread());
+
+ if (!NS_IsMainThread()) {
+ return NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WebTransportSessionProxy::OnOutgoingDatagramOutCome",
+ [self = RefPtr{this}, id(aId), outcome(aOutCome)] {
+ self->OnOutgoingDatagramOutComeInternal(id, outcome);
+ }));
+ }
+
+ OnOutgoingDatagramOutComeInternal(aId, aOutCome);
+ return NS_OK;
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.h b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
new file mode 100644
index 0000000000..c2edf2e3c1
--- /dev/null
+++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_WebTransportProxy_h
+#define mozilla_net_WebTransportProxy_h
+
+#include <functional>
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIRedirectResultListener.h"
+#include "nsIStreamListener.h"
+#include "nsIWebTransport.h"
+
+/*
+ * WebTransportSessionProxy is introduced to enable the creation of a
+ * Http3WebTransportSession and coordination of actions that are performed on
+ * the main thread and on the socket thread.
+ *
+ * mChannel, mRedirectChannel, and mListener are used only on the main thread.
+ *
+ * mWebTransportSession is used only on the socket thread.
+ *
+ * mState and mSessionId are used on both threads, socket and main thread and it
+ * is only used with a lock.
+ *
+ *
+ * WebTransportSessionProxyState:
+ * - INIT: before AsyncConnect is called.
+ *
+ * - NEGOTIATING: It is set during AsyncConnect. During this state HttpChannel
+ * is open but OnStartRequest has not been called yet. This state can
+ * transfer into:
+ * - NEGOTIATING_SUCCEEDED: when a Http3WebTransportSession has been
+ * negotiated.
+ * - DONE: when a WebTransport session has been canceled.
+ *
+ * - NEGOTIATING_SUCCEEDED: It is set during parsing of
+ * Http3WebTransportSession response when the response has been successful.
+ * mWebTransport is set to the Http3WebTransportSession at the same time the
+ * session changes to this state. This state can transfer into:
+ * - ACTIVE: during the OnStopRequest call if the WebTransport has not been
+ * canceled or failed for other reason, e.g. a browser shutdown or content
+ * blocking policies.
+ * - SESSION_CLOSE_PENDING: if the WebTransport has been canceled via an API
+ * call or content blocking policies. (the main thread initiated close).
+ * - CLOSE_CALLBACK_PENDING: if Http3WebTransportSession has been canceled
+ * due to a shutdown or a server closing a session. (the socket thread
+ * initiated close).
+ *
+ * - ACTIVE: In this state the session is negotiated and ready to use. This
+ * state can transfer into:
+ * - SESSION_CLOSE_PENDING: if the WebTransport has been canceled via an API
+ * call(nsIWebTransport::closeSession) or content blocking policies. (the
+ * main thread initiated close).
+ * - CLOSE_CALLBACK_PENDING: if Http3WebTransportSession has been canceled
+ * due to a shutdown or a server closing a session. (the socket thread
+ * initiated close).
+ *
+ * - CLOSE_CALLBACK_PENDING: This is the socket thread initiated close. In this
+ * state, the Http3WebTransportSession has been closed and a
+ * CallOnSessionClosed call is dispatched to the main thread to call the
+ * appropriate listener.
+ *
+ * - SESSION_CLOSE_PENDING: This is the main thread initiated close. In this
+ * state, the WebTransport has been closed via an API call
+ * (nsIWebTransport::closeSession) and a CloseSessionInternal call is
+ * dispatched to the socket thread to close the appropriate
+ * Http3WebTransportSession.
+ *
+ * - DONE: everything has been cleaned up on both threads.
+ *
+ *
+ * AsyncConnect creates mChannel on the main thread. Redirect callbacks are also
+ * performed on the main thread (mRedirectChannel set and access only on the
+ * main thread). Before this point, there are no activities on the socket thread
+ * and Http3WebTransportSession is nullptr. mChannel is going to create a
+ * nsHttpTransaction. The transaction will be dispatched on a nsAHttpConnection,
+ * i.e. currently only the HTTP/3 version is implemented, therefore this will be
+ * a HttpConnectionUDP and a Http3Session. The Http3Session creates a
+ * Http3WebTransportSession. Until a response is received
+ * Http3WebTransportSession is only accessed by Http3Session. During parsing of
+ * a successful received from a server on the socket thread,
+ * WebTransportSessionProxy::mWebTransportSession will take a reference to
+ * Http3WebTransportSession and mState will be set to NEGOTIATING_SUCCEEDED.
+ * From now on WebTransportSessionProxy is responsible for closing
+ * Http3WebTransportSession if the closing of the session is initiated on the
+ * main thread. OnStartRequest and OnStopRequest will be called on the main
+ * thread. The session negotiation can have 2 outcomes:
+ * - If both calls, i.e. OnStartRequest an OnStopRequest, indicate that the
+ * request has succeeded and mState is NEGOTIATING_SUCCEEDED, the
+ * mListener->OnSessionReady will be called during OnStopRequest.
+ * - Otherwise, mListener->OnSessionClosed will be called, the state transferred
+ * into SESSION_CLOSE_PENDING, and CloseSessionInternal will be dispatched to
+ * the socket thread.
+ *
+ * CloseSession is called on the main thread. If the session is already closed
+ * it returns an error. If the session is in state NEGOTIATING or
+ * NEGOTIATING_SUCCEEDED mChannel will be canceled. If the session is in state
+ * NEGOTIATING_SUCCEEDED or ACTIVE the state transferred into
+ * SESSION_CLOSE_PENDING, and CloseSessionInternal will be dispatched to the
+ * socket thread
+ *
+ * OnSessionReadyInternal is called on the socket thread. If mState is
+ * NEGOTIATING the state will be set to NEGOTIATING_SUCCEEDED and mWebTransport
+ * will be set to the newly negotiated Http3WebTransportSession. If mState is
+ * DONE, the Http3WebTransportSession will be close.
+ *
+ * OnSessionClosed is called on the socket thread. mState will be set to
+ * CLOSE_CALLBACK_PENDING and CallOnSessionClosed will be dispatched to the main
+ * thread.
+ *
+ * mWebTransport is set during states NEGOTIATING_SUCCEEDED, ACTIVE and
+ * SESSION_CLOSE_PENDING.
+ */
+
+namespace mozilla::net {
+
+class WebTransportStreamCallbackWrapper;
+
+class WebTransportSessionProxy final : public nsIWebTransport,
+ public WebTransportSessionEventListener,
+ public nsIStreamListener,
+ public nsIChannelEventSink,
+ public nsIRedirectResultListener,
+ public nsIInterfaceRequestor {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIWEBTRANSPORT
+ NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENER
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSICHANNELEVENTSINK
+ NS_DECL_NSIREDIRECTRESULTLISTENER
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ WebTransportSessionProxy();
+
+ private:
+ ~WebTransportSessionProxy();
+
+ void CloseSessionInternal();
+ void CallOnSessionClosed();
+
+ enum WebTransportSessionProxyState {
+ INIT,
+ NEGOTIATING,
+ NEGOTIATING_SUCCEEDED,
+ ACTIVE,
+ CLOSE_CALLBACK_PENDING,
+ SESSION_CLOSE_PENDING,
+ DONE,
+ };
+ mozilla::Mutex mMutex;
+ WebTransportSessionProxyState mState MOZ_GUARDED_BY(mMutex) =
+ WebTransportSessionProxyState::INIT;
+ void ChangeState(WebTransportSessionProxyState newState);
+ void CreateStreamInternal(WebTransportStreamCallbackWrapper* aCallback,
+ bool aBidi);
+ void SendDatagramInternal(const RefPtr<Http3WebTransportSession>& aSession,
+ nsTArray<uint8_t>&& aData, uint64_t aTrackingId);
+ void NotifyDatagramReceived(nsTArray<uint8_t>&& aData);
+ void GetMaxDatagramSizeInternal(
+ const RefPtr<Http3WebTransportSession>& aSession);
+ void OnMaxDatagramSizeInternal(uint64_t aSize);
+ void OnOutgoingDatagramOutComeInternal(
+ uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome);
+
+ nsCOMPtr<nsIChannel> mChannel;
+ nsCOMPtr<nsIChannel> mRedirectChannel;
+ nsCOMPtr<WebTransportSessionEventListener> mListener MOZ_GUARDED_BY(mMutex);
+ RefPtr<Http3WebTransportSession> mWebTransportSession MOZ_GUARDED_BY(mMutex);
+ uint64_t mSessionId MOZ_GUARDED_BY(mMutex) = UINT64_MAX;
+ uint32_t mCloseStatus MOZ_GUARDED_BY(mMutex) = 0;
+ nsCString mReason MOZ_GUARDED_BY(mMutex);
+ // This is used to store events happened before OnSessionReady.
+ nsTArray<std::function<void()>> mPendingEvents MOZ_GUARDED_BY(mMutex);
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_WebTransportProxy_h
diff --git a/netwerk/protocol/webtransport/WebTransportStreamProxy.cpp b/netwerk/protocol/webtransport/WebTransportStreamProxy.cpp
new file mode 100644
index 0000000000..6046872f67
--- /dev/null
+++ b/netwerk/protocol/webtransport/WebTransportStreamProxy.cpp
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebTransportStreamProxy.h"
+
+#include "WebTransportLog.h"
+#include "Http3WebTransportStream.h"
+#include "nsProxyRelease.h"
+#include "nsSocketTransportService2.h"
+
+namespace mozilla::net {
+
+NS_IMPL_ADDREF(WebTransportStreamProxy)
+NS_IMPL_RELEASE(WebTransportStreamProxy)
+
+NS_INTERFACE_MAP_BEGIN(WebTransportStreamProxy)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebTransportReceiveStream)
+ NS_INTERFACE_MAP_ENTRY(nsIWebTransportReceiveStream)
+ NS_INTERFACE_MAP_ENTRY(nsIWebTransportSendStream)
+ NS_INTERFACE_MAP_ENTRY(nsIWebTransportBidirectionalStream)
+ NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+ NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
+ NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
+ NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
+NS_INTERFACE_MAP_END
+
+WebTransportStreamProxy::WebTransportStreamProxy(
+ Http3WebTransportStream* aStream)
+ : mWebTransportStream(aStream) {
+ mWebTransportStream->GetWriterAndReader(getter_AddRefs(mWriter),
+ getter_AddRefs(mReader));
+}
+
+WebTransportStreamProxy::~WebTransportStreamProxy() {
+ // mWebTransportStream needs to be destroyed on the socket thread.
+ NS_ProxyRelease("WebTransportStreamProxy::~WebTransportStreamProxy",
+ gSocketTransportService, mWebTransportStream.forget());
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::SendStopSending(uint8_t aError) {
+ if (!OnSocketThread()) {
+ RefPtr<WebTransportStreamProxy> self(this);
+ return gSocketTransportService->Dispatch(
+ NS_NewRunnableFunction("WebTransportStreamProxy::SendStopSending",
+ [self{std::move(self)}, error(aError)]() {
+ self->SendStopSending(error);
+ }));
+ }
+
+ mWebTransportStream->SendStopSending(aError);
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::SendFin(void) {
+ if (!mWriter) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mWriter->Close();
+
+ if (!OnSocketThread()) {
+ RefPtr<WebTransportStreamProxy> self(this);
+ return gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportStreamProxy::SendFin",
+ [self{std::move(self)}]() { self->mWebTransportStream->SendFin(); }));
+ }
+
+ mWebTransportStream->SendFin();
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::Reset(uint8_t aErrorCode) {
+ if (!mWriter) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mWriter->Close();
+
+ if (!OnSocketThread()) {
+ RefPtr<WebTransportStreamProxy> self(this);
+ return gSocketTransportService->Dispatch(
+ NS_NewRunnableFunction("WebTransportStreamProxy::Reset",
+ [self{std::move(self)}, error(aErrorCode)]() {
+ self->mWebTransportStream->Reset(error);
+ }));
+ }
+
+ mWebTransportStream->Reset(aErrorCode);
+ return NS_OK;
+}
+
+namespace {
+
+class StatsCallbackWrapper : public nsIWebTransportStreamStatsCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit StatsCallbackWrapper(nsIWebTransportStreamStatsCallback* aCallback)
+ : mCallback(aCallback), mTarget(GetCurrentEventTarget()) {}
+
+ NS_IMETHOD OnSendStatsAvailable(
+ nsIWebTransportSendStreamStats* aStats) override {
+ if (!mTarget->IsOnCurrentThread()) {
+ RefPtr<StatsCallbackWrapper> self(this);
+ nsCOMPtr<nsIWebTransportSendStreamStats> stats = aStats;
+ Unused << mTarget->Dispatch(NS_NewRunnableFunction(
+ "StatsCallbackWrapper::OnSendStatsAvailable",
+ [self{std::move(self)}, stats{std::move(stats)}]() {
+ self->OnSendStatsAvailable(stats);
+ }));
+ return NS_OK;
+ }
+
+ mCallback->OnSendStatsAvailable(aStats);
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnReceiveStatsAvailable(
+ nsIWebTransportReceiveStreamStats* aStats) override {
+ if (!mTarget->IsOnCurrentThread()) {
+ RefPtr<StatsCallbackWrapper> self(this);
+ nsCOMPtr<nsIWebTransportReceiveStreamStats> stats = aStats;
+ Unused << mTarget->Dispatch(NS_NewRunnableFunction(
+ "StatsCallbackWrapper::OnReceiveStatsAvailable",
+ [self{std::move(self)}, stats{std::move(stats)}]() {
+ self->OnReceiveStatsAvailable(stats);
+ }));
+ return NS_OK;
+ }
+
+ mCallback->OnReceiveStatsAvailable(aStats);
+ return NS_OK;
+ }
+
+ private:
+ virtual ~StatsCallbackWrapper() {
+ NS_ProxyRelease("StatsCallbackWrapper::~StatsCallbackWrapper", mTarget,
+ mCallback.forget());
+ }
+
+ nsCOMPtr<nsIWebTransportStreamStatsCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+};
+
+NS_IMPL_ISUPPORTS(StatsCallbackWrapper, nsIWebTransportStreamStatsCallback)
+
+} // namespace
+
+NS_IMETHODIMP WebTransportStreamProxy::GetSendStreamStats(
+ nsIWebTransportStreamStatsCallback* aCallback) {
+ if (!OnSocketThread()) {
+ RefPtr<WebTransportStreamProxy> self(this);
+ nsCOMPtr<nsIWebTransportStreamStatsCallback> callback =
+ new StatsCallbackWrapper(aCallback);
+ return gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportStreamProxy::GetSendStreamStats",
+ [self{std::move(self)}, callback{std::move(callback)}]() {
+ self->GetSendStreamStats(callback);
+ }));
+ }
+
+ nsCOMPtr<nsIWebTransportSendStreamStats> stats =
+ mWebTransportStream->GetSendStreamStats();
+ aCallback->OnSendStatsAvailable(stats);
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::GetReceiveStreamStats(
+ nsIWebTransportStreamStatsCallback* aCallback) {
+ if (!OnSocketThread()) {
+ RefPtr<WebTransportStreamProxy> self(this);
+ nsCOMPtr<nsIWebTransportStreamStatsCallback> callback =
+ new StatsCallbackWrapper(aCallback);
+ return gSocketTransportService->Dispatch(NS_NewRunnableFunction(
+ "WebTransportStreamProxy::GetReceiveStreamStats",
+ [self{std::move(self)}, callback{std::move(callback)}]() {
+ self->GetReceiveStreamStats(callback);
+ }));
+ }
+
+ nsCOMPtr<nsIWebTransportReceiveStreamStats> stats =
+ mWebTransportStream->GetReceiveStreamStats();
+ aCallback->OnReceiveStatsAvailable(stats);
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::Close() {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::Available(uint64_t* aAvailable) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::Read(char* aBuf, uint32_t aCount,
+ uint32_t* aResult) {
+ if (!mReader) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return mReader->Read(aBuf, aCount, aResult);
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aResult) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::IsNonBlocking(bool* aResult) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::CloseWithStatus(nsresult aStatus) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::AsyncWait(
+ nsIInputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount, nsIEventTarget* aEventTarget) {
+ if (!mReader) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return mReader->AsyncWait(aCallback, aFlags, aRequestedCount, aEventTarget);
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::AsyncWaitForRead(
+ nsIInputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount, nsIEventTarget* aTarget) {
+ return AsyncWait(aCallback, aFlags, aRequestedCount, aTarget);
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::Flush() {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::Write(const char* aBuf, uint32_t aCount,
+ uint32_t* aResult) {
+ if (!mWriter) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return mWriter->Write(aBuf, aCount, aResult);
+}
+
+// static
+nsresult WebTransportStreamProxy::WriteFromSegments(
+ nsIInputStream* input, void* closure, const char* fromSegment,
+ uint32_t offset, uint32_t count, uint32_t* countRead) {
+ WebTransportStreamProxy* self = (WebTransportStreamProxy*)closure;
+ return self->Write(fromSegment, count, countRead);
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::WriteFrom(nsIInputStream* aFromStream,
+ uint32_t aCount,
+ uint32_t* aResult) {
+ return aFromStream->ReadSegments(WriteFromSegments, this, aCount, aResult);
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::WriteSegments(nsReadSegmentFun aReader,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aResult) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP WebTransportStreamProxy::AsyncWait(
+ nsIOutputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount, nsIEventTarget* aEventTarget) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/protocol/webtransport/WebTransportStreamProxy.h b/netwerk/protocol/webtransport/WebTransportStreamProxy.h
new file mode 100644
index 0000000000..5506e86eed
--- /dev/null
+++ b/netwerk/protocol/webtransport/WebTransportStreamProxy.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_WebTransportStreamProxy_h
+#define mozilla_net_WebTransportStreamProxy_h
+
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIWebTransportStream.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla::net {
+
+class Http3WebTransportStream;
+
+class WebTransportStreamProxy final : public nsIWebTransportReceiveStream,
+ public nsIWebTransportSendStream,
+ public nsIWebTransportBidirectionalStream,
+ public nsIAsyncInputStream,
+ public nsIAsyncOutputStream {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit WebTransportStreamProxy(Http3WebTransportStream* aStream);
+
+ NS_IMETHOD SendStopSending(uint8_t aError) override;
+ NS_IMETHOD SendFin() override;
+ NS_IMETHOD Reset(uint8_t aErrorCode) override;
+ NS_IMETHOD GetSendStreamStats(
+ nsIWebTransportStreamStatsCallback* aCallback) override;
+ NS_IMETHOD GetReceiveStreamStats(
+ nsIWebTransportStreamStatsCallback* aCallback) override;
+
+ NS_IMETHOD Close() override;
+ NS_IMETHOD Available(uint64_t* aAvailable) override;
+ NS_IMETHOD Read(char* aBuf, uint32_t aCount, uint32_t* aResult) override;
+ NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ uint32_t aCount, uint32_t* aResult) override;
+ NS_IMETHOD IsNonBlocking(bool* aResult) override;
+ NS_IMETHOD CloseWithStatus(nsresult aStatus) override;
+ NS_IMETHOD AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount,
+ nsIEventTarget* aEventTarget) override;
+ // XPCConnect seems to be confused about the other AsyncWait (with
+ // nsIOutputStreamCallback), so we need this function for reading.
+ NS_IMETHOD AsyncWaitForRead(nsIInputStreamCallback* aCallback,
+ uint32_t aFlags, uint32_t aRequestedCount,
+ nsIEventTarget* aTarget) override;
+
+ NS_IMETHOD Flush() override;
+ NS_IMETHOD Write(const char* aBuf, uint32_t aCount,
+ uint32_t* aResult) override;
+ NS_IMETHOD WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
+ uint32_t* aResult) override;
+ NS_IMETHOD WriteSegments(nsReadSegmentFun aReader, void* aClosure,
+ uint32_t aCount, uint32_t* aResult) override;
+ NS_IMETHOD AsyncWait(nsIOutputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount,
+ nsIEventTarget* aEventTarget) override;
+
+ private:
+ virtual ~WebTransportStreamProxy();
+
+ static nsresult WriteFromSegments(nsIInputStream*, void*, const char*,
+ uint32_t offset, uint32_t count,
+ uint32_t* countRead);
+
+ RefPtr<Http3WebTransportStream> mWebTransportStream;
+ nsCOMPtr<nsIAsyncOutputStream> mWriter;
+ nsCOMPtr<nsIAsyncInputStream> mReader;
+};
+
+} // namespace mozilla::net
+
+#endif
diff --git a/netwerk/protocol/webtransport/moz.build b/netwerk/protocol/webtransport/moz.build
new file mode 100644
index 0000000000..ef0f6705ab
--- /dev/null
+++ b/netwerk/protocol/webtransport/moz.build
@@ -0,0 +1,34 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Networking: WebTransport")
+
+XPIDL_SOURCES += [
+ "nsIWebTransport.idl",
+ "nsIWebTransportStream.idl",
+]
+
+XPIDL_MODULE = "necko_webtransport"
+
+EXPORTS.mozilla.net += [
+ "WebTransportSessionProxy.h",
+ "WebTransportStreamProxy.h",
+]
+
+UNIFIED_SOURCES += [
+ "WebTransportSessionProxy.cpp",
+ "WebTransportStreamProxy.cpp",
+]
+
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "/netwerk/base",
+ "/netwerk/protocol/http",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/netwerk/protocol/webtransport/nsIWebTransport.idl b/netwerk/protocol/webtransport/nsIWebTransport.idl
new file mode 100644
index 0000000000..38dcf4d588
--- /dev/null
+++ b/netwerk/protocol/webtransport/nsIWebTransport.idl
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIURI.idl"
+#include "nsIPrincipal.idl"
+
+interface WebTransportSessionEventListener;
+interface nsIWebTransportStreamCallback;
+interface nsIWebTransportBidirectionalStream;
+interface nsIWebTransportSendStream;
+interface nsIWebTransportReceiveStream;
+
+%{C++
+namespace mozilla::net {
+class Http3WebTransportSession;
+class Http3WebTransportStream;
+}
+%}
+
+[ptr] native Http3WebTransportSessionPtr(mozilla::net::Http3WebTransportSession);
+[ptr] native Http3WebTransportStreamPtr(mozilla::net::Http3WebTransportStream);
+native Datagram(nsTArray<uint8_t>&&);
+
+[builtinclass, scriptable, uuid(c20d6e77-8cb1-4838-a88d-fff826080aa3)]
+interface nsIWebTransport : nsISupports {
+ cenum WebTransportError : 16 {
+ UNKNOWN_ERROR,
+ INVALID_STATE_ERROR,
+ };
+
+ // When called, perform steps in "Initialization WebTransport over HTTP".
+ void asyncConnect(in nsIURI aURI,
+ in nsIPrincipal aLoadingPrincipal,
+ in unsigned long aSecurityFlags,
+ in WebTransportSessionEventListener aListener);
+ // Asynchronously get states.
+ void getStats();
+
+ // Close the session.
+ void closeSession(in uint32_t aErrorCode,
+ in ACString aReason);
+
+ // Create and open a new WebTransport stream.
+ void createOutgoingBidirectionalStream(in nsIWebTransportStreamCallback aListener);
+ void createOutgoingUnidirectionalStream(in nsIWebTransportStreamCallback aListener);
+
+ void sendDatagram(in Array<uint8_t> aData, in uint64_t aTrackingId);
+
+ void getMaxDatagramSize();
+};
+
+// Events related to a WebTransport session.
+[scriptable, uuid(0e3cb269-f318-43c8-959e-897f57894b71)]
+interface WebTransportSessionEventListener : nsISupports {
+ // This is used to let the consumer of nsIWebTransport know that the
+ // underlying WebTransportSession object is ready to use.
+ void onSessionReady(in uint64_t aSessionId);
+ // This is used internally to pass the reference of WebTransportSession
+ // object to WebTransportSessionProxy.
+ void onSessionReadyInternal(in Http3WebTransportSessionPtr aSession);
+ void onSessionClosed(in uint32_t aErrorCode,
+ in ACString aReason);
+
+ // When a new stream has been received.
+ void onIncomingBidirectionalStreamAvailable(in nsIWebTransportBidirectionalStream aStream);
+ void onIncomingUnidirectionalStreamAvailable(in nsIWebTransportReceiveStream aStream);
+
+ // This is used internally to pass the reference of Http3WebTransportStream
+ // object to WebTransportSessionProxy.
+ void onIncomingStreamAvailableInternal(in Http3WebTransportStreamPtr aStream);
+
+ // When a new datagram has been received.
+ void onDatagramReceived(in Array<uint8_t> aData);
+
+ // This is used internally to pass the datagram to WebTransportSessionProxy.
+ void onDatagramReceivedInternal(in Datagram aData);
+
+ void onMaxDatagramSize(in uint64_t aSize);
+
+ cenum DatagramOutcome: 32 {
+ UNKNOWN = 0,
+ DROPPED_TOO_MUCH_DATA = 1,
+ SENT = 2,
+ };
+
+ void onOutgoingDatagramOutCome(
+ in uint64_t aId,
+ in WebTransportSessionEventListener_DatagramOutcome aOutCome);
+
+ // void onStatsAvailable(in WebTransportStats aStats);
+};
+
+// This interface is used as a callback when creating an outgoing
+// unidirectional or bidirectional stream.
+[scriptable, uuid(c6eeff1d-599b-40a8-9157-c7a40c3d51a2)]
+interface nsIWebTransportStreamCallback : nsISupports {
+ void onBidirectionalStreamReady(in nsIWebTransportBidirectionalStream aStream);
+ void onUnidirectionalStreamReady(in nsIWebTransportSendStream aStream);
+ void onError(in uint8_t aError);
+};
diff --git a/netwerk/protocol/webtransport/nsIWebTransportStream.idl b/netwerk/protocol/webtransport/nsIWebTransportStream.idl
new file mode 100644
index 0000000000..8917807ba7
--- /dev/null
+++ b/netwerk/protocol/webtransport/nsIWebTransportStream.idl
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIInputStreamCallback;
+interface nsIEventTarget;
+
+%{C++
+namespace mozilla {
+class TimeStamp;
+}
+%}
+
+native TimeStamp(mozilla::TimeStamp);
+
+[builtinclass, scriptable, uuid(ccc3e685-8411-48f0-8b3e-ff6d1fae4809)]
+interface nsIWebTransportSendStreamStats : nsISupports {
+ [noscript] readonly attribute TimeStamp timestamp;
+ readonly attribute unsigned long long bytesSent;
+ readonly attribute unsigned long long bytesAcknowledged;
+};
+
+[builtinclass, scriptable, uuid(43ce1145-30ef-41a7-b97d-fa797f7f7d18)]
+interface nsIWebTransportReceiveStreamStats : nsISupports {
+ [noscript] readonly attribute TimeStamp timestamp;
+ readonly attribute unsigned long long bytesReceived;
+};
+
+[scriptable, uuid(9c1df3f5-bf04-46b6-9977-eb6389076db8)]
+interface nsIWebTransportStreamStatsCallback : nsISupports {
+ void onSendStatsAvailable(in nsIWebTransportSendStreamStats aStats);
+ void onReceiveStatsAvailable(in nsIWebTransportReceiveStreamStats aStats);
+};
+
+[builtinclass, scriptable, uuid(d461b235-6291-4817-adcc-a2a3b3dfc10b)]
+interface nsIWebTransportReceiveStream : nsISupports {
+ // Sends the STOP_SENDING on the stream.
+ void sendStopSending(in uint8_t aError);
+
+ void getReceiveStreamStats(
+ in nsIWebTransportStreamStatsCallback aCallback);
+
+ void asyncWaitForRead(in nsIInputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+};
+
+[builtinclass, scriptable, uuid(804f245c-52ea-403c-8a78-f751533bdd70)]
+interface nsIWebTransportSendStream : nsISupports {
+ // Sends the FIN on the stream.
+ void sendFin();
+
+ // Reset the stream with the specified error code.
+ void reset(in uint8_t aErrorCode);
+
+ void getSendStreamStats(in nsIWebTransportStreamStatsCallback aCallback);
+};
+
+[builtinclass, scriptable, uuid(f9ecb509-36db-4689-97d6-137639a08750)]
+interface nsIWebTransportBidirectionalStream : nsISupports {
+ // Sends the STOP_SENDING on the stream.
+ void sendStopSending(in uint8_t aError);
+
+ // Sends the FIN on the stream.
+ void sendFin();
+
+ // Reset the stream with the specified error code.
+ void reset(in uint8_t aErrorCode);
+
+ void asyncWaitForRead(in nsIInputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+};