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
765 lines
22 KiB
C++
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
|