зеркало из https://github.com/mozilla/gecko-dev.git
765 строки
22 KiB
C++
765 строки
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, 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
|