bug 378637 part 11 - proxy over TLS (i.e. https proxying) r=hurley

--HG--
extra : rebase_source : 8962538247666781e30eaa3b9673b857ec150204
This commit is contained in:
Patrick McManus 2014-04-16 09:52:43 -04:00
Родитель 4aec6b5f79
Коммит f685e08634
30 изменённых файлов: 2536 добавлений и 339 удалений

Просмотреть файл

@ -753,6 +753,7 @@ nsSocketTransport::nsSocketTransport()
, mProxyPort(0)
, mProxyTransparent(false)
, mProxyTransparentResolvesHost(false)
, mHttpsProxy(false)
, mConnectionFlags(0)
, mState(STATE_CLOSED)
, mAttached(false)
@ -783,13 +784,21 @@ nsSocketTransport::~nsSocketTransport()
{
SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
CleanupTypes();
}
void
nsSocketTransport::CleanupTypes()
{
// cleanup socket type info
if (mTypes) {
uint32_t i;
for (i=0; i<mTypeCount; ++i)
for (uint32_t i = 0; i < mTypeCount; ++i) {
PL_strfree(mTypes[i]);
}
free(mTypes);
mTypes = nullptr;
}
mTypeCount = 0;
}
nsresult
@ -810,16 +819,22 @@ nsSocketTransport::Init(const char **types, uint32_t typeCount,
mPort = port;
mHost = host;
if (proxyInfo) {
mHttpsProxy = proxyInfo->IsHTTPS();
}
const char *proxyType = nullptr;
if (proxyInfo) {
mProxyPort = proxyInfo->Port();
mProxyHost = proxyInfo->Host();
// grab proxy type (looking for "socks" for example)
proxyType = proxyInfo->Type();
if (proxyType && (strcmp(proxyType, "http") == 0 ||
strcmp(proxyType, "direct") == 0 ||
strcmp(proxyType, "unknown") == 0))
if (proxyType && (proxyInfo->IsHTTP() ||
proxyInfo->IsHTTPS() ||
proxyInfo->IsDirect() ||
!strcmp(proxyType, "unknown"))) {
proxyType = nullptr;
}
}
SOCKET_LOG(("nsSocketTransport::Init [this=%p host=%s:%hu proxy=%s:%hu]\n",
@ -1105,8 +1120,14 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &us
if (i == 0) {
// if this is the first type, we'll want the
// service to allocate a new socket
// when https proxying we want to just connect to the proxy as if
// it were the end host (i.e. expect the proxy's cert)
rv = provider->NewSocket(mNetAddr.raw.family,
host, port, proxyHost, proxyPort,
mHttpsProxy ? proxyHost : host,
mHttpsProxy ? proxyPort : port,
proxyHost, proxyPort,
proxyFlags, &fd,
getter_AddRefs(secinfo));
@ -1163,6 +1184,7 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &us
}
}
CleanupTypes();
return rv;
}

Просмотреть файл

@ -150,6 +150,7 @@ public:
protected:
virtual ~nsSocketTransport();
void CleanupTypes();
private:
@ -269,6 +270,7 @@ private:
uint16_t mProxyPort;
bool mProxyTransparent;
bool mProxyTransparentResolvesHost;
bool mHttpsProxy;
uint32_t mConnectionFlags;
uint16_t SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }

Просмотреть файл

@ -28,6 +28,14 @@
namespace mozilla {
namespace net {
ASpdySession::ASpdySession()
{
}
ASpdySession::~ASpdySession()
{
}
ASpdySession *
ASpdySession::NewSpdySession(uint32_t version,
nsISocketTransport *aTransport)
@ -185,7 +193,6 @@ SpdyPushCache::RemovePushedStreamHttp2(nsCString key)
mHashHttp2.Remove(key);
return rv;
}
} // namespace mozilla::net
} // namespace mozilla

Просмотреть файл

@ -15,10 +15,16 @@ class nsISocketTransport;
namespace mozilla { namespace net {
class nsHttpConnectionInfo;
class ASpdySession : public nsAHttpTransaction
{
public:
virtual bool AddStream(nsAHttpTransaction *, int32_t) = 0;
ASpdySession();
virtual ~ASpdySession();
virtual bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *) = 0;
virtual bool CanReuse() = 0;
virtual bool RoomForMoreStreams() = 0;
virtual PRIntervalTime IdleTime() = 0;

Просмотреть файл

@ -360,7 +360,9 @@ Http2Session::RegisterStreamID(Http2Stream *stream, uint32_t aNewID)
bool
Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
int32_t aPriority)
int32_t aPriority,
bool aUseTunnel,
nsIInterfaceRequestor *aCallbacks)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -2879,6 +2881,7 @@ Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
this, stream->StreamID()));
mReadyForWrite.Push(stream);
SetWriteCallbacks();
}
void
@ -2928,10 +2931,17 @@ Http2Session::Classification()
return mConnection->Classification();
}
void
Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
{
*aOut = nullptr;
}
//-----------------------------------------------------------------------------
// unused methods of nsAHttpTransaction
// We can be sure of this because Http2Session is only constructed in
// nsHttpConnection and is never passed out of that object
// nsHttpConnection and is never passed out of that object or a TLSFilterTransaction
// TLS tunnel
//-----------------------------------------------------------------------------
void
@ -2941,13 +2951,6 @@ Http2Session::SetConnection(nsAHttpConnection *)
MOZ_ASSERT(false, "Http2Session::SetConnection()");
}
void
Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **)
{
// This is unexpected
MOZ_ASSERT(false, "Http2Session::GetSecurityCallbacks()");
}
void
Http2Session::SetProxyConnectFailed()
{

Просмотреть файл

@ -41,7 +41,8 @@ public:
Http2Session(nsISocketTransport *);
~Http2Session();
bool AddStream(nsAHttpTransaction *, int32_t);
bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *);
bool CanReuse() { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams();

Просмотреть файл

@ -15,7 +15,7 @@
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS0(NullHttpTransaction)
NS_IMPL_ISUPPORTS(NullHttpTransaction, nsISupportsWeakReference)
NullHttpTransaction::NullHttpTransaction(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,

Просмотреть файл

@ -21,7 +21,7 @@ class nsAHttpConnection;
class nsHttpConnectionInfo;
class nsHttpRequestHead;
class NullHttpTransaction MOZ_FINAL : public nsAHttpTransaction
class NullHttpTransaction : public nsAHttpTransaction
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -30,7 +30,7 @@ public:
NullHttpTransaction(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
uint32_t caps);
~NullHttpTransaction();
virtual ~NullHttpTransaction();
// Overload of nsAHttpTransaction methods
bool IsNullTransaction() MOZ_OVERRIDE MOZ_FINAL { return true; }

Просмотреть файл

@ -24,6 +24,7 @@
#include "SpdyStream3.h"
#include "PSpdyPush.h"
#include "SpdyZlibReporter.h"
#include "TunnelUtils.h"
#include <algorithm>
@ -334,7 +335,9 @@ SpdySession3::RegisterStreamID(SpdyStream3 *stream, uint32_t aNewID)
bool
SpdySession3::AddStream(nsAHttpTransaction *aHttpTransaction,
int32_t aPriority)
int32_t aPriority,
bool aUseTunnel,
nsIInterfaceRequestor *aCallbacks)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -345,23 +348,23 @@ SpdySession3::AddStream(nsAHttpTransaction *aHttpTransaction,
return false;
}
// assert that
// a] in the case we have a connection, that the new transaction connection
// is either undefined or on the same connection
// b] in the case we don't have a connection, that the new transaction
// connection is defined so we can adopt it
MOZ_ASSERT((mConnection && (!aHttpTransaction->Connection() ||
mConnection == aHttpTransaction->Connection())) ||
(!mConnection && aHttpTransaction->Connection()));
if (!mConnection) {
mConnection = aHttpTransaction->Connection();
}
aHttpTransaction->SetConnection(this);
if (aUseTunnel) {
LOG3(("SpdySession3::AddStream session=%p trans=%p OnTunnel",
this, aHttpTransaction));
DispatchOnTunnel(aHttpTransaction, aCallbacks);
return true;
}
SpdyStream3 *stream = new SpdyStream3(aHttpTransaction, this, aPriority);
LOG3(("SpdySession3::AddStream session=%p stream=%p NextID=0x%X (tentative)",
this, stream, mNextStreamID));
LOG3(("SpdySession3::AddStream session=%p stream=%p serial=%u "
"NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
mStreamTransactionHash.Put(aHttpTransaction, stream);
@ -907,6 +910,10 @@ SpdySession3::CloseStream(SpdyStream3 *aStream, nsresult aResult)
RemoveStreamFromQueues(aStream);
if (aStream->IsTunnel()) {
UnRegisterTunnel(aStream);
}
// Send the stream the close() indication
aStream->Close(aResult);
}
@ -1028,7 +1035,13 @@ SpdySession3::HandleSynStream(SpdySession3 *self)
self->mPushedStreams.AppendElement(pushedStream);
// The pushed stream is unidirectional so it is fully open immediately
pushedStream->SetFullyOpen();
rv = pushedStream->SetFullyOpen();
if (NS_FAILED(rv)) {
LOG(("SpdySession3::HandleSynStream pushedstream fully open failed\n"));
self->CleanupStream(pushedStream, rv, RST_CANCEL);
self->ResetDownstreamState();
return NS_OK;
}
// Uncompress the response headers into a stream specific buffer, leaving them
// in spdy format for the time being.
@ -1101,10 +1114,10 @@ SpdySession3::HandleSynReply(SpdySession3 *self)
return NS_ERROR_ILLEGAL_VALUE;
}
LOG3(("SpdySession3::HandleSynReply %p lookup via streamID in syn_reply.\n",
self));
uint32_t streamID =
NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
LOG3(("SpdySession3::HandleSynReply %p lookup via streamID 0x%X in syn_reply.\n",
self, streamID));
nsresult rv = self->SetInputFrameDataStream(streamID);
if (NS_FAILED(rv))
return rv;
@ -1167,7 +1180,19 @@ SpdySession3::HandleSynReply(SpdySession3 *self)
self->ResetDownstreamState();
return NS_OK;
}
self->mInputFrameDataStream->SetFullyOpen();
rv = self->mInputFrameDataStream->SetFullyOpen();
if (NS_FAILED(rv)) {
LOG(("SpdySession3::HandleSynReply SetFullyOpen failed\n"));
if (self->mInputFrameDataStream->IsTunnel()) {
gHttpHandler->ConnMgr()->CancelTransactions(
self->mInputFrameDataStream->Transaction()->ConnectionInfo(),
NS_ERROR_CONNECTION_REFUSED);
}
self->CleanupStream(self->mInputFrameDataStream, rv, RST_CANCEL);
self->ResetDownstreamState();
return NS_OK;
}
self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
@ -1996,6 +2021,7 @@ SpdySession3::WriteSegments(nsAHttpSegmentWriter *writer,
MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
mNeedsCleanup = nullptr; /* just in case */
SpdyStream3 *stream = mInputFrameDataStream;
mSegmentWriter = writer;
rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
mSegmentWriter = nullptr;
@ -2006,7 +2032,6 @@ SpdySession3::WriteSegments(nsAHttpSegmentWriter *writer,
// This will happen when the transaction figures out it is EOF, generally
// due to a content-length match being made. Return OK from this function
// otherwise the whole session would be torn down.
SpdyStream3 *stream = mInputFrameDataStream;
// if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
// back to PROCESSING_DATA_FRAME where we came from
@ -2020,8 +2045,8 @@ SpdySession3::WriteSegments(nsAHttpSegmentWriter *writer,
this, stream, stream ? stream->StreamID() : 0,
mNeedsCleanup, rv));
CleanupStream(stream, NS_OK, RST_CANCEL);
MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
mNeedsCleanup = nullptr; /* just in case */
MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup == stream);
mNeedsCleanup = nullptr;
return NS_OK;
}
@ -2444,6 +2469,78 @@ SpdySession3::ConnectPushedStream(SpdyStream3 *stream)
ForceRecv();
}
uint32_t
SpdySession3::FindTunnelCount(nsHttpConnectionInfo *aConnInfo)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
uint32_t rv = 0;
mTunnelHash.Get(aConnInfo->HashKey(), &rv);
return rv;
}
void
SpdySession3::RegisterTunnel(SpdyStream3 *aTunnel)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
uint32_t newcount = FindTunnelCount(ci) + 1;
mTunnelHash.Remove(ci->HashKey());
mTunnelHash.Put(ci->HashKey(), newcount);
LOG3(("SpdySession3::RegisterTunnel %p stream=%p tunnels=%d [%s]",
this, aTunnel, newcount, ci->HashKey().get()));
}
void
SpdySession3::UnRegisterTunnel(SpdyStream3 *aTunnel)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
MOZ_ASSERT(FindTunnelCount(ci));
uint32_t newcount = FindTunnelCount(ci) - 1;
mTunnelHash.Remove(ci->HashKey());
if (newcount) {
mTunnelHash.Put(ci->HashKey(), newcount);
}
LOG3(("SpdySession3::UnRegisterTunnel %p stream=%p tunnels=%d [%s]",
this, aTunnel, newcount, ci->HashKey().get()));
}
void
SpdySession3::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
nsIInterfaceRequestor *aCallbacks)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
MOZ_ASSERT(trans);
LOG3(("SpdySession3::DispatchOnTunnel %p trans=%p", this, trans));
aHttpTransaction->SetConnection(nullptr);
// this transaction has done its work of setting up a tunnel, let
// the connection manager queue it if necessary
trans->SetDontRouteViaWildCard(true);
if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
LOG3(("SpdySession3::DispatchOnTunnel %p create on new tunnel %s",
this, ci->HashKey().get()));
nsRefPtr<SpdyConnectTransaction> connectTrans =
new SpdyConnectTransaction(ci, aCallbacks,
trans->Caps(), trans, this);
AddStream(connectTrans, trans->Priority(),
false, nullptr);
SpdyStream3 *tunnel = mStreamTransactionHash.Get(connectTrans);
MOZ_ASSERT(tunnel);
RegisterTunnel(tunnel);
}
// requeue it. The connection manager is responsible for actually putting
// this on the tunnel connection with the specific ci now that it
// has DontRouteViaWildCard set.
gHttpHandler->InitiateTransaction(trans, trans->Priority());
}
//-----------------------------------------------------------------------------
// Modified methods of nsAHttpConnection
//-----------------------------------------------------------------------------
@ -2468,6 +2565,12 @@ SpdySession3::TransactionHasDataToWrite(nsAHttpTransaction *caller)
this, stream->StreamID()));
mReadyForWrite.Push(stream);
SetWriteCallbacks();
// NSPR poll will not poll the network if there are non system PR_FileDesc's
// that are ready - so we can get into a deadlock waiting for the system IO
// to come back here if we don't force the send loop manually.
ForceSend();
}
void
@ -2479,6 +2582,7 @@ SpdySession3::TransactionHasDataToWrite(SpdyStream3 *stream)
mReadyForWrite.Push(stream);
SetWriteCallbacks();
ForceSend();
}
bool
@ -2518,10 +2622,17 @@ SpdySession3::Classification()
return mConnection->Classification();
}
void
SpdySession3::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
{
*aOut = nullptr;
}
//-----------------------------------------------------------------------------
// unused methods of nsAHttpTransaction
// We can be sure of this because SpdySession3 is only constructed in
// nsHttpConnection and is never passed out of that object
// nsHttpConnection and is never passed out of that object or a TLSFilterTransaction
// TLS tunnel
//-----------------------------------------------------------------------------
void
@ -2531,13 +2642,6 @@ SpdySession3::SetConnection(nsAHttpConnection *)
MOZ_ASSERT(false, "SpdySession3::SetConnection()");
}
void
SpdySession3::GetSecurityCallbacks(nsIInterfaceRequestor **)
{
// This is unexpected
MOZ_ASSERT(false, "SpdySession3::GetSecurityCallbacks()");
}
void
SpdySession3::SetProxyConnectFailed()
{

Просмотреть файл

@ -24,6 +24,7 @@ namespace mozilla { namespace net {
class SpdyPushedStream3;
class SpdyStream3;
class nsHttpTransaction;
class SpdySession3 MOZ_FINAL : public ASpdySession
, public nsAHttpConnection
@ -40,7 +41,8 @@ public:
SpdySession3(nsISocketTransport *);
~SpdySession3();
bool AddStream(nsAHttpTransaction *, int32_t);
bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *);
bool CanReuse() { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams();
@ -382,6 +384,15 @@ private:
// by the load group and the serial number can be used as part of the cache key
// to make sure streams aren't shared across sessions.
uint64_t mSerial;
private:
/// connect tunnels
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
void RegisterTunnel(SpdyStream3 *);
void UnRegisterTunnel(SpdyStream3 *);
uint32_t FindTunnelCount(nsHttpConnectionInfo *);
nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
};
}} // namespace mozilla::net

