/* -*- 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" #include "Http2Session.h" #include "nsHttp.h" #include "nsHttpHandler.h" #include "nsHttpRequestHead.h" #include "nsISocketProvider.h" #include "nsISocketProviderService.h" #include "nsISSLSocketControl.h" #include "nsISocketTransport.h" #include "nsISupportsPriority.h" #include "nsNetAddr.h" #include "prerror.h" #include "prio.h" #include "TunnelUtils.h" #include "nsNetCID.h" #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" namespace mozilla { namespace net { static PRDescIdentity sLayerIdentity; static PRIOMethods sLayerMethods; static PRIOMethods *sLayerMethodsPtr = nullptr; TLSFilterTransaction::TLSFilterTransaction(nsAHttpTransaction *aWrapped, const char *aTLSHost, int32_t aTLSPort, nsAHttpSegmentReader *aReader, nsAHttpSegmentWriter *aWriter) : mTransaction(aWrapped) , mEncryptedTextUsed(0) , mEncryptedTextSize(0) , mSegmentReader(aReader) , mSegmentWriter(aWriter) , mForce(false) , mNudgeCounter(0) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("TLSFilterTransaction ctor %p\n", this)); nsCOMPtr provider; nsCOMPtr spserv = do_GetService(NS_SOCKETPROVIDERSERVICE_CONTRACTID); if (spserv) { spserv->GetSocketProvider("ssl", getter_AddRefs(provider)); } // Install an NSPR layer to handle getpeername() with a failure. This is kind // of silly, but the default one used by the pipe asserts when called and the // nss code calls it to see if we are connected to a real socket or not. if (!sLayerMethodsPtr) { // one time initialization sLayerIdentity = PR_GetUniqueIdentity("TLSFilterTransaction Layer"); sLayerMethods = *PR_GetDefaultIOMethods(); sLayerMethods.getpeername = GetPeerName; sLayerMethods.getsocketoption = GetSocketOption; sLayerMethods.setsocketoption = SetSocketOption; sLayerMethods.read = FilterRead; sLayerMethods.write = FilterWrite; sLayerMethods.send = FilterSend; sLayerMethods.recv = FilterRecv; sLayerMethods.close = FilterClose; sLayerMethodsPtr = &sLayerMethods; } mFD = PR_CreateIOLayerStub(sLayerIdentity, &sLayerMethods); if (provider && mFD) { mFD->secret = reinterpret_cast(this); provider->AddToSocket(PR_AF_INET, aTLSHost, aTLSPort, nullptr, 0, mFD, getter_AddRefs(mSecInfo)); } if (mTransaction) { nsCOMPtr callbacks; mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); nsCOMPtr secCtrl(do_QueryInterface(mSecInfo)); if (secCtrl) { secCtrl->SetNotificationCallbacks(callbacks); } } } TLSFilterTransaction::~TLSFilterTransaction() { LOG(("TLSFilterTransaction dtor %p\n", this)); Cleanup(); } void TLSFilterTransaction::Cleanup() { if (mTransaction) { mTransaction->Close(NS_ERROR_ABORT); mTransaction = nullptr; } if (mFD) { PR_Close(mFD); mFD = nullptr; } mSecInfo = nullptr; if (mTimer) { mTimer->Cancel(); mTimer = nullptr; } } void TLSFilterTransaction::Close(nsresult aReason) { if (!mTransaction) { return; } if (mTimer) { mTimer->Cancel(); mTimer = nullptr; } mTransaction->Close(aReason); mTransaction = nullptr; } nsresult TLSFilterTransaction::OnReadSegment(const char *aData, uint32_t aCount, uint32_t *outCountRead) { LOG(("TLSFilterTransaction %p OnReadSegment %d (buffered %d)\n", this, aCount, mEncryptedTextUsed)); mReadSegmentBlocked = false; MOZ_ASSERT(mSegmentReader); if (!mSecInfo) { return NS_ERROR_FAILURE; } nsresult rv; *outCountRead = 0; // get rid of buffer first if (mEncryptedTextUsed) { rv = mSegmentReader->CommitToSegmentSize(mEncryptedTextUsed, mForce); if (rv == NS_BASE_STREAM_WOULD_BLOCK) { return rv; } uint32_t amt; rv = mSegmentReader->OnReadSegment(mEncryptedText.get(), mEncryptedTextUsed, &amt); if (NS_FAILED(rv)) { return rv; } mEncryptedTextUsed -= amt; if (mEncryptedTextUsed) { memmove(mEncryptedText.get(), &mEncryptedText[amt], mEncryptedTextUsed); return NS_OK; } } // encrypt for network write // write aData down the SSL layer into the FilterWrite() method where it will // be queued into mEncryptedText. We need to copy it like this in order to // guarantee atomic writes EnsureBuffer(mEncryptedText, aCount + 4096, 0, mEncryptedTextSize); while (aCount > 0) { int32_t written = PR_Write(mFD, aData, aCount); LOG(("TLSFilterTransaction %p OnReadSegment PRWrite(%d) = %d %d\n", this, aCount, written, PR_GetError() == PR_WOULD_BLOCK_ERROR)); if (written < 1) { if (*outCountRead) { return NS_OK; } // mTransaction ReadSegments actually obscures this code, so // keep it in a member var for this::ReadSegments to insepct. Similar // to nsHttpConnection::mSocketOutCondition mReadSegmentBlocked = (PR_GetError() == PR_WOULD_BLOCK_ERROR); return mReadSegmentBlocked ? NS_BASE_STREAM_WOULD_BLOCK : NS_ERROR_FAILURE; } aCount -= written; aData += written; *outCountRead += written; mNudgeCounter = 0; } LOG(("TLSFilterTransaction %p OnReadSegment2 (buffered %d)\n", this, mEncryptedTextUsed)); uint32_t amt = 0; if (mEncryptedTextUsed) { // If we are tunneled on spdy CommitToSegmentSize will prevent partial // writes that could interfere with multiplexing. H1 is fine with // partial writes. rv = mSegmentReader->CommitToSegmentSize(mEncryptedTextUsed, mForce); if (rv != NS_BASE_STREAM_WOULD_BLOCK) { rv = mSegmentReader->OnReadSegment(mEncryptedText.get(), mEncryptedTextUsed, &amt); } if (rv == NS_BASE_STREAM_WOULD_BLOCK) { // return OK because all the data was consumed and stored in this buffer Connection()->TransactionHasDataToWrite(this); return NS_OK; } else if (NS_FAILED(rv)) { return rv; } } if (amt == mEncryptedTextUsed) { mEncryptedText = nullptr; mEncryptedTextUsed = 0; mEncryptedTextSize = 0; } else { memmove(mEncryptedText.get(), &mEncryptedText[amt], mEncryptedTextUsed - amt); mEncryptedTextUsed -= amt; } return NS_OK; } int32_t TLSFilterTransaction::FilterOutput(const char *aBuf, int32_t aAmount) { EnsureBuffer(mEncryptedText, mEncryptedTextUsed + aAmount, mEncryptedTextUsed, mEncryptedTextSize); memcpy(&mEncryptedText[mEncryptedTextUsed], aBuf, aAmount); mEncryptedTextUsed += aAmount; return aAmount; } nsresult TLSFilterTransaction::CommitToSegmentSize(uint32_t size, bool forceCommitment) { if (!mSegmentReader) { return NS_ERROR_FAILURE; } // pad the commit by a little bit to leave room for encryption overhead // this isn't foolproof and we may still have to buffer, but its a good start mForce = forceCommitment; return mSegmentReader->CommitToSegmentSize(size + 1024, forceCommitment); } nsresult TLSFilterTransaction::OnWriteSegment(char *aData, uint32_t aCount, uint32_t *outCountRead) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(mSegmentWriter); LOG(("TLSFilterTransaction::OnWriteSegment %p max=%d\n", this, aCount)); if (!mSecInfo) { return NS_ERROR_FAILURE; } // this will call through to FilterInput to get data from the higher // level connection before removing the local TLS layer mFilterReadCode = NS_OK; int32_t bytesRead = PR_Read(mFD, aData, aCount); if (bytesRead == -1) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) { return NS_BASE_STREAM_WOULD_BLOCK; } return NS_ERROR_FAILURE; } *outCountRead = bytesRead; if (NS_SUCCEEDED(mFilterReadCode) && !bytesRead) { LOG(("TLSFilterTransaction::OnWriteSegment %p " "Second layer of TLS stripping results in STREAM_CLOSED\n", this)); mFilterReadCode = NS_BASE_STREAM_CLOSED; } LOG(("TLSFilterTransaction::OnWriteSegment %p rv=%x didread=%d " "2 layers of ssl stripped to plaintext\n", this, mFilterReadCode, bytesRead)); return mFilterReadCode; } int32_t TLSFilterTransaction::FilterInput(char *aBuf, int32_t aAmount) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(mSegmentWriter); LOG(("TLSFilterTransaction::FilterInput max=%d\n", aAmount)); uint32_t outCountRead = 0; mFilterReadCode = mSegmentWriter->OnWriteSegment(aBuf, aAmount, &outCountRead); if (NS_SUCCEEDED(mFilterReadCode) && outCountRead) { LOG(("TLSFilterTransaction::FilterInput rv=%x read=%d input from net " "1 layer stripped, 1 still on\n", mFilterReadCode, outCountRead)); if (mReadSegmentBlocked) { mNudgeCounter = 0; } } if (mFilterReadCode == NS_BASE_STREAM_WOULD_BLOCK) { PR_SetError(PR_WOULD_BLOCK_ERROR, 0); return -1; } return outCountRead; } nsresult TLSFilterTransaction::ReadSegments(nsAHttpSegmentReader *aReader, uint32_t aCount, uint32_t *outCountRead) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("TLSFilterTransaction::ReadSegments %p max=%d\n", this, aCount)); if (!mTransaction) { return NS_ERROR_UNEXPECTED; } mReadSegmentBlocked = false; mSegmentReader = aReader; nsresult rv = mTransaction->ReadSegments(this, aCount, outCountRead); LOG(("TLSFilterTransaction %p called trans->ReadSegments rv=%x %d\n", this, rv, *outCountRead)); if (NS_SUCCEEDED(rv) && mReadSegmentBlocked) { rv = NS_BASE_STREAM_WOULD_BLOCK; LOG(("TLSFilterTransaction %p read segment blocked found rv=%x\n", this, rv)); Connection()->ForceSend(); } return rv; } nsresult TLSFilterTransaction::WriteSegments(nsAHttpSegmentWriter *aWriter, uint32_t aCount, uint32_t *outCountWritten) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("TLSFilterTransaction::WriteSegments %p max=%d\n", this, aCount)); if (!mTransaction) { return NS_ERROR_UNEXPECTED; } mSegmentWriter = aWriter; nsresult rv = mTransaction->WriteSegments(this, aCount, outCountWritten); if (NS_SUCCEEDED(rv) && NS_FAILED(mFilterReadCode) && !(*outCountWritten)) { // nsPipe turns failures into silent OK.. undo that! rv = mFilterReadCode; if (mFilterReadCode == NS_BASE_STREAM_WOULD_BLOCK) { Connection()->ResumeRecv(); } } LOG(("TLSFilterTransaction %p called trans->WriteSegments rv=%x %d\n", this, rv, *outCountWritten)); return rv; } nsresult TLSFilterTransaction::GetTransactionSecurityInfo(nsISupports **outSecInfo) { if (!mSecInfo) { return NS_ERROR_FAILURE; } nsCOMPtr temp(mSecInfo); temp.forget(outSecInfo); return NS_OK; } nsresult TLSFilterTransaction::NudgeTunnel(NudgeTunnelCallback *aCallback) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("TLSFilterTransaction %p NudgeTunnel\n", this)); mNudgeCallback = nullptr; if (!mSecInfo) { return NS_ERROR_FAILURE; } uint32_t notUsed; int32_t written = PR_Write(mFD, "", 0); if ((written < 0) && (PR_GetError() != PR_WOULD_BLOCK_ERROR)) { // fatal handshake failure LOG(("TLSFilterTransaction %p Fatal Handshake Failure: %d\n", this, PR_GetError())); return NS_ERROR_FAILURE; } OnReadSegment("", 0, ¬Used); // The SSL Layer does some unusual things with PR_Poll that makes it a bad // match for multiplexed SSL sessions. We work around this by manually polling for // the moment during the brief handshake phase or otherwise blocked on write. // Thankfully this is a pretty unusual state. NSPR doesn't help us here - // asserting when polling without the NSPR IO layer on the bottom of // the stack. As a follow-on we can do some NSPR and maybe libssl changes // to make this more event driven, but this is acceptable for getting started. uint32_t counter = mNudgeCounter++; uint32_t delay; if (!counter) { delay = 0; } else if (counter < 8) { // up to 48ms at 6 delay = 6; } else if (counter < 34) { // up to 499 ms at 17ms delay = 17; } else { // after that at 51ms (3 old windows ticks) delay = 51; } if(!mTimer) { mTimer = do_CreateInstance("@mozilla.org/timer;1"); } mNudgeCallback = aCallback; if (!mTimer || NS_FAILED(mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT))) { return StartTimerCallback(); } LOG(("TLSFilterTransaction %p NudgeTunnel timer started\n", this)); return NS_OK; } NS_IMETHODIMP TLSFilterTransaction::Notify(nsITimer *timer) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("TLSFilterTransaction %p NudgeTunnel notify\n", this)); if (timer != mTimer) { return NS_ERROR_UNEXPECTED; } StartTimerCallback(); return NS_OK; } nsresult TLSFilterTransaction::StartTimerCallback() { LOG(("TLSFilterTransaction %p NudgeTunnel StartTimerCallback %p\n", this, mNudgeCallback.get())); if (mNudgeCallback) { // This class can be called re-entrantly, so cleanup m* before ->on() RefPtr cb(mNudgeCallback); mNudgeCallback = nullptr; cb->OnTunnelNudged(this); } return NS_OK; } PRStatus TLSFilterTransaction::GetPeerName(PRFileDesc *aFD, PRNetAddr*addr) { NetAddr peeraddr; TLSFilterTransaction *self = reinterpret_cast(aFD->secret); if (!self->mTransaction || NS_FAILED(self->mTransaction->Connection()->Transport()->GetPeerAddr(&peeraddr))) { return PR_FAILURE; } NetAddrToPRNetAddr(&peeraddr, addr); return PR_SUCCESS; } PRStatus TLSFilterTransaction::GetSocketOption(PRFileDesc *aFD, PRSocketOptionData *aOpt) { if (aOpt->option == PR_SockOpt_Nonblocking) { aOpt->value.non_blocking = PR_TRUE; return PR_SUCCESS; } return PR_FAILURE; } PRStatus TLSFilterTransaction::SetSocketOption(PRFileDesc *aFD, const PRSocketOptionData *aOpt) { return PR_FAILURE; } PRStatus TLSFilterTransaction::FilterClose(PRFileDesc *aFD) { return PR_SUCCESS; } int32_t TLSFilterTransaction::FilterWrite(PRFileDesc *aFD, const void *aBuf, int32_t aAmount) { TLSFilterTransaction *self = reinterpret_cast(aFD->secret); return self->FilterOutput(static_cast(aBuf), aAmount); } int32_t TLSFilterTransaction::FilterSend(PRFileDesc *aFD, const void *aBuf, int32_t aAmount, int , PRIntervalTime) { return FilterWrite(aFD, aBuf, aAmount); } int32_t TLSFilterTransaction::FilterRead(PRFileDesc *aFD, void *aBuf, int32_t aAmount) { TLSFilterTransaction *self = reinterpret_cast(aFD->secret); return self->FilterInput(static_cast(aBuf), aAmount); } int32_t TLSFilterTransaction::FilterRecv(PRFileDesc *aFD, void *aBuf, int32_t aAmount, int , PRIntervalTime) { return FilterRead(aFD, aBuf, aAmount); } ///// // The other methods of TLSFilterTransaction just call mTransaction->method ///// void TLSFilterTransaction::SetConnection(nsAHttpConnection *aConnection) { if (!mTransaction) { return; } mTransaction->SetConnection(aConnection); } nsAHttpConnection * TLSFilterTransaction::Connection() { if (!mTransaction) { return nullptr; } return mTransaction->Connection(); } void TLSFilterTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **outCB) { if (!mTransaction) { return; } mTransaction->GetSecurityCallbacks(outCB); } void TLSFilterTransaction::OnTransportStatus(nsITransport* aTransport, nsresult aStatus, int64_t aProgress) { if (!mTransaction) { return; } mTransaction->OnTransportStatus(aTransport, aStatus, aProgress); } nsHttpConnectionInfo * TLSFilterTransaction::ConnectionInfo() { if (!mTransaction) { return nullptr; } return mTransaction->ConnectionInfo(); } bool TLSFilterTransaction::IsDone() { if (!mTransaction) { return true; } return mTransaction->IsDone(); } nsresult TLSFilterTransaction::Status() { if (!mTransaction) { return NS_ERROR_UNEXPECTED; } return mTransaction->Status(); } uint32_t TLSFilterTransaction::Caps() { if (!mTransaction) { return 0; } return mTransaction->Caps(); } void TLSFilterTransaction::SetDNSWasRefreshed() { if (!mTransaction) { return; } mTransaction->SetDNSWasRefreshed(); } uint64_t TLSFilterTransaction::Available() { if (!mTransaction) { return 0; } return mTransaction->Available(); } void TLSFilterTransaction::SetProxyConnectFailed() { if (!mTransaction) { return; } mTransaction->SetProxyConnectFailed(); } nsHttpRequestHead * TLSFilterTransaction::RequestHead() { if (!mTransaction) { return nullptr; } return mTransaction->RequestHead(); } uint32_t TLSFilterTransaction::Http1xTransactionCount() { if (!mTransaction) { return 0; } return mTransaction->Http1xTransactionCount(); } nsresult TLSFilterTransaction::TakeSubTransactions( nsTArray > &outTransactions) { LOG(("TLSFilterTransaction::TakeSubTransactions [this=%p] mTransaction %p\n", this, mTransaction.get())); if (!mTransaction) { return NS_ERROR_UNEXPECTED; } if (mTransaction->TakeSubTransactions(outTransactions) == NS_ERROR_NOT_IMPLEMENTED) { outTransactions.AppendElement(mTransaction); } mTransaction = nullptr; return NS_OK; } nsresult TLSFilterTransaction::SetProxiedTransaction(nsAHttpTransaction *aTrans) { LOG(("TLSFilterTransaction::SetProxiedTransaction [this=%p] aTrans=%p\n", this, aTrans)); mTransaction = aTrans; nsCOMPtr callbacks; mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); nsCOMPtr secCtrl(do_QueryInterface(mSecInfo)); if (secCtrl && callbacks) { secCtrl->SetNotificationCallbacks(callbacks); } return NS_OK; } // AddTransaction is for adding pipelined subtransactions nsresult TLSFilterTransaction::AddTransaction(nsAHttpTransaction *aTrans) { LOG(("TLSFilterTransaction::AddTransaction passing on subtransaction " "[this=%p] aTrans=%p ,mTransaction=%p\n", this, aTrans, mTransaction.get())); if (!mTransaction) { return NS_ERROR_FAILURE; } return mTransaction->AddTransaction(aTrans); } uint32_t TLSFilterTransaction::PipelineDepth() { if (!mTransaction) { return 0; } return mTransaction->PipelineDepth(); } nsresult TLSFilterTransaction::SetPipelinePosition(int32_t aPosition) { if (!mTransaction) { return NS_OK; } return mTransaction->SetPipelinePosition(aPosition); } int32_t TLSFilterTransaction::PipelinePosition() { if (!mTransaction) { return 1; } return mTransaction->PipelinePosition(); } nsHttpPipeline * TLSFilterTransaction::QueryPipeline() { if (!mTransaction) { return nullptr; } return mTransaction->QueryPipeline(); } bool TLSFilterTransaction::IsNullTransaction() { if (!mTransaction) { return false; } return mTransaction->IsNullTransaction(); } NullHttpTransaction * TLSFilterTransaction::QueryNullTransaction() { if (!mTransaction) { return nullptr; } return mTransaction->QueryNullTransaction(); } nsHttpTransaction * TLSFilterTransaction::QueryHttpTransaction() { if (!mTransaction) { return nullptr; } return mTransaction->QueryHttpTransaction(); } class SocketInWrapper : public nsIAsyncInputStream , public nsAHttpSegmentWriter { NS_DECL_THREADSAFE_ISUPPORTS NS_FORWARD_NSIASYNCINPUTSTREAM(mStream->) SocketInWrapper(nsIAsyncInputStream *aWrapped, TLSFilterTransaction *aFilter) : mStream(aWrapped) , mTLSFilter(aFilter) { } NS_IMETHODIMP Close() override { mTLSFilter = nullptr; return mStream->Close(); } NS_IMETHODIMP Available(uint64_t *_retval) override { return mStream->Available(_retval); } NS_IMETHODIMP IsNonBlocking(bool *_retval) override { return mStream->IsNonBlocking(_retval); } NS_IMETHODIMP ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *_retval) override { return mStream->ReadSegments(aWriter, aClosure, aCount, _retval); } // finally, ones that don't get forwarded :) NS_IMETHOD Read(char *aBuf, uint32_t aCount, uint32_t *_retval) override; virtual nsresult OnWriteSegment(char *segment, uint32_t count, uint32_t *countWritten) override; private: virtual ~SocketInWrapper() {}; nsCOMPtr mStream; RefPtr mTLSFilter; }; nsresult SocketInWrapper::OnWriteSegment(char *segment, uint32_t count, uint32_t *countWritten) { LOG(("SocketInWrapper OnWriteSegment %d %p filter=%p\n", count, this, mTLSFilter.get())); nsresult rv = mStream->Read(segment, count, countWritten); LOG(("SocketInWrapper OnWriteSegment %p wrapped read %x %d\n", this, rv, *countWritten)); return rv; } NS_IMETHODIMP SocketInWrapper::Read(char *aBuf, uint32_t aCount, uint32_t *_retval) { LOG(("SocketInWrapper Read %d %p filter=%p\n", aCount, this, mTLSFilter.get())); if (!mTLSFilter) { return NS_ERROR_UNEXPECTED; // protect potentially dangling mTLSFilter } // mTLSFilter->mSegmentWriter MUST be this at ctor time return mTLSFilter->OnWriteSegment(aBuf, aCount, _retval); } class SocketOutWrapper : public nsIAsyncOutputStream , public nsAHttpSegmentReader { NS_DECL_THREADSAFE_ISUPPORTS NS_FORWARD_NSIASYNCOUTPUTSTREAM(mStream->) SocketOutWrapper(nsIAsyncOutputStream *aWrapped, TLSFilterTransaction *aFilter) : mStream(aWrapped) , mTLSFilter(aFilter) { } NS_IMETHODIMP Close() override { mTLSFilter = nullptr; return mStream->Close(); } NS_IMETHODIMP Flush() override { return mStream->Flush(); } NS_IMETHODIMP IsNonBlocking(bool *_retval) override { return mStream->IsNonBlocking(_retval); } NS_IMETHODIMP WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval) override { return mStream->WriteSegments(aReader, aClosure, aCount, _retval); } NS_IMETHODIMP WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval) override { return mStream->WriteFrom(aFromStream, aCount, _retval); } // finally, ones that don't get forwarded :) NS_IMETHOD Write(const char *aBuf, uint32_t aCount, uint32_t *_retval) override; virtual nsresult OnReadSegment(const char *segment, uint32_t count, uint32_t *countRead) override; private: virtual ~SocketOutWrapper() {}; nsCOMPtr mStream; RefPtr mTLSFilter; }; nsresult SocketOutWrapper::OnReadSegment(const char *segment, uint32_t count, uint32_t *countWritten) { return mStream->Write(segment, count, countWritten); } NS_IMETHODIMP SocketOutWrapper::Write(const char *aBuf, uint32_t aCount, uint32_t *_retval) { LOG(("SocketOutWrapper Write %d %p filter=%p\n", aCount, this, mTLSFilter.get())); // mTLSFilter->mSegmentReader MUST be this at ctor time if (!mTLSFilter) { return NS_ERROR_UNEXPECTED; // protect potentially dangling mTLSFilter } return mTLSFilter->OnReadSegment(aBuf, aCount, _retval); } void TLSFilterTransaction::newIODriver(nsIAsyncInputStream *aSocketIn, nsIAsyncOutputStream *aSocketOut, nsIAsyncInputStream **outSocketIn, nsIAsyncOutputStream **outSocketOut) { SocketInWrapper *inputWrapper = new SocketInWrapper(aSocketIn, this); mSegmentWriter = inputWrapper; nsCOMPtr newIn(inputWrapper); newIn.forget(outSocketIn); SocketOutWrapper *outputWrapper = new SocketOutWrapper(aSocketOut, this); mSegmentReader = outputWrapper; nsCOMPtr newOut(outputWrapper); newOut.forget(outSocketOut); } SpdyConnectTransaction * TLSFilterTransaction::QuerySpdyConnectTransaction() { if (!mTransaction) { return nullptr; } return mTransaction->QuerySpdyConnectTransaction(); } class SocketTransportShim : public nsISocketTransport { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSITRANSPORT NS_DECL_NSISOCKETTRANSPORT explicit SocketTransportShim(nsISocketTransport *aWrapped) : mWrapped(aWrapped) {}; private: virtual ~SocketTransportShim() {}; nsCOMPtr mWrapped; }; class OutputStreamShim : public nsIAsyncOutputStream { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOUTPUTSTREAM NS_DECL_NSIASYNCOUTPUTSTREAM friend class SpdyConnectTransaction; explicit OutputStreamShim(SpdyConnectTransaction *aTrans) : mCallback(nullptr) , mStatus(NS_OK) { mWeakTrans = do_GetWeakReference(aTrans); } private: virtual ~OutputStreamShim() {}; nsWeakPtr mWeakTrans; // SpdyConnectTransaction * nsIOutputStreamCallback *mCallback; nsresult mStatus; }; class InputStreamShim : public nsIAsyncInputStream { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIINPUTSTREAM NS_DECL_NSIASYNCINPUTSTREAM friend class SpdyConnectTransaction; explicit InputStreamShim(SpdyConnectTransaction *aTrans) : mCallback(nullptr) , mStatus(NS_OK) { mWeakTrans = do_GetWeakReference(aTrans); } private: virtual ~InputStreamShim() {}; nsWeakPtr mWeakTrans; // SpdyConnectTransaction * nsIInputStreamCallback *mCallback; nsresult mStatus; }; SpdyConnectTransaction::SpdyConnectTransaction(nsHttpConnectionInfo *ci, nsIInterfaceRequestor *callbacks, uint32_t caps, nsHttpTransaction *trans, nsAHttpConnection *session) : NullHttpTransaction(ci, callbacks, caps | NS_HTTP_ALLOW_KEEPALIVE) , mConnectStringOffset(0) , mSession(session) , mSegmentReader(nullptr) , mInputDataSize(0) , mInputDataUsed(0) , mInputDataOffset(0) , mOutputDataSize(0) , mOutputDataUsed(0) , mOutputDataOffset(0) , mForcePlainText(false) { LOG(("SpdyConnectTransaction ctor %p\n", this)); mTimestampSyn = TimeStamp::Now(); mRequestHead = new nsHttpRequestHead(); nsHttpConnection::MakeConnectString(trans, mRequestHead, mConnectString); mDrivingTransaction = trans; } SpdyConnectTransaction::~SpdyConnectTransaction() { LOG(("SpdyConnectTransaction dtor %p\n", this)); if (mDrivingTransaction) { // requeue it I guess. This should be gone. gHttpHandler->InitiateTransaction(mDrivingTransaction, mDrivingTransaction->Priority()); } } void SpdyConnectTransaction::ForcePlainText() { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(!mInputDataUsed && !mInputDataSize && !mInputDataOffset); MOZ_ASSERT(!mForcePlainText); MOZ_ASSERT(!mTunnelTransport, "call before mapstreamtohttpconnection"); mForcePlainText = true; return; } void SpdyConnectTransaction::MapStreamToHttpConnection(nsISocketTransport *aTransport, nsHttpConnectionInfo *aConnInfo) { mConnInfo = aConnInfo; mTunnelTransport = new SocketTransportShim(aTransport); mTunnelStreamIn = new InputStreamShim(this); mTunnelStreamOut = new OutputStreamShim(this); mTunneledConn = new nsHttpConnection(); // this new http connection has a specific hashkey (i.e. to a particular // host via the tunnel) and is associated with the tunnel streams LOG(("SpdyConnectTransaction new httpconnection %p %s\n", mTunneledConn.get(), aConnInfo->HashKey().get())); nsCOMPtr callbacks; GetSecurityCallbacks(getter_AddRefs(callbacks)); mTunneledConn->SetTransactionCaps(Caps()); MOZ_ASSERT(aConnInfo->UsingHttpsProxy()); TimeDuration rtt = TimeStamp::Now() - mTimestampSyn; mTunneledConn->Init(aConnInfo, gHttpHandler->ConnMgr()->MaxRequestDelay(), mTunnelTransport, mTunnelStreamIn, mTunnelStreamOut, true, callbacks, PR_MillisecondsToInterval( static_cast(rtt.ToMilliseconds()))); if (mForcePlainText) { mTunneledConn->ForcePlainText(); } else { mTunneledConn->SetupSecondaryTLS(); mTunneledConn->SetInSpdyTunnel(true); } // make the originating transaction stick to the tunneled conn RefPtr wrappedConn = gHttpHandler->ConnMgr()->MakeConnectionHandle(mTunneledConn); mDrivingTransaction->SetConnection(wrappedConn); mDrivingTransaction->MakeSticky(); // jump the priority and start the dispatcher gHttpHandler->InitiateTransaction( mDrivingTransaction, nsISupportsPriority::PRIORITY_HIGHEST - 60); mDrivingTransaction = nullptr; } nsresult SpdyConnectTransaction::Flush(uint32_t count, uint32_t *countRead) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("SpdyConnectTransaction::Flush %p count %d avail %d\n", this, count, mOutputDataUsed - mOutputDataOffset)); if (!mSegmentReader) { return NS_ERROR_UNEXPECTED; } *countRead = 0; count = std::min(count, (mOutputDataUsed - mOutputDataOffset)); if (count) { nsresult rv; rv = mSegmentReader->OnReadSegment(&mOutputData[mOutputDataOffset], count, countRead); if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) { LOG(("SpdyConnectTransaction::Flush %p Error %x\n", this, rv)); CreateShimError(rv); return rv; } } mOutputDataOffset += *countRead; if (mOutputDataOffset == mOutputDataUsed) { mOutputDataOffset = mOutputDataUsed = 0; } if (!(*countRead)) { return NS_BASE_STREAM_WOULD_BLOCK; } if (mOutputDataUsed != mOutputDataOffset) { LOG(("SpdyConnectTransaction::Flush %p Incomplete %d\n", this, mOutputDataUsed - mOutputDataOffset)); mSession->TransactionHasDataToWrite(this); } return NS_OK; } nsresult SpdyConnectTransaction::ReadSegments(nsAHttpSegmentReader *reader, uint32_t count, uint32_t *countRead) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("SpdyConnectTransaction::ReadSegments %p count %d conn %p\n", this, count, mTunneledConn.get())); mSegmentReader = reader; // spdy stream carrying tunnel is not setup yet. if (!mTunneledConn) { uint32_t toWrite = mConnectString.Length() - mConnectStringOffset; toWrite = std::min(toWrite, count); *countRead = toWrite; if (toWrite) { nsresult rv = mSegmentReader-> OnReadSegment(mConnectString.BeginReading() + mConnectStringOffset, toWrite, countRead); if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) { LOG(("SpdyConnectTransaction::ReadSegments %p OnReadSegmentError %x\n", this, rv)); CreateShimError(rv); } else { mConnectStringOffset += toWrite; if (mConnectString.Length() == mConnectStringOffset) { mConnectString.Truncate(); mConnectStringOffset = 0; } } return rv; } return NS_BASE_STREAM_WOULD_BLOCK; } if (mForcePlainText) { // this path just ignores sending the request so that we can // send a synthetic reply in writesegments() LOG(("SpdyConnectTransaciton::ReadSegments %p dropping %d output bytes " "due to synthetic reply\n", this, mOutputDataUsed - mOutputDataOffset)); *countRead = mOutputDataUsed - mOutputDataOffset; mOutputDataOffset = mOutputDataUsed = 0; mTunneledConn->DontReuse(); return NS_OK; } *countRead = 0; Flush(count, countRead); if (!mTunnelStreamOut->mCallback) { return NS_BASE_STREAM_WOULD_BLOCK; } nsresult rv = mTunnelStreamOut->mCallback->OnOutputStreamReady(mTunnelStreamOut); if (NS_FAILED(rv)) { return rv; } uint32_t subtotal; count -= *countRead; rv = Flush(count, &subtotal); *countRead += subtotal; return rv; } void SpdyConnectTransaction::CreateShimError(nsresult code) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(NS_FAILED(code)); if (mTunnelStreamOut && NS_SUCCEEDED(mTunnelStreamOut->mStatus)) { mTunnelStreamOut->mStatus = code; } if (mTunnelStreamIn && NS_SUCCEEDED(mTunnelStreamIn->mStatus)) { mTunnelStreamIn->mStatus = code; } if (mTunnelStreamIn && mTunnelStreamIn->mCallback) { mTunnelStreamIn->mCallback->OnInputStreamReady(mTunnelStreamIn); } if (mTunnelStreamOut && mTunnelStreamOut->mCallback) { mTunnelStreamOut->mCallback->OnOutputStreamReady(mTunnelStreamOut); } } nsresult SpdyConnectTransaction::WriteSegments(nsAHttpSegmentWriter *writer, uint32_t count, uint32_t *countWritten) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG(("SpdyConnectTransaction::WriteSegments %p max=%d cb=%p\n", this, count, mTunneledConn ? mTunnelStreamIn->mCallback : nullptr)); // first call into the tunnel stream to get the demux'd data out of the // spdy session. EnsureBuffer(mInputData, mInputDataUsed + count, mInputDataUsed, mInputDataSize); nsresult rv = writer->OnWriteSegment(&mInputData[mInputDataUsed], count, countWritten); if (NS_FAILED(rv)) { if (rv != NS_BASE_STREAM_WOULD_BLOCK) { LOG(("SpdyConnectTransaction::WriteSegments wrapped writer %p Error %x\n", this, rv)); CreateShimError(rv); } return rv; } mInputDataUsed += *countWritten; LOG(("SpdyConnectTransaction %p %d new bytes [%d total] of ciphered data buffered\n", this, *countWritten, mInputDataUsed - mInputDataOffset)); if (!mTunneledConn || !mTunnelStreamIn->mCallback) { return NS_BASE_STREAM_WOULD_BLOCK; } rv = mTunnelStreamIn->mCallback->OnInputStreamReady(mTunnelStreamIn); LOG(("SpdyConnectTransaction::WriteSegments %p " "after InputStreamReady callback %d total of ciphered data buffered rv=%x\n", this, mInputDataUsed - mInputDataOffset, rv)); LOG(("SpdyConnectTransaction::WriteSegments %p " "goodput %p out %llu\n", this, mTunneledConn.get(), mTunneledConn->ContentBytesWritten())); if (NS_SUCCEEDED(rv) && !mTunneledConn->ContentBytesWritten()) { mTunnelStreamOut->AsyncWait(mTunnelStreamOut->mCallback, 0, 0, nullptr); } return rv; } bool SpdyConnectTransaction::ConnectedReadyForInput() { return mTunneledConn && mTunnelStreamIn->mCallback; } nsHttpRequestHead * SpdyConnectTransaction::RequestHead() { return mRequestHead; } void SpdyConnectTransaction::Close(nsresult code) { LOG(("SpdyConnectTransaction close %p %x\n", this, code)); NullHttpTransaction::Close(code); if (NS_FAILED(code) && (code != NS_BASE_STREAM_WOULD_BLOCK)) { CreateShimError(code); } else { CreateShimError(NS_BASE_STREAM_CLOSED); } } NS_IMETHODIMP OutputStreamShim::AsyncWait(nsIOutputStreamCallback *callback, unsigned int, unsigned int, nsIEventTarget *target) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); bool currentThread; if (target && (NS_FAILED(target->IsOnCurrentThread(¤tThread)) || !currentThread)) { return NS_ERROR_FAILURE; } LOG(("OutputStreamShim::AsyncWait %p callback %p\n", this, callback)); mCallback = callback; RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } trans->mSession->TransactionHasDataToWrite(trans); return NS_OK; } NS_IMETHODIMP OutputStreamShim::CloseWithStatus(nsresult reason) { RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } trans->mSession->CloseTransaction(trans, reason); return NS_OK; } NS_IMETHODIMP OutputStreamShim::Close() { return CloseWithStatus(NS_OK); } NS_IMETHODIMP OutputStreamShim::Flush() { RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } uint32_t count = trans->mOutputDataUsed - trans->mOutputDataOffset; if (!count) { return NS_OK; } uint32_t countRead; nsresult rv = trans->Flush(count, &countRead); LOG(("OutputStreamShim::Flush %p before %d after %d\n", this, count, trans->mOutputDataUsed - trans->mOutputDataOffset)); return rv; } NS_IMETHODIMP OutputStreamShim::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); if (NS_FAILED(mStatus)) { return mStatus; } RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } if ((trans->mOutputDataUsed + aCount) >= 512000) { *_retval = 0; // time for some flow control; return NS_BASE_STREAM_WOULD_BLOCK; } EnsureBuffer(trans->mOutputData, trans->mOutputDataUsed + aCount, trans->mOutputDataUsed, trans->mOutputDataSize); memcpy(&trans->mOutputData[trans->mOutputDataUsed], aBuf, aCount); trans->mOutputDataUsed += aCount; *_retval = aCount; LOG(("OutputStreamShim::Write %p new %d total %d\n", this, aCount, trans->mOutputDataUsed)); trans->mSession->TransactionHasDataToWrite(trans); return NS_OK; } NS_IMETHODIMP OutputStreamShim::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP OutputStreamShim::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP OutputStreamShim::IsNonBlocking(bool *_retval) { *_retval = true; return NS_OK; } NS_IMETHODIMP InputStreamShim::AsyncWait(nsIInputStreamCallback *callback, unsigned int, unsigned int, nsIEventTarget *target) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); bool currentThread; if (target && (NS_FAILED(target->IsOnCurrentThread(¤tThread)) || !currentThread)) { return NS_ERROR_FAILURE; } LOG(("InputStreamShim::AsyncWait %p callback %p\n", this, callback)); mCallback = callback; return NS_OK; } NS_IMETHODIMP InputStreamShim::CloseWithStatus(nsresult reason) { RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } trans->mSession->CloseTransaction(trans, reason); return NS_OK; } NS_IMETHODIMP InputStreamShim::Close() { return CloseWithStatus(NS_OK); } NS_IMETHODIMP InputStreamShim::Available(uint64_t *_retval) { RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } *_retval = trans->mInputDataUsed - trans->mInputDataOffset; return NS_OK; } NS_IMETHODIMP InputStreamShim::Read(char *aBuf, uint32_t aCount, uint32_t *_retval) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); if (NS_FAILED(mStatus)) { return mStatus; } RefPtr baseTrans(do_QueryReferent(mWeakTrans)); if (!baseTrans) { return NS_ERROR_FAILURE; } SpdyConnectTransaction *trans = baseTrans->QuerySpdyConnectTransaction(); MOZ_ASSERT(trans); if (!trans) { return NS_ERROR_UNEXPECTED; } uint32_t avail = trans->mInputDataUsed - trans->mInputDataOffset; uint32_t tocopy = std::min(aCount, avail); *_retval = tocopy; memcpy(aBuf, &trans->mInputData[trans->mInputDataOffset], tocopy); trans->mInputDataOffset += tocopy; if (trans->mInputDataOffset == trans->mInputDataUsed) { trans->mInputDataOffset = trans->mInputDataUsed = 0; } return tocopy ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; } NS_IMETHODIMP InputStreamShim::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP InputStreamShim::IsNonBlocking(bool *_retval) { *_retval = true; return NS_OK; } NS_IMETHODIMP SocketTransportShim::SetKeepaliveEnabled(bool aKeepaliveEnabled) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::SetKeepaliveVals(int32_t keepaliveIdleTime, int32_t keepaliveRetryInterval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::SetSecurityCallbacks(nsIInterfaceRequestor *aSecurityCallbacks) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize, uint32_t aSegmentCount, nsIInputStream * *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize, uint32_t aSegmentCount, nsIOutputStream * *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::Close(nsresult aReason) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::SetEventSink(nsITransportEventSink *aSink, nsIEventTarget *aEventTarget) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP SocketTransportShim::Bind(NetAddr *aLocalAddr) { return NS_ERROR_NOT_IMPLEMENTED; } #define FWD_TS_PTR(fx, ts) NS_IMETHODIMP \ SocketTransportShim::fx(ts *arg) { return mWrapped->fx(arg); } #define FWD_TS_ADDREF(fx, ts) NS_IMETHODIMP \ SocketTransportShim::fx(ts **arg) { return mWrapped->fx(arg); } #define FWD_TS(fx, ts) NS_IMETHODIMP \ SocketTransportShim::fx(ts arg) { return mWrapped->fx(arg); } FWD_TS_PTR(GetKeepaliveEnabled, bool); FWD_TS_PTR(GetSendBufferSize, uint32_t); FWD_TS(SetSendBufferSize, uint32_t); FWD_TS_PTR(GetPort, int32_t); FWD_TS_PTR(GetPeerAddr, mozilla::net::NetAddr); FWD_TS_PTR(GetSelfAddr, mozilla::net::NetAddr); FWD_TS_ADDREF(GetScriptablePeerAddr, nsINetAddr); FWD_TS_ADDREF(GetScriptableSelfAddr, nsINetAddr); FWD_TS_ADDREF(GetSecurityInfo, nsISupports); FWD_TS_ADDREF(GetSecurityCallbacks, nsIInterfaceRequestor); FWD_TS_PTR(IsAlive, bool); FWD_TS_PTR(GetConnectionFlags, uint32_t); FWD_TS(SetConnectionFlags, uint32_t); FWD_TS_PTR(GetRecvBufferSize, uint32_t); FWD_TS(SetRecvBufferSize, uint32_t); NS_IMETHODIMP SocketTransportShim::GetHost(nsACString & aHost) { return mWrapped->GetHost(aHost); } NS_IMETHODIMP SocketTransportShim::GetTimeout(uint32_t aType, uint32_t *_retval) { return mWrapped->GetTimeout(aType, _retval); } NS_IMETHODIMP SocketTransportShim::GetNetworkInterfaceId(nsACString_internal &aNetworkInterfaceId) { return mWrapped->GetNetworkInterfaceId(aNetworkInterfaceId); } NS_IMETHODIMP SocketTransportShim::SetNetworkInterfaceId(const nsACString_internal &aNetworkInterfaceId) { return mWrapped->SetNetworkInterfaceId(aNetworkInterfaceId); } NS_IMETHODIMP SocketTransportShim::SetTimeout(uint32_t aType, uint32_t aValue) { return mWrapped->SetTimeout(aType, aValue); } NS_IMETHODIMP SocketTransportShim::GetQoSBits(uint8_t *aQoSBits) { return mWrapped->GetQoSBits(aQoSBits); } NS_IMETHODIMP SocketTransportShim::SetQoSBits(uint8_t aQoSBits) { return mWrapped->SetQoSBits(aQoSBits); } NS_IMPL_ISUPPORTS(TLSFilterTransaction, nsITimerCallback) NS_IMPL_ISUPPORTS(SocketTransportShim, nsISocketTransport, nsITransport) NS_IMPL_ISUPPORTS(InputStreamShim, nsIInputStream, nsIAsyncInputStream) NS_IMPL_ISUPPORTS(OutputStreamShim, nsIOutputStream, nsIAsyncOutputStream) NS_IMPL_ISUPPORTS(SocketInWrapper, nsIAsyncInputStream) NS_IMPL_ISUPPORTS(SocketOutWrapper, nsIAsyncOutputStream) } // namespace net } // namespace mozilla