зеркало из https://github.com/mozilla/gecko-dev.git
1645 строки
44 KiB
C++
1645 строки
44 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"
|
|
|
|
#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"
|
|
|
|
#ifdef DEBUG
|
|
// defined by the socket transport service while active
|
|
extern PRThread *gSocketThread;
|
|
#endif
|
|
|
|
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<nsISocketProvider> provider;
|
|
nsCOMPtr<nsISocketProviderService> 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<PRFilePrivate *>(this);
|
|
provider->AddToSocket(PR_AF_INET, aTLSHost, aTLSPort, nullptr,
|
|
0, 0, mFD, getter_AddRefs(mSecInfo));
|
|
}
|
|
|
|
if (mTransaction) {
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
|
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
|
|
nsCOMPtr<nsISSLSocketControl> 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;
|
|
}
|
|
|
|
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, mEncryptedTextUsed, &amt);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
mEncryptedTextUsed -= amt;
|
|
if (mEncryptedTextUsed) {
|
|
memmove(mEncryptedText, 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, 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, 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<nsISupports> 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()
|
|
nsRefPtr<NudgeTunnelCallback> cb(mNudgeCallback);
|
|
mNudgeCallback = nullptr;
|
|
cb->OnTunnelNudged(this);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
PRStatus
|
|
TLSFilterTransaction::GetPeerName(PRFileDesc *aFD, PRNetAddr*addr)
|
|
{
|
|
NetAddr peeraddr;
|
|
TLSFilterTransaction *self = reinterpret_cast<TLSFilterTransaction *>(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<TLSFilterTransaction *>(aFD->secret);
|
|
return self->FilterOutput(static_cast<const char *>(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<TLSFilterTransaction *>(aFD->secret);
|
|
return self->FilterInput(static_cast<char *>(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<nsRefPtr<nsAHttpTransaction> > &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<nsIInterfaceRequestor> callbacks;
|
|
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
|
|
nsCOMPtr<nsISSLSocketControl> 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<nsIAsyncInputStream> mStream;
|
|
nsRefPtr<TLSFilterTransaction> 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<nsIAsyncOutputStream> mStream;
|
|
nsRefPtr<TLSFilterTransaction> 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<nsIAsyncInputStream> newIn(inputWrapper);
|
|
newIn.forget(outSocketIn);
|
|
|
|
SocketOutWrapper *outputWrapper = new SocketOutWrapper(aSocketOut, this);
|
|
mSegmentReader = outputWrapper;
|
|
nsCOMPtr<nsIAsyncOutputStream> 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<nsISocketTransport> 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<nsIInterfaceRequestor> 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<uint32_t>(rtt.ToMilliseconds())));
|
|
if (mForcePlainText) {
|
|
mTunneledConn->ForcePlainText();
|
|
} else {
|
|
mTunneledConn->SetupSecondaryTLS();
|
|
mTunneledConn->SetInSpdyTunnel(true);
|
|
}
|
|
|
|
// make the originating transaction stick to the tunneled conn
|
|
nsRefPtr<nsAHttpConnection> 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;
|
|
}
|
|
|
|
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;
|
|
|
|
nsRefPtr<NullHttpTransaction> 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)
|
|
{
|
|
nsRefPtr<NullHttpTransaction> 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()
|
|
{
|
|
nsRefPtr<NullHttpTransaction> 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;
|
|
}
|
|
|
|
nsRefPtr<NullHttpTransaction> 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)
|
|
{
|
|
nsRefPtr<NullHttpTransaction> 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)
|
|
{
|
|
nsRefPtr<NullHttpTransaction> 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;
|
|
}
|
|
|
|
nsRefPtr<NullHttpTransaction> 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 mozilla::net
|
|
} // namespace mozilla
|