Bug 881804 (part 1) - Add support for predictive network actions. r=mcmanus f=honzab sr=biesi

This commit is contained in:
Nicholas Hurley 2013-10-25 14:56:51 -07:00
Родитель 09296164ad
Коммит 14a7d1f5a2
15 изменённых файлов: 3148 добавлений и 16 удалений

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

@ -1374,6 +1374,24 @@ pref("network.dir.format", 2);
// enables the prefetch service (i.e., prefetching of <link rel="next"> URLs).
pref("network.prefetch-next", true);
// enables the predictive service
pref("network.seer.enabled", true);
pref("network.seer.enable-hover-on-ssl", false);
pref("network.seer.page-degradation.day", 0);
pref("network.seer.page-degradation.week", 5);
pref("network.seer.page-degradation.month", 10);
pref("network.seer.page-degradation.year", 25);
pref("network.seer.page-degradation.max", 50);
pref("network.seer.subresource-degradation.day", 1);
pref("network.seer.subresource-degradation.week", 10);
pref("network.seer.subresource-degradation.month", 25);
pref("network.seer.subresource-degradation.year", 50);
pref("network.seer.subresource-degradation.max", 100);
pref("network.seer.preconnect-min-confidence", 90);
pref("network.seer.preresolve-min-confidence", 60);
pref("network.seer.redirect-likely-confidence", 75);
pref("network.seer.max-queue-size", 50);
// The following prefs pertain to the negotiate-auth extension (see bug 17578),
// which provides transparent Kerberos or NTLM authentication using the SPNEGO

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

