bug 1133177 - https tunnel of h1 without pconn inside h2 session stall r=hurley

This commit is contained in:
Patrick McManus 2015-02-19 13:50:15 -05:00
parent d0b1783640
commit 0f9824c683
8 changed files with 150 additions and 46 deletions

View File

@ -33,6 +33,15 @@ public:
static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *);
// MaybeReTunnel() is called by the connection manager when it cannot
// dispatch a tunneled transaction. That might be because the tunnels it
// expects to see are dead (and we may or may not be able to make more),
// or it might just need to wait longer for one of them to become free.
//
// return true if the session takes back ownership of the transaction from
// the connection manager.
virtual bool MaybeReTunnel(nsAHttpTransaction *) = 0;
virtual void PrintDiagnostics (nsCString &log) = 0;
bool ResponseTimeoutEnabled() const MOZ_OVERRIDE MOZ_FINAL {

View File

@ -3271,6 +3271,24 @@ Http2Session::UnRegisterTunnel(Http2Stream *aTunnel)
this, aTunnel, newcount, ci->HashKey().get()));
}
void
Http2Session::CreateTunnel(nsHttpTransaction *trans,
nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *aCallbacks)
{
LOG(("Http2Session::CreateTunnel %p %p make new tunnel\n", this, trans));
// The connect transaction will hold onto the underlying http
// transaction so that an auth created by the connect can be mappped
// to the correct security callbacks
nsRefPtr<SpdyConnectTransaction> connectTrans =
new SpdyConnectTransaction(ci, aCallbacks, trans->Caps(), trans, this);
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL, false, nullptr);
Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
MOZ_ASSERT(tunnel);
RegisterTunnel(tunnel);
}
void
Http2Session::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
nsIInterfaceRequestor *aCallbacks)
@ -3286,33 +3304,55 @@ Http2Session::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
// this transaction has done its work of setting up a tunnel, let
// the connection manager queue it if necessary
trans->SetDontRouteViaWildCard(true);
trans->SetTunnelProvider(this);
trans->EnableKeepAlive();
if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
LOG3(("Http2Session::DispatchOnTunnel %p create on new tunnel %s",
this, ci->HashKey().get()));
// The connect transaction will hold onto the underlying http
// transaction so that an auth created by the connect can be mappped
// to the correct security callbacks
nsRefPtr<SpdyConnectTransaction> connectTrans =
new SpdyConnectTransaction(ci, aCallbacks,
trans->Caps(), trans, this);
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
false, nullptr);
Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
MOZ_ASSERT(tunnel);
RegisterTunnel(tunnel);
CreateTunnel(trans, ci, aCallbacks);
} else {
// 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.
// this on the tunnel connection with the specific ci. If that can't
// happen the cmgr checks with us via MaybeReTunnel() to see if it should
// make a new tunnel or just wait longer.
LOG3(("Http2Session::DispatchOnTunnel %p trans=%p queue in connection manager",
this, trans));
gHttpHandler->InitiateTransaction(trans, trans->Priority());
}
}
// From ASpdySession
bool
Http2Session::MaybeReTunnel(nsAHttpTransaction *aHttpTransaction)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
LOG(("Http2Session::MaybeReTunnel %p trans=%p\n", this, trans));
if (!trans || trans->TunnelProvider() != this) {
// this isn't really one of our transactions.
return false;
}
if (mClosed || mShouldGoAway) {
LOG(("Http2Session::MaybeReTunnel %p %p session closed - requeue\n", this, trans));
trans->SetTunnelProvider(nullptr);
gHttpHandler->InitiateTransaction(trans, trans->Priority());
return true;
}
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
LOG(("Http2Session:MaybeReTunnel %p %p count=%d limit %d\n",
this, trans, FindTunnelCount(ci), gHttpHandler->MaxConnectionsPerOrigin()));
if (FindTunnelCount(ci) >= gHttpHandler->MaxConnectionsPerOrigin()) {
// patience - a tunnel will open up.
return false;
}
LOG(("Http2Session::MaybeReTunnel %p %p make new tunnel\n", this, trans));
CreateTunnel(trans, ci, trans->SecurityCallbacks());
return true;
}
nsresult
Http2Session::BufferOutput(const char *buf,

View File

@ -224,7 +224,7 @@ public:
void GetNegotiatedToken(nsACString &s) { s.Assign(mNegotiatedToken); }
void SendPing() MOZ_OVERRIDE;
bool MaybeReTunnel(nsAHttpTransaction *) MOZ_OVERRIDE;
bool UseH2Deps() { return mUseH2Deps; }
private:
@ -488,10 +488,10 @@ private:
private:
/// connect tunnels
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
void RegisterTunnel(Http2Stream *);
void UnRegisterTunnel(Http2Stream *);
uint32_t FindTunnelCount(nsHttpConnectionInfo *);
nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
};

