Source code

Revision control

Copy as Markdown

Other Tools

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