summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/Http2StreamTunnel.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /netwerk/protocol/http/Http2StreamTunnel.cpp
parentInitial commit. (diff)
downloadfirefox-esr-upstream/115.8.0esr.tar.xz
firefox-esr-upstream/115.8.0esr.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/protocol/http/Http2StreamTunnel.cpp')
-rw-r--r--netwerk/protocol/http/Http2StreamTunnel.cpp764
1 files changed, 764 insertions, 0 deletions
diff --git a/netwerk/protocol/http/Http2StreamTunnel.cpp b/netwerk/protocol/http/Http2StreamTunnel.cpp
new file mode 100644
index 0000000000..4e0f05d58b
--- /dev/null
+++ b/netwerk/protocol/http/Http2StreamTunnel.cpp
@@ -0,0 +1,764 @@
+/* -*- 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/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+// Log on level :5, instead of default :4.
+#undef LOG
+#define LOG(args) LOG5(args)
+#undef LOG_ENABLED
+#define LOG_ENABLED() LOG5_ENABLED()
+
+#include "nsHttpHandler.h"
+#include "Http2StreamTunnel.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsQueryObject.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla::net {
+
+bool Http2StreamTunnel::DispatchRelease() {
+ if (OnSocketThread()) {
+ return false;
+ }
+
+ gSocketTransportService->Dispatch(
+ NewNonOwningRunnableMethod("net::Http2StreamTunnel::Release", this,
+ &Http2StreamTunnel::Release),
+ NS_DISPATCH_NORMAL);
+
+ return true;
+}
+
+NS_IMPL_ADDREF(Http2StreamTunnel)
+NS_IMETHODIMP_(MozExternalRefCountType)
+Http2StreamTunnel::Release() {
+ nsrefcnt count = mRefCnt - 1;
+ if (DispatchRelease()) {
+ // Redispatched to the socket thread.
+ return count;
+ }
+
+ MOZ_ASSERT(0 != mRefCnt, "dup release");
+ count = --mRefCnt;
+ NS_LOG_RELEASE(this, count, "Http2StreamTunnel");
+
+ if (0 == count) {
+ mRefCnt = 1;
+ delete (this);
+ return 0;
+ }
+
+ return count;
+}
+
+NS_INTERFACE_MAP_BEGIN(Http2StreamTunnel)
+ NS_INTERFACE_MAP_ENTRY(nsITransport)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(Http2StreamTunnel)
+ NS_INTERFACE_MAP_ENTRY(nsITransport)
+ NS_INTERFACE_MAP_ENTRY(nsISocketTransport)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END_INHERITING(Http2StreamTunnel)
+
+Http2StreamTunnel::Http2StreamTunnel(Http2Session* session, int32_t priority,
+ uint64_t bcId,
+ nsHttpConnectionInfo* aConnectionInfo)
+ : Http2StreamBase(0, session, priority, bcId),
+ mConnectionInfo(aConnectionInfo) {}
+
+Http2StreamTunnel::~Http2StreamTunnel() { ClearTransactionsBlockedOnTunnel(); }
+
+void Http2StreamTunnel::HandleResponseHeaders(nsACString& aHeadersOut,
+ int32_t httpResponseCode) {}
+
+// TODO We do not need this. Fix in bug 1772212.
+void Http2StreamTunnel::ClearTransactionsBlockedOnTunnel() {
+ nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnectionInfo);
+ if (NS_FAILED(rv)) {
+ LOG3(
+ ("Http2StreamTunnel::ClearTransactionsBlockedOnTunnel %p\n"
+ " ProcessPendingQ failed: %08x\n",
+ this, static_cast<uint32_t>(rv)));
+ }
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetKeepaliveEnabled(bool aKeepaliveEnabled) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetKeepaliveVals(int32_t keepaliveIdleTime,
+ int32_t keepaliveRetryInterval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetSecurityCallbacks(
+ nsIInterfaceRequestor** aSecurityCallbacks) {
+ return mSocketTransport->GetSecurityCallbacks(aSecurityCallbacks);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetSecurityCallbacks(
+ nsIInterfaceRequestor* aSecurityCallbacks) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize,
+ uint32_t aSegmentCount,
+ nsIInputStream** _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize,
+ uint32_t aSegmentCount,
+ nsIOutputStream** _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void Http2StreamTunnel::CloseStream(nsresult aReason) {
+ LOG(("Http2StreamTunnel::CloseStream this=%p", this));
+ RefPtr<Http2Session> session = Session();
+ if (NS_SUCCEEDED(mCondition)) {
+ mSession = nullptr;
+ // Let the session pickup that the stream has been closed.
+ mCondition = aReason;
+ if (NS_SUCCEEDED(aReason)) {
+ aReason = NS_BASE_STREAM_CLOSED;
+ }
+ mOutput->OnSocketReady(aReason);
+ mInput->OnSocketReady(aReason);
+ }
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::Close(nsresult aReason) {
+ LOG(("Http2StreamTunnel::Close this=%p", this));
+ RefPtr<Http2Session> session = Session();
+ if (NS_SUCCEEDED(mCondition)) {
+ mSession = nullptr;
+ if (NS_SUCCEEDED(aReason)) {
+ aReason = NS_BASE_STREAM_CLOSED;
+ }
+ mOutput->CloseWithStatus(aReason);
+ mInput->CloseWithStatus(aReason);
+ // Let the session pickup that the stream has been closed.
+ mCondition = aReason;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetEventSink(nsITransportEventSink* aSink,
+ nsIEventTarget* aEventTarget) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::Bind(NetAddr* aLocalAddr) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetEchConfigUsed(bool* aEchConfigUsed) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetEchConfig(const nsACString& aEchConfig) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::ResolvedByTRR(bool* aResolvedByTRR) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP Http2StreamTunnel::GetEffectiveTRRMode(
+ nsIRequest::TRRMode* aEffectiveTRRMode) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP Http2StreamTunnel::GetTrrSkipReason(
+ nsITRRSkipReason::value* aTrrSkipReason) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::IsAlive(bool* aAlive) {
+ RefPtr<Http2Session> session = Session();
+ if (mSocketTransport && session) {
+ return mSocketTransport->IsAlive(aAlive);
+ }
+ *aAlive = false;
+ return NS_OK;
+}
+
+#define FWD_TS_T_PTR(fx, ts) \
+ NS_IMETHODIMP \
+ Http2StreamTunnel::fx(ts* arg) { return mSocketTransport->fx(arg); }
+
+#define FWD_TS_T_ADDREF(fx, ts) \
+ NS_IMETHODIMP \
+ Http2StreamTunnel::fx(ts** arg) { return mSocketTransport->fx(arg); }
+
+#define FWD_TS_T(fx, ts) \
+ NS_IMETHODIMP \
+ Http2StreamTunnel::fx(ts arg) { return mSocketTransport->fx(arg); }
+
+FWD_TS_T_PTR(GetKeepaliveEnabled, bool);
+FWD_TS_T_PTR(GetSendBufferSize, uint32_t);
+FWD_TS_T(SetSendBufferSize, uint32_t);
+FWD_TS_T_PTR(GetPort, int32_t);
+FWD_TS_T_PTR(GetPeerAddr, mozilla::net::NetAddr);
+FWD_TS_T_PTR(GetSelfAddr, mozilla::net::NetAddr);
+FWD_TS_T_ADDREF(GetScriptablePeerAddr, nsINetAddr);
+FWD_TS_T_ADDREF(GetScriptableSelfAddr, nsINetAddr);
+FWD_TS_T_ADDREF(GetTlsSocketControl, nsITLSSocketControl);
+FWD_TS_T_PTR(GetConnectionFlags, uint32_t);
+FWD_TS_T(SetConnectionFlags, uint32_t);
+FWD_TS_T(SetIsPrivate, bool);
+FWD_TS_T_PTR(GetTlsFlags, uint32_t);
+FWD_TS_T(SetTlsFlags, uint32_t);
+FWD_TS_T_PTR(GetRecvBufferSize, uint32_t);
+FWD_TS_T(SetRecvBufferSize, uint32_t);
+FWD_TS_T_PTR(GetResetIPFamilyPreference, bool);
+
+nsresult Http2StreamTunnel::GetOriginAttributes(
+ mozilla::OriginAttributes* aOriginAttributes) {
+ return mSocketTransport->GetOriginAttributes(aOriginAttributes);
+}
+
+nsresult Http2StreamTunnel::SetOriginAttributes(
+ const mozilla::OriginAttributes& aOriginAttributes) {
+ return mSocketTransport->SetOriginAttributes(aOriginAttributes);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetScriptableOriginAttributes(
+ JSContext* aCx, JS::MutableHandle<JS::Value> aOriginAttributes) {
+ return mSocketTransport->GetScriptableOriginAttributes(aCx,
+ aOriginAttributes);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetScriptableOriginAttributes(
+ JSContext* aCx, JS::Handle<JS::Value> aOriginAttributes) {
+ return mSocketTransport->SetScriptableOriginAttributes(aCx,
+ aOriginAttributes);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetHost(nsACString& aHost) {
+ return mSocketTransport->GetHost(aHost);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetTimeout(uint32_t aType, uint32_t* _retval) {
+ return mSocketTransport->GetTimeout(aType, _retval);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetTimeout(uint32_t aType, uint32_t aValue) {
+ return mSocketTransport->SetTimeout(aType, aValue);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetReuseAddrPort(bool aReuseAddrPort) {
+ return mSocketTransport->SetReuseAddrPort(aReuseAddrPort);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetLinger(bool aPolarity, int16_t aTimeout) {
+ return mSocketTransport->SetLinger(aPolarity, aTimeout);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetQoSBits(uint8_t* aQoSBits) {
+ return mSocketTransport->GetQoSBits(aQoSBits);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::SetQoSBits(uint8_t aQoSBits) {
+ return mSocketTransport->SetQoSBits(aQoSBits);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetRetryDnsIfPossible(bool* aRetry) {
+ return mSocketTransport->GetRetryDnsIfPossible(aRetry);
+}
+
+NS_IMETHODIMP
+Http2StreamTunnel::GetStatus(nsresult* aStatus) {
+ return mSocketTransport->GetStatus(aStatus);
+}
+
+already_AddRefed<nsHttpConnection> Http2StreamTunnel::CreateHttpConnection(
+ nsAHttpTransaction* httpTransaction, nsIInterfaceRequestor* aCallbacks,
+ PRIntervalTime aRtt, bool aIsWebSocket) {
+ mInput = new InputStreamTunnel(this);
+ mOutput = new OutputStreamTunnel(this);
+ RefPtr<nsHttpConnection> conn = new nsHttpConnection();
+
+ conn->SetTransactionCaps(httpTransaction->Caps());
+ nsresult rv =
+ conn->Init(httpTransaction->ConnectionInfo(),
+ gHttpHandler->ConnMgr()->MaxRequestDelay(), this, mInput,
+ mOutput, true, NS_OK, aCallbacks, aRtt, aIsWebSocket);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ mTransaction = httpTransaction;
+ return conn.forget();
+}
+
+nsresult Http2StreamTunnel::CallToReadData(uint32_t count,
+ uint32_t* countRead) {
+ LOG(("Http2StreamTunnel::CallToReadData this=%p", this));
+ return mOutput->OnSocketReady(NS_OK);
+}
+
+nsresult Http2StreamTunnel::CallToWriteData(uint32_t count,
+ uint32_t* countWritten) {
+ LOG(("Http2StreamTunnel::CallToWriteData this=%p", this));
+ if (!mInput->HasCallback()) {
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+ return mInput->OnSocketReady(NS_OK);
+}
+
+nsresult Http2StreamTunnel::GenerateHeaders(nsCString& aCompressedData,
+ uint8_t& firstFrameFlags) {
+ nsAutoCString authorityHeader;
+ authorityHeader = mConnectionInfo->GetOrigin();
+ authorityHeader.Append(':');
+ authorityHeader.AppendInt(mConnectionInfo->OriginPort());
+
+ RefPtr<Http2Session> session = Session();
+
+ LOG3(("Http2StreamTunnel %p Stream ID 0x%X [session=%p] for %s\n", this,
+ mStreamID, session.get(), authorityHeader.get()));
+
+ mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
+
+ nsresult rv = session->Compressor()->EncodeHeaderBlock(
+ mFlatHttpRequestHeaders, "CONNECT"_ns, EmptyCString(), authorityHeader,
+ EmptyCString(), EmptyCString(), true, aCompressedData);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The size of the input headers is approximate
+ uint32_t ratio =
+ aCompressedData.Length() * 100 /
+ (11 + authorityHeader.Length() + mFlatHttpRequestHeaders.Length());
+
+ Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
+
+ return NS_OK;
+}
+
+OutputStreamTunnel::OutputStreamTunnel(Http2StreamTunnel* aStream) {
+ mWeakStream = do_GetWeakReference(aStream);
+}
+
+OutputStreamTunnel::~OutputStreamTunnel() {
+ NS_ProxyRelease("OutputStreamTunnel::~OutputStreamTunnel",
+ gSocketTransportService, mWeakStream.forget());
+}
+
+nsresult OutputStreamTunnel::OnSocketReady(nsresult condition) {
+ LOG(("OutputStreamTunnel::OnSocketReady [this=%p cond=%" PRIx32
+ " callback=%p]\n",
+ this, static_cast<uint32_t>(condition), mCallback.get()));
+
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ nsCOMPtr<nsIOutputStreamCallback> callback;
+
+ // update condition, but be careful not to erase an already
+ // existing error condition.
+ if (NS_SUCCEEDED(mCondition)) {
+ mCondition = condition;
+ }
+ callback = std::move(mCallback);
+
+ nsresult rv = NS_OK;
+ if (callback) {
+ rv = callback->OnOutputStreamReady(this);
+ MaybeSetRequestDone(callback);
+ }
+
+ return rv;
+}
+
+void OutputStreamTunnel::MaybeSetRequestDone(
+ nsIOutputStreamCallback* aCallback) {
+ RefPtr<nsHttpConnection> conn = do_QueryObject(aCallback);
+ if (!conn) {
+ return;
+ }
+
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ if (conn->RequestDone()) {
+ tunnel->SetRequestDone();
+ }
+}
+
+NS_IMPL_ISUPPORTS(OutputStreamTunnel, nsIOutputStream, nsIAsyncOutputStream)
+
+NS_IMETHODIMP
+OutputStreamTunnel::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
+
+NS_IMETHODIMP
+OutputStreamTunnel::Flush() { return NS_OK; }
+
+NS_IMETHODIMP
+OutputStreamTunnel::StreamStatus() { return mCondition; }
+
+NS_IMETHODIMP
+OutputStreamTunnel::Write(const char* buf, uint32_t count,
+ uint32_t* countWritten) {
+ LOG(("OutputStreamTunnel::Write [this=%p count=%u]\n", this, count));
+
+ *countWritten = 0;
+ if (NS_FAILED(mCondition)) {
+ return mCondition;
+ }
+
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return tunnel->OnReadSegment(buf, count, countWritten);
+}
+
+NS_IMETHODIMP
+OutputStreamTunnel::WriteSegments(nsReadSegmentFun reader, void* closure,
+ uint32_t count, uint32_t* countRead) {
+ // stream is unbuffered
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OutputStreamTunnel::WriteFrom(nsIInputStream* stream, uint32_t count,
+ uint32_t* countRead) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+OutputStreamTunnel::IsNonBlocking(bool* nonblocking) {
+ *nonblocking = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+OutputStreamTunnel::CloseWithStatus(nsresult reason) {
+ LOG(("OutputStreamTunnel::CloseWithStatus [this=%p reason=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(reason)));
+ mCondition = reason;
+
+ RefPtr<Http2StreamTunnel> tunnel = do_QueryReferent(mWeakStream);
+ mWeakStream = nullptr;
+ if (!tunnel) {
+ return NS_OK;
+ }
+ RefPtr<Http2Session> session = tunnel->Session();
+ if (!session) {
+ return NS_OK;
+ }
+ session->CleanupStream(tunnel, reason, Http2Session::CANCEL_ERROR);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+OutputStreamTunnel::AsyncWait(nsIOutputStreamCallback* callback, uint32_t flags,
+ uint32_t amount, nsIEventTarget* target) {
+ LOG(("OutputStreamTunnel::AsyncWait [this=%p]\n", this));
+
+ // The following parametr are not used:
+ MOZ_ASSERT(!flags);
+ MOZ_ASSERT(!amount);
+ Unused << target;
+
+ RefPtr<OutputStreamTunnel> self(this);
+ if (NS_FAILED(mCondition)) {
+ Unused << NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ "OutputStreamTunnel::CallOnSocketReady",
+ [self{std::move(self)}]() { self->OnSocketReady(NS_OK); }));
+ } else if (callback) {
+ // Inform the proxy connection that the inner connetion wants to
+ // read data.
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ RefPtr<Http2Session> session;
+ rv = GetSession(getter_AddRefs(session));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ session->TransactionHasDataToWrite(tunnel);
+ }
+
+ mCallback = callback;
+ return NS_OK;
+}
+
+InputStreamTunnel::InputStreamTunnel(Http2StreamTunnel* aStream) {
+ mWeakStream = do_GetWeakReference(aStream);
+}
+
+InputStreamTunnel::~InputStreamTunnel() {
+ NS_ProxyRelease("InputStreamTunnel::~InputStreamTunnel",
+ gSocketTransportService, mWeakStream.forget());
+}
+
+nsresult InputStreamTunnel::OnSocketReady(nsresult condition) {
+ LOG(("InputStreamTunnel::OnSocketReady [this=%p cond=%" PRIx32 "]\n", this,
+ static_cast<uint32_t>(condition)));
+
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ nsCOMPtr<nsIInputStreamCallback> callback;
+
+ // update condition, but be careful not to erase an already
+ // existing error condition.
+ if (NS_SUCCEEDED(mCondition)) {
+ mCondition = condition;
+ }
+ callback = std::move(mCallback);
+
+ return callback ? callback->OnInputStreamReady(this) : NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(InputStreamTunnel, nsIInputStream, nsIAsyncInputStream)
+
+NS_IMETHODIMP
+InputStreamTunnel::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
+
+NS_IMETHODIMP
+InputStreamTunnel::Available(uint64_t* avail) {
+ LOG(("InputStreamTunnel::Available [this=%p]\n", this));
+
+ if (NS_FAILED(mCondition)) {
+ return mCondition;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+InputStreamTunnel::StreamStatus() {
+ LOG(("InputStreamTunnel::StreamStatus [this=%p]\n", this));
+
+ return mCondition;
+}
+
+NS_IMETHODIMP
+InputStreamTunnel::Read(char* buf, uint32_t count, uint32_t* countRead) {
+ LOG(("InputStreamTunnel::Read [this=%p count=%u]\n", this, count));
+
+ *countRead = 0;
+
+ if (NS_FAILED(mCondition)) {
+ return mCondition;
+ }
+
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return tunnel->OnWriteSegment(buf, count, countRead);
+}
+
+NS_IMETHODIMP
+InputStreamTunnel::ReadSegments(nsWriteSegmentFun writer, void* closure,
+ uint32_t count, uint32_t* countRead) {
+ // socket stream is unbuffered
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+InputStreamTunnel::IsNonBlocking(bool* nonblocking) {
+ *nonblocking = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InputStreamTunnel::CloseWithStatus(nsresult reason) {
+ LOG(("InputStreamTunnel::CloseWithStatus [this=%p reason=%" PRIx32 "]\n",
+ this, static_cast<uint32_t>(reason)));
+ mCondition = reason;
+
+ RefPtr<Http2StreamTunnel> tunnel = do_QueryReferent(mWeakStream);
+ mWeakStream = nullptr;
+ if (!tunnel) {
+ return NS_OK;
+ }
+ RefPtr<Http2Session> session = tunnel->Session();
+ if (!session) {
+ return NS_OK;
+ }
+ session->CleanupStream(tunnel, reason, Http2Session::CANCEL_ERROR);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InputStreamTunnel::AsyncWait(nsIInputStreamCallback* callback, uint32_t flags,
+ uint32_t amount, nsIEventTarget* target) {
+ LOG(("InputStreamTunnel::AsyncWait [this=%p mCondition=%x]\n", this,
+ static_cast<uint32_t>(mCondition)));
+
+ // The following parametr are not used:
+ MOZ_ASSERT(!flags);
+ MOZ_ASSERT(!amount);
+ Unused << target;
+
+ RefPtr<InputStreamTunnel> self(this);
+ if (NS_FAILED(mCondition)) {
+ Unused << NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ "InputStreamTunnel::CallOnSocketReady",
+ [self{std::move(self)}]() { self->OnSocketReady(NS_OK); }));
+ } else if (callback) {
+ // Inform the proxy connection that the inner connetion wants to
+ // read data.
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ RefPtr<Http2Session> session;
+ rv = GetSession(getter_AddRefs(session));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (tunnel->DataBuffered()) {
+ session->TransactionHasDataToRecv(tunnel);
+ }
+ }
+
+ mCallback = callback;
+ return NS_OK;
+}
+
+nsresult OutputStreamTunnel::GetStream(Http2StreamTunnel** aStream) {
+ RefPtr<Http2StreamTunnel> tunnel = do_QueryReferent(mWeakStream);
+ MOZ_ASSERT(tunnel);
+ if (!tunnel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ tunnel.forget(aStream);
+ return NS_OK;
+}
+
+nsresult OutputStreamTunnel::GetSession(Http2Session** aSession) {
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ RefPtr<Http2Session> session = tunnel->Session();
+ MOZ_ASSERT(session);
+ if (!session) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ session.forget(aSession);
+ return NS_OK;
+}
+
+nsresult InputStreamTunnel::GetStream(Http2StreamTunnel** aStream) {
+ RefPtr<Http2StreamTunnel> tunnel = do_QueryReferent(mWeakStream);
+ MOZ_ASSERT(tunnel);
+ if (!tunnel) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ tunnel.forget(aStream);
+ return NS_OK;
+}
+
+nsresult InputStreamTunnel::GetSession(Http2Session** aSession) {
+ RefPtr<Http2StreamTunnel> tunnel;
+ nsresult rv = GetStream(getter_AddRefs(tunnel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ RefPtr<Http2Session> session = tunnel->Session();
+ if (!session) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ session.forget(aSession);
+ return NS_OK;
+}
+
+Http2StreamWebSocket::Http2StreamWebSocket(
+ Http2Session* session, int32_t priority, uint64_t bcId,
+ nsHttpConnectionInfo* aConnectionInfo)
+ : Http2StreamTunnel(session, priority, bcId, aConnectionInfo) {
+ LOG(("Http2StreamWebSocket ctor:%p", this));
+}
+
+Http2StreamWebSocket::~Http2StreamWebSocket() {
+ LOG(("Http2StreamWebSocket dtor:%p", this));
+}
+
+nsresult Http2StreamWebSocket::GenerateHeaders(nsCString& aCompressedData,
+ uint8_t& firstFrameFlags) {
+ nsHttpRequestHead* head = mTransaction->RequestHead();
+
+ nsAutoCString authorityHeader;
+ nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<Http2Session> session = Session();
+ LOG3(("Http2StreamWebSocket %p Stream ID 0x%X [session=%p] for %s\n", this,
+ mStreamID, session.get(), authorityHeader.get()));
+
+ nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
+ nsAutoCString path;
+ head->Path(path);
+
+ rv = session->Compressor()->EncodeHeaderBlock(
+ mFlatHttpRequestHeaders, "CONNECT"_ns, path, authorityHeader, scheme,
+ "websocket"_ns, false, aCompressedData);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
+
+ // The size of the input headers is approximate
+ uint32_t ratio =
+ aCompressedData.Length() * 100 /
+ (11 + authorityHeader.Length() + mFlatHttpRequestHeaders.Length());
+
+ Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
+ return NS_OK;
+}
+
+void Http2StreamWebSocket::CloseStream(nsresult aReason) {
+ LOG(("Http2StreamWebSocket::CloseStream this=%p aReason=%x", this,
+ static_cast<uint32_t>(aReason)));
+ if (mTransaction) {
+ mTransaction->Close(aReason);
+ mTransaction = nullptr;
+ }
+ Http2StreamTunnel::CloseStream(aReason);
+}
+
+} // namespace mozilla::net