@ -56,6 +56,8 @@ XPIDL_SOURCES += [
'nsINetUtil.idl',
'nsINetworkLinkService.idl',
'nsINetworkProperties.idl',
'nsINetworkSeer.idl',
'nsINetworkSeerVerifier.idl',
'nsINSSErrorsService.idl',
'nsIParentChannel.idl',
'nsIParentRedirectingChannel.idl',

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

@ -0,0 +1,164 @@
/* vim: set ts=2 sts=2 et sw=2: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIURI;
interface nsILoadContext;
interface nsINetworkSeerVerifier;
typedef unsigned long SeerPredictReason;
typedef unsigned long SeerLearnReason;
/**
* nsINetworkSeer - learn about pages users visit, and allow us to take
* predictive actions upon future visits.
* NOTE: nsINetworkSeer should only be used on the main thread
*/
[scriptable, uuid(884a39a0-a3ed-4855-826a-fabb73ae878d)]
interface nsINetworkSeer : nsISupports
{
/**
* Prediction reasons
*
* PREDICT_LINK - we are being asked to take predictive action because
* the user is hovering over a link.
*
* PREDICT_LOAD - we are being asked to take predictive action because
* the user has initiated a pageload.
*
* PREDICT_STARTUP - we are being asked to take predictive action
* because the browser is starting up.
*/
const SeerPredictReason PREDICT_LINK = 0;
const SeerPredictReason PREDICT_LOAD = 1;
const SeerPredictReason PREDICT_STARTUP = 2;
/**
* Start taking predictive actions
*
* Calling this will cause the seer to (possibly) start
* taking actions such as DNS prefetch and/or TCP preconnect based on
* (1) the host name that we are given, and (2) the reason we are being
* asked to take actions.
*
* @param targetURI - The URI we are being asked to take actions based on.
* @param sourceURI - The URI that is currently loaded. This is so we can
* avoid doing predictive actions for link hover on an HTTPS page (for
* example).
* @param reason - The reason we are being asked to take actions. Can be
* any of the PREDICT_* values above.
* In the case of PREDICT_LINK, targetURI should be the URI of the link
* that is being hovered over, and sourceURI should be the URI of the page
* on which the link appears.
* In the case of PREDICT_LOAD, targetURI should be the URI of the page that
* is being loaded and sourceURI should be null.
* In the case of PREDICT_STARTUP, both targetURI and sourceURI should be
* null.
* @param loadContext - The nsILoadContext of the page load we are predicting
* about.
* @param verifier - An nsINetworkSeerVerifier used in testing to ensure we're
* predicting the way we expect to. Not necessary (or desired) for normal
* operation.
*/
void predict(in nsIURI targetURI,
in nsIURI sourceURI,
in SeerPredictReason reason,
in nsILoadContext loadContext,
in nsINetworkSeerVerifier verifier);
/*
* Reasons we are learning something
*
* LEARN_LOAD_TOPLEVEL - we are learning about the toplevel resource of a
* pageload (NOTE: this should ONLY be used by tests)
*
* LEARN_LOAD_SUBRESOURCE - we are learning a subresource from a pageload
*
* LEARN_LOAD_REDIRECT - we are learning about the re-direct of a URI
*
* LEARN_STARTUP - we are learning about a page loaded during startup
*/
const SeerLearnReason LEARN_LOAD_TOPLEVEL = 0;
const SeerLearnReason LEARN_LOAD_SUBRESOURCE = 1;
const SeerLearnReason LEARN_LOAD_REDIRECT = 2;
const SeerLearnReason LEARN_STARTUP = 3;
/**
* Add to our compendium of knowledge
*
* This adds to our prediction database to make things (hopefully)
* smarter next time we predict something.
*
* @param targetURI - The URI that was loaded that we are keeping track of.
* @param sourceURI - The URI that caused targetURI to be loaded (for page
* loads). This means the DOCUMENT URI.
* @param reason - The reason we are learning this bit of knowledge.
* Reason can be any of the LEARN_* values.
* In the case of LEARN_LOAD_SUBRESOURCE, targetURI should be the URI of a
* subresource of a page, and sourceURI should be the top-level URI.
* In the case of LEARN_LOAD_REDIRECT, targetURI is the NEW URI of a
* top-level resource that was redirected to, and sourceURI is the
* ORIGINAL URI of said top-level resource.
* In the case of LEARN_STARTUP, targetURI should be the URI of a page
* that was loaded immediately after browser startup, and sourceURI should
* be null.
* @param loadContext - The nsILoadContext for the page load that we are
* learning about.
*/
void learn(in nsIURI targetURI,
in nsIURI sourceURI,
in SeerLearnReason reason,
in nsILoadContext loadContext);
/**
* Clear out all our learned knowledge
*
* This removes everything from our database so that any predictions begun
* after this completes will start from a blank slate.
*/
void reset();
};
%{C++
// Wrapper functions to make use of the seer easier and less invasive
class nsIChannel;
class nsIDocument;
class nsILoadContext;
class nsILoadGroup;
class nsINetworkSeerVerifier;
namespace mozilla {
namespace net {
nsresult SeerPredict(nsIURI *targetURI,
nsIURI *sourceURI,
SeerPredictReason reason,
nsILoadContext *loadContext,
nsINetworkSeerVerifier *verifier);
nsresult SeerLearn(nsIURI *targetURI,
nsIURI *sourceURI,
SeerLearnReason reason,
nsILoadContext *loadContext);
nsresult SeerLearn(nsIURI *targetURI,
nsIURI *sourceURI,
SeerLearnReason reason,
nsILoadGroup *loadGroup);
nsresult SeerLearn(nsIURI *targetURI,
nsIURI *sourceURI,
SeerLearnReason reason,
nsIDocument *document);
nsresult SeerLearnRedirect(nsIURI *targetURI,
nsIChannel *channel,
nsILoadContext *loadContext);
} // mozilla::net
} // mozilla
%}

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

@ -0,0 +1,31 @@
/* vim: set ts=2 sts=2 et sw=2: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* nsINetworkSeerVerifier - used for testing the network seer to ensure it
* does what we expect it to do.
*/
#include "nsISupports.idl"
interface nsIURI;
[scriptable, uuid(ea273653-43a8-4632-8b30-4032e0918e8b)]
interface nsINetworkSeerVerifier : nsISupports
{
/**
* Callback for when we do a predictive preconnect
*
* @param uri - The URI that was preconnected to
*/
void onPredictPreconnect(in nsIURI uri);
/**
* Callback for when we do a predictive DNS lookup
*
* @param uri - The URI that was looked up
*/
void onPredictDNS(in nsIURI uri);
};

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

@ -30,3 +30,31 @@ interface nsISpeculativeConnect : nsISupports
};
/**
* This is used to override the default values for various values (documented
* inline) to determine whether or not to actually make a speculative
* connection.
*/
[builtinclass, uuid(2b6d6fb6-ab28-4f4c-af84-bfdbb7866d72)]
interface nsISpeculativeConnectionOverrider : nsISupports
{
/**
* Used to determine the maximum number of unused speculative connections
* we will have open for a host at any one time
*/
[infallible] readonly attribute unsigned long parallelSpeculativeConnectLimit;
/**
* Used to loosen the restrictions nsHttpConnectionMgr::RestrictConnections
* to allow more speculative connections when we're unsure if a host will
* connect via SPDY or not.
*/
[infallible] readonly attribute boolean ignorePossibleSpdyConnections;
/**
* Used to determine if we will ignore the existence of any currently idle
* connections when we decide whether or not to make a speculative
* connection.
*/
[infallible] readonly attribute boolean ignoreIdle;
};

