--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1591,16 +1591,19 @@ pref("network.http.spdy.default-hpack-bu
// alt-svc allows separation of transport routing from
// the origin host without using a proxy.
pref("network.http.altsvc.enabled", true);
pref("network.http.altsvc.oe", true);
// Turn on 0RTT data for TLS 1.3
pref("security.tls.enable_0rtt_data", true);
+// the origin extension impacts h2 coalescing
+pref("network.http.originextension", true);
+
pref("network.http.diagnostics", false);
pref("network.http.pacing.requests.enabled", true);
pref("network.http.pacing.requests.min-parallelism", 6);
pref("network.http.pacing.requests.hz", 80);
pref("network.http.pacing.requests.burst", 10);
// TCP Keepalive config for HTTP connections.
--- a/netwerk/protocol/http/ASpdySession.h
+++ b/netwerk/protocol/http/ASpdySession.h
@@ -27,16 +27,19 @@ public:
virtual bool RoomForMoreStreams() = 0;
virtual PRIntervalTime IdleTime() = 0;
virtual uint32_t ReadTimeoutTick(PRIntervalTime now) = 0;
virtual void DontReuse() = 0;
virtual uint32_t SpdyVersion() = 0;
static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *, bool);
+ virtual bool TestJoinConnection(const nsACString &hostname, int32_t port) = 0;
+ virtual bool JoinConnection(const nsACString &hostname, int32_t port) = 0;
+
// 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;
--- a/netwerk/protocol/http/ConnectionDiagnostics.cpp
+++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp
@@ -62,18 +62,17 @@ nsHttpConnectionMgr::OnMsgPrintDiagnosti
mLogData.AppendPrintf(" Active Conns Length = %" PRIuSIZE "\n",
ent->mActiveConns.Length());
mLogData.AppendPrintf(" Idle Conns Length = %" PRIuSIZE "\n",
ent->mIdleConns.Length());
mLogData.AppendPrintf(" Half Opens Length = %" PRIuSIZE "\n",
ent->mHalfOpens.Length());
mLogData.AppendPrintf(" Coalescing Keys Length = %" PRIuSIZE "\n",
ent->mCoalescingKeys.Length());
- mLogData.AppendPrintf(" Spdy using = %d, preferred = %d\n",
- ent->mUsingSpdy, ent->mInPreferredHash);
+ mLogData.AppendPrintf(" Spdy using = %d\n", ent->mUsingSpdy);
uint32_t i;
for (i = 0; i < ent->mActiveConns.Length(); ++i) {
mLogData.AppendPrintf(" :: Active Connection #%u\n", i);
ent->mActiveConns[i]->PrintDiagnostics(mLogData);
}
for (i = 0; i < ent->mIdleConns.Length(); ++i) {
mLogData.AppendPrintf(" :: Idle Connection #%u\n", i);
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -106,16 +106,17 @@ Http2Session::Http2Session(nsISocketTran
, mOutputQueueSent(0)
, mLastReadEpoch(PR_IntervalNow())
, mPingSentEpoch(0)
, mPreviousUsed(false)
, mWaitingForSettingsAck(false)
, mGoAwayOnPush(false)
, mUseH2Deps(false)
, mAttemptingEarlyData(attemptingEarlyData)
+ , mOriginFrameActivated(false)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
static uint64_t sSerial;
mSerial = ++sSerial;
LOG3(("Http2Session::Http2Session %p serial=0x%" PRIX64 "\n", this, mSerial));
@@ -219,17 +220,18 @@ static Http2ControlFx sControlFunctions[
Http2Session::RecvPriority,
Http2Session::RecvRstStream,
Http2Session::RecvSettings,
Http2Session::RecvPushPromise,
Http2Session::RecvPing,
Http2Session::RecvGoAway,
Http2Session::RecvWindowUpdate,
Http2Session::RecvContinuation,
- Http2Session::RecvAltSvc // extension for type 0x0A
+ Http2Session::RecvAltSvc, // extension for type 0x0A
+ Http2Session::RecvOrigin // extension for type 0x0B
};
bool
Http2Session::RoomForMoreConcurrent()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
return (mConcurrent < mMaxConcurrent);
}
@@ -1793,36 +1795,33 @@ Http2Session::RecvPushPromise(Http2Sessi
nsAutoCString key;
if (!pushedStream->GetHashKey(key)) {
LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n"));
self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR);
self->ResetDownstreamState();
return NS_OK;
}
- RefPtr<nsStandardURL> associatedURL, pushedURL;
- rv = Http2Stream::MakeOriginURL(associatedStream->Origin(), associatedURL);
- if (NS_SUCCEEDED(rv)) {
- rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
- }
- LOG3(("Http2Session::RecvPushPromise %p checking %s == %s", self,
- associatedStream->Origin().get(), pushedStream->Origin().get()));
- bool match = false;
+ // does the pushed origin belong on this connection?
+ LOG3(("Http2Session::RecvPushPromise %p origin check %s", self,
+ pushedStream->Origin().get()));
+ RefPtr<nsStandardURL> pushedURL;
+ rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
+ nsAutoCString pushedHostName;
+ int32_t pushedPort = -1;
if (NS_SUCCEEDED(rv)) {
- rv = associatedURL->Equals(pushedURL, &match);
+ rv = pushedURL->GetHost(pushedHostName);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ rv = pushedURL->GetPort(&pushedPort);
}
- if (NS_FAILED(rv)) {
- // Fallback to string equality of origins. This won't be guaranteed to be as
- // liberal as we want it to be, but it will at least be safe
- match = associatedStream->Origin().Equals(pushedStream->Origin());
- }
- if (!match) {
- LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin "
- "associated origin %s .. pushed origin %s\n", self,
- associatedStream->Origin().get(), pushedStream->Origin().get()));
+ if (NS_FAILED(rv) ||
+ !self->TestJoinConnection(pushedHostName, pushedPort)) {
+ LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin %s\n",
+ self, pushedStream->Origin().get()));
self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
self->ResetDownstreamState();
return NS_OK;
}
if (pushedStream->TryOnPush()) {
LOG3(("Http2Session::RecvPushPromise %p channel implements nsIHttpPushListener "
"stream %p will not be placed into session cache.\n", self, pushedStream));
@@ -2286,16 +2285,137 @@ Http2Session::RecvAltSvc(Http2Session *s
RefPtr<UpdateAltSvcEvent> event =
new UpdateAltSvcEvent(altSvcFieldValue, origin, ci, irCallbacks);
NS_DispatchToMainThread(event);
self->ResetDownstreamState();
return NS_OK;
}
+void
+Http2Session::Received421(nsHttpConnectionInfo *ci)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ LOG3(("Http2Session::Recevied421 %p %d\n", this, mOriginFrameActivated));
+ if (!mOriginFrameActivated || !ci) {
+ return;
+ }
+
+ nsAutoCString key(ci->GetOrigin());
+ key.Append(':');
+ key.AppendInt(ci->OriginPort());
+ mOriginFrame.Remove(key);
+ LOG3(("Http2Session::Received421 %p key %s removed\n", this, key.get()));
+}
+
+void
+Http2Session::InitializeOriginSet()
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ LOG3(("Http2Session::InitializeOriginSet %d\n", mOriginFrameActivated));
+ if (!mOriginFrameActivated) {
+ mOriginFrameActivated = true;
+ // The SNI is implicitly in the origin set - but we don't implement that
+ // as coalescing, so you don't need to explicitly put it here
+ }
+}
+
+// defined as an http2 extension - origin
+// defines receipt of frame type 0x0b.. http://httpwg.org/http-extensions/origin-frame.html
+// as this is an extension, never generate protocol error - just ignore problems
+nsresult
+Http2Session::RecvOrigin(Http2Session *self)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ORIGIN);
+ LOG3(("Http2Session::RecvOrigin %p Flags 0x%X id 0x%X\n", self,
+ self->mInputFrameFlags, self->mInputFrameID));
+
+ if (self->mInputFrameFlags & 0xF0) {
+ LOG3(("Http2Session::RecvOrigin %p leading flags must be 0", self));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ if (self->mInputFrameID) {
+ LOG3(("Http2Session::RecvOrigin %p not stream 0", self));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ if (self->ConnectionInfo()->UsingProxy()) {
+ LOG3(("Http2Session::RecvOrigin %p must not use proxy", self));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ if (!gHttpHandler->AllowOriginExtension()) {
+ LOG3(("Http2Session::RecvOrigin %p origin extension pref'd off", self));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ uint32_t offset = 0;
+ while (self->mInputFrameDataSize >= (offset + 2U)) {
+
+ uint16_t originLen = NetworkEndian::readUint16(
+ self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset);
+ LOG3(("Http2Session::RecvOrigin %p origin extension defined as %d bytes\n", self, originLen));
+ if (originLen + 2U + offset > self->mInputFrameDataSize) {
+ LOG3(("Http2Session::RecvOrigin %p origin len too big for frame", self));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ nsAutoCString originString;
+ RefPtr<nsStandardURL> originURL;
+ originString.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset + 2, originLen);
+ if (NS_FAILED(Http2Stream::MakeOriginURL(originString, originURL))){
+ LOG3(("Http2Session::RecvOrigin %p origin frame string %s failed to parse\n", self, originString.get()));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ LOG3(("Http2Session::RecvOrigin %p origin frame string %s parsed OK\n", self, originString.get()));
+ bool isHttps = false;
+ if (NS_FAILED(originURL->SchemeIs("https", &isHttps)) || !isHttps) {
+ LOG3(("Http2Session::RecvOrigin %p origin frame not https\n", self));
+ self->ResetDownstreamState();
+ return NS_OK;
+ }
+
+ self->InitializeOriginSet();
+ int32_t port = -1;
+ originURL->GetPort(&port);
+ if (port == -1) {
+ port = 443;
+ }
+ // dont use ->GetHostPort because we want explicit 443
+ nsAutoCString host;
+ originURL->GetHost(host);
+ nsAutoCString key(host);
+ key.Append(':');
+ key.AppendInt(port);
+ if (!self->mOriginFrame.Get(key)) {
+ self->mOriginFrame.Put(key, true);
+ RefPtr<nsHttpConnection> conn(self->HttpConnection());
+ MOZ_ASSERT(conn.get());
+ gHttpHandler->ConnMgr()->RegisterOriginCoalescingKey(conn, host, port);
+ } else {
+ LOG3(("Http2Session::RecvOrigin %p origin frame already in set\n", self));
+ }
+ offset += originLen + 2;
+ }
+ // init in case the loop did not run and we didn't hit early return on first iter
+ self->InitializeOriginSet();
+
+ self->ResetDownstreamState();
+ return NS_OK;
+}
+
//-----------------------------------------------------------------------------
// nsAHttpTransaction. It is expected that nsHttpConnection is the caller
// of these methods
//-----------------------------------------------------------------------------
void
Http2Session::OnTransportStatus(nsITransport* aTransport,
nsresult aStatus, int64_t aProgress)
@@ -3843,16 +3963,25 @@ Http2Session::TakeTransport(nsISocketTra
already_AddRefed<nsHttpConnection>
Http2Session::TakeHttpConnection()
{
MOZ_ASSERT(false, "TakeHttpConnection of Http2Session");
return nullptr;
}
+already_AddRefed<nsHttpConnection>
+Http2Session::HttpConnection()
+{
+ if (mConnection) {
+ return mConnection->HttpConnection();
+ }
+ return nullptr;
+}
+
void
Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
{
*aOut = nullptr;
}
//-----------------------------------------------------------------------------
// unused methods of nsAHttpTransaction
@@ -4000,10 +4129,107 @@ Http2Session::SendPing()
mPreviousPingThreshold = mPingThreshold;
mPreviousUsed = true;
mPingThreshold = gHttpHandler->NetworkChangedTimeout();
}
GeneratePing(false);
Unused << ResumeRecv();
}
+bool
+Http2Session::TestOriginFrame(const nsACString &hostname, int32_t port)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(mOriginFrameActivated);
+
+ nsAutoCString key(hostname);
+ key.Append (':');
+ key.AppendInt(port);
+ bool rv = mOriginFrame.Get(key);
+ LOG3(("TestOriginFrame() %p %s %d\n", this, key.get(), rv));
+ return rv;
+}
+
+bool
+Http2Session::TestJoinConnection(const nsACString &hostname, int32_t port)
+{
+ if (!mConnection || mClosed || mShouldGoAway) {
+ return false;
+ }
+
+ if (mOriginFrameActivated) {
+ bool originFrameResult = TestOriginFrame(hostname, port);
+ if (!originFrameResult) {
+ return false;
+ }
+ } else {
+ LOG3(("TestJoinConnection %p no origin frame check used.\n", this));
+ }
+
+ nsresult rv;
+ bool isJoined = false;
+
+ nsCOMPtr<nsISupports> securityInfo;
+ nsCOMPtr<nsISSLSocketControl> sslSocketControl;
+
+ mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
+ sslSocketControl = do_QueryInterface(securityInfo, &rv);
+ if (NS_FAILED(rv) || !sslSocketControl) {
+ return false;
+ }
+
+ // try all the coalescable versions we support.
+ const SpdyInformation *info = gHttpHandler->SpdyInfo();
+ for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
+ if (info->ProtocolEnabled(index - 1)) {
+ rv = sslSocketControl->TestJoinConnection(info->VersionString[index - 1],
+ hostname, port, &isJoined);
+ if (NS_SUCCEEDED(rv) && isJoined) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+Http2Session::JoinConnection(const nsACString &hostname, int32_t port)
+{
+ if (!mConnection || mClosed || mShouldGoAway) {
+ return false;
+ }
+ if (mOriginFrameActivated) {
+ bool originFrameResult = TestOriginFrame(hostname, port);
+ if (!originFrameResult) {
+ return false;
+ }
+ } else {
+ LOG3(("JoinConnection %p no origin frame check used.\n", this));
+ }
+
+ nsresult rv;
+ bool isJoined = false;
+
+ nsCOMPtr<nsISupports> securityInfo;
+ nsCOMPtr<nsISSLSocketControl> sslSocketControl;
+
+ mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
+ sslSocketControl = do_QueryInterface(securityInfo, &rv);
+ if (NS_FAILED(rv) || !sslSocketControl) {
+ return false;
+ }
+
+ // try all the coalescable versions we support.
+ const SpdyInformation *info = gHttpHandler->SpdyInfo();
+ for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
+ if (info->ProtocolEnabled(index - 1)) {
+ rv = sslSocketControl->JoinConnection(info->VersionString[index - 1],
+ hostname, port, &isJoined);
+ if (NS_SUCCEEDED(rv) && isJoined) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -45,16 +45,18 @@ public:
Http2Session(nsISocketTransport *, uint32_t version, bool attemptingEarlyData);
MOZ_MUST_USE bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *) override;
bool CanReuse() override { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams() override;
uint32_t SpdyVersion() override;
+ bool TestJoinConnection(const nsACString &hostname, int32_t port) override;
+ bool JoinConnection(const nsACString &hostname, int32_t port) override;
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
// have this invoked. It might happen sooner depending on the needs of
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now) override;
// Idle time represents time since "goodput".. e.g. a data or header frame
@@ -84,17 +86,18 @@ public:
FRAME_TYPE_RST_STREAM = 0x3,
FRAME_TYPE_SETTINGS = 0x4,
FRAME_TYPE_PUSH_PROMISE = 0x5,
FRAME_TYPE_PING = 0x6,
FRAME_TYPE_GOAWAY = 0x7,
FRAME_TYPE_WINDOW_UPDATE = 0x8,
FRAME_TYPE_CONTINUATION = 0x9,
FRAME_TYPE_ALTSVC = 0xA,
- FRAME_TYPE_LAST = 0xB
+ FRAME_TYPE_ORIGIN = 0xB,
+ FRAME_TYPE_LAST = 0xC
};
// NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway
// code NO_ERROR to be NO_HTTP_ERROR
enum errorType {
NO_HTTP_ERROR = 0,
PROTOCOL_ERROR = 1,
INTERNAL_ERROR = 2,
@@ -180,16 +183,17 @@ public:
static nsresult RecvRstStream(Http2Session *);
static nsresult RecvSettings(Http2Session *);
static nsresult RecvPushPromise(Http2Session *);
static nsresult RecvPing(Http2Session *);
static nsresult RecvGoAway(Http2Session *);
static nsresult RecvWindowUpdate(Http2Session *);
static nsresult RecvContinuation(Http2Session *);
static nsresult RecvAltSvc(Http2Session *);
+ static nsresult RecvOrigin(Http2Session *);
char *EnsureOutputBuffer(uint32_t needed);
template<typename charType>
void CreateFrameHeader(charType dest, uint16_t frameLength,
uint8_t frameType, uint8_t frameFlags,
uint32_t streamID);
@@ -238,16 +242,19 @@ public:
bool UseH2Deps() { return mUseH2Deps; }
// overload of nsAHttpTransaction
MOZ_MUST_USE nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final;
MOZ_MUST_USE nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final;
MOZ_MUST_USE bool Do0RTT() override final { return true; }
MOZ_MUST_USE nsresult Finish0RTT(bool aRestart, bool aAlpnChanged) override final;
+ // For use by an HTTP2Stream
+ void Received421(nsHttpConnectionInfo *ci);
+
private:
// These internal states do not correspond to the states of the HTTP/2 specification
enum internalStateType {
BUFFERING_OPENING_SETTINGS,
BUFFERING_FRAME_HEADER,
BUFFERING_CONTROL_FRAME,
PROCESSING_DATA_FRAME_PADDING_CONTROL,
@@ -503,16 +510,21 @@ private:
bool mGoAwayOnPush;
bool mUseH2Deps;
bool mAttemptingEarlyData;
// The ID(s) of the stream(s) that we are getting 0RTT data from.
nsTArray<uint32_t> m0RTTStreams;
+ void InitializeOriginSet();
+ bool TestOriginFrame(const nsACString &name, int32_t port);
+ bool mOriginFrameActivated;
+ nsDataHashtable<nsCStringHashKey, bool> mOriginFrame;
+
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;
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -1017,29 +1017,35 @@ Http2Stream::ConvertResponseHeaders(Http
decompressor->GetStatus(statusString);
if (statusString.IsEmpty()) {
LOG3(("Http2Stream::ConvertResponseHeaders %p Error - no status\n", this));
return NS_ERROR_ILLEGAL_VALUE;
}
nsresult errcode;
httpResponseCode = statusString.ToInteger(&errcode);
+ LOG3(("Http2Stream::ConvertResponseHeaders %p response code %d\n", this, httpResponseCode));
if (mIsTunnel) {
LOG3(("Http2Stream %p Tunnel Response code %d", this, httpResponseCode));
if ((httpResponseCode / 100) != 2) {
MapStreamToPlainText();
}
}
if (httpResponseCode == 101) {
// 8.1.1 of h2 disallows 101.. throw PROTOCOL_ERROR on stream
LOG3(("Http2Stream::ConvertResponseHeaders %p Error - status == 101\n", this));
return NS_ERROR_ILLEGAL_VALUE;
}
+ if (httpResponseCode == 421) {
+ // Origin Frame requires 421 to remove this origin from the origin set
+ mSession->Received421(mTransaction->ConnectionInfo());
+ }
+
if (aHeadersIn.Length() && aHeadersOut.Length()) {
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
uint32_t ratio =
aHeadersIn.Length() * 100 / aHeadersOut.Length();
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
}
// The decoding went ok. Now we can customize and clean up.
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -124,16 +124,19 @@ public:
// the same connection and work around buggy servers.
virtual bool LastTransactionExpectedNoContent() = 0;
virtual void SetLastTransactionExpectedNoContent(bool) = 0;
// Transfer the base http connection object along with a
// reference to it to the caller.
virtual already_AddRefed<nsHttpConnection> TakeHttpConnection() = 0;
+ // Like TakeHttpConnection() but do not drop our own ref
+ virtual already_AddRefed<nsHttpConnection> HttpConnection() = 0;
+
// Get the nsISocketTransport used by the connection without changing
// references or ownership.
virtual nsISocketTransport *Transport() = 0;
// The number of transaction bytes written out on this HTTP Connection, does
// not count CONNECT tunnel setup
virtual int64_t BytesWritten() = 0;
@@ -156,17 +159,18 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpCon
MOZ_MUST_USE nsresult TakeTransport(nsISocketTransport **, \
nsIAsyncInputStream **, \
nsIAsyncOutputStream **) override; \
bool IsPersistent() override; \
bool IsReused() override; \
void DontReuse() override; \
MOZ_MUST_USE nsresult PushBack(const char *, uint32_t) override; \
already_AddRefed<nsHttpConnection> TakeHttpConnection() override; \
- /* \
+ already_AddRefed<nsHttpConnection> HttpConnection() override; \
+ /* \
Thes methods below have automatic definitions that just forward the \
function to a lower level connection object \
*/ \
void GetConnectionInfo(nsHttpConnectionInfo **result) \
override \
{ \
if (!(fwdObject)) { \
*result = nullptr; \
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -879,16 +879,34 @@ nsHttpConnection::DontReuse()
mKeepAlive = false;
mDontReuse = true;
mIdleTimeout = 0;
if (mSpdySession)
mSpdySession->DontReuse();
}
bool
+nsHttpConnection::TestJoinConnection(const nsACString &hostname, int32_t port)
+{
+ if (mSpdySession && CanDirectlyActivate()) {
+ return mSpdySession->TestJoinConnection(hostname, port);
+ }
+ return false;
+}
+
+bool
+nsHttpConnection::JoinConnection(const nsACString &hostname, int32_t port)
+{
+ if (mSpdySession && CanDirectlyActivate()) {
+ return mSpdySession->JoinConnection(hostname, port);
+ }
+ return false;
+}
+
+bool
nsHttpConnection::CanReuse()
{
if (mDontReuse || !mRemainingConnectionUses) {
return false;
}
if ((mTransaction ? (mTransaction->IsDone() ? 0U : 1U) : 0U) >=
mRemainingConnectionUses) {
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -217,16 +217,19 @@ public:
// connection since CheckForTraffic() was called.
bool NoTraffic() {
return mTrafficStamp &&
(mTrafficCount == (mTotalBytesWritten + mTotalBytesRead));
}
// override of nsAHttpConnection
virtual uint32_t Version();
+ bool TestJoinConnection(const nsACString &hostname, int32_t port);
+ bool JoinConnection(const nsACString &hostname, int32_t port);
+
private:
// Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
enum TCPKeepaliveConfig {
kTCPKeepaliveDisabled = 0,
kTCPKeepaliveShortLivedConfig,
kTCPKeepaliveLongLivedConfig
};
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -554,123 +554,28 @@ nsHttpConnectionMgr::ClearConnectionHist
ent->PendingQLength() == 0) {
iter.Remove();
}
}
return NS_OK;
}
-
-nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::LookupPreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
-{
- nsConnectionEntry *preferred = nullptr;
- uint32_t len = ent->mCoalescingKeys.Length();
- for (uint32_t i = 0; !preferred && (i < len); ++i) {
- preferred = mSpdyPreferredHash.Get(ent->mCoalescingKeys[i]);
- }
- return preferred;
-}
-
-void
-nsHttpConnectionMgr::StorePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
-{
- if (ent->mCoalescingKeys.IsEmpty()) {
- return;
- }
-
- ent->mInPreferredHash = true;
- uint32_t len = ent->mCoalescingKeys.Length();
- for (uint32_t i = 0; i < len; ++i) {
- mSpdyPreferredHash.Put(ent->mCoalescingKeys[i], ent);
- }
-}
-
-void
-nsHttpConnectionMgr::RemovePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent)
-{
- if (!ent->mInPreferredHash || ent->mCoalescingKeys.IsEmpty()) {
- return;
- }
-
- ent->mInPreferredHash = false;
- uint32_t len = ent->mCoalescingKeys.Length();
- for (uint32_t i = 0; i < len; ++i) {
- mSpdyPreferredHash.Remove(ent->mCoalescingKeys[i]);
- }
-}
-
-// Given a nsHttpConnectionInfo find the connection entry object that
-// contains either the nshttpconnection or nshttptransaction parameter.
-// Normally this is done by the hashkey lookup of connectioninfo,
-// but if spdy coalescing is in play it might be found in a redirected
-// entry
-nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
- nsHttpConnection *conn,
- nsHttpTransaction *trans)
-{
- MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
- if (!ci)
- return nullptr;
-
- nsConnectionEntry *ent = mCT.Get(ci->HashKey());
-
- // If there is no sign of coalescing (or it is disabled) then just
- // return the primary hash lookup
- if (!ent || !ent->mUsingSpdy || ent->mCoalescingKeys.IsEmpty())
- return ent;
-
- // If there is no preferred coalescing entry for this host (or the
- // preferred entry is the one that matched the mCT hash lookup) then
- // there is only option
- nsConnectionEntry *preferred = LookupPreferredHash(ent);
- if (!preferred || (preferred == ent))
- return ent;
-
- if (conn) {
- // The connection could be either in preferred or ent. It is most
- // likely the only active connection in preferred - so start with that.
- if (preferred->mActiveConns.Contains(conn))
- return preferred;
- if (preferred->mIdleConns.Contains(conn))
- return preferred;
- }
-
- if (trans) {
- if (preferred->mUrgentStartQ.Contains(trans, PendingComparator())) {
- return preferred;
- }
-
- nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
- if (preferred->mPendingTransactionTable.Get(
- trans->TopLevelOuterContentWindowId(), &infoArray)) {
- if (infoArray->Contains(trans, PendingComparator())) {
- return preferred;
- }
- }
- }
-
- // Neither conn nor trans found in preferred, use the default entry
- return ent;
-}
-
nsresult
nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
this, conn));
- if (!conn->ConnectionInfo())
+ if (!conn->ConnectionInfo()) {
return NS_ERROR_UNEXPECTED;
-
- nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
- conn, nullptr);
+ }
+
+ nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
RefPtr<nsHttpConnection> deleteProtector(conn);
if (!ent || !ent->mIdleConns.RemoveElement(conn))
return NS_ERROR_UNEXPECTED;
conn->Close(NS_ERROR_ABORT);
mNumIdleConns--;
ConditionallyStopPruneDeadConnectionsTimer();
@@ -684,237 +589,241 @@ nsHttpConnectionMgr::RemoveIdleConnectio
LOG(("nsHttpConnectionMgr::RemoveIdleConnection %p conn=%p",
this, conn));
if (!conn->ConnectionInfo()) {
return NS_ERROR_UNEXPECTED;
}
- nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
- conn, nullptr);
+ nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
if (!ent || !ent->mIdleConns.RemoveElement(conn)) {
return NS_ERROR_UNEXPECTED;
}
mNumIdleConns--;
return NS_OK;
}
+nsHttpConnection *
+nsHttpConnectionMgr::FindCoalescableConnectionByHashKey(nsConnectionEntry *ent,
+ const nsCString &key,
+ bool justKidding)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(ent->mConnInfo);
+ nsHttpConnectionInfo *ci = ent->mConnInfo;
+
+ nsTArray<nsWeakPtr> *listOfWeakConns = mCoalescingHash.Get(key);
+ if (!listOfWeakConns) {
+ return nullptr;
+ }
+
+ uint32_t listLen = listOfWeakConns->Length();
+ for (uint32_t j = 0; j < listLen; ) {
+ RefPtr<nsHttpConnection> potentialMatch = do_QueryReferent(listOfWeakConns->ElementAt(j));
+ if (!potentialMatch) {
+ // This is a connection that needs to be removed from the list
+ LOG(("FindCoalescableConnectionByHashKey() found old conn %p that has null weak ptr - removing\n",
+ listOfWeakConns->ElementAt(j).get()));
+ if (j != listLen - 1) {
+ listOfWeakConns->Elements()[j] = listOfWeakConns->Elements()[listLen - 1];
+ }
+ listOfWeakConns->RemoveElementAt(listLen - 1);
+ MOZ_ASSERT(listOfWeakConns->Length() == listLen - 1);
+ listLen--;
+ continue; // without adjusting iterator
+ }
+ bool couldJoin;
+ if (justKidding) {
+ couldJoin = potentialMatch->TestJoinConnection(ci->GetOrigin(), ci->OriginPort());
+ } else {
+ couldJoin = potentialMatch->JoinConnection(ci->GetOrigin(), ci->OriginPort());
+ }
+ if (couldJoin) {
+ LOG(("FindCoalescableConnectionByHashKey() found hashtable match %p for key %s of CI %s join ok\n",
+ potentialMatch.get(), key.get(), ci->HashKey().get()));
+ return potentialMatch.get();
+ } else {
+ LOG(("FindCoalescableConnectionByHashKey() found hashtable match %p for key %s of CI %s join failed\n",
+ potentialMatch.get(), key.get(), ci->HashKey().get()));
+ }
+ ++j; // bypassed by continue when weakptr fails
+ }
+ if (!listLen) { // shurnk to 0 while iterating
+ LOG(("FindCoalescableConnectionByHashKey() removing empty list element\n"));
+ mCoalescingHash.Remove(key);
+ }
+ return nullptr;
+}
+
+static void
+BuildOriginFrameHashKey(nsACString &newKey, nsHttpConnectionInfo *ci,
+ const nsACString &host, int32_t port)
+{
+ newKey.Assign(host);
+ if (ci->GetAnonymous()) {
+ newKey.AppendLiteral("~A:");
+ } else {
+ newKey.AppendLiteral("~.:");
+ }
+ newKey.AppendInt(port);
+ newKey.AppendLiteral("/[");
+ nsAutoCString suffix;
+ ci->GetOriginAttributes().CreateSuffix(suffix);
+ newKey.Append(suffix);
+ newKey.AppendLiteral("]viaORIGIN.FRAME");
+}
+
+nsHttpConnection *
+nsHttpConnectionMgr::FindCoalescableConnection(nsConnectionEntry *ent,
+ bool justKidding)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(ent->mConnInfo);
+ nsHttpConnectionInfo *ci = ent->mConnInfo;
+ LOG(("FindCoalescableConnection %s\n", ci->HashKey().get()));
+ // First try and look it up by origin frame
+ nsCString newKey;
+ BuildOriginFrameHashKey(newKey, ci, ci->GetOrigin(), ci->OriginPort());
+ nsHttpConnection *conn =
+ FindCoalescableConnectionByHashKey(ent, newKey, justKidding);
+ if (conn) {
+ LOG(("FindCoalescableConnection(%s) match conn %p on frame key %s\n",
+ ci->HashKey().get(), conn, newKey.get()));
+ return conn;
+ }
+
+ // now check for DNS based keys
+ // deleted conns (null weak pointers) are removed from list
+ uint32_t keyLen = ent->mCoalescingKeys.Length();
+ for (uint32_t i = 0; i < keyLen; ++i) {
+ conn = FindCoalescableConnectionByHashKey(ent, ent->mCoalescingKeys[i], justKidding);
+ if (conn) {
+ LOG(("FindCoalescableConnection(%s) match conn %p on dns key %s\n",
+ ci->HashKey().get(), conn, ent->mCoalescingKeys[i].get()));
+ return conn;
+ }
+ }
+
+ LOG(("FindCoalescableConnection(%s) no match conn\n", ci->HashKey().get()));
+ return nullptr;
+}
+
+
+void
+nsHttpConnectionMgr::UpdateCoalescingForNewConn(nsHttpConnection *conn,
+ nsConnectionEntry *ent)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(conn);
+ MOZ_ASSERT(conn->ConnectionInfo());
+ MOZ_ASSERT(ent);
+ MOZ_ASSERT(mCT.Get(conn->ConnectionInfo()->HashKey()) == ent);
+
+ nsHttpConnection *existingConn = FindCoalescableConnection(ent, true);
+ if (existingConn) {
+ LOG(("UpdateCoalescingForNewConn() found active conn that could have served newConn\n"
+ "graceful close of conn=%p to migrate to %p\n", conn, existingConn));
+ conn->DontReuse();
+ return;
+ }
+
+ // This connection might go into the mCoalescingHash for new transactions to be coalesced onto
+ // if it can accept new transactions
+ if (!conn->CanDirectlyActivate()) {
+ return;
+ }
+
+ uint32_t keyLen = ent->mCoalescingKeys.Length();
+ for (uint32_t i = 0;i < keyLen; ++i) {
+ LOG(("UpdateCoalescingForNewConn() registering conn %p %s under key %s\n",
+ conn, conn->ConnectionInfo()->HashKey().get(), ent->mCoalescingKeys[i].get()));
+ nsTArray<nsWeakPtr> *listOfWeakConns = mCoalescingHash.Get(ent->mCoalescingKeys[i]);
+ if (!listOfWeakConns) {
+ LOG(("UpdateCoalescingForNewConn() need new list element\n"));
+ listOfWeakConns = new nsTArray<nsWeakPtr>(1);
+ mCoalescingHash.Put(ent->mCoalescingKeys[i], listOfWeakConns);
+ }
+ listOfWeakConns->AppendElement(do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
+ }
+
+ // Cancel any other pending connections - their associated transactions
+ // are in the pending queue and will be dispatched onto this new connection
+ for (int32_t index = ent->mHalfOpens.Length() - 1; index >= 0; --index) {
+ LOG(("UpdateCoalescingForNewConn() forcing halfopen abandon %p\n",
+ ent->mHalfOpens[index]));
+ ent->mHalfOpens[index]->Abandon();
+ }
+
+ if (ent->mActiveConns.Length() > 1) {
+ // this is a new connection that can be coalesced onto
+ // if there is more than 1 live and established spdy connection (e.g.
+ // some could still be handshaking, shutting down, etc..) then close
+ // this one down after any transactions that are on it are complete.
+ // This probably happened due to the parallel connection algorithm
+ // that is used only before the host is known to speak spdy.
+ for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
+ nsHttpConnection *otherConn = ent->mActiveConns[index];
+ if (otherConn != conn) {
+ LOG(("UpdateCoalescingForNewConn() shutting down connection (%p) because new "
+ "spdy connection (%p) takes precedence\n", otherConn, conn));
+ otherConn->DontReuse();
+ }
+ }
+ }
+}
+
// This function lets a connection, after completing the NPN phase,
// report whether or not it is using spdy through the usingSpdy
// argument. It would not be necessary if NPN were driven out of
// the connection manager. The connection entry associated with the
// connection is then updated to indicate whether or not we want to use
-// spdy with that host and update the preliminary preferred host
+// spdy with that host and update the coalescing hash
// entries used for de-sharding hostsnames.
void
nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
bool usingSpdy)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
- nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
- conn, nullptr);
-
- if (!ent)
+ if (!conn->ConnectionInfo()) {
return;
-
- if (!usingSpdy)
+ }
+ nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
+ if (!ent || !usingSpdy) {
return;
+ }
ent->mUsingSpdy = true;
mNumSpdyActiveConns++;
+ // adjust timeout timer
uint32_t ttl = conn->TimeToLive();
uint64_t timeOfExpire = NowInSeconds() + ttl;
- if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
+ if (!mTimer || timeOfExpire < mTimeOfNextWakeUp) {
PruneDeadConnectionsAfter(ttl);
-
- // Lookup preferred directly from the hash instead of using
- // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
- // check at this point because the cert is never part of the hash
- // lookup. Filtering on that has to be done at the time of use
- // rather than the time of registration (i.e. now).
- nsConnectionEntry *joinedConnection;
- nsConnectionEntry *preferred = LookupPreferredHash(ent);
-
- LOG(("ReportSpdyConnection %p,%s conn %p prefers %p,%s\n",
- ent, ent->mConnInfo->Origin(), conn, preferred,
- preferred ? preferred->mConnInfo->Origin() : ""));
-
- if (!preferred) {
- // this becomes the preferred entry
- StorePreferredHash(ent);
- preferred = ent;
- } else if ((preferred != ent) &&
- (joinedConnection = GetSpdyPreferredEnt(ent)) &&
- (joinedConnection != ent)) {
- //
- // A connection entry (e.g. made with a different hostname) with
- // the same IP address is preferred for future transactions over this
- // connection entry. Gracefully close down the connection to help
- // new transactions migrate over.
-
- LOG(("ReportSpdyConnection graceful close of conn=%p ent=%p to "
- "migrate to preferred (desharding)\n", conn, ent));
- conn->DontReuse();
- } else if (preferred != ent) {
- LOG (("ReportSpdyConnection preferred host may be in false start or "
- "may have insufficient cert. Leave mapping in place but do not "
- "abandon this connection yet."));
}
- if ((preferred == ent) && conn->CanDirectlyActivate()) {
- // this is a new spdy connection to the preferred entry
-
- // Cancel any other pending connections - their associated transactions
- // are in the pending queue and will be dispatched onto this connection
- for (int32_t index = ent->mHalfOpens.Length() - 1;
- index >= 0; --index) {
- LOG(("ReportSpdyConnection forcing halfopen abandon %p\n",
- ent->mHalfOpens[index]));
- ent->mHalfOpens[index]->Abandon();
- }
-
- if (ent->mActiveConns.Length() > 1) {
- // this is a new connection to an established preferred spdy host.
- // if there is more than 1 live and established spdy connection (e.g.
- // some could still be handshaking, shutting down, etc..) then close
- // this one down after any transactions that are on it are complete.
- // This probably happened due to the parallel connection algorithm
- // that is used only before the host is known to speak spdy.
- for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
- nsHttpConnection *otherConn = ent->mActiveConns[index];
- if (otherConn != conn) {
- LOG(("ReportSpdyConnection shutting down connection (%p) because new "
- "spdy connection (%p) takes precedence\n", otherConn, conn));
- otherConn->DontReuse();
- }
- }
- }
- }
+ UpdateCoalescingForNewConn(conn, ent);
nsresult rv = ProcessPendingQ(ent->mConnInfo);
if (NS_FAILED(rv)) {
LOG(("ReportSpdyConnection conn=%p ent=%p "
"failed to process pending queue (%08x)\n", conn, ent,
static_cast<uint32_t>(rv)));
}
rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
if (NS_FAILED(rv)) {
LOG(("ReportSpdyConnection conn=%p ent=%p "
"failed to post event (%08x)\n", conn, ent,
static_cast<uint32_t>(rv)));
}
}
-nsHttpConnectionMgr::nsConnectionEntry *
-nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
-{
- if (!gHttpHandler->IsSpdyEnabled() ||
- !gHttpHandler->CoalesceSpdy() ||
- aOriginalEntry->mConnInfo->GetNoSpdy() ||
- aOriginalEntry->mCoalescingKeys.IsEmpty()) {
- return nullptr;
- }
-
- nsConnectionEntry *preferred = LookupPreferredHash(aOriginalEntry);
-
- // if there is no redirection no cert validation is required
- if (preferred == aOriginalEntry)
- return aOriginalEntry;
-
- // if there is no preferred host or it is no longer using spdy
- // then skip pooling
- if (!preferred || !preferred->mUsingSpdy)
- return nullptr;
-
- // if there is not an active spdy session in this entry then
- // we cannot pool because the cert upon activation may not
- // be the same as the old one. Active sessions are prohibited
- // from changing certs.
-
- nsHttpConnection *activeSpdy = nullptr;
-
- for (uint32_t index = 0; index < preferred->mActiveConns.Length(); ++index) {
- if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
- activeSpdy = preferred->mActiveConns[index];
- break;
- }
- }
-
- if (!activeSpdy) {
- // remove the preferred status of this entry if it cannot be
- // used for pooling.
- RemovePreferredHash(preferred);
- LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
- "preferred host mapping %s to %s removed due to inactivity.\n",
- aOriginalEntry->mConnInfo->Origin(),
- preferred->mConnInfo->Origin()));
-
- return nullptr;
- }
-
- // Check that the server cert supports redirection
- nsresult rv;
- bool isJoined = false;
-
- nsCOMPtr<nsISupports> securityInfo;
- nsCOMPtr<nsISSLSocketControl> sslSocketControl;
- nsAutoCString negotiatedNPN;
-
- activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
- if (!securityInfo) {
- NS_WARNING("cannot obtain spdy security info");
- return nullptr;
- }
-
- sslSocketControl = do_QueryInterface(securityInfo, &rv);
- if (NS_FAILED(rv)) {
- NS_WARNING("sslSocketControl QI Failed");
- return nullptr;
- }
-
- // try all the spdy versions we support.
- const SpdyInformation *info = gHttpHandler->SpdyInfo();
- for (uint32_t index = SpdyInformation::kCount;
- NS_SUCCEEDED(rv) && index > 0; --index) {
- if (info->ProtocolEnabled(index - 1)) {
- rv = sslSocketControl->JoinConnection(info->VersionString[index - 1],
- aOriginalEntry->mConnInfo->GetOrigin(),
- aOriginalEntry->mConnInfo->OriginPort(),
- &isJoined);
- if (NS_SUCCEEDED(rv) && isJoined) {
- break;
- }
- }
- }
-
- if (NS_FAILED(rv) || !isJoined) {
- LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
- "Host %s cannot be confirmed to be joined "
- "with %s connections. rv=%" PRIx32 " isJoined=%d",
- preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
- static_cast<uint32_t>(rv), isJoined));
- Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, false);
- return nullptr;
- }
-
- // IP pooling confirmed
- LOG(("nsHttpConnectionMgr::GetSpdyPreferredEnt "
- "Host %s has cert valid for %s connections, "
- "so %s will be coalesced with %s",
- preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
- aOriginalEntry->mConnInfo->Origin(), preferred->mConnInfo->Origin()));
- Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, true);
- return preferred;
-}
-
//-----------------------------------------------------------------------------
bool
nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
nsConnectionEntry *ent,
bool considerAll)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -1042,16 +951,19 @@ nsHttpConnectionMgr::ProcessPendingQForE
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
"[ci=%s ent=%p active=%" PRIuSIZE " idle=%" PRIuSIZE " urgent-start queue=%" PRIuSIZE
"queued=%" PRIuSIZE "]\n",
ent->mConnInfo->HashKey().get(), ent, ent->mActiveConns.Length(),
ent->mIdleConns.Length(), ent->mUrgentStartQ.Length(),
ent->PendingQLength()));
+ if (!ent->mUrgentStartQ.Length() && !ent->mPendingTransactionTable.Count()) {
+ return false;
+ }
ProcessSpdyPendingQ(ent);
bool dispatchedSuccessfully = false;
if (!ent->mUrgentStartQ.IsEmpty()) {
dispatchedSuccessfully = DispatchPendingQ(ent->mUrgentStartQ,
ent,
considerAll);
@@ -1453,17 +1365,17 @@ nsHttpConnectionMgr::TryDispatchTransact
RefPtr<nsHttpConnection> unusedSpdyPersistentConnection;
// step 0
// look for existing spdy connection - that's always best because it is
// essentially pipelining without head of line blocking
if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
- RefPtr<nsHttpConnection> conn = GetSpdyPreferredConn(ent);
+ RefPtr<nsHttpConnection> conn = GetSpdyActiveConn(ent);
if (conn) {
if ((caps & NS_HTTP_ALLOW_KEEPALIVE) || !conn->IsExperienced()) {
LOG((" dispatch to spdy: [conn=%p]\n", conn.get()));
trans->RemoveDispatchedAsBlocking(); /* just in case */
nsresult rv = DispatchTransaction(ent, trans, conn);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@@ -1771,27 +1683,16 @@ nsHttpConnectionMgr::ProcessNewTransacti
nsresult rv = NS_OK;
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
MOZ_ASSERT(ci);
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(ci, !!trans->TunnelProvider());
- // SPDY coalescing of hostnames means we might redirect from this
- // connection entry onto the preferred one.
- nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
- if (preferredEntry && (preferredEntry != ent)) {
- LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
- "redirected via coalescing from %s to %s\n", trans,
- ent->mConnInfo->Origin(), preferredEntry->mConnInfo->Origin()));
-
- ent = preferredEntry;
- }
-
ReportProxyTelemetry(ent);
// Check if the transaction already has a sticky reference to a connection.
// If so, then we can just use it directly by transferring its reference
// to the new connection variable instead of searching for a new one
nsAHttpConnection *wrappedConnection = trans->Connection();
RefPtr<nsHttpConnection> conn;
@@ -1997,24 +1898,23 @@ nsHttpConnectionMgr::DispatchSpdyPending
// Put the leftovers back in the pending queue and get rid of the
// transactions we dispatched
leftovers.SwapElements(pendingQ);
leftovers.Clear();
}
// This function tries to dispatch the pending spdy transactions on
// the connection entry sent in as an argument. It will do so on the
-// active spdy connection either in that same entry or in the
-// redirected 'preferred' entry for the same coalescing hash key if
-// coalescing is enabled.
+// active spdy connection either in that same entry or from the
+// coalescing hash table
void
nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
{
- nsHttpConnection *conn = GetSpdyPreferredConn(ent);
+ nsHttpConnection *conn = GetSpdyActiveConn(ent);
if (!conn || !conn->CanDirectlyActivate()) {
return;
}
DispatchSpdyPendingQ(ent->mUrgentStartQ, ent, conn);
if (!conn->CanDirectlyActivate()) {
return;
}
@@ -2035,70 +1935,78 @@ nsHttpConnectionMgr::OnMsgProcessAllSpdy
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
ProcessSpdyPendingQ(iter.Data());
}
}
+// Given a connection entry, return an active h2 connection
+// that can be directly activated or null
nsHttpConnection *
-nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
+nsHttpConnectionMgr::GetSpdyActiveConn(nsConnectionEntry *ent)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(ent);
- nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
- // this entry is spdy-enabled if it is involved in a redirect
- if (preferred) {
- // all new connections for this entry will use spdy too
- ent->mUsingSpdy = true;
- } else {
- preferred = ent;
- }
-
- if (!preferred->mUsingSpdy) {
- return nullptr;
- }
-
- nsHttpConnection *rv = nullptr;
- uint32_t activeLen = preferred->mActiveConns.Length();
+ nsHttpConnection *experienced = nullptr;
+ nsHttpConnection *noExperience = nullptr;
+ uint32_t activeLen = ent->mActiveConns.Length();
+ nsHttpConnectionInfo *ci = ent->mConnInfo;
uint32_t index;
// activeLen should generally be 1.. this is a setup race being resolved
// take a conn who can activate and is experienced
for (index = 0; index < activeLen; ++index) {
- nsHttpConnection *tmp = preferred->mActiveConns[index];
- if (tmp->CanDirectlyActivate() && tmp->IsExperienced()) {
- rv = tmp;
- break;
+ nsHttpConnection *tmp = ent->mActiveConns[index];
+ if (tmp->CanDirectlyActivate()) {
+ if (tmp->IsExperienced()) {
+ experienced = tmp;
+ break;
+ }
+ noExperience = tmp; // keep looking for a better option
}
}
- // if that worked, cleanup anything else
- if (rv) {
+ // if that worked, cleanup anything else and exit
+ if (experienced) {
for (index = 0; index < activeLen; ++index) {
- nsHttpConnection *tmp = preferred->mActiveConns[index];
+ nsHttpConnection *tmp = ent->mActiveConns[index];
// in the case where there is a functional h2 session, drop the others
- if (tmp != rv) {
+ if (tmp != experienced) {
tmp->DontReuse();
}
}
- return rv;
+ LOG(("GetSpdyActiveConn() request for ent %p %s "
+ "found an active experienced connection %p in native connection entry\n",
+ ent, ci->HashKey().get(), experienced));
+ return experienced;
+ }
+
+ if (noExperience) {
+ LOG(("GetSpdyActiveConn() request for ent %p %s "
+ "found an active but inexperienced connection %p in native connection entry\n",
+ ent, ci->HashKey().get(), noExperience));
+ return noExperience;
}
- // take a conn who can activate and leave the rest alone
- for (index = 0; index < activeLen; ++index) {
- nsHttpConnection *tmp = preferred->mActiveConns[index];
- if (tmp->CanDirectlyActivate()) {
- rv = tmp;
- break;
- }
+ // there was no active spdy connection in the connection entry, but
+ // there might be one in the hash table for coalescing
+ nsHttpConnection *existingConn = FindCoalescableConnection(ent, false);
+ if (existingConn) {
+ LOG(("GetSpdyActiveConn() request for ent %p %s "
+ "found an active connection %p in the coalescing hashtable\n",
+ ent, ci->HashKey().get(), existingConn));
+ return existingConn;
}
- return rv;
+
+ LOG(("GetSpdyActiveConn() request for ent %p %s "
+ "did not find an active connection\n", ent, ci->HashKey().get()));
+ return nullptr;
}
//-----------------------------------------------------------------------------
void
nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -2169,16 +2077,17 @@ nsHttpConnectionMgr::OnMsgShutdown(int32
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
if (mTrafficTimer) {
mTrafficTimer->Cancel();
mTrafficTimer = nullptr;
}
+ mCoalescingHash.Clear();
// signal shutdown complete
nsCOMPtr<nsIRunnable> runnable =
new ConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
0, param);
NS_DispatchToMainThread(runnable);
}
@@ -2208,18 +2117,20 @@ void
nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
RefPtr<nsHttpTransaction> trans = static_cast<nsHttpTransaction *>(param);
trans->SetPriority(priority);
- nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
- nullptr, trans);
+ if (!trans->ConnectionInfo()) {
+ return;
+ }
+ nsConnectionEntry *ent = mCT.Get(trans->ConnectionInfo()->HashKey());
if (ent) {
int32_t caps = trans->Caps();
nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ = nullptr;
if (caps & NS_HTTP_URGENT_START) {
pendingQ = &(ent->mUrgentStartQ);
} else {
pendingQ =
@@ -2253,19 +2164,20 @@ nsHttpConnectionMgr::OnMsgCancelTransact
// if the transaction owns a connection and the transaction is not done,
// then ask the connection to close the transaction. otherwise, close the
// transaction directly (removing it from the pending queue first).
//
RefPtr<nsAHttpConnection> conn(trans->Connection());
if (conn && !trans->IsDone()) {
conn->CloseTransaction(trans, closeCode);
} else {
- nsConnectionEntry *ent =
- LookupConnectionEntry(trans->ConnectionInfo(), nullptr, trans);
-
+ nsConnectionEntry *ent = nullptr;
+ if (trans->ConnectionInfo()) {
+ ent = mCT.Get(trans->ConnectionInfo()->HashKey());
+ }
if (ent) {
uint32_t caps = trans->Caps();
int32_t transIndex;
// We will abandon all half-open sockets belonging to the given
// transaction.
nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
RefPtr<PendingTransactionInfo> pendingTransInfo;
if (caps & NS_HTTP_URGENT_START) {
@@ -2597,18 +2509,19 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
nsHttpConnection *conn = static_cast<nsHttpConnection *>(param);
//
// 1) remove the connection from the active list
// 2) if keep-alive, add connection to idle list
// 3) post event to process the pending transaction queue
//
- nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
- conn, nullptr);
+ nsConnectionEntry *ent = conn->ConnectionInfo() ?
+ mCT.Get(conn->ConnectionInfo()->HashKey()) : nullptr;
+
if (!ent) {
// 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()));
@@ -2735,17 +2648,16 @@ nsHttpConnectionMgr::OnMsgUpdateParam(in
NS_NOTREACHED("unexpected parameter name");
}
}
// nsHttpConnectionMgr::nsConnectionEntry
nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
{
MOZ_COUNT_DTOR(nsConnectionEntry);
- gHttpHandler->ConnMgr()->RemovePreferredHash(this);
}
// Read Timeout Tick handlers
void
nsHttpConnectionMgr::ActivateTimeoutTick()
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -2962,23 +2874,16 @@ nsHttpConnectionMgr::OnMsgSpeculativeCon
SpeculativeConnectArgs *args = static_cast<SpeculativeConnectArgs *>(param);
LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
args->mTrans->ConnectionInfo()->HashKey().get()));
nsConnectionEntry *ent =
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
- // the one directly passed in here.
- nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
- if (preferredEntry)
- ent = preferredEntry;
-
uint32_t parallelSpeculativeConnectLimit =
gHttpHandler->ParallelSpeculativeConnectLimit();
bool ignoreIdle = false;
bool isFromPredictor = false;
bool allow1918 = false;
if (args->mOverridesOK) {
parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
@@ -3568,16 +3473,43 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
}
}
}
}
return rv;
}
+// register a connection to receive CanJoinConnection() for particular
+// origin keys
+void
+nsHttpConnectionMgr::RegisterOriginCoalescingKey(nsHttpConnection *conn,
+ const nsACString &host,
+ int32_t port)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ nsHttpConnectionInfo *ci = conn ? conn->ConnectionInfo() : nullptr;
+ if (!ci || !conn->CanDirectlyActivate()) {
+ return;
+ }
+
+ nsCString newKey;
+ BuildOriginFrameHashKey(newKey, ci, host, port);
+ nsTArray<nsWeakPtr> *listOfWeakConns = mCoalescingHash.Get(newKey);
+ if (!listOfWeakConns) {
+ listOfWeakConns = new nsTArray<nsWeakPtr>(1);
+ mCoalescingHash.Put(newKey, listOfWeakConns);
+ }
+ listOfWeakConns->AppendElement(do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
+
+ LOG(("nsHttpConnectionMgr::RegisterOriginCoalescingKey "
+ "Established New Coalescing Key %s to %p %s\n",
+ newKey.get(), conn, ci->HashKey().get()));
+}
+
// method for nsITransportEventSink
NS_IMETHODIMP
nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
nsresult status,
int64_t progress,
int64_t progressMax)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -3645,17 +3577,17 @@ nsHttpConnectionMgr::nsHalfOpenSocket::O
} else {
newKey->AppendLiteral("~.:");
}
newKey->AppendInt(mEnt->mConnInfo->OriginPort());
newKey->AppendLiteral("/[");
nsAutoCString suffix;
mEnt->mConnInfo->GetOriginAttributes().CreateSuffix(suffix);
newKey->Append(suffix);
- newKey->Append(']');
+ newKey->AppendLiteral("]viaDNS");
LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
"STATUS_CONNECTING_TO Established New Coalescing Key # %d for host "
"%s [%s]", i, mEnt->mConnInfo->Origin(), newKey->get()));
}
gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
}
}
@@ -3746,40 +3678,45 @@ already_AddRefed<nsHttpConnection>
ConnectionHandle::TakeHttpConnection()
{
// return our connection object to the caller and clear it internally
// do not drop our reference - the caller now owns it.
MOZ_ASSERT(mConn);
return mConn.forget();
}
+already_AddRefed<nsHttpConnection>
+ConnectionHandle::HttpConnection()
+{
+ RefPtr<nsHttpConnection> rv(mConn);
+ return rv.forget();
+}
// nsConnectionEntry
nsHttpConnectionMgr::
nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
: mConnInfo(ci)
, mUsingSpdy(false)
- , mInPreferredHash(false)
, mPreferIPv4(false)
, mPreferIPv6(false)
, mUsedForConnection(false)
{
MOZ_COUNT_CTOR(nsConnectionEntry);
}
bool
nsHttpConnectionMgr::nsConnectionEntry::AvailableForDispatchNow()
{
if (mIdleConns.Length() && mIdleConns[0]->CanReuse()) {
return true;
}
return gHttpHandler->ConnMgr()->
- GetSpdyPreferredConn(this) ? true : false;
+ GetSpdyActiveConn(this) ? true : false;
}
bool
nsHttpConnectionMgr::GetConnectionData(nsTArray<HttpRetParams> *aArg)
{
for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
@@ -3823,19 +3760,20 @@ nsHttpConnectionMgr::GetConnectionData(n
return true;
}
void
nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo *ci)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
- nsConnectionEntry *ent = LookupConnectionEntry(ci, nullptr, nullptr);
- if (ent)
+ nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+ if (ent) {
ent->ResetIPFamilyPreference();
+ }
}
uint32_t
nsHttpConnectionMgr::
nsConnectionEntry::UnconnectedHalfOpens()
{
uint32_t unconnectedHalfOpens = 0;
for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) {
@@ -4016,17 +3954,17 @@ nsHttpConnectionMgr::MoveToWildCardConnE
{
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);
+ nsConnectionEntry *ent = mCT.Get(specificCI->HashKey());
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);
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -239,32 +239,26 @@ private:
// connection complete
uint32_t UnconnectedHalfOpens();
// Remove a particular half open socket from the mHalfOpens array
void RemoveHalfOpen(nsHalfOpenSocket *);
// Spdy sometimes resolves the address in the socket manager in order
// to re-coalesce sharded HTTP hosts. The dotted decimal address is
- // combined with the Anonymous flag from the connection information
+ // combined with the Anonymous flag and OA from the connection information
// to build the hash key for hosts in the same ip pool.
//
- // When a set of hosts are coalesced together one of them is marked
- // mSpdyPreferred. The mapping is maintained in the connection mananger
- // mSpdyPreferred hash.
- //
nsTArray<nsCString> mCoalescingKeys;
// To have the UsingSpdy flag means some host with the same connection
// entry has done NPN=spdy/* at some point. It does not mean every
// connection is currently using spdy.
bool mUsingSpdy : 1;
- bool mInPreferredHash : 1;
-
// Flags to remember our happy-eyeballs decision.
// Reset only by Ctrl-F5 reload.
// True when we've first connected an IPv4 server for this host,
// initially false.
bool mPreferIPv4 : 1;
// True when we've first connected an IPv6 server for this host,
// initially false.
bool mPreferIPv6 : 1;
@@ -302,16 +296,17 @@ private:
uint32_t maxCount = 0);
// Remove the empty pendingQ in |mPendingTransactionTable|.
void RemoveEmptyPendingQ();
};
public:
static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped);
+ void RegisterOriginCoalescingKey(nsHttpConnection *, const nsACString &host, int32_t port);
private:
// nsHalfOpenSocket is used to hold the state of an opening TCP socket
// while we wait for it to establish and bind it to a connection
class nsHalfOpenSocket final : public nsIOutputStreamCallback,
public nsITransportEventSink,
@@ -513,26 +508,24 @@ private:
PendingTransactionInfo *pendingTransInfo);
nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *,
bool allowWildCard);
MOZ_MUST_USE nsresult MakeNewConnection(nsConnectionEntry *ent,
PendingTransactionInfo *pendingTransInfo);
- // Manage the preferred spdy connection entry for this address
- nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
- nsConnectionEntry *LookupPreferredHash(nsConnectionEntry *ent);
- void StorePreferredHash(nsConnectionEntry *ent);
- void RemovePreferredHash(nsConnectionEntry *ent);
- nsHttpConnection *GetSpdyPreferredConn(nsConnectionEntry *ent);
- nsDataHashtable<nsCStringHashKey, nsConnectionEntry *> mSpdyPreferredHash;
- nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
- nsHttpConnection *conn,
- nsHttpTransaction *trans);
+ // Manage h2 connection coalescing
+ // The hashtable contains arrays of weak pointers to nsHttpConnections
+ nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr> > mCoalescingHash;
+
+ nsHttpConnection *FindCoalescableConnection(nsConnectionEntry *ent, bool justKidding);
+ nsHttpConnection *FindCoalescableConnectionByHashKey(nsConnectionEntry *ent, const nsCString &key, bool justKidding);
+ void UpdateCoalescingForNewConn(nsHttpConnection *conn, nsConnectionEntry *ent);
+ nsHttpConnection *GetSpdyActiveConn(nsConnectionEntry *ent);
void ProcessSpdyPendingQ(nsConnectionEntry *ent);
void DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
nsConnectionEntry *ent,
nsHttpConnection *conn);
// used to marshall events to the socket transport thread.
MOZ_MUST_USE nsresult PostEvent(nsConnEventHandler handler,
int32_t iparam = 0,
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -210,16 +210,17 @@ nsHttpHandler::nsHttpHandler()
, mHttp2Enabled(true)
, mUseH2Deps(true)
, mEnforceHttp2TlsProfile(true)
, mCoalesceSpdy(true)
, mSpdyPersistentSettings(false)
, mAllowPush(true)
, mEnableAltSvc(false)
, mEnableAltSvcOE(false)
+ , mEnableOriginExtension(false)
, mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
, mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
, mSpdyPushAllowance(32768)
, mSpdyPullAllowance(ASpdySession::kInitialRwin)
, mDefaultSpdyConcurrent(ASpdySession::kDefaultMaxConcurrent)
, mSpdyPingThreshold(PR_SecondsToInterval(58))
, mSpdyPingTimeout(PR_SecondsToInterval(8))
, mConnectTimeout(90000)
@@ -1370,16 +1371,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
if (PREF_CHANGED(HTTP_PREF("altsvc.oe"))) {
rv = prefs->GetBoolPref(HTTP_PREF("altsvc.oe"),
&cVar);
if (NS_SUCCEEDED(rv))
mEnableAltSvcOE = cVar;
}
+ if (PREF_CHANGED(HTTP_PREF("originextension"))) {
+ rv = prefs->GetBoolPref(HTTP_PREF("originextension"),
+ &cVar);
+ if (NS_SUCCEEDED(rv))
+ mEnableOriginExtension = cVar;
+ }
+
if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val);
if (NS_SUCCEEDED(rv)) {
mSpdyPushAllowance =
static_cast<uint32_t>
(clamped(val, 1024, static_cast<int32_t>(ASpdySession::kInitialRwin)));
}
}
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -119,16 +119,17 @@ public:
uint32_t SpdyPushAllowance() { return mSpdyPushAllowance; }
uint32_t SpdyPullAllowance() { return mSpdyPullAllowance; }
uint32_t DefaultSpdyConcurrent() { return mDefaultSpdyConcurrent; }
PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
bool AllowPush() { return mAllowPush; }
bool AllowAltSvc() { return mEnableAltSvc; }
bool AllowAltSvcOE() { return mEnableAltSvcOE; }
+ bool AllowOriginExtension() { return mEnableOriginExtension; }
uint32_t ConnectTimeout() { return mConnectTimeout; }
uint32_t ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
bool CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
bool UseH2Deps() { return mUseH2Deps; }
uint32_t MaxConnectionsPerOrigin() { return mMaxPersistentConnectionsPerServer; }
bool UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
uint16_t RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
@@ -498,16 +499,17 @@ private:
uint32_t mHttp2Enabled : 1;
uint32_t mUseH2Deps : 1;
uint32_t mEnforceHttp2TlsProfile : 1;
uint32_t mCoalesceSpdy : 1;
uint32_t mSpdyPersistentSettings : 1;
uint32_t mAllowPush : 1;
uint32_t mEnableAltSvc : 1;
uint32_t mEnableAltSvcOE : 1;
+ uint32_t mEnableOriginExtension : 1;
// Try to use SPDY features instead of HTTP/1.1 over SSL
SpdyInformation mSpdyInfo;
uint32_t mSpdySendingChunkSize;
uint32_t mSpdySendBufferSize;
uint32_t mSpdyPushAllowance;
uint32_t mSpdyPullAllowance;