diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /netwerk/protocol/http/Http2StreamTunnel.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | netwerk/protocol/http/Http2StreamTunnel.cpp | 764 |
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..b3147fb732 --- /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) { + 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, false); + 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 |