2189
netwerk/base/src/Seer.cpp Normal file

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

207
netwerk/base/src/Seer.h Normal file
Просмотреть файл

@ -0,0 +1,207 @@
/* vim: set ts=2 sts=2 et sw=2: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_Seer_h
#define mozilla_net_Seer_h
#include "nsINetworkSeer.h"
#include "nsCOMPtr.h"
#include "nsIDNSListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsIObserver.h"
#include "nsISpeculativeConnect.h"
#include "nsProxyRelease.h"
#include "mozilla/Mutex.h"
#include "mozilla/storage/StatementCache.h"
#include "mozilla/TimeStamp.h"
class nsIDNSService;
class nsINetworkSeerVerifier;
class nsIThread;
class mozIStorageConnection;
class mozIStorageService;
class mozIStorageStatement;
namespace mozilla {
namespace net {
typedef nsMainThreadPtrHandle<nsINetworkSeerVerifier> SeerVerifierHandle;
class SeerPredictionRunner;
struct SeerTelemetryAccumulators;
class SeerDNSListener;
class Seer : public nsINetworkSeer
, public nsIObserver
, public nsISpeculativeConnectionOverrider
, public nsIInterfaceRequestor
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETWORKSEER
NS_DECL_NSIOBSERVER
NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER
NS_DECL_NSIINTERFACEREQUESTOR
Seer();
virtual ~Seer();
nsresult Init();
void Shutdown();
static nsresult Create(nsISupports *outer, const nsIID& iid, void **result);
private:
friend class SeerPredictionEvent;
friend class SeerLearnEvent;
friend class SeerResetEvent;
friend class SeerPredictionRunner;
friend class SeerDBShutdownRunner;
nsresult EnsureInitStorage();
// This is a proxy for the information we need from an nsIURI
struct UriInfo {
nsAutoCString spec;
nsAutoCString origin;
};
void PredictForLink(nsIURI *targetURI,
nsIURI *sourceURI,
nsINetworkSeerVerifier *verifier);
void PredictForPageload(const UriInfo &dest,
SeerVerifierHandle &verifier,
int stackCount,
TimeStamp &predictStartTime);
void PredictForStartup(SeerVerifierHandle &verifier,
TimeStamp &predictStartTime);
// Whether we're working on a page or an origin
enum QueryType {
QUERY_PAGE = 0,
QUERY_ORIGIN
};
// Holds info from the db about a top-level page or origin
struct TopLevelInfo {
int32_t id;
int32_t loadCount;
PRTime lastLoad;
};
// Holds info from the db about a subresource
struct SubresourceInfo {
int32_t id;
int32_t hitCount;
PRTime lastHit;
};
nsresult ReserveSpaceInQueue();
void FreeSpaceInQueue();
int CalculateGlobalDegradation(PRTime now,
PRTime lastLoad);
int CalculateConfidence(int baseConfidence,
PRTime lastHit,
PRTime lastPossible,
int globalDegradation);
void SetupPrediction(int confidence,
const nsACString &uri,
SeerPredictionRunner *runner);
bool LookupTopLevel(QueryType queryType,
const nsACString &key,
TopLevelInfo &info);
void AddTopLevel(QueryType queryType,
const nsACString &key,
PRTime now);
void UpdateTopLevel(QueryType queryType,
const TopLevelInfo &info,
PRTime now);
bool TryPredict(QueryType queryType,
const TopLevelInfo &info,
PRTime now,
SeerVerifierHandle &verifier,
TimeStamp &predictStartTime);
bool WouldRedirect(const TopLevelInfo &info,
PRTime now,
UriInfo &newUri);
bool LookupSubresource(QueryType queryType,
const int32_t parentId,
const nsACString &key,
SubresourceInfo &info);
void AddSubresource(QueryType queryType,
const int32_t parentId,
const nsACString &key, PRTime now);
void UpdateSubresource(QueryType queryType,
const SubresourceInfo &info,
PRTime now);
void MaybeLearnForStartup(const UriInfo &uri, const PRTime now);
void LearnForToplevel(const UriInfo &uri);
void LearnForSubresource(const UriInfo &targetURI, const UriInfo &sourceURI);
void LearnForRedirect(const UriInfo &targetURI, const UriInfo &sourceURI);
void LearnForStartup(const UriInfo &uri);
void ResetInternal();
// Observer-related stuff
nsresult InstallObserver();
void RemoveObserver();
bool mInitialized;
bool mEnabled;
bool mEnableHoverOnSSL;
int mPageDegradationDay;
int mPageDegradationWeek;
int mPageDegradationMonth;
int mPageDegradationYear;
int mPageDegradationMax;
int mSubresourceDegradationDay;
int mSubresourceDegradationWeek;
int mSubresourceDegradationMonth;
int mSubresourceDegradationYear;
int mSubresourceDegradationMax;
int mPreconnectMinConfidence;
int mPreresolveMinConfidence;
int mRedirectLikelyConfidence;
int32_t mMaxQueueSize;
nsCOMPtr<nsIThread> mIOThread;
nsCOMPtr<nsISpeculativeConnect> mSpeculativeService;
nsCOMPtr<nsIFile> mDBFile;
nsCOMPtr<mozIStorageService> mStorageService;
nsCOMPtr<mozIStorageConnection> mDB;
mozilla::storage::StatementCache<mozIStorageStatement> mStatements;
PRTime mStartupTime;
PRTime mLastStartupTime;
int32_t mStartupCount;
nsCOMPtr<nsIDNSService> mDnsService;
int32_t mQueueSize;
mozilla::Mutex mQueueSizeLock;
nsAutoPtr<SeerTelemetryAccumulators> mAccumulators;
nsRefPtr<SeerDNSListener> mDNSListener;
};
} // ::mozilla::net
} // ::mozilla
#endif // mozilla_net_Seer_h

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

@ -73,6 +73,7 @@ SOURCES += [
'nsURLParsers.cpp',
'ProxyAutoConfig.cpp',
'RedirectChannelRegistrar.cpp',
'Seer.cpp',
'StreamingProtocolService.cpp',
'Tickler.cpp',
]

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

@ -436,6 +436,17 @@
{ 0x8d, 0x17, 0xa2, 0x7e, 0x44, 0xa8, 0x39, 0x3e } \
}
// service implementing nsINetworkSeer
#define NS_NETWORKSEER_CONTRACTID \
"@mozilla.org/network/seer;1"
#define NS_NETWORKSEER_CID \
{ /* {1C218009-A531-46AD-8351-1E7F45D5A3C4} */ \
0x1C218009, \
0xA531, \
0x46AD, \
{ 0x83, 0x51, 0x1E, 0x7F, 0x45, 0xD5, 0xA3, 0xC4 } \
}
/******************************************************************************
* netwerk/cache/ classes
*/

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

