Files
tubestation/netwerk/protocol/http/Http2StreamTunnel.cpp
Nika Layzell 2e6ea183a0 Bug 1818305 - Part 2: Add a streamStatus method to nsIInputStream, r=xpcom-reviewers,necko-reviewers,geckoview-reviewers,valentin,jesup,m_kato,mccr8
This is semantically similar to the existing available() method, however will
not block, and doesn't need to do the work to actually determine the number of
available bytes.

As part of this patch, I also fixed one available() implementation which was
incorrectly throwing NS_BASE_STREAM_WOULD_BLOCK.

Differential Revision: https://phabricator.services.mozilla.com/D170697
2023-03-15 19:52:34 +00:00

765 lines
22 KiB
C++

/* -*- 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