View File

@ -2711,13 +2711,30 @@ SpdySession31::UnRegisterTunnel(SpdyStream31 *aTunnel)
this, aTunnel, newcount, ci->HashKey().get()));
}
void
SpdySession31::CreateTunnel(nsHttpTransaction *trans,
nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *aCallbacks)
{
LOG(("SpdySession31::CreateTunnel %p %p make new tunnel\n", this, trans));
// The connect transaction will hold onto the underlying http
// transaction so that an auth created by the connect can be mappped
// to the correct security callbacks
nsRefPtr<SpdyConnectTransaction> connectTrans =
new SpdyConnectTransaction(ci, aCallbacks, trans->Caps(), trans, this);
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL, false, nullptr);
SpdyStream31 *tunnel = mStreamTransactionHash.Get(connectTrans);
MOZ_ASSERT(tunnel);
RegisterTunnel(tunnel);
}
void
SpdySession31::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
nsIInterfaceRequestor *aCallbacks)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
MOZ_ASSERT(trans);
LOG3(("SpdySession31::DispatchOnTunnel %p trans=%p", this, trans));
@ -2726,33 +2743,57 @@ SpdySession31::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
// this transaction has done its work of setting up a tunnel, let
// the connection manager queue it if necessary
trans->SetDontRouteViaWildCard(true);
trans->SetTunnelProvider(this);
trans->EnableKeepAlive();
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
LOG3(("SpdySession31::DispatchOnTunnel %p create on new tunnel %s",
this, ci->HashKey().get()));
// The connect transaction will hold onto the underlying http
// transaction so that an auth created by the connect can be mappped
// to the correct security callbacks
nsRefPtr<SpdyConnectTransaction> connectTrans =
new SpdyConnectTransaction(ci, aCallbacks,
trans->Caps(), trans, this);
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
false, nullptr);
SpdyStream31 *tunnel = mStreamTransactionHash.Get(connectTrans);
MOZ_ASSERT(tunnel);
RegisterTunnel(tunnel);
CreateTunnel(trans, ci, aCallbacks);
} else {
// 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.
// this on the tunnel connection with the specific ci. If that can't
// happen the cmgr checks with us via MaybeReTunnel() to see if it should
// make a new tunnel or just wait longer.
LOG3(("SpdySession31::DispatchOnTunnel %p trans=%p queue in connection manager",
this, trans));
gHttpHandler->InitiateTransaction(trans, trans->Priority());
}
}
// From ASpdySession
bool
SpdySession31::MaybeReTunnel(nsAHttpTransaction *aHttpTransaction)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
LOG(("SpdySession31::MaybeReTunnel %p trans=%p\n", this, trans));
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
if (!trans || trans->TunnelProvider() != this) {
// this isn't really one of our transactions.
return false;
}
if (mClosed || mShouldGoAway) {
LOG(("SpdySession31::MaybeReTunnel %p %p session closed - requeue\n", this, trans));
trans->SetTunnelProvider(nullptr);
gHttpHandler->InitiateTransaction(trans, trans->Priority());
return true;
}
LOG(("SpdySession31::MaybeReTunnel %p %p count=%d limit %d\n",
this, trans, FindTunnelCount(ci), gHttpHandler->MaxConnectionsPerOrigin()));
if (FindTunnelCount(ci) >= gHttpHandler->MaxConnectionsPerOrigin()) {
// patience - a tunnel will open up.
return false;
}
LOG(("SpdySession31::MaybeReTunnel %p %p make new tunnel\n", this, trans));
CreateTunnel(trans, ci, trans->SecurityCallbacks());
return true;
}
nsresult
SpdySession31::BufferOutput(const char *buf,
uint32_t count,

View File

@ -195,6 +195,8 @@ public:
void SendPing() MOZ_OVERRIDE;
bool MaybeReTunnel(nsAHttpTransaction *) MOZ_OVERRIDE;
private:
enum stateType {
@ -415,6 +417,7 @@ private:
private:
/// connect tunnels
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
void RegisterTunnel(SpdyStream31 *);
void UnRegisterTunnel(SpdyStream31 *);
uint32_t FindTunnelCount(nsHttpConnectionInfo *);

View File

@ -1207,7 +1207,7 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool consid
}
rv = TryDispatchTransaction(ent,
alreadyHalfOpen || trans->DontRouteViaWildCard(),
alreadyHalfOpen || !!trans->TunnelProvider(),
trans);
if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
if (NS_SUCCEEDED(rv))
@ -1734,10 +1734,10 @@ nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
"[trans=%p ci=%p ci=%s caps=%x wildcardok=%d onlyreused=%d "
"[trans=%p ci=%p ci=%s caps=%x tunnelprovider=%p onlyreused=%d "
"active=%d idle=%d]\n", trans,
ent->mConnInfo.get(), ent->mConnInfo->HashKey().get(),
uint32_t(trans->Caps()), !trans->DontRouteViaWildCard(),
uint32_t(trans->Caps()), trans->TunnelProvider(),
onlyReusedConnection, ent->mActiveConns.Length(),
ent->mIdleConns.Length()));
@ -1901,6 +1901,10 @@ nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
LOG((" failed step 4 (%x) trans=%p\n", rv, trans));
return rv;
}
} else if (trans->TunnelProvider() && trans->TunnelProvider()->MaybeReTunnel(trans)) {
LOG((" sort of dispatched step 4a tunnel requeue trans=%p\n", trans));
// the tunnel provider took responsibility for making a new tunnel
return NS_OK;
}
// step 5
@ -2111,7 +2115,7 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
MOZ_ASSERT(ci);
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(ci, trans->DontRouteViaWildCard());
GetOrCreateConnectionEntry(ci, !!trans->TunnelProvider());
// SPDY coalescing of hostnames means we might redirect from this
// connection entry onto the preferred one.
@ -2155,7 +2159,7 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
trans->SetConnection(nullptr);
rv = DispatchTransaction(ent, trans, conn);
} else {
rv = TryDispatchTransaction(ent, trans->DontRouteViaWildCard(), trans);
rv = TryDispatchTransaction(ent, !!trans->TunnelProvider(), trans);
}
if (NS_SUCCEEDED(rv)) {

View File

@ -123,7 +123,6 @@ nsHttpTransaction::nsHttpTransaction()
, mPreserveStream(false)
, mDispatchedAsBlocking(false)
, mResponseTimeoutEnabled(true)
, mDontRouteViaWildCard(false)
, mForceRestart(false)
, mReuseOnRestart(false)
, mReportedStart(false)
@ -848,6 +847,7 @@ nsHttpTransaction::Close(nsresult reason)
if (mConnection)
connReused = mConnection->IsReused();
mConnected = false;
mTunnelProvider = nullptr;
//
// if the connection was reset or closed before we wrote any part of the
@ -1123,7 +1123,7 @@ nsHttpTransaction::Restart()
}
LOG(("restarting transaction @%p\n", this));
SetDontRouteViaWildCard(false);
mTunnelProvider = nullptr;
// rewind streams in case we already wrote out the request
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);

View File

@ -107,13 +107,6 @@ 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; }
void EnableKeepAlive() { mCaps |= NS_HTTP_ALLOW_KEEPALIVE; }
void MakeSticky() { mCaps |= NS_HTTP_STICKY_CONNECTION; }
@ -282,7 +275,6 @@ private:
bool mPreserveStream;
bool mDispatchedAsBlocking;
bool mResponseTimeoutEnabled;
bool mDontRouteViaWildCard;
bool mForceRestart;
bool mReuseOnRestart;
@ -414,6 +406,21 @@ public:
uint32_t ClassOfService() { return mClassOfService; }
private:
uint32_t mClassOfService;
public:
// setting TunnelProvider to non-null 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.
// The tunnel provider is used for ASpdySession::MaybeReTunnel() checks.
void SetTunnelProvider(ASpdySession *provider) { mTunnelProvider = provider; }
ASpdySession *TunnelProvider() { return mTunnelProvider; }
nsIInterfaceRequestor *SecurityCallbacks() { return mCallbacks; }
private:
nsRefPtr<ASpdySession> mTunnelProvider;
};
}} // namespace mozilla::net