@ -36,6 +36,7 @@
#include "nsXULAppAPI.h"
#include "nsCategoryCache.h"
#include "nsIContentSniffer.h"
#include "Seer.h"
#include "nsNetUtil.h"
#include "nsIThreadPool.h"
#include "mozilla/net/NeckoChild.h"
@ -810,6 +811,7 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID);
NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_NETWORKSEER_CID);
static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
{ &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor },
@ -950,6 +952,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
{ &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor },
{ &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor },
{ &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor },
{ &kNS_NETWORKSEER_CID, false, NULL, mozilla::net::Seer::Create },
{ nullptr }
};
@ -1093,6 +1096,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
{ NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID },
{ NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID },
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID },
{ NS_NETWORKSEER_CONTRACTID, &kNS_NETWORKSEER_CID },
{ nullptr }
};

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

@ -318,27 +318,70 @@ nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
return rv;
}
class SpeculativeConnectArgs
{
public:
SpeculativeConnectArgs() { mOverridesOK = false; }
virtual ~SpeculativeConnectArgs() {}
// Added manually so we can use nsRefPtr without inheriting from
// nsISupports
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
public: // intentional!
nsRefPtr<NullHttpTransaction> mTrans;
bool mOverridesOK;
uint32_t mParallelSpeculativeConnectLimit;
bool mIgnoreIdle;
bool mIgnorePossibleSpdyConnections;
// As above, added manually so we can use nsRefPtr without inheriting from
// nsISupports
protected:
::mozilla::ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
NS_IMPL_ADDREF(SpeculativeConnectArgs)
NS_IMPL_RELEASE(SpeculativeConnectArgs)
nsresult
nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
nsIInterfaceRequestor *callbacks,
uint32_t caps)
{
MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!");
LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
ci->HashKey().get()));
nsRefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
// Wrap up the callbacks and the target to ensure they're released on the target
// thread properly.
nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
nsRefPtr<NullHttpTransaction> trans =
new NullHttpTransaction(ci, wrappedCallbacks, caps);
args->mTrans = new NullHttpTransaction(ci, wrappedCallbacks, caps);
nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
do_GetInterface(callbacks);
if (overrider) {
args->mOverridesOK = true;
overrider->GetParallelSpeculativeConnectLimit(
&args->mParallelSpeculativeConnectLimit);
overrider->GetIgnoreIdle(&args->mIgnoreIdle);
overrider->GetIgnorePossibleSpdyConnections(
&args->mIgnorePossibleSpdyConnections);
}
nsresult rv =
PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, trans);
PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
if (NS_SUCCEEDED(rv))
trans.forget();
args.forget();
return rv;
}
@ -1227,7 +1270,8 @@ nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key,
}
bool
nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent)
nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent,
bool ignorePossibleSpdyConnections)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@ -1237,7 +1281,8 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent)
bool doRestrict = ent->mConnInfo->UsingSSL() &&
gHttpHandler->IsSpdyEnabled() &&
(!ent->mTestedSpdy || ent->mUsingSpdy) &&
((!ent->mTestedSpdy && !ignorePossibleSpdyConnections) ||
ent->mUsingSpdy) &&
(ent->mHalfOpens.Length() || ent->mActiveConns.Length());
// If there are no restrictions, we are done
@ -1246,7 +1291,7 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent)
// If the restriction is based on a tcp handshake in progress
// let that connect and then see if it was SPDY or not
if (ent->UnconnectedHalfOpens())
if (ent->UnconnectedHalfOpens() && !ignorePossibleSpdyConnections)
return true;
// There is a concern that a host is using a mix of HTTP/1 and SPDY.
@ -2518,14 +2563,14 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
nsRefPtr<NullHttpTransaction> trans =
dont_AddRef(static_cast<NullHttpTransaction *>(param));
nsRefPtr<SpeculativeConnectArgs> args =
dont_AddRef(static_cast<SpeculativeConnectArgs *>(param));
LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
trans->ConnectionInfo()->HashKey().get()));
args->mTrans->ConnectionInfo()->HashKey().get()));
nsConnectionEntry *ent =
GetOrCreateConnectionEntry(trans->ConnectionInfo());
GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo());
// 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
@ -2534,10 +2579,22 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
if (preferredEntry)
ent = preferredEntry;
if (mNumHalfOpenConns < gHttpHandler->ParallelSpeculativeConnectLimit() &&
!ent->mIdleConns.Length() && !RestrictConnections(ent) &&
!AtActiveConnectionLimit(ent, trans->Caps())) {
CreateTransport(ent, trans, trans->Caps(), true);
uint32_t parallelSpeculativeConnectLimit =
gHttpHandler->ParallelSpeculativeConnectLimit();
bool ignorePossibleSpdyConnections = false;
bool ignoreIdle = false;
if (args->mOverridesOK) {
parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections;
ignoreIdle = args->mIgnoreIdle;
}
if (mNumHalfOpenConns < parallelSpeculativeConnectLimit &&
(ignoreIdle || !ent->mIdleConns.Length()) &&
!RestrictConnections(ent, ignorePossibleSpdyConnections) &&
!AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true);
}
else {
LOG((" Transport not created due to existing connection count\n"));

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

@ -512,7 +512,7 @@ private:
nsresult BuildPipeline(nsConnectionEntry *,
nsAHttpTransaction *,
nsHttpPipeline **);
bool RestrictConnections(nsConnectionEntry *);
bool RestrictConnections(nsConnectionEntry *, bool = false);
nsresult ProcessNewTransaction(nsHttpTransaction *);
nsresult EnsureSocketThreadTarget();
void ClosePersistentConnections(nsConnectionEntry *ent);

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

@ -0,0 +1,295 @@
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
var seer = null;
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var profile = null;
function extract_origin(uri) {
var o = uri.scheme + "://" + uri.asciiHost;
if (uri.port !== -1) {
o = o + ":" + uri.port;
}
return o;
}
var LoadContext = function _loadContext() {
};
LoadContext.prototype = {
usePrivateBrowsing: false,
getInterface: function loadContext_getInterface(iid) {
return this.QueryInterface(iid);
},
QueryInterface: function loadContext_QueryInterface(iid) {
if (iid.equals(Ci.nsINetworkSeerVerifier) ||
iid.equals(Ci.nsILoadContext)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
}
};
var load_context = new LoadContext();
var Verifier = function _verifier(testing, expected_preconnects, expected_preresolves) {
this.verifying = testing;
this.expected_preconnects = expected_preconnects;
this.expected_preresolves = expected_preresolves;
};
Verifier.prototype = {
verifying: null,
expected_preconnects: null,
expected_preresolves: null,
getInterface: function verifier_getInterface(iid) {
return this.QueryInterface(iid);
},
QueryInterface: function verifier_QueryInterface(iid) {
if (iid.equals(Ci.nsINetworkSeerVerifier) ||
iid.equals(Ci.nsISupports)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
maybe_run_next_test: function verifier_maybe_run_next_test() {
if (this.expected_preconnects.length === 0 &&
this.expected_preresolves.length === 0) {
do_check_true(true, "Well this is unexpected...");
run_next_test();
}
},
onPredictPreconnect: function verifier_onPredictPreconnect(uri) {
var origin = extract_origin(uri);
var index = this.expected_preconnects.indexOf(origin);
if (index == -1) {
do_check_true(false, "Got preconnect for unexpected uri " + origin);
} else {
this.expected_preconnects.splice(index, 1);
}
this.maybe_run_next_test();
},
onPredictDNS: function verifier_onPredictDNS(uri) {
var origin = extract_origin(uri);
var index = this.expected_preresolves.indexOf(origin);
if (index == -1) {
do_check_true(false, "Got preresolve for unexpected uri " + origin);
} else {
this.expected_preresolves.splice(index, 1);
}
this.maybe_run_next_test();
}
};
function reset_seer() {
seer.reset();
}
function newURI(s) {
return ios.newURI(s, null, null);
}
function test_link_hover() {
reset_seer();
var uri = newURI("http://localhost:4444/foo/bar");
var referrer = newURI("http://localhost:4444/foo");
var preconns = ["http://localhost:4444"];
var verifier = new Verifier("hover", preconns, []);
seer.predict(uri, referrer, seer.PREDICT_LINK, load_context, verifier);
}
function test_pageload() {
reset_seer();
var toplevel = "http://localhost:4444/index.html";
var subresources = [
"http://localhost:4444/style.css",
"http://localhost:4443/jquery.js",
"http://localhost:4444/image.png"
];
var tluri = newURI(toplevel);
seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context);
var preconns = [];
for (var i = 0; i < subresources.length; i++) {
var sruri = newURI(subresources[i]);
seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context);
preconns.push(extract_origin(sruri));
}
var verifier = new Verifier("pageload", preconns, []);
seer.predict(tluri, null, seer.PREDICT_LOAD, load_context, verifier);
}
function test_redirect() {
reset_seer();
var initial = "http://localhost:4443/redirect";
var target = "http://localhost:4444/index.html";
var subresources = [
"http://localhost:4444/style.css",
"http://localhost:4443/jquery.js",
"http://localhost:4444/image.png"
];
var inituri = newURI(initial);
var targeturi = newURI(target);
seer.learn(inituri, null, seer.LEARN_LOAD_TOPLEVEL, load_context);
seer.learn(targeturi, inituri, seer.LEARN_LOAD_REDIRECT, load_context);
seer.learn(targeturi, null, seer.LEARN_LOAD_TOPLEVEL, load_context);
var preconns = [];
preconns.push(extract_origin(targeturi));
for (var i = 0; i < subresources.length; i++) {
var sruri = newURI(subresources[i]);
seer.learn(sruri, targeturi, seer.LEARN_LOAD_SUBRESOURCE, load_context);
preconns.push(extract_origin(sruri));
}
var verifier = new Verifier("redirect", preconns, []);
seer.predict(inituri, null, seer.PREDICT_LOAD, load_context, verifier);
}
function test_startup() {
reset_seer();
var uris = [
"http://localhost:4444/startup",
"http://localhost:4443/startup"
];
var preconns = [];
for (var i = 0; i < uris.length; i++) {
var uri = newURI(uris[i]);
seer.learn(uri, null, seer.LEARN_STARTUP, load_context);
preconns.push(extract_origin(uri));
}
var verifier = new Verifier("startup", preconns, []);
seer.predict(null, null, seer.PREDICT_STARTUP, load_context, verifier);
}
// A class used to guarantee serialization of SQL queries so we can properly
// update last hit times on subresources to ensure the seer tries to do DNS
// preresolve on them instead of preconnecting
var DnsContinueVerifier = function _dnsContinueVerifier(subresource, tluri, preresolves) {
this.subresource = subresource;
this.tluri = tluri;
this.preresolves = preresolves;
};
DnsContinueVerifier.prototype = {
subresource: null,
tluri: null,
preresolves: null,
getInterface: function _dnsContinueVerifier_getInterface(iid) {
return this.QueryInterface(iid);
},
QueryInterface: function _dnsContinueVerifier_QueryInterface(iid) {
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsINetworkSeerVerifier)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
onPredictPreconnect: function _dnsContinueVerifier_onPredictPreconnect() {
// This means that the seer has learned and done our "checkpoint" prediction
// Now we can get on with the prediction we actually want to test
// tstamp is 10 days older than now - just over 1 week, which will ensure we
// hit our cutoff for dns vs. preconnect. This is all in usec, hence the
// x1000 on the Date object value.
var tstamp = (new Date().valueOf() * 1000) - (10 * 86400 * 1000000);
var dbfile = FileUtils.getFile("ProfD", ["seer.sqlite"]);
var dbconn = Services.storage.openDatabase(dbfile);
// We also need to update hits, since the toplevel has been "loaded" a
// second time (from the prediction that kicked off this callback) to ensure
// that the seer will try to do anything for this subresource.
var stmt = "UPDATE moz_subresources SET last_hit = " + tstamp + ", hits = 2 WHERE uri = '" + this.subresource + "';";
dbconn.executeSimpleSQL(stmt);
dbconn.close();
var verifier = new Verifier("dns", [], this.preresolves);
seer.predict(this.tluri, null, seer.PREDICT_LOAD, load_context, verifier);
},
onPredictDNS: function _dnsContinueVerifier_onPredictDNS() {
do_check_true(false, "Shouldn't have gotten a preresolve prediction here!");
}
};
function test_dns() {
reset_seer();
var toplevel = "http://localhost:4444/index.html";
var subresource = "http://localhost:4443/jquery.js";
var tluri = newURI(toplevel);
seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context);
var sruri = newURI(subresource);
seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context);
var preresolves = [extract_origin(sruri)];
var continue_verifier = new DnsContinueVerifier(subresource, tluri, preresolves);
// Fire off a prediction that will do preconnects so we know when the seer
// thread has gotten to the point where we can update the database manually
seer.predict(tluri, null, seer.PREDICT_LOAD, load_context, continue_verifier);
}
function test_origin() {
reset_seer();
var toplevel = "http://localhost:4444/index.html";
var subresources = [
"http://localhost:4444/style.css",
"http://localhost:4443/jquery.js",
"http://localhost:4444/image.png"
];
var tluri = newURI(toplevel);
seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context);
var preconns = [];
for (var i = 0; i < subresources.length; i++) {
var sruri = newURI(subresources[i]);
seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context);
var origin = extract_origin(sruri);
if (preconns.indexOf(origin) === -1) {
preconns.push(origin);
}
}
var loaduri = newURI("http://localhost:4444/anotherpage.html");
var verifier = new Verifier("origin", preconns, []);
seer.predict(loaduri, null, seer.PREDICT_LOAD, load_context, verifier);
}
var tests = [
test_link_hover,
test_pageload,
test_redirect,
test_startup,
test_dns,
test_origin
];
function run_test() {
tests.forEach(add_test);
profile = do_get_profile();
seer = Cc["@mozilla.org/network/seer;1"].getService(Ci.nsINetworkSeer);
do_register_cleanup(reset_seer);
run_next_test();
}

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

