diff --git a/netwerk/protocol/http/ASpdySession.h b/netwerk/protocol/http/ASpdySession.h index d680b0b93091..b427fb9aa635 100644 --- a/netwerk/protocol/http/ASpdySession.h +++ b/netwerk/protocol/http/ASpdySession.h @@ -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 { diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index fbafd608c7c5..1337b05db984 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -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 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 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, diff --git a/netwerk/protocol/http/Http2Session.h b/netwerk/protocol/http/Http2Session.h index af7eeb5b6169..8c0da58a13b4 100644 --- a/netwerk/protocol/http/Http2Session.h +++ b/netwerk/protocol/http/Http2Session.h @@ -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 mTunnelHash; }; diff --git a/netwerk/protocol/http/SpdySession31.cpp b/netwerk/protocol/http/SpdySession31.cpp index 3912337174ce..f5084996689a 100644 --- a/netwerk/protocol/http/SpdySession31.cpp +++ b/netwerk/protocol/http/SpdySession31.cpp @@ -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 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 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, diff --git a/netwerk/protocol/http/SpdySession31.h b/netwerk/protocol/http/SpdySession31.h index 4b153fab0852..4768950755d0 100644 --- a/netwerk/protocol/http/SpdySession31.h +++ b/netwerk/protocol/http/SpdySession31.h @@ -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 *); diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 42260918c6fb..f0dac337d6ac 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -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)) { diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index f2b35ac07a40..b151954b9341 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -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 seekable = do_QueryInterface(mRequestStream); diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index a564d0095976..20fe32a9b6bb 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -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 mTunnelProvider; }; }} // namespace mozilla::net