Просмотреть файл

@ -338,7 +338,9 @@ SpdySession31::RegisterStreamID(SpdyStream31 *stream, uint32_t aNewID)
bool
SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction,
int32_t aPriority)
int32_t aPriority,
bool aUseTunnel,
nsIInterfaceRequestor *aCallbacks)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -2613,6 +2615,7 @@ SpdySession31::TransactionHasDataToWrite(nsAHttpTransaction *caller)
this, stream->StreamID()));
mReadyForWrite.Push(stream);
SetWriteCallbacks();
}
void
@ -2663,10 +2666,17 @@ SpdySession31::Classification()
return mConnection->Classification();
}
void
SpdySession31::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
{
*aOut = nullptr;
}
//-----------------------------------------------------------------------------
// unused methods of nsAHttpTransaction
// We can be sure of this because SpdySession31 is only constructed in
// nsHttpConnection and is never passed out of that object
// nsHttpConnection and is never passed out of that object or a TLSFilterTransaction
// TLS tunnel
//-----------------------------------------------------------------------------
void
@ -2676,13 +2686,6 @@ SpdySession31::SetConnection(nsAHttpConnection *)
MOZ_ASSERT(false, "SpdySession31::SetConnection()");
}
void
SpdySession31::GetSecurityCallbacks(nsIInterfaceRequestor **)
{
// This is unexpected
MOZ_ASSERT(false, "SpdySession31::GetSecurityCallbacks()");
}
void
SpdySession31::SetProxyConnectFailed()
{

Просмотреть файл

@ -39,7 +39,8 @@ public:
SpdySession31(nsISocketTransport *);
~SpdySession31();
bool AddStream(nsAHttpTransaction *, int32_t);
bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *);
bool CanReuse() { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams();

Просмотреть файл

@ -26,6 +26,7 @@
#include "SpdyStream3.h"
#include "PSpdyPush.h"
#include "SpdyZlibReporter.h"
#include "TunnelUtils.h"
#include <algorithm>
@ -70,6 +71,7 @@ SpdyStream3::SpdyStream3(nsAHttpTransaction *httpTransaction,
, mTotalSent(0)
, mTotalRead(0)
, mPushSource(nullptr)
, mIsTunnel(false)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -84,6 +86,7 @@ SpdyStream3::SpdyStream3(nsAHttpTransaction *httpTransaction,
SpdyStream3::~SpdyStream3()
{
ClearTransactionsBlockedOnTunnel();
mStreamID = SpdySession3::kDeadStreamID;
}
@ -114,7 +117,8 @@ SpdyStream3::ReadSegments(nsAHttpSegmentReader *reader,
mSegmentReader = reader;
rv = mTransaction->ReadSegments(this, count, countRead);
mSegmentReader = nullptr;
LOG3(("SpdyStream3::ReadSegments %p trans readsegments rv %x read=%d\n",
this, rv, *countRead));
// Check to see if the transaction's request could be written out now.
// If not, mark the stream for callback when writing can proceed.
if (NS_SUCCEEDED(rv) &&
@ -139,7 +143,8 @@ SpdyStream3::ReadSegments(nsAHttpSegmentReader *reader,
if (!mBlockedOnRwin &&
!mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
LOG3(("SpdyStream3::ReadSegments %p 0x%X: Sending request data complete, "
"mUpstreamState=%x",this, mStreamID, mUpstreamState));
"mUpstreamState=%x finondata=%d",this, mStreamID,
mUpstreamState, mSentFinOnData));
if (mSentFinOnData) {
ChangeState(UPSTREAM_COMPLETE);
}
@ -283,7 +288,7 @@ SpdyStream3::ParseHttpRequestHeaders(const char *buf,
nsAutoCString hashkey;
mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
CreatePushHashKey(NS_LITERAL_CSTRING("https"),
CreatePushHashKey(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"),
hostHeader, mSession->Serial(),
mTransaction->RequestHead()->RequestURI(),
mOrigin, hashkey);
@ -458,24 +463,54 @@ SpdyStream3::ParseHttpRequestHeaders(const char *buf,
// headers at this same level so it is not necessary to do so here.
const char *methodHeader = mTransaction->RequestHead()->Method().get();
LOG3(("Stream method %p 0x%X %s\n", this, mStreamID, methodHeader));
// The header block length
uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */
uint16_t count = hdrHash.Count() + 4; /* :method, :path, :version, :host */
if (mTransaction->RequestHead()->IsConnect()) {
mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
} else {
++count; // :scheme used if not connect
}
CompressToFrame(count);
// :method, :path, :version comprise a HTTP/1 request line, so send those first
// to make life easy for any gateways
CompressToFrame(NS_LITERAL_CSTRING(":method"));
CompressToFrame(methodHeader, strlen(methodHeader));
CompressToFrame(NS_LITERAL_CSTRING(":path"));
CompressToFrame(mTransaction->RequestHead()->RequestURI());
if (!mTransaction->RequestHead()->IsConnect()) {
CompressToFrame(mTransaction->RequestHead()->RequestURI());
} else {
#ifdef DEBUG
nsRefPtr<SpdyConnectTransaction> qiTrans(do_QueryObject(mTransaction));
MOZ_ASSERT(qiTrans);
#endif
mIsTunnel = true;
// Connect places host:port in :path. Don't use default port.
nsHttpConnectionInfo *ci = mTransaction->ConnectionInfo();
if (!ci) {
return NS_ERROR_UNEXPECTED;
}
nsAutoCString route;
route = ci->GetHost();
route.AppendLiteral(":");
route.AppendInt(ci->Port());
CompressToFrame(route);
}
CompressToFrame(NS_LITERAL_CSTRING(":version"));
CompressToFrame(versionHeader);
CompressToFrame(NS_LITERAL_CSTRING(":host"));
CompressToFrame(hostHeader);
CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
CompressToFrame(NS_LITERAL_CSTRING("https"));
if (!mTransaction->RequestHead()->IsConnect()) {
// no :scheme with connect
CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
CompressToFrame(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"));
}
hdrHash.Enumerate(hdrHashEnumerate, this);
CompressFlushFrame();
@ -490,9 +525,8 @@ SpdyStream3::ParseHttpRequestHeaders(const char *buf,
// to wait for a data packet to put it on.
if (mTransaction->RequestHead()->IsGet() ||
mTransaction->RequestHead()->IsConnect() ||
mTransaction->RequestHead()->IsHead()) {
// for GET, CONNECT, and HEAD place the fin bit right on the
// for GET and HEAD place the fin bit right on the
// syn stream packet
mSentFinOnData = 1;
@ -500,6 +534,7 @@ SpdyStream3::ParseHttpRequestHeaders(const char *buf,
}
else if (mTransaction->RequestHead()->IsPost() ||
mTransaction->RequestHead()->IsPut() ||
mTransaction->RequestHead()->IsConnect() ||
mTransaction->RequestHead()->IsOptions()) {
// place fin in a data frame even for 0 length messages, I've seen
// the google gateway be unhappy with fin-on-syn for 0 length POST
@ -594,6 +629,9 @@ void
SpdyStream3::UpdateTransportReadEvents(uint32_t count)
{
mTotalRead += count;
if (!mSocketTransport) {
return;
}
mTransaction->OnTransportStatus(mSocketTransport,
NS_NET_STATUS_RECEIVING_FROM,
@ -1246,6 +1284,12 @@ SpdyStream3::ConvertHeaders(nsACString &aHeadersOut)
mDecompressBufferSize = 0;
mDecompressBufferUsed = 0;
if (mIsTunnel) {
aHeadersOut.Truncate();
LOG(("SpdyStream3::ConvertHeaders %p 0x%X headers removed for tunnel\n",
this, mStreamID));
}
return NS_OK;
}
@ -1313,6 +1357,38 @@ SpdyStream3::CompressFlushFrame()
ExecuteCompress(Z_SYNC_FLUSH);
}
bool
SpdyStream3::GetFullyOpen()
{
return mFullyOpen;
}
nsresult
SpdyStream3::SetFullyOpen()
{
MOZ_ASSERT(!mFullyOpen);
mFullyOpen = 1;
if (mIsTunnel) {
nsDependentCSubstring statusSubstring;
nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
statusSubstring);
if (NS_SUCCEEDED(rv)) {
nsCString status(statusSubstring);
nsresult errcode;
if (status.ToInteger(&errcode) != 200) {
LOG3(("SpdyStream3::SetFullyOpen %p Tunnel not 200", this));
return NS_ERROR_FAILURE;
}
LOG3(("SpdyStream3::SetFullyOpen %p Tunnel 200 OK", this));
}
MapStreamToHttpConnection();
ClearTransactionsBlockedOnTunnel();
}
return NS_OK;
}
void
SpdyStream3::Close(nsresult reason)
{
@ -1396,11 +1472,15 @@ SpdyStream3::OnReadSegment(const char *buf,
this, mStreamID, mRemoteWindow, dataLength));
mRemoteWindow -= dataLength;
LOG3(("SpdyStream3 %p id %x request len remaining %d, "
"count avail %d, chunk used %d",
LOG3(("SpdyStream3 %p id %x request len remaining %u, "
"count avail %u, chunk used %u",
this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
if (dataLength > mRequestBodyLenRemaining)
if (!dataLength && mRequestBodyLenRemaining) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
if (dataLength > mRequestBodyLenRemaining) {
return NS_ERROR_UNEXPECTED;
}
mRequestBodyLenRemaining -= dataLength;
GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
ChangeState(SENDING_REQUEST_BODY);
@ -1466,6 +1546,27 @@ SpdyStream3::OnWriteSegment(char *buf,
return NS_OK;
}
/// connect tunnels
void
SpdyStream3::ClearTransactionsBlockedOnTunnel()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (!mIsTunnel) {
return;
}
gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
}
void
SpdyStream3::MapStreamToHttpConnection()
{
nsRefPtr<SpdyConnectTransaction> qiTrans(do_QueryObject(mTransaction));
MOZ_ASSERT(qiTrans);
qiTrans->MapStreamToHttpConnection(mSocketTransport,
mTransaction->ConnectionInfo());
}
} // namespace mozilla::net
} // namespace mozilla

Просмотреть файл

@ -39,13 +39,10 @@ public:
return static_cast<bool>(mRequestBlockedOnRead);
}
// returns false if called more than once
bool GetFullyOpen() {return mFullyOpen;}
void SetFullyOpen()
{
MOZ_ASSERT(!mFullyOpen);
mFullyOpen = 1;
}
bool GetFullyOpen();
// returns failure if stream cannot be made ready and stream
// should be canceled
nsresult SetFullyOpen();
bool HasRegisteredID() { return mStreamID != 0; }
@ -220,7 +217,8 @@ private:
// place the fin flag on the last data packet instead of waiting
// for a stream closed indication. Relying on stream close results
// in an extra 0-length runt packet and seems to have some interop
// problems with the google servers.
// problems with the google servers. Connect does rely on stream
// close by setting this to the max value.
int64_t mRequestBodyLenRemaining;
// based on nsISupportsPriority definitions
@ -253,6 +251,15 @@ private:
// For SpdyPush
SpdyPushedStream3 *mPushSource;
/// connect tunnels
public:
bool IsTunnel() { return mIsTunnel; }
private:
void ClearTransactionsBlockedOnTunnel();
void MapStreamToHttpConnection();
bool mIsTunnel;
};
}} // namespace mozilla::net

Просмотреть файл

@ -288,7 +288,7 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
nsAutoCString hashkey;
mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
CreatePushHashKey(NS_LITERAL_CSTRING("https"),
CreatePushHashKey(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"),
hostHeader, mSession->Serial(),
mTransaction->RequestHead()->RequestURI(),
mOrigin, hashkey);
@ -481,7 +481,7 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
CompressToFrame(NS_LITERAL_CSTRING(":host"));
CompressToFrame(hostHeader);
CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
CompressToFrame(NS_LITERAL_CSTRING("https"));
CompressToFrame(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"));
hdrHash.Enumerate(hdrHashEnumerate, this);
CompressFlushFrame();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,219 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_net_TLSFilterTransaction_h
#define mozilla_net_TLSFilterTransaction_h
#include "mozilla/Attributes.h"
#include "nsAHttpTransaction.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsISocketTransport.h"
#include "nsITimer.h"
#include "NullHttpTransaction.h"
// a TLSFilterTransaction wraps another nsAHttpTransaction but
// applies a encode/decode filter of TLS onto the ReadSegments
// and WriteSegments data. It is not used for basic https://
// but it is used for supplemental TLS tunnels - such as those
// needed by CONNECT tunnels in HTTP/2 or even CONNECT tunnels when
// the underlying proxy connection is already running TLS
//
// HTTP/2 CONNECT tunnels cannot use pushed IO layers because of
// the multiplexing involved on the base stream. i.e. the base stream
// once it is decrypted may have parts that are encrypted with a
// variety of keys, or none at all
/* ************************************************************************
The input path of http over a spdy CONNECT tunnel once it is established as a stream
note the "real http transaction" can be either a http/1 transaction or another spdy session
inside the tunnel.
nsHttpConnection::OnInputStreamReady (real socket)
nsHttpConnection::OnSocketReadable()
SpdySession::WriteSegment()
SpdyStream::WriteSegment (tunnel stream)
SpdyConnectTransaction::WriteSegment
SpdyStream::OnWriteSegment(tunnel stream)
SpdySession::OnWriteSegment()
SpdySession::NetworkRead()
nsHttpConnection::OnWriteSegment (real socket)
realSocketIn->Read() return data from network
now pop the stack back up to SpdyConnectTransaction::WriteSegment, the data
that has been read is stored mInputData
SpdyConnectTransaction.mTunneledConn::OnInputStreamReady(mTunnelStreamIn)
SpdyConnectTransaction.mTunneledConn::OnSocketReadable()
TLSFilterTransaction::WriteSegment()
nsHttpTransaction::WriteSegment(real http transaction)
TLSFilterTransaction::OnWriteSegment() removes tls on way back up stack
SpdyConnectTransaction.mTunneledConn::OnWriteSegment()
SpdyConnectTransaction.mTunneledConn.mTunnelStreamIn->Read() // gets data from mInputData
The output path works similarly:
nsHttpConnection::OnOutputStreamReady (real socket)
nsHttpConnection::OnSocketWritable()
SpdySession::ReadSegments (locates tunnel)
SpdyStream::ReadSegments (tunnel stream)
SpdyConnectTransaction::ReadSegments()
SpdyConnectTransaction.mTunneledConn::OnOutputStreamReady (tunnel connection)
SpdyConnectTransaction.mTunneledConn::OnSocketWritable (tunnel connection)
TLSFilterTransaction::ReadSegment()
nsHttpTransaction::ReadSegment (real http transaction generates plaintext on way down)
TLSFilterTransaction::OnReadSegment (BUF and LEN gets encrypted here on way down)
SpdyConnectTransaction.mTunneledConn::OnReadSegment (BUF and LEN) (tunnel connection)
SpdyConnectTransaction.mTunneledConn.mTunnelStreamOut->Write(BUF, LEN) .. get stored in mOutputData
Now pop the stack back up to SpdyConnectTransaction::ReadSegment(), where it has
the encrypted text available in mOutputData
SpdyStream->OnReadSegment(BUF,LEN) from mOutputData. Tunnel stream
SpdySession->OnReadSegment() // encrypted data gets put in a data frame
nsHttpConnection->OnReadSegment()
realSocketOut->write() writes data to network
**************************************************************************/
struct PRSocketOptionData;
namespace mozilla { namespace net {
class nsHttpRequestHead;
class TLSFilterTransaction;
class NudgeTunnelCallback : public nsISupports
{
public:
virtual void OnTunnelNudged(TLSFilterTransaction *) = 0;
};
#define NS_DECL_NUDGETUNNELCALLBACK void OnTunnelNudged(TLSFilterTransaction *);
class TLSFilterTransaction MOZ_FINAL
: public nsAHttpTransaction
, public nsAHttpSegmentReader
, public nsAHttpSegmentWriter
, public nsITimerCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSAHTTPTRANSACTION
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER
NS_DECL_NSITIMERCALLBACK
TLSFilterTransaction(nsAHttpTransaction *aWrappedTransaction,
const char *tlsHost, int32_t tlsPort);
~TLSFilterTransaction();
const nsAHttpTransaction *Transaction() const { return mTransaction.get(); }
nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment);
nsresult GetTransactionSecurityInfo(nsISupports **);
nsresult NudgeTunnel(NudgeTunnelCallback *callback,
nsAHttpSegmentReader *reader,
nsAHttpSegmentWriter *writer);
private:
nsresult StartTimerCallback();
void Cleanup();
int32_t FilterOutput(const char *aBuf, int32_t aAmount);
int32_t FilterInput(char *aBuf, int32_t aAmount);
static PRStatus GetPeerName(PRFileDesc *fd, PRNetAddr*addr);
static PRStatus GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data);
static PRStatus SetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data);
static int32_t FilterWrite(PRFileDesc *fd, const void *buf, int32_t amount);
static int32_t FilterRead(PRFileDesc *fd, void *buf, int32_t amount);
static int32_t FilterSend(PRFileDesc *fd, const void *buf, int32_t amount, int flags,
PRIntervalTime timeout);
static int32_t FilterRecv(PRFileDesc *fd, void *buf, int32_t amount, int flags,
PRIntervalTime timeout);
static PRStatus FilterClose(PRFileDesc *fd);
private:
nsRefPtr<nsAHttpTransaction> mTransaction;
nsCOMPtr<nsISupports> mSecInfo;
nsCOMPtr<nsITimer> mTimer;
nsRefPtr<NudgeTunnelCallback> mNudgeCallback;
// buffered network output, after encryption
nsAutoArrayPtr<char> mEncryptedText;
uint32_t mEncryptedTextUsed;
uint32_t mEncryptedTextSize;
PRFileDesc *mFD;
nsAHttpSegmentReader *mSegmentReader;
nsAHttpSegmentWriter *mSegmentWriter;
nsresult mFilterReadCode;
bool mForce;
bool mReadSegmentBlocked;
uint32_t mNudgeCounter;
};
class SocketTransportShim;
class InputStreamShim;
class OutputStreamShim;
class nsHttpConnection;
class ASpdySession;
class SpdyConnectTransaction MOZ_FINAL : public NullHttpTransaction
{
public:
SpdyConnectTransaction(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
uint32_t caps,
nsAHttpTransaction *trans,
ASpdySession *session);
~SpdyConnectTransaction();
void MapStreamToHttpConnection(nsISocketTransport *aTransport,
nsHttpConnectionInfo *aConnInfo);
nsresult ReadSegments(nsAHttpSegmentReader *reader,
uint32_t count, uint32_t *countRead) MOZ_OVERRIDE MOZ_FINAL;
nsresult WriteSegments(nsAHttpSegmentWriter *writer,
uint32_t count, uint32_t *countWritten) MOZ_OVERRIDE MOZ_FINAL;
nsHttpRequestHead *RequestHead() MOZ_OVERRIDE MOZ_FINAL;
void Close(nsresult reason) MOZ_OVERRIDE MOZ_FINAL;
private:
friend class InputStreamShim;
friend class OutputStreamShim;
nsresult Flush(uint32_t count, uint32_t *countRead);
void CreateShimError(nsresult code);
nsCString mConnectString;
uint32_t mConnectStringOffset;
nsHttpRequestHead *mRequestHead;
ASpdySession *mSession;
nsAHttpSegmentReader *mSegmentReader;
nsAutoArrayPtr<char> mInputData;
uint32_t mInputDataSize;
uint32_t mInputDataUsed;
uint32_t mInputDataOffset;
nsAutoArrayPtr<char> mOutputData;
uint32_t mOutputDataSize;
uint32_t mOutputDataUsed;
uint32_t mOutputDataOffset;
TimeStamp mTimestampSyn;
nsRefPtr<nsHttpConnection> mTunneledConn;
nsRefPtr<nsHttpConnectionInfo> mConnInfo;
nsRefPtr<SocketTransportShim> mTunnelTransport;
nsRefPtr<InputStreamShim> mTunnelStreamIn;
nsRefPtr<OutputStreamShim> mTunnelStreamOut;
};
}} // namespace mozilla::net
#endif // mozilla_net_TLSFilterTransaction_h

Просмотреть файл

@ -78,6 +78,7 @@ SOURCES += [
'SpdyStream3.cpp',
'SpdyStream31.cpp',
'SpdyZlibReporter.cpp',
'TunnelUtils.cpp',
]
# These files cannot be built in unified mode because of OS X headers.

Просмотреть файл

@ -48,8 +48,9 @@ public:
virtual nsresult ResumeSend() = 0;
virtual nsresult ResumeRecv() = 0;
// called by a transaction to force a "read from network" iteration
// called by a transaction to force a "send/recv from network" iteration
// even if not scheduled by socket associated with connection
virtual nsresult ForceSend() = 0;
virtual nsresult ForceRecv() = 0;
// After a connection has had ResumeSend() called by a transaction,
@ -180,6 +181,12 @@ public:
return NS_ERROR_FAILURE; \
return (fwdObject)->ResumeRecv(); \
} \
nsresult ForceSend() \
{ \
if (!(fwdObject)) \
return NS_ERROR_FAILURE; \
return (fwdObject)->ForceSend(); \
} \
nsresult ForceRecv() \
{ \
if (!(fwdObject)) \

Просмотреть файл

@ -7,6 +7,7 @@
#include "nsISupports.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
class nsIInterfaceRequestor;
class nsIEventTarget;
@ -32,7 +33,7 @@ class nsHttpConnectionInfo;
// write function returns NS_BASE_STREAM_WOULD_BLOCK in this case).
//----------------------------------------------------------------------------
class nsAHttpTransaction : public nsISupports
class nsAHttpTransaction : public nsSupportsWeakReference
{
public:
// called by the connection when it takes ownership of the transaction.
@ -162,6 +163,17 @@ public:
CLASS_MAX
};
// conceptually the security info is part of the connection, but sometimes
// in the case of TLS tunneled within TLS the transaction might present
// a more specific security info that cannot be represented as a layer in
// the connection due to multiplexing. This interface represents such an
// overload. If it returns NS_FAILURE the connection should be considered
// authoritative.
virtual nsresult GetTransactionSecurityInfo(nsISupports **)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
};
#define NS_DECL_NSAHTTPTRANSACTION \
@ -175,12 +187,12 @@ public:
uint32_t Caps(); \
void SetDNSWasRefreshed(); \
uint64_t Available(); \
nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *); \
nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *); \
virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *); \
virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *); \
void Close(nsresult reason); \
nsHttpConnectionInfo *ConnectionInfo(); \
void SetProxyConnectFailed(); \
nsHttpRequestHead *RequestHead(); \
virtual nsHttpRequestHead *RequestHead(); \
uint32_t Http1xTransactionCount(); \
nsresult TakeSubTransactions(nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions); \
nsresult AddTransaction(nsAHttpTransaction *); \

Просмотреть файл

@ -86,5 +86,6 @@ HTTP_ATOM(Version, "Version")
HTTP_ATOM(WWW_Authenticate, "WWW-Authenticate")
HTTP_ATOM(Warning, "Warning")
HTTP_ATOM(X_Firefox_Spdy, "X-Firefox-Spdy")
HTTP_ATOM(X_Firefox_Spdy_Proxy, "X-Firefox-Spdy-Proxy")
// methods are case sensitive and do not use atom table

Просмотреть файл

@ -13,25 +13,25 @@
#undef LOG_ENABLED
#define LOG_ENABLED() LOG5_ENABLED()
#include "ASpdySession.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/Telemetry.h"
#include "nsHttpConnection.h"
#include "nsHttpHandler.h"
#include "nsHttpPipeline.h"
#include "nsHttpRequestHead.h"
#include "nsHttpResponseHead.h"
#include "nsHttpHandler.h"
#include "nsIOService.h"
#include "nsISocketTransport.h"
#include "nsSocketTransportService2.h"
#include "nsISSLSocketControl.h"
#include "sslt.h"
#include "nsStringStream.h"
#include "nsISupportsPriority.h"
#include "nsPreloadedStream.h"
#include "nsProxyRelease.h"
#include "nsSocketTransport2.h"
#include "nsPreloadedStream.h"
#include "ASpdySession.h"
#include "mozilla/Telemetry.h"
#include "nsISupportsPriority.h"
#include "nsHttpPipeline.h"
#include <algorithm>
#include "mozilla/ChaosMode.h"
#include "nsStringStream.h"
#include "sslt.h"
#include "TunnelUtils.h"
#ifdef DEBUG
// defined by the socket transport service while active
@ -55,6 +55,7 @@ nsHttpConnection::nsHttpConnection()
, mMaxBytesRead(0)
, mTotalBytesRead(0)
, mTotalBytesWritten(0)
, mContentBytesWritten(0)
, mConnectedTransport(false)
, mKeepAlive(true) // assume to keep-alive by default
, mKeepAliveMask(true)
@ -66,6 +67,7 @@ nsHttpConnection::nsHttpConnection()
, mIdleMonitoring(false)
, mProxyConnectInProgress(false)
, mExperienced(false)
, mInSpdyTunnel(false)
, mHttp1xTransactionCount(0)
, mRemainingConnectionUses(0xffffffff)
, mClassification(nsAHttpTransaction::CLASS_GENERAL)
@ -121,13 +123,7 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info,
nsIInterfaceRequestor *callbacks,
PRIntervalTime rtt)
{
MOZ_ASSERT(transport && instream && outstream,
"invalid socket information");
LOG(("nsHttpConnection::Init [this=%p "
"transport=%p instream=%p outstream=%p rtt=%d]\n",
this, transport, instream, outstream,
PR_IntervalToMilliseconds(rtt)));
LOG(("nsHttpConnection::Init this=%p", this));
NS_ENSURE_ARG_POINTER(info);
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
@ -142,13 +138,12 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info,
mSocketTransport = transport;
mSocketIn = instream;
mSocketOut = outstream;
nsresult rv = mSocketTransport->SetEventSink(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
// See explanation for non-strictness of this operation in SetSecurityCallbacks.
mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(callbacks, false);
rv = mSocketTransport->SetSecurityCallbacks(this);
NS_ENSURE_SUCCESS(rv, rv);
mSocketTransport->SetEventSink(this, nullptr);
mSocketTransport->SetSecurityCallbacks(this);
return NS_OK;
}
@ -163,6 +158,11 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
mUsingSpdyVersion = spdyVersion;
mEverUsedSpdy = true;
if (!mReportedSpdy) {
mReportedSpdy = true;
gHttpHandler->ConnMgr()->ReportSpdyConnection(this, true);
}
// Setting the connection as reused allows some transactions that fail
// with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
// to handle clean rejections (such as those that arrived after
@ -197,22 +197,39 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
return;
}
if (NeedSpdyTunnel()) {
LOG3(("nsHttpConnection::StartSpdy %p Connecting To a HTTP/2 "
"Proxy and Need Connect", this));
MOZ_ASSERT(mProxyConnectStream);
mProxyConnectStream = nullptr;
mCompletedProxyConnect = true;
mProxyConnectInProgress = false;
}
mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport);
bool spdyProxy = mConnInfo->UsingHttpsProxy() && !mTLSFilter;
if (spdyProxy) {
nsRefPtr<nsHttpConnectionInfo> wildCardProxyCi;
mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi));
gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo,
wildCardProxyCi, this);
mConnInfo = wildCardProxyCi;
}
if (NS_FAILED(rv)) { // includes NS_ERROR_NOT_IMPLEMENTED
MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty");
// This is ok - treat mTransaction as a single real request.
// Wrap the old http transaction into the new spdy session
// as the first stream.
if (!mSpdySession->AddStream(mTransaction, mPriority)) {
MOZ_ASSERT(false); // this cannot happen!
mTransaction->Close(NS_ERROR_ABORT);
rv = AddTransaction(mTransaction, mPriority);
if (NS_FAILED(rv)) {
return;
}
LOG(("nsHttpConnection::StartSpdy moves single transaction %p "
"into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
}
else {
} else {
int32_t count = list.Length();
LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
@ -224,11 +241,8 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
}
for (int32_t index = 0; index < count; ++index) {
// AddStream() cannot fail
if (!mSpdySession->AddStream(list[index], mPriority)) {
MOZ_ASSERT(false, "SpdySession::AddStream failed");
LOG(("SpdySession::AddStream failed\n"));
mTransaction->Close(NS_ERROR_ABORT);
rv = AddTransaction(list[index], mPriority);
if (NS_FAILED(rv)) {
return;
}
}
@ -236,14 +250,19 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
// Disable TCP Keepalives - use SPDY ping instead.
rv = DisableTCPKeepalives();
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
LOG(("nsHttpConnection::StartSpdy [%p] DisableTCPKeepalives failed "
"rv[0x%x]", this, rv));
}
mSupportsPipelining = false; // dont use http/1 pipelines with spdy
mTransaction = mSpdySession;
mSupportsPipelining = false; // don't use http/1 pipelines with spdy
mIdleTimeout = gHttpHandler->SpdyTimeout();
if (!mTLSFilter) {
mTransaction = mSpdySession;
} else {
mTLSFilter->AddTransaction(mSpdySession);
}
}
bool
@ -259,18 +278,19 @@ nsHttpConnection::EnsureNPNComplete()
return true;
}
if (mNPNComplete)
if (mNPNComplete) {
return true;
}
nsresult rv;
nsCOMPtr<nsISupports> securityInfo;
nsCOMPtr<nsISSLSocketControl> ssl;
nsAutoCString negotiatedNPN;
rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
if (NS_FAILED(rv))
GetSecurityInfo(getter_AddRefs(securityInfo));
if (!securityInfo) {
goto npnComplete;
}
ssl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv))
@ -278,28 +298,30 @@ nsHttpConnection::EnsureNPNComplete()
rv = ssl->GetNegotiatedNPN(negotiatedNPN);
if (rv == NS_ERROR_NOT_CONNECTED) {
// By writing 0 bytes to the socket the SSL handshake machine is
// pushed forward.
uint32_t count = 0;
rv = mSocketOut->Write("", 0, &count);
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK)
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
goto npnComplete;
}
return false;
}
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
goto npnComplete;
LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'\n",
this, mConnInfo->Host(), negotiatedNPN.get()));
}
LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n",
this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
mTLSFilter ? " [Double Tunnel]" : ""));
uint8_t spdyVersion;
rv = gHttpHandler->SpdyInfo()->GetNPNVersionIndex(negotiatedNPN,
&spdyVersion);
if (NS_SUCCEEDED(rv))
if (NS_SUCCEEDED(rv)) {
StartSpdy(spdyVersion);
}
Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy());
@ -309,6 +331,18 @@ npnComplete:
return true;
}
void
nsHttpConnection::OnTunnelNudged(TLSFilterTransaction *trans)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnection::OnTunnelNudged %p\n", this));
if (trans != mTLSFilter) {
return;
}
LOG(("nsHttpConnection::OnTunnelNudged %p Calling OnSocketWritable\n", this));
OnSocketWritable();
}
// called on the socket thread
nsresult
nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri)
@ -322,8 +356,9 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
mTransactionCaps = caps;
mPriority = pri;
if (mTransaction && mUsingSpdyVersion)
if (mTransaction && mUsingSpdyVersion) {
return AddTransaction(trans, pri);
}
NS_ENSURE_ARG_POINTER(trans);
NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
@ -355,8 +390,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
nsCOMPtr<nsIInterfaceRequestor> callbacks;
trans->GetSecurityCallbacks(getter_AddRefs(callbacks));
SetSecurityCallbacks(callbacks);
SetupSSL(caps);
SetupSSL();
// take ownership of the transaction
mTransaction = trans;
@ -387,12 +421,17 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
mTransaction->ResponseTimeoutEnabled();
rv = StartShortLivedTCPKeepalives();
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
LOG(("nsHttpConnection::Activate [%p] "
"StartShortLivedTCPKeepalives failed rv[0x%x]",
this, rv));
}
if (mTLSFilter) {
mTLSFilter->AddTransaction(trans);
mTransaction = mTLSFilter;
}
rv = OnOutputStreamReady(mSocketOut);
failed_activation:
@ -404,9 +443,10 @@ failed_activation:
}
void
nsHttpConnection::SetupSSL(uint32_t caps)
nsHttpConnection::SetupSSL()
{
LOG(("nsHttpConnection::SetupSSL %p caps=0x%X\n", this, caps));
LOG(("nsHttpConnection::SetupSSL %p caps=0x%X %s\n",
this, mTransactionCaps,mConnInfo->HashKey().get()));
if (mSetupSSLCalled) // do only once
return;
@ -419,27 +459,23 @@ nsHttpConnection::SetupSSL(uint32_t caps)
// of this function
mNPNComplete = true;
if (!mConnInfo->EndToEndSSL())
if (!mConnInfo->FirstHopSSL()) {
return;
LOG(("nsHttpConnection::SetupSSL Setting up "
"Next Protocol Negotiation"));
nsCOMPtr<nsISupports> securityInfo;
nsresult rv =
mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
if (NS_FAILED(rv))
return;
nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv))
return;
if (caps & NS_HTTP_ALLOW_RSA_FALSESTART) {
LOG(("nsHttpConnection::SetupSSL %p "
">= RSA Key Exchange Expected\n", this));
ssl->SetKEAExpected(ssl_kea_rsa);
}
// if we are connected to the proxy with TLS, start the TLS
// flow immediately without waiting for a CONNECT sequence.
if (mInSpdyTunnel) {
InitSSLParams(false, true);
} else {
bool usingHttpsProxy = mConnInfo->UsingHttpsProxy();
InitSSLParams(usingHttpsProxy, usingHttpsProxy);
}
}
nsresult
nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps)
{
nsTArray<nsCString> protocolArray;
// The first protocol is used as the fallback if none of the
@ -458,32 +494,41 @@ nsHttpConnection::SetupSSL(uint32_t caps)
}
}
if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
mNPNComplete = false;
}
nsresult rv = ssl->SetNPNList(protocolArray);
LOG(("nsHttpConnection::SetupNPNList %p %x\n",this, rv));
return rv;
}
nsresult
nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
int32_t priority)
{
LOG(("nsHttpConnection::AddTransaction for SPDY"));
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(mSpdySession && mUsingSpdyVersion,
"AddTransaction to live http connection without spdy");
MOZ_ASSERT(mTransaction,
"AddTransaction to idle http connection");
if (!mSpdySession->AddStream(httpTransaction, priority)) {
MOZ_ASSERT(false, "AddStream should never fail due to"
"RoomForMore() admission check");
// If this is a wild card nshttpconnection (i.e. a spdy proxy) then
// it is important to start the stream using the specific connection
// info of the transaction to ensure it is routed on the right tunnel
nsHttpConnectionInfo *transCI = httpTransaction->ConnectionInfo();
bool needTunnel = transCI->UsingHttpsProxy();
needTunnel = needTunnel && !mTLSFilter;
needTunnel = needTunnel && transCI->UsingConnect();
needTunnel = needTunnel && httpTransaction->QueryHttpTransaction();
LOG(("nsHttpConnection::AddTransaction for SPDY%s",
needTunnel ? " over tunnel" : ""));
if (!mSpdySession->AddStream(httpTransaction, priority,
needTunnel, mCallbacks)) {
MOZ_ASSERT(false); // this cannot happen!
httpTransaction->Close(NS_ERROR_ABORT);
return NS_ERROR_FAILURE;
}
ResumeSend();
return NS_OK;
}
@ -504,6 +549,8 @@ nsHttpConnection::Close(nsresult reason)
if (mIdleMonitoring)
EndIdleMonitoring();
mTLSFilter = nullptr;
if (mSocketTransport) {
mSocketTransport->SetEventSink(nullptr, nullptr);
@ -536,19 +583,48 @@ nsHttpConnection::Close(nsresult reason)
// called on the socket thread
nsresult
nsHttpConnection::ProxyStartSSL()
nsHttpConnection::InitSSLParams(bool connectingToProxy, bool proxyStartSSL)
{
LOG(("nsHttpConnection::ProxyStartSSL [this=%p]\n", this));
LOG(("nsHttpConnection::InitSSLParams [this=%p] connectingToProxy=%d\n",
this, connectingToProxy));
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsresult rv;
nsCOMPtr<nsISupports> securityInfo;
nsresult rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
if (NS_FAILED(rv)) return rv;
GetSecurityInfo(getter_AddRefs(securityInfo));
if (!securityInfo) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv)){
return rv;
}
return ssl->ProxyStartSSL();
if (proxyStartSSL) {
rv = ssl->ProxyStartSSL();
if (NS_FAILED(rv)){
return rv;
}
}
if (NS_SUCCEEDED(SetupNPNList(ssl, mTransactionCaps))) {
LOG(("InitSSLParams Setting up SPDY Negotiation OK"));
mNPNComplete = false;
}
// transaction caps apply only to origin. we don't track
// proxy history.
if (!connectingToProxy &&
(mTransactionCaps & NS_HTTP_ALLOW_RSA_FALSESTART)) {
LOG(("nsHttpConnection::InitSSLParams %p "
">= RSA Key Exchange Expected\n", this));
ssl->SetKEAExpected(ssl_kea_rsa);
} else {
ssl->SetKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN);
}
return NS_OK;
}
void
@ -654,7 +730,7 @@ nsHttpConnection::IsAlive()
// SocketTransport::IsAlive can run the SSL state machine, so make sure
// the NPN options are set before that happens.
SetupSSL(mTransactionCaps);
SetupSSL();
bool alive;
nsresult rv = mSocketTransport->IsAlive(&alive);
@ -746,6 +822,11 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
NS_ENSURE_ARG_POINTER(trans);
MOZ_ASSERT(responseHead, "No response head?");
if (mInSpdyTunnel) {
responseHead->SetHeader(nsHttp::X_Firefox_Spdy_Proxy,
NS_LITERAL_CSTRING("true"));
}
// we won't change our keep-alive policy unless the server has explicitly
// told us to do so.
@ -892,16 +973,21 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
if (mProxyConnectStream) {
MOZ_ASSERT(!mUsingSpdyVersion,
"SPDY NPN Complete while using proxy connect stream");
mProxyConnectStream = 0;
mProxyConnectStream = nullptr;
if (responseStatus == 200) {
LOG(("proxy CONNECT succeeded! endtoendssl=%s\n",
mConnInfo->EndToEndSSL() ? "true" :"false"));
*reset = true;
nsresult rv;
if (mConnInfo->EndToEndSSL()) {
rv = ProxyStartSSL();
if (NS_FAILED(rv)) // XXX need to handle this for real
LOG(("ProxyStartSSL failed [rv=%x]\n", rv));
if (mConnInfo->UsingHttpsProxy()) {
LOG(("%p new TLSFilterTransaction %s %d\n",
this, mConnInfo->Host(), mConnInfo->Port()));
SetupSecondaryTLS();
}
rv = InitSSLParams(false, true);
LOG(("InitSSLParams [rv=%x]\n", rv));
}
mCompletedProxyConnect = true;
mProxyConnectInProgress = false;
@ -988,7 +1074,7 @@ nsHttpConnection::TakeTransport(nsISocketTransport **aTransport,
nsresult rv = StartLongLivedTCPKeepalives();
LOG(("nsHttpConnection::TakeTransport [%p] calling "
"StartLongLivedTCPKeepalives", this));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
LOG(("nsHttpConnection::TakeTransport [%p] "
"StartLongLivedTCPKeepalives failed rv[0x%x]", this, rv));
}
@ -1123,7 +1209,7 @@ nsHttpConnection::UpdateTCPKeepalive(nsITimer *aTimer, void *aClosure)
}
nsresult rv = self->StartLongLivedTCPKeepalives();
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
LOG(("nsHttpConnection::UpdateTCPKeepalive [%p] "
"StartLongLivedTCPKeepalives failed rv[0x%x]",
self, rv));
@ -1134,11 +1220,25 @@ void
nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnection::GetSecurityInfo trans=%p tlsfilter=%p socket=%p\n",
mTransaction.get(), mTLSFilter.get(), mSocketTransport.get()));
if (mSocketTransport) {
if (NS_FAILED(mSocketTransport->GetSecurityInfo(secinfo)))
*secinfo = nullptr;
if (mTransaction &&
NS_SUCCEEDED(mTransaction->GetTransactionSecurityInfo(secinfo))) {
return;
}
if (mTLSFilter &&
NS_SUCCEEDED(mTLSFilter->GetTransactionSecurityInfo(secinfo))) {
return;
}
if (mSocketTransport &&
NS_SUCCEEDED(mSocketTransport->GetSecurityInfo(secinfo))) {
return;
}
*secinfo = nullptr;
}
void
@ -1203,22 +1303,30 @@ nsHttpConnection::ResumeRecv()
}
class nsHttpConnectionForceRecv : public nsRunnable
class nsHttpConnectionForceIO : public nsRunnable
{
public:
nsHttpConnectionForceRecv(nsHttpConnection *aConn)
: mConn(aConn) {}
nsHttpConnectionForceIO(nsHttpConnection *aConn, bool doRecv)
: mConn(aConn)
, mDoRecv(doRecv)
{}
NS_IMETHOD Run()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (!mConn->mSocketIn)
if (mDoRecv) {
if (!mConn->mSocketIn)
return NS_OK;
return mConn->OnInputStreamReady(mConn->mSocketIn);
}
if (!mConn->mSocketOut)
return NS_OK;
return mConn->OnInputStreamReady(mConn->mSocketIn);
return mConn->OnOutputStreamReady(mConn->mSocketOut);
}
private:
nsRefPtr<nsHttpConnection> mConn;
bool mDoRecv;
};
// trigger an asynchronous read
@ -1228,7 +1336,21 @@ nsHttpConnection::ForceRecv()
LOG(("nsHttpConnection::ForceRecv [this=%p]\n", this));
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
return NS_DispatchToCurrentThread(new nsHttpConnectionForceRecv(this));
return NS_DispatchToCurrentThread(new nsHttpConnectionForceIO(this, true));
}
// trigger an asynchronous write
nsresult
nsHttpConnection::ForceSend()
{
LOG(("nsHttpConnection::ForceSend [this=%p]\n", this));
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mTLSFilter) {
return mTLSFilter->NudgeTunnel(this, this, this);
}
return NS_DispatchToCurrentThread(new nsHttpConnectionForceIO(this, false));
}
void
@ -1270,7 +1392,8 @@ nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
LOG(("nsHttpConnection::CloseTransaction[this=%p trans=%p reason=%x]\n",
this, trans, reason));
MOZ_ASSERT(trans == mTransaction, "wrong transaction");
MOZ_ASSERT((trans == mTransaction) ||
(mTLSFilter && mTLSFilter->Transaction() == trans));
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
if (mCurrentBytesRead > mMaxBytesRead)
@ -1355,56 +1478,53 @@ nsHttpConnection::OnSocketWritable()
this, mConnInfo->Host()));
nsresult rv;
uint32_t n;
uint32_t transactionBytes;
bool again = true;
do {
mSocketOutCondition = NS_OK;
rv = mSocketOutCondition = NS_OK;
transactionBytes = 0;
// If we're doing a proxy connect, then we need to bypass calling into
// the transaction.
//
// NOTE: this code path can't be shared since the transaction doesn't
// implement nsIInputStream. doing so is not worth the added cost of
// extra indirections during normal reading.
//
if (mProxyConnectStream) {
// The SSL handshake must be completed before the transaction->readsegments()
// processing can proceed because we need to know how to format the
// request differently for http/1, http/2, spdy, etc.. and that is
// negotiated with NPN/ALPN in the SSL handshake.
if (mConnInfo->UsingHttpsProxy() && !EnsureNPNComplete()) {
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
} else if (mProxyConnectStream) {
// If we're need an HTTP/1 CONNECT tunnel through a proxy
// send it before doing the SSL handshake
LOG((" writing CONNECT request stream\n"));
rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
nsIOService::gDefaultSegmentSize,
&n);
}
else if (!EnsureNPNComplete()) {
// When SPDY is disabled this branch is not executed because Activate()
// sets mNPNComplete to true in that case.
// We are ready to proceed with SSL but the handshake is not done.
// When using NPN to negotiate between HTTPS and SPDY, we need to
// see the results of the handshake to know what bytes to send, so
// we cannot proceed with the request headers.
rv = NS_OK;
nsIOService::gDefaultSegmentSize,
&transactionBytes);
} else if (!EnsureNPNComplete()) {
mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
n = 0;
}
else {
} else {
// for non spdy sessions let the connection manager know
if (!mReportedSpdy) {
mReportedSpdy = true;
gHttpHandler->ConnMgr()->ReportSpdyConnection(this, mEverUsedSpdy);
MOZ_ASSERT(!mEverUsedSpdy);
gHttpHandler->ConnMgr()->ReportSpdyConnection(this, false);
}
LOG((" writing transaction request stream\n"));
mProxyConnectInProgress = false;
rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize,
&transactionBytes);
mContentBytesWritten += transactionBytes;
}
LOG((" ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
rv, n, mSocketOutCondition));
LOG(("nsHttpConnection::OnSocketWritable %p "
"ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
this, rv, transactionBytes, mSocketOutCondition));
// XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) {
rv = NS_OK;
n = 0;
transactionBytes = 0;
}
if (NS_FAILED(rv)) {
@ -1413,15 +1533,19 @@ nsHttpConnection::OnSocketWritable()
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
rv = NS_OK;
again = false;
}
else if (NS_FAILED(mSocketOutCondition)) {
if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK)
rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing
else
} else if (NS_FAILED(mSocketOutCondition)) {
if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) {
if (mTLSFilter) {
LOG((" blocked tunnel (handshake?)\n"));
mTLSFilter->NudgeTunnel(this, this, this);
} else {
rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing
}
} else {
rv = mSocketOutCondition;
}
again = false;
}
else if (n == 0) {
} else if (!transactionBytes) {
rv = NS_OK;
if (mTransaction) { // in case the ReadSegments stack called CloseTransaction()
@ -1583,6 +1707,33 @@ nsHttpConnection::OnSocketReadable()
return rv;
}
void
nsHttpConnection::SetupSecondaryTLS()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(!mTLSFilter);
LOG(("nsHttpConnection %p SetupSecondaryTLS %s %d\n",
this, mConnInfo->Host(), mConnInfo->Port()));
mTLSFilter = new TLSFilterTransaction(mTransaction,
mConnInfo->Host(),
mConnInfo->Port());
if (mTransaction) {
mTransaction = mTLSFilter;
}
}
void
nsHttpConnection::SetInSpdyTunnel(bool arg)
{
MOZ_ASSERT(mTLSFilter);
mInSpdyTunnel = arg;
// don't setup another tunnel :)
mProxyConnectStream = nullptr;
mCompletedProxyConnect = true;
mProxyConnectInProgress = false;
}
nsresult
nsHttpConnection::MakeConnectString(nsAHttpTransaction *trans,
nsHttpRequestHead *request,
@ -1667,7 +1818,7 @@ nsHttpConnection::StartShortLivedTCPKeepalives()
retryIntervalS =
std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
return rv;
}
rv = mSocketTransport->SetKeepaliveEnabled(true);
@ -1676,7 +1827,7 @@ nsHttpConnection::StartShortLivedTCPKeepalives()
rv = mSocketTransport->SetKeepaliveEnabled(false);
mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
return rv;
}
@ -1741,14 +1892,14 @@ nsHttpConnection::StartLongLivedTCPKeepalives()
int32_t retryIntervalS =
std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
return rv;
}
// Ensure keepalive is enabled, if current status is disabled.
if (mTCPKeepaliveConfig == kTCPKeepaliveDisabled) {
rv = mSocketTransport->SetKeepaliveEnabled(true);
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
return rv;
}
}
@ -1758,7 +1909,7 @@ nsHttpConnection::StartLongLivedTCPKeepalives()
mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
@ -1771,10 +1922,11 @@ nsHttpConnection::DisableTCPKeepalives()
if (!mSocketTransport) {
return NS_ERROR_NOT_INITIALIZED;
}
LOG(("nsHttpConnection::DisableTCPKeepalives [%p]", this));
if (mTCPKeepaliveConfig != kTCPKeepaliveDisabled) {
nsresult rv = mSocketTransport->SetKeepaliveEnabled(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_FAILED(rv)) {
return rv;
}
mTCPKeepaliveConfig = kTCPKeepaliveDisabled;

Просмотреть файл

@ -12,6 +12,7 @@
#include "nsAutoPtr.h"
#include "nsProxyRelease.h"
#include "prinrval.h"
#include "TunnelUtils.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
@ -19,6 +20,7 @@
#include "nsITimer.h"
class nsISocketTransport;
class nsISSLSocketControl;
namespace mozilla {
namespace net {
@ -39,6 +41,7 @@ class nsHttpConnection : public nsAHttpSegmentReader
, public nsIOutputStreamCallback
, public nsITransportEventSink
, public nsIInterfaceRequestor
, public NudgeTunnelCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -48,6 +51,7 @@ public:
NS_DECL_NSIOUTPUTSTREAMCALLBACK
NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NUDGETUNNELCALLBACK
nsHttpConnection();
virtual ~nsHttpConnection();
@ -101,6 +105,11 @@ public:
mLastTransactionExpectedNoContent = val;
}
bool NeedSpdyTunnel()
{
return mConnInfo->UsingHttpsProxy() && !mTLSFilter && mConnInfo->UsingConnect();
}
nsISocketTransport *Transport() { return mSocketTransport; }
nsAHttpTransaction *Transaction() { return mTransaction; }
nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; }
@ -122,7 +131,8 @@ public:
int64_t MaxBytesRead() {return mMaxBytesRead;}
uint8_t GetLastHttpResponseVersion() { return mLastHttpResponseVersion; }
friend class nsHttpConnectionForceRecv;
friend class nsHttpConnectionForceIO;
nsresult ForceSend();
nsresult ForceRecv();
static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
@ -164,7 +174,8 @@ public:
// When the connection is active this is called every second
void ReadTimeoutTick();
int64_t BytesWritten() { return mTotalBytesWritten; }
int64_t BytesWritten() { return mTotalBytesWritten; } // includes TLS
int64_t ContentBytesWritten() { return mContentBytesWritten; }
void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks);
void PrintDiagnostics(nsCString &log);
@ -178,6 +189,8 @@ public:
static nsresult MakeConnectString(nsAHttpTransaction *trans,
nsHttpRequestHead *request,
nsACString &result);
void SetupSecondaryTLS();
void SetInSpdyTunnel(bool arg);
private:
// Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
@ -188,7 +201,8 @@ private:
};
// called to cause the underlying socket to start speaking SSL
nsresult ProxyStartSSL();
nsresult InitSSLParams(bool connectingToProxy, bool ProxyStartSSL);
nsresult SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps);
nsresult OnTransactionDone(nsresult reason);
nsresult OnSocketWritable();
@ -203,7 +217,7 @@ private:
// Makes certain the SSL handshake is complete and NPN negotiation
// has had a chance to happen
bool EnsureNPNComplete();
void SetupSSL(uint32_t caps);
void SetupSSL();
// Start the Spdy transaction handler when NPN indicates spdy/*
void StartSpdy(uint8_t versionLevel);
@ -231,6 +245,7 @@ private:
// mTransaction only points to the HTTP Transaction callbacks if the
// transaction is open, otherwise it is null.
nsRefPtr<nsAHttpTransaction> mTransaction;
nsRefPtr<TLSFilterTransaction> mTLSFilter;
nsRefPtr<nsHttpHandler> mHttpHandler; // keep gHttpHandler alive
@ -249,6 +264,7 @@ private:
int64_t mMaxBytesRead; // max read in 1 activation
int64_t mTotalBytesRead; // total data read
int64_t mTotalBytesWritten; // does not include CONNECT tunnel
int64_t mContentBytesWritten; // does not include CONNECT tunnel or TLS
nsRefPtr<nsIAsyncInputStream> mInputOverflow;
@ -265,6 +281,7 @@ private:
bool mIdleMonitoring;
bool mProxyConnectInProgress;
bool mExperienced;
bool mInSpdyTunnel;
// The number of <= HTTP/1.1 transactions performed on this connection. This
// excludes spdy transactions.

Просмотреть файл

@ -68,8 +68,7 @@ nsHttpConnectionInfo::SetOriginServer(const nsACString &host, int32_t port)
if (mUsingHttpProxy && !mUsingConnect) {
keyHost = ProxyHost();
keyPort = ProxyPort();
}
else {
} else {
keyHost = Host();
keyPort = Port();
}
@ -129,10 +128,31 @@ nsHttpConnectionInfo::Clone() const
// Make sure the anonymous and private flags are transferred!
clone->SetAnonymous(GetAnonymous());
clone->SetPrivate(GetPrivate());
MOZ_ASSERT(clone->Equals(this));
return clone;
}
nsresult
nsHttpConnectionInfo::CreateWildCard(nsHttpConnectionInfo **outParam)
{
// T???mozilla.org:443 (https:proxy.ducksong.com:3128) [specifc form]
// TS??*:0 (https:proxy.ducksong.com:3128) [wildcard form]
if (!mUsingHttpsProxy) {
MOZ_ASSERT(false);
return NS_ERROR_NOT_IMPLEMENTED;
}
nsRefPtr<nsHttpConnectionInfo> clone;
clone = new nsHttpConnectionInfo(NS_LITERAL_CSTRING("*"), 0,
mUsername, mProxyInfo, true);
// Make sure the anonymous and private flags are transferred!
clone->SetAnonymous(GetAnonymous());
clone->SetPrivate(GetPrivate());
clone.forget(outParam);
return NS_OK;
}
bool
nsHttpConnectionInfo::UsingProxy()
{

Просмотреть файл

@ -18,6 +18,15 @@ extern PRLogModuleInfo *gHttpLog;
// nsHttpConnectionInfo - holds the properties of a connection
//-----------------------------------------------------------------------------
// http:// uris through a proxy will all share the same CI, because they can
// all use the same connection. (modulo pb and anonymous flags). They just use
// the proxy as the origin host name.
// however, https:// uris tunnel through the proxy so they will have different
// CIs - the CI reflects both the proxy and the origin.
// however, proxy conenctions made with http/2 (or spdy) can tunnel to the origin
// and multiplex non tunneled transactions at the same time, so they have a
// special wildcard CI that accepts all origins through that proxy.
namespace mozilla { namespace net {
class nsHttpConnectionInfo
@ -42,8 +51,9 @@ public:
SetOriginServer(nsDependentCString(host), port);
}
// OK to treat this as an infalible allocation
// OK to treat these as an infalible allocation
nsHttpConnectionInfo* Clone() const;
nsresult CreateWildCard(nsHttpConnectionInfo **outParam);
const char *ProxyHost() const { return mProxyInfo ? mProxyInfo->Host().get() : nullptr; }
int32_t ProxyPort() const { return mProxyInfo ? mProxyInfo->Port() : -1; }

Просмотреть файл

@ -1044,8 +1044,10 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool consid
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
ent->mConnInfo->HashKey().get()));
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
"[ci=%s ent=%p active=%d idle=%d queued=%d]\n",
ent->mConnInfo->HashKey().get(), ent, ent->mActiveConns.Length(),
ent->mIdleConns.Length(), ent->mPendingQ.Length()));
ProcessSpdyPendingQ(ent);
@ -1072,7 +1074,9 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool consid
}
}
rv = TryDispatchTransaction(ent, alreadyHalfOpen, trans);
rv = TryDispatchTransaction(ent,
alreadyHalfOpen || trans->DontRouteViaWildCard(),
trans);
if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
if (NS_SUCCEEDED(rv))
LOG((" dispatching pending transaction...\n"));
@ -1567,8 +1571,10 @@ nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
"[ci=%s caps=%x]\n",
ent->mConnInfo->HashKey().get(), uint32_t(trans->Caps())));
"[trans=%p ci=%s caps=%x wildcardok=%d onlyreused=%d]\n",
trans, ent->mConnInfo->HashKey().get(),
uint32_t(trans->Caps()), !trans->DontRouteViaWildCard(),
onlyReusedConnection));
nsHttpTransaction::Classifier classification = trans->Classification();
uint32_t caps = trans->Caps();
@ -1757,7 +1763,7 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
nsresult rv;
LOG(("nsHttpConnectionMgr::DispatchTransaction "
"[ci=%s trans=%p caps=%x conn=%p priority=%d]\n",
"[ent-ci=%s trans=%p caps=%x conn=%p priority=%d]\n",
ent->mConnInfo->HashKey().get(), trans, caps, conn, priority));
// It is possible for a rate-paced transaction to be dispatched independent
@ -1766,7 +1772,7 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
trans->CancelPacing(NS_OK);
if (conn->UsingSpdy()) {
LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s, "
"Connection host = %s\n",
trans->ConnectionInfo()->Host(),
conn->ConnectionInfo()->Host()));
@ -1925,7 +1931,8 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
MOZ_ASSERT(ci);
nsConnectionEntry *ent = GetOrCreateConnectionEntry(ci);
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(ci, trans->DontRouteViaWildCard());
// SPDY coalescing of hostnames means we might redirect from this
// connection entry onto the preferred one.
@ -1957,9 +1964,9 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
"sticky connection=%p\n", trans, conn.get()));
trans->SetConnection(nullptr);
rv = DispatchTransaction(ent, trans, conn);
} else {
rv = TryDispatchTransaction(ent, trans->DontRouteViaWildCard(), trans);
}
else
rv = TryDispatchTransaction(ent, false, trans);
if (NS_SUCCEEDED(rv)) {
LOG((" ProcessNewTransaction Dispatch Immediately trans=%p\n", trans));
@ -2288,6 +2295,44 @@ nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, void *param)
NS_RELEASE(ci);
}
nsresult
nsHttpConnectionMgr::CancelTransactions(nsHttpConnectionInfo *aCI, nsresult code)
{
nsRefPtr<nsHttpConnectionInfo> ci(aCI);
LOG(("nsHttpConnectionMgr::CancelTransactions %s\n",ci->HashKey().get()));
int32_t intReason = static_cast<int32_t>(code);
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransactions, intReason, ci);
if (NS_SUCCEEDED(rv)) {
unused << ci.forget();
}
return rv;
}
void
nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, void *param)
{
nsresult reason = static_cast<nsresult>(code);
nsRefPtr<nsHttpConnectionInfo> ci =
dont_AddRef(static_cast<nsHttpConnectionInfo *>(param));
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p\n",
ci->HashKey().get(), ent));
if (!ent) {
return;
}
nsRefPtr<nsHttpTransaction> trans;
for (int32_t i = ent->mPendingQ.Length() - 1; i >= 0; --i) {
trans = dont_AddRef(ent->mPendingQ[i]);
LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
ci->HashKey().get(), ent, trans.get()));
ent->mPendingQ.RemoveElementAt(i);
trans->Close(reason);
trans = nullptr;
}
}
void
nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, void *)
{
@ -2333,80 +2378,82 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, void *param)
nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
conn, nullptr);
nsHttpConnectionInfo *ci = nullptr;
if (!ent) {
// this should never happen
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
MOZ_ASSERT(false, "no connection entry");
NS_ADDREF(ci = conn->ConnectionInfo());
// this can happen if the connection is made outside of the
// connection manager and is being "reclaimed" for use with
// future transactions. HTTP/2 tunnels work like this.
ent = GetOrCreateConnectionEntry(conn->ConnectionInfo(), true);
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection conn %p "
"forced new hash entry %s\n",
conn, conn->ConnectionInfo()->HashKey().get()));
}
else {
NS_ADDREF(ci = ent->mConnInfo);
// If the connection is in the active list, remove that entry
// and the reference held by the mActiveConns list.
// This is never the final reference on conn as the event context
// is also holding one that is released at the end of this function.
MOZ_ASSERT(ent);
nsHttpConnectionInfo *ci = nullptr;
NS_ADDREF(ci = ent->mConnInfo);
if (ent->mUsingSpdy) {
// Spdy connections aren't reused in the traditional HTTP way in
// the idleconns list, they are actively multplexed as active
// conns. Even when they have 0 transactions on them they are
// considered active connections. So when one is reclaimed it
// is really complete and is meant to be shut down and not
// reused.
conn->DontReuse();
// If the connection is in the active list, remove that entry
// and the reference held by the mActiveConns list.
// This is never the final reference on conn as the event context
// is also holding one that is released at the end of this function.
if (conn->EverUsedSpdy()) {
// Spdy connections aren't reused in the traditional HTTP way in
// the idleconns list, they are actively multplexed as active
// conns. Even when they have 0 transactions on them they are
// considered active connections. So when one is reclaimed it
// is really complete and is meant to be shut down and not
// reused.
conn->DontReuse();
}
// a connection that still holds a reference to a transaction was
// not closed naturally (i.e. it was reset or aborted) and is
// therefore not something that should be reused.
if (conn->Transaction()) {
conn->DontReuse();
}
if (ent->mActiveConns.RemoveElement(conn)) {
if (conn == ent->mYellowConnection) {
ent->OnYellowComplete();
}
nsHttpConnection *temp = conn;
NS_RELEASE(temp);
DecrementActiveConnCount(conn);
ConditionallyStopTimeoutTick();
}
if (conn->CanReuse()) {
LOG((" adding connection to idle list\n"));
// Keep The idle connection list sorted with the connections that
// have moved the largest data pipelines at the front because these
// connections have the largest cwnds on the server.
// The linear search is ok here because the number of idleconns
// in a single entry is generally limited to a small number (i.e. 6)
uint32_t idx;
for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
nsHttpConnection *idleConn = ent->mIdleConns[idx];
if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
break;
}
if (ent->mActiveConns.RemoveElement(conn)) {
if (conn == ent->mYellowConnection)
ent->OnYellowComplete();
nsHttpConnection *temp = conn;
NS_RELEASE(temp);
DecrementActiveConnCount(conn);
ConditionallyStopTimeoutTick();
}
NS_ADDREF(conn);
ent->mIdleConns.InsertElementAt(idx, conn);
mNumIdleConns++;
conn->BeginIdleMonitoring();
// a connection that still holds a reference to a transaction was
// not closed naturally (i.e. it was reset or aborted) and is
// therefore not something that should be reused.
if (conn->Transaction()) {
conn->DontReuse();
}
if (conn->CanReuse()) {
LOG((" adding connection to idle list\n"));
// Keep The idle connection list sorted with the connections that
// have moved the largest data pipelines at the front because these
// connections have the largest cwnds on the server.
// The linear search is ok here because the number of idleconns
// in a single entry is generally limited to a small number (i.e. 6)
uint32_t idx;
for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
nsHttpConnection *idleConn = ent->mIdleConns[idx];
if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
break;
}
NS_ADDREF(conn);
ent->mIdleConns.InsertElementAt(idx, conn);
mNumIdleConns++;
conn->BeginIdleMonitoring();
// If the added connection was first idle connection or has shortest
// time to live among the watched connections, pruning dead
// connections needs to be done when it can't be reused anymore.
uint32_t timeToLive = conn->TimeToLive();
if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
PruneDeadConnectionsAfter(timeToLive);
}
else {
LOG((" connection cannot be reused; closing connection\n"));
conn->Close(NS_ERROR_ABORT);
}
// If the added connection was first idle connection or has shortest
// time to live among the watched connections, pruning dead
// connections needs to be done when it can't be reused anymore.
uint32_t timeToLive = conn->TimeToLive();
if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
PruneDeadConnectionsAfter(timeToLive);
} else {
LOG((" connection cannot be reused; closing connection\n"));
conn->Close(NS_ERROR_ABORT);
}
OnMsgProcessPendingQ(0, ci); // releases |ci|
@ -2609,17 +2656,43 @@ nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
NS_IMPL_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *ci)
{
nsConnectionEntry *ent = mCT.Get(ci->HashKey());
if (ent)
return ent;
// GetOrCreateConnectionEntry finds a ent for a particular CI for use in
// dispatching a transaction according to these rules
// 1] use an ent that matches the ci that can be dispatched immediately
// 2] otherwise use an ent of wildcard(ci) than can be dispatched immediately
// 3] otherwise create an ent that matches ci and make new conn on it
nsHttpConnectionInfo *clone = ci->Clone();
ent = new nsConnectionEntry(clone);
mCT.Put(ci->HashKey(), ent);
return ent;
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI,
bool prohibitWildCard)
{
// step 1
nsConnectionEntry *specificEnt = mCT.Get(specificCI->HashKey());
if (specificEnt && specificEnt->AvailableForDispatchNow()) {
return specificEnt;
}
if (!specificCI->UsingHttpsProxy()) {
prohibitWildCard = true;
}
// step 2
if (!prohibitWildCard) {
nsRefPtr<nsHttpConnectionInfo> wildCardProxyCI;
specificCI->CreateWildCard(getter_AddRefs(wildCardProxyCI));
nsConnectionEntry *wildCardEnt = mCT.Get(wildCardProxyCI->HashKey());
if (wildCardEnt && wildCardEnt->AvailableForDispatchNow()) {
return wildCardEnt;
}
}
// step 3
if (!specificEnt) {
nsRefPtr<nsHttpConnectionInfo> clone(specificCI->Clone());
specificEnt = new nsConnectionEntry(clone);
mCT.Put(clone->HashKey(), specificEnt);
}
return specificEnt;
}
nsresult
@ -2658,7 +2731,7 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
args->mTrans->ConnectionInfo()->HashKey().get()));
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo());
GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo(), false);
// If spdy has previously made a preferred entry for this host via
// the ip pooling rules. If so, connect to the preferred host instead of
@ -2760,11 +2833,16 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
bool isBackup)
{
nsresult rv;
const char* types[1];
types[0] = (mEnt->mConnInfo->EndToEndSSL()) ?
"ssl" : gHttpHandler->DefaultSocketType();
uint32_t typeCount = (types[0] != nullptr);
const char *socketTypes[1];
uint32_t typeCount = 0;
if (mEnt->mConnInfo->FirstHopSSL()) {
socketTypes[typeCount++] = "ssl";
} else {
socketTypes[typeCount] = gHttpHandler->DefaultSocketType();
if (socketTypes[typeCount]) {
typeCount++;
}
}
nsCOMPtr<nsISocketTransport> socketTransport;
nsCOMPtr<nsISocketTransportService> sts;
@ -2772,7 +2850,7 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = sts->CreateTransport(types, typeCount,
rv = sts->CreateTransport(socketTypes, typeCount,
nsDependentCString(mEnt->mConnInfo->Host()),
mEnt->mConnInfo->Port(),
mEnt->mConnInfo->ProxyInfo(),
@ -3278,6 +3356,17 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
memset(mPipeliningClassPenalty, 0, sizeof(int16_t) * nsAHttpTransaction::CLASS_MAX);
}
bool
nsHttpConnectionMgr::nsConnectionEntry::AvailableForDispatchNow()
{
if (mIdleConns.Length() && mIdleConns[0]->CanReuse()) {
return true;
}
return gHttpHandler->ConnMgr()->
GetSpdyPreferredConn(this) ? true : false;
}
bool
nsHttpConnectionMgr::nsConnectionEntry::SupportsPipelining()
{
@ -3615,5 +3704,62 @@ nsConnectionEntry::ResetIPFamilyPreference()
mPreferIPv6 = false;
}
void
nsHttpConnectionMgr::MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
nsHttpConnectionInfo *wildCardCI,
nsHttpConnection *proxyConn)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(specificCI->UsingHttpsProxy());
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p has requested to "
"change CI from %s to %s\n", proxyConn, specificCI->HashKey().get(),
wildCardCI->HashKey().get()));
nsConnectionEntry *ent = LookupConnectionEntry(specificCI, proxyConn, nullptr);
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p using ent %p (spdy %d)\n",
proxyConn, ent, ent ? ent->mUsingSpdy : 0));
if (!ent || !ent->mUsingSpdy) {
return;
}
nsConnectionEntry *wcEnt = GetOrCreateConnectionEntry(wildCardCI, true);
if (wcEnt == ent) {
// nothing to do!
return;
}
wcEnt->mUsingSpdy = true;
wcEnt->mTestedSpdy = true;
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard ent %p "
"idle=%d active=%d half=%d pending=%d\n", ent,
ent->mIdleConns.Length(), ent->mActiveConns.Length(),
ent->mHalfOpens.Length(), ent->mPendingQ.Length()));
LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard wc-ent %p "
"idle=%d active=%d half=%d pending=%d\n", wcEnt,
wcEnt->mIdleConns.Length(), wcEnt->mActiveConns.Length(),
wcEnt->mHalfOpens.Length(), wcEnt->mPendingQ.Length()));
int32_t count = ent->mActiveConns.Length();
for (int32_t i = 0; i < count; ++i) {
if (ent->mActiveConns[i] == proxyConn) {
ent->mActiveConns.RemoveElementAt(i);
wcEnt->mActiveConns.InsertElementAt(0, proxyConn);
return;
}
}
count = ent->mIdleConns.Length();
for (int32_t i = 0; i < count; ++i) {
if (ent->mIdleConns[i] == proxyConn) {
ent->mIdleConns.RemoveElementAt(i);
wcEnt->mIdleConns.InsertElementAt(0, proxyConn);
return;
}
}
}
} // namespace mozilla::net
} // namespace mozilla

Просмотреть файл

@ -84,6 +84,7 @@ public:
// cancels a transaction w/ the given reason.
nsresult CancelTransaction(nsHttpTransaction *, nsresult reason);
nsresult CancelTransactions(nsHttpConnectionInfo *, nsresult reason);
// called to force the connection manager to prune its list of idle
// connections.
@ -202,6 +203,12 @@ public:
// NOTE: functions below may be called only on the socket thread.
//-------------------------------------------------------------------------
// called to change the connection entry associated with conn from specific into
// a wildcard (i.e. http2 proxy friendy) mapping
void MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
nsHttpConnectionInfo *wildcardCI,
nsHttpConnection *conn);
// called to force the transaction queue to be processed once more, giving
// preference to the specified connection.
nsresult ProcessPendingQ(nsHttpConnectionInfo *);
@ -231,6 +238,8 @@ public:
void ResetIPFamilyPreference(nsHttpConnectionInfo *);
uint16_t MaxRequestDelay() { return mMaxRequestDelay; }
private:
virtual ~nsHttpConnectionMgr();
@ -271,6 +280,8 @@ private:
nsTArray<nsHttpConnection*> mIdleConns; // idle persistent connections
nsTArray<nsHalfOpenSocket*> mHalfOpens; // half open connections
bool AvailableForDispatchNow();
// calculate the number of half open sockets that have not had at least 1
// connection complete
uint32_t UnconnectedHalfOpens();
@ -525,7 +536,8 @@ private:
void StartedConnect();
void RecvdConnect();
nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *);
nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *,
bool allowWildCard);
nsresult MakeNewConnection(nsConnectionEntry *ent,
nsHttpTransaction *trans);
@ -601,6 +613,7 @@ private:
void OnMsgNewTransaction (int32_t, void *);
void OnMsgReschedTransaction (int32_t, void *);
void OnMsgCancelTransaction (int32_t, void *);
void OnMsgCancelTransactions (int32_t, void *);
void OnMsgProcessPendingQ (int32_t, void *);
void OnMsgPruneDeadConnections (int32_t, void *);
void OnMsgSpeculativeConnect (int32_t, void *);

Просмотреть файл

@ -110,6 +110,7 @@ public:
bool CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
double BypassCacheLockThreshold() { return mBypassCacheLockThreshold; }
uint32_t MaxConnectionsPerOrigin() { return mMaxPersistentConnectionsPerServer; }
bool UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
uint16_t RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
uint32_t RequestTokenBucketHz() { return mRequestTokenBucketHz; }

Просмотреть файл

@ -123,6 +123,7 @@ nsHttpTransaction::nsHttpTransaction()
, mPreserveStream(false)
, mDispatchedAsBlocking(false)
, mResponseTimeoutEnabled(true)
, mDontRouteViaWildCard(false)
, mReportedStart(false)
, mReportedResponseHeader(false)
, mForTakeResponseHead(nullptr)
@ -1064,6 +1065,7 @@ nsHttpTransaction::Restart()
}
LOG(("restarting transaction @%p\n", this));
SetDontRouteViaWildCard(false);
// rewind streams in case we already wrote out the request
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);

Просмотреть файл

@ -102,6 +102,14 @@ public:
bool ProxyConnectFailed() { return mProxyConnectFailed; }
// setting mDontRouteViaWildCard to true means the transaction should only
// be dispatched on a specific ConnectionInfo Hash Key (as opposed to a
// generic wild card one). That means in the specific case of carrying this
// transaction on an HTTP/2 tunnel it will only be dispatched onto an
// existing tunnel instead of triggering creation of a new one.
void SetDontRouteViaWildCard(bool var) { mDontRouteViaWildCard = var; }
bool DontRouteViaWildCard() { return mDontRouteViaWildCard; }
// SetPriority() may only be used by the connection manager.
void SetPriority(int32_t priority) { mPriority = priority; }
int32_t Priority() { return mPriority; }
@ -247,6 +255,7 @@ private:
bool mPreserveStream;
bool mDispatchedAsBlocking;
bool mResponseTimeoutEnabled;
bool mDontRouteViaWildCard;
// mClosed := transaction has been explicitly closed
// mTransactionDone := transaction ran to completion or was interrupted