@ -295,3 +295,8 @@ skip-if = os == "android"
[test_addr_in_use_error.js]
[test_about_networking.js]
[test_ping_aboutnetworking.js]
[test_seer.js]
# Android version detection w/in gecko does not work right on infra, so we just
# disable this test on all android versions, even though it's enabled on 2.3+ in
# the wild.
skip-if = os == "android"

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

@ -1759,6 +1759,126 @@
"extended_statistics_ok": true,
"description": "Time for an unsuccessful DNS OS resolution (msec)"
},
"SEER_PREDICT_ATTEMPTS": {
"kind": "exponential",
"high": "1000 * 1000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "Number of times nsINetworkSeer::Predict is called and attempts to predict"
},
"SEER_LEARN_ATTEMPTS": {
"kind": "exponential",
"high": "1000 * 1000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "Number of times nsINetworkSeer::Learn is called and attempts to learn"
},
"SEER_PREDICT_FULL_QUEUE": {
"kind": "exponential",
"high": "60000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "Number of times nsINetworkSeer::Predict doesn't continue because the queue is full"
},
"SEER_LEARN_FULL_QUEUE": {
"kind": "exponential",
"high": "60000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "Number of times nsINetworkSeer::Learn doesn't continue because the queue is full"
},
"SEER_PREDICT_WAIT_TIME": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Amount of time a predict event waits in the queue (ms)"
},
"SEER_PREDICT_WORK_TIME": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Amount of time spent doing the work for predict (ms)"
},
"SEER_LEARN_WAIT_TIME": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Amount of time a learn event waits in the queue (ms)"
},
"SEER_LEARN_WORK_TIME": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Amount of time spent doing the work for learn (ms)"
},
"SEER_TOTAL_PREDICTIONS": {
"kind": "exponential",
"high": "1000 * 1000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "How many actual predictions (preresolves, preconnects, ...) happen"
},
"SEER_TOTAL_PRECONNECTS": {
"kind": "exponential",
"high": "1000 * 1000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "How many actual preconnects happen"
},
"SEER_TOTAL_PRERESOLVES": {
"kind": "exponential",
"high": "1000 * 1000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "How many actual preresolves happen"
},
"SEER_PREDICTIONS_CALCULATED": {
"kind": "exponential",
"high": "1000 * 1000",
"n_buckets": 50,
"extended_statistics_ok": true,
"description": "How many prediction calculations are performed"
},
"SEER_GLOBAL_DEGRADATION": {
"kind": "linear",
"high": "100",
"n_buckets": 50,
"description": "The global degradation calculated"
},
"SEER_SUBRESOURCE_DEGRADATION": {
"kind": "linear",
"high": "100",
"n_buckets": 50,
"description": "The degradation calculated for a subresource"
},
"SEER_BASE_CONFIDENCE": {
"kind": "linear",
"high": "100",
"n_buckets": 50,
"description": "The base confidence calculated for a subresource"
},
"SEER_CONFIDENCE": {
"kind": "linear",
"high": "100",
"n_buckets": 50,
"description": "The final confidence calculated for a subresource"
},
"SEER_PREDICT_TIME_TO_ACTION": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"description": "How long it takes from the time Predict() is called to the time we take action"
},
"SEER_PREDICT_TIME_TO_INACTION": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"description": "How long it takes from the time Predict() is called to the time we figure out there's nothing to do"
},
"FIND_PLUGINS": {
"kind": "exponential",
"high": "3000",