michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:set ts=4 sw=4 sts=4 et cin: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "nsHttp.h" michael@0: #include "nsHttpHandler.h" michael@0: #include "nsHttpChannel.h" michael@0: #include "nsHttpAuthCache.h" michael@0: #include "nsStandardURL.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMNavigator.h" michael@0: #include "nsIMozNavigatorNetwork.h" michael@0: #include "nsINetworkProperties.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsIStandardURL.h" michael@0: #include "LoadContextInfo.h" michael@0: #include "nsCategoryManagerUtils.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefLocalizedString.h" michael@0: #include "nsISocketProviderService.h" michael@0: #include "nsISocketProvider.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsNetCID.h" michael@0: #include "prprf.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsAsyncRedirectVerifyHelper.h" michael@0: #include "nsSocketTransportService2.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "ASpdySession.h" michael@0: #include "mozIApplicationClearPrivateDataParams.h" michael@0: #include "EventTokenBucket.h" michael@0: #include "Tickler.h" michael@0: #include "nsIXULAppInfo.h" michael@0: #include "nsICacheSession.h" michael@0: #include "nsICookieService.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsISiteSecurityService.h" michael@0: #include "nsIStreamConverterService.h" michael@0: #include "nsITimer.h" michael@0: #include "nsCRT.h" michael@0: #include "SpdyZlibReporter.h" michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsIParentalControlsService.h" michael@0: michael@0: #include "mozilla/net/NeckoChild.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(XP_WIN) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(XP_MACOSX) michael@0: #include michael@0: #include "nsCocoaFeatures.h" michael@0: #endif michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: #include "mozilla/net/HttpChannelChild.h" michael@0: michael@0: michael@0: #ifdef DEBUG michael@0: // defined by the socket transport service while active michael@0: extern PRThread *gSocketThread; michael@0: #endif michael@0: michael@0: #define UA_PREF_PREFIX "general.useragent." michael@0: #ifdef XP_WIN michael@0: #define UA_SPARE_PLATFORM michael@0: #endif michael@0: michael@0: #define HTTP_PREF_PREFIX "network.http." michael@0: #define INTL_ACCEPT_LANGUAGES "intl.accept_languages" michael@0: #define BROWSER_PREF_PREFIX "browser.cache." michael@0: #define DONOTTRACK_HEADER_ENABLED "privacy.donottrackheader.enabled" michael@0: #define DONOTTRACK_HEADER_VALUE "privacy.donottrackheader.value" michael@0: #define DONOTTRACK_VALUE_UNSET 2 michael@0: #define TELEMETRY_ENABLED "toolkit.telemetry.enabled" michael@0: #define ALLOW_EXPERIMENTS "network.allow-experiments" michael@0: #define SAFE_HINT_HEADER_VALUE "safeHint.enabled" michael@0: michael@0: #define UA_PREF(_pref) UA_PREF_PREFIX _pref michael@0: #define HTTP_PREF(_pref) HTTP_PREF_PREFIX _pref michael@0: #define BROWSER_PREF(_pref) BROWSER_PREF_PREFIX _pref michael@0: michael@0: #define NS_HTTP_PROTOCOL_FLAGS (URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_LOADABLE_BY_ANYONE) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: static nsresult michael@0: NewURI(const nsACString &aSpec, michael@0: const char *aCharset, michael@0: nsIURI *aBaseURI, michael@0: int32_t aDefaultPort, michael@0: nsIURI **aURI) michael@0: { michael@0: nsStandardURL *url = new nsStandardURL(); michael@0: if (!url) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(url); michael@0: michael@0: nsresult rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, michael@0: aDefaultPort, aSpec, aCharset, aBaseURI); michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(url); michael@0: return rv; michael@0: } michael@0: michael@0: *aURI = url; // no QI needed michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsHttpHandler *gHttpHandler = nullptr; michael@0: michael@0: nsHttpHandler::nsHttpHandler() michael@0: : mConnMgr(nullptr) michael@0: , mHttpVersion(NS_HTTP_VERSION_1_1) michael@0: , mProxyHttpVersion(NS_HTTP_VERSION_1_1) michael@0: , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE) michael@0: , mReferrerLevel(0xff) // by default we always send a referrer michael@0: , mSpoofReferrerSource(false) michael@0: , mReferrerTrimmingPolicy(0) michael@0: , mReferrerXOriginPolicy(0) michael@0: , mFastFallbackToIPv4(false) michael@0: , mProxyPipelining(true) michael@0: , mIdleTimeout(PR_SecondsToInterval(10)) michael@0: , mSpdyTimeout(PR_SecondsToInterval(180)) michael@0: , mResponseTimeout(300) michael@0: , mResponseTimeoutEnabled(false) michael@0: , mMaxRequestAttempts(10) michael@0: , mMaxRequestDelay(10) michael@0: , mIdleSynTimeout(250) michael@0: , mPipeliningEnabled(false) michael@0: , mMaxConnections(24) michael@0: , mMaxPersistentConnectionsPerServer(2) michael@0: , mMaxPersistentConnectionsPerProxy(4) michael@0: , mMaxPipelinedRequests(32) michael@0: , mMaxOptimisticPipelinedRequests(4) michael@0: , mPipelineAggressive(false) michael@0: , mMaxPipelineObjectSize(300000) michael@0: , mPipelineRescheduleOnTimeout(true) michael@0: , mPipelineRescheduleTimeout(PR_MillisecondsToInterval(1500)) michael@0: , mPipelineReadTimeout(PR_MillisecondsToInterval(30000)) michael@0: , mRedirectionLimit(10) michael@0: , mPhishyUserPassLength(1) michael@0: , mQoSBits(0x00) michael@0: , mPipeliningOverSSL(false) michael@0: , mEnforceAssocReq(false) michael@0: , mLastUniqueID(NowInSeconds()) michael@0: , mSessionStartTime(0) michael@0: , mLegacyAppName("Mozilla") michael@0: , mLegacyAppVersion("5.0") michael@0: , mProduct("Gecko") michael@0: , mUserAgentIsDirty(true) michael@0: , mUseCache(true) michael@0: , mPromptTempRedirect(true) michael@0: , mSendSecureXSiteReferrer(true) michael@0: , mEnablePersistentHttpsCaching(false) michael@0: , mDoNotTrackEnabled(false) michael@0: , mDoNotTrackValue(1) michael@0: , mSafeHintEnabled(false) michael@0: , mParentalControlEnabled(false) michael@0: , mTelemetryEnabled(false) michael@0: , mAllowExperiments(true) michael@0: , mHandlerActive(false) michael@0: , mEnableSpdy(false) michael@0: , mSpdyV3(true) michael@0: , mSpdyV31(true) michael@0: , mHttp2DraftEnabled(true) michael@0: , mEnforceHttp2TlsProfile(true) michael@0: , mCoalesceSpdy(true) michael@0: , mSpdyPersistentSettings(false) michael@0: , mAllowPush(true) michael@0: , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize) michael@0: , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize) michael@0: , mSpdyPushAllowance(32768) michael@0: , mSpdyPingThreshold(PR_SecondsToInterval(58)) michael@0: , mSpdyPingTimeout(PR_SecondsToInterval(8)) michael@0: , mConnectTimeout(90000) michael@0: , mBypassCacheLockThreshold(250.0) michael@0: , mParallelSpeculativeConnectLimit(6) michael@0: , mRequestTokenBucketEnabled(true) michael@0: , mRequestTokenBucketMinParallelism(6) michael@0: , mRequestTokenBucketHz(100) michael@0: , mRequestTokenBucketBurst(32) michael@0: , mTCPKeepaliveShortLivedEnabled(false) michael@0: , mTCPKeepaliveShortLivedTimeS(60) michael@0: , mTCPKeepaliveShortLivedIdleTimeS(10) michael@0: , mTCPKeepaliveLongLivedEnabled(false) michael@0: , mTCPKeepaliveLongLivedIdleTimeS(600) michael@0: { michael@0: #if defined(PR_LOGGING) michael@0: gHttpLog = PR_NewLogModule("nsHttp"); michael@0: #endif michael@0: michael@0: LOG(("Creating nsHttpHandler [this=%p].\n", this)); michael@0: michael@0: RegisterStrongMemoryReporter(new SpdyZlibReporter()); michael@0: michael@0: MOZ_ASSERT(!gHttpHandler, "HTTP handler already created!"); michael@0: gHttpHandler = this; michael@0: } michael@0: michael@0: nsHttpHandler::~nsHttpHandler() michael@0: { michael@0: LOG(("Deleting nsHttpHandler [this=%p]\n", this)); michael@0: michael@0: // make sure the connection manager is shutdown michael@0: if (mConnMgr) { michael@0: mConnMgr->Shutdown(); michael@0: NS_RELEASE(mConnMgr); michael@0: } michael@0: michael@0: // Note: don't call NeckoChild::DestroyNeckoChild() here, as it's too late michael@0: // and it'll segfault. NeckoChild will get cleaned up by process exit. michael@0: michael@0: nsHttp::DestroyAtomTable(); michael@0: if (mPipelineTestTimer) { michael@0: mPipelineTestTimer->Cancel(); michael@0: mPipelineTestTimer = nullptr; michael@0: } michael@0: michael@0: gHttpHandler = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::Init() michael@0: { michael@0: nsresult rv; michael@0: michael@0: LOG(("nsHttpHandler::Init\n")); michael@0: michael@0: rv = nsHttp::CreateAtomTable(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr service = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("unable to continue without io service"); michael@0: return rv; michael@0: } michael@0: mIOService = new nsMainThreadPtrHolder(service); michael@0: michael@0: if (IsNeckoChild()) michael@0: NeckoChild::InitNeckoChild(); michael@0: michael@0: InitUserAgentComponents(); michael@0: michael@0: // monitor some preference changes michael@0: nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (prefBranch) { michael@0: prefBranch->AddObserver(HTTP_PREF_PREFIX, this, true); michael@0: prefBranch->AddObserver(UA_PREF_PREFIX, this, true); michael@0: prefBranch->AddObserver(INTL_ACCEPT_LANGUAGES, this, true); michael@0: prefBranch->AddObserver(BROWSER_PREF("disk_cache_ssl"), this, true); michael@0: prefBranch->AddObserver(DONOTTRACK_HEADER_ENABLED, this, true); michael@0: prefBranch->AddObserver(DONOTTRACK_HEADER_VALUE, this, true); michael@0: prefBranch->AddObserver(TELEMETRY_ENABLED, this, true); michael@0: prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.short_lived_connections"), this, true); michael@0: prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.long_lived_connections"), this, true); michael@0: prefBranch->AddObserver(SAFE_HINT_HEADER_VALUE, this, true); michael@0: PrefsChanged(prefBranch, nullptr); michael@0: } michael@0: michael@0: mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION); michael@0: michael@0: mCompatFirefox.AssignLiteral("Firefox/" MOZILLA_UAVERSION); michael@0: michael@0: nsCOMPtr appInfo = michael@0: do_GetService("@mozilla.org/xre/app-info;1"); michael@0: michael@0: mAppName.AssignLiteral(MOZ_APP_UA_NAME); michael@0: if (mAppName.Length() == 0 && appInfo) { michael@0: // Try to get the UA name from appInfo, falling back to the name michael@0: appInfo->GetUAName(mAppName); michael@0: if (mAppName.Length() == 0) { michael@0: appInfo->GetName(mAppName); michael@0: } michael@0: appInfo->GetVersion(mAppVersion); michael@0: mAppName.StripChars(" ()<>@,;:\\\"/[]?={}"); michael@0: } else { michael@0: mAppVersion.AssignLiteral(MOZ_APP_UA_VERSION); michael@0: } michael@0: michael@0: mSessionStartTime = NowInSeconds(); michael@0: mHandlerActive = true; michael@0: michael@0: rv = mAuthCache.Init(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mPrivateAuthCache.Init(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = InitConnectionMgr(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: #ifdef ANDROID michael@0: mProductSub.AssignLiteral(MOZILLA_UAVERSION); michael@0: #else michael@0: mProductSub.AssignLiteral("20100101"); michael@0: #endif michael@0: michael@0: #if DEBUG michael@0: // dump user agent prefs michael@0: LOG(("> legacy-app-name = %s\n", mLegacyAppName.get())); michael@0: LOG(("> legacy-app-version = %s\n", mLegacyAppVersion.get())); michael@0: LOG(("> platform = %s\n", mPlatform.get())); michael@0: LOG(("> oscpu = %s\n", mOscpu.get())); michael@0: LOG(("> misc = %s\n", mMisc.get())); michael@0: LOG(("> product = %s\n", mProduct.get())); michael@0: LOG(("> product-sub = %s\n", mProductSub.get())); michael@0: LOG(("> app-name = %s\n", mAppName.get())); michael@0: LOG(("> app-version = %s\n", mAppVersion.get())); michael@0: LOG(("> compat-firefox = %s\n", mCompatFirefox.get())); michael@0: LOG(("> user-agent = %s\n", UserAgent().get())); michael@0: #endif michael@0: michael@0: // Startup the http category michael@0: // Bring alive the objects in the http-protocol-startup category michael@0: NS_CreateServicesFromCategory(NS_HTTP_STARTUP_CATEGORY, michael@0: static_cast(static_cast(this)), michael@0: NS_HTTP_STARTUP_TOPIC); michael@0: michael@0: nsCOMPtr obsService = services::GetObserverService(); michael@0: mObserverService = new nsMainThreadPtrHolder(obsService); michael@0: if (mObserverService) { michael@0: mObserverService->AddObserver(this, "profile-change-net-teardown", true); michael@0: mObserverService->AddObserver(this, "profile-change-net-restore", true); michael@0: mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true); michael@0: mObserverService->AddObserver(this, "net:clear-active-logins", true); michael@0: mObserverService->AddObserver(this, "net:prune-dead-connections", true); michael@0: mObserverService->AddObserver(this, "net:prune-all-connections", true); michael@0: mObserverService->AddObserver(this, "net:failed-to-process-uri-content", true); michael@0: mObserverService->AddObserver(this, "last-pb-context-exited", true); michael@0: } michael@0: michael@0: MakeNewRequestTokenBucket(); michael@0: mWifiTickler = new Tickler(); michael@0: if (NS_FAILED(mWifiTickler->Init())) michael@0: mWifiTickler = nullptr; michael@0: michael@0: nsCOMPtr pc = do_CreateInstance("@mozilla.org/parental-controls-service;1"); michael@0: if (pc) { michael@0: pc->GetParentalControlsEnabled(&mParentalControlEnabled); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsHttpHandler::MakeNewRequestTokenBucket() michael@0: { michael@0: if (!mConnMgr) michael@0: return; michael@0: michael@0: nsRefPtr tokenBucket = michael@0: new EventTokenBucket(RequestTokenBucketHz(), michael@0: RequestTokenBucketBurst()); michael@0: mConnMgr->UpdateRequestTokenBucket(tokenBucket); michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::InitConnectionMgr() michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!mConnMgr) { michael@0: mConnMgr = new nsHttpConnectionMgr(); michael@0: if (!mConnMgr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(mConnMgr); michael@0: } michael@0: michael@0: rv = mConnMgr->Init(mMaxConnections, michael@0: mMaxPersistentConnectionsPerServer, michael@0: mMaxPersistentConnectionsPerProxy, michael@0: mMaxRequestDelay, michael@0: mMaxPipelinedRequests, michael@0: mMaxOptimisticPipelinedRequests); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Add the "User-Agent" header michael@0: rv = request->SetHeader(nsHttp::User_Agent, UserAgent()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // MIME based content negotiation lives! michael@0: // Add the "Accept" header michael@0: rv = request->SetHeader(nsHttp::Accept, mAccept); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Add the "Accept-Language" header michael@0: if (!mAcceptLanguages.IsEmpty()) { michael@0: // Add the "Accept-Language" header michael@0: rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // Add the "Accept-Encoding" header michael@0: rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Add the "Do-Not-Track" header michael@0: if (mDoNotTrackEnabled) { michael@0: rv = request->SetHeader(nsHttp::DoNotTrack, michael@0: nsPrintfCString("%d", mDoNotTrackValue)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // add the "Send Hint" header michael@0: if (mSafeHintEnabled || mParentalControlEnabled) { michael@0: rv = request->SetHeader(nsHttp::Prefer, NS_LITERAL_CSTRING("safe")); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::AddConnectionHeader(nsHttpHeaderArray *request, michael@0: uint32_t caps) michael@0: { michael@0: // RFC2616 section 19.6.2 states that the "Connection: keep-alive" michael@0: // and "Keep-alive" request headers should not be sent by HTTP/1.1 michael@0: // user-agents. But this is not a problem in practice, and the michael@0: // alternative proxy-connection is worse. see 570283 michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(close, "close"); michael@0: NS_NAMED_LITERAL_CSTRING(keepAlive, "keep-alive"); michael@0: michael@0: const nsACString *connectionType = &close; michael@0: if (caps & NS_HTTP_ALLOW_KEEPALIVE) { michael@0: connectionType = &keepAlive; michael@0: } michael@0: michael@0: return request->SetHeader(nsHttp::Connection, *connectionType); michael@0: } michael@0: michael@0: bool michael@0: nsHttpHandler::IsAcceptableEncoding(const char *enc) michael@0: { michael@0: if (!enc) michael@0: return false; michael@0: michael@0: // HTTP 1.1 allows servers to send x-gzip and x-compress instead michael@0: // of gzip and compress, for example. So, we'll always strip off michael@0: // an "x-" prefix before matching the encoding to one we claim michael@0: // to accept. michael@0: if (!PL_strncasecmp(enc, "x-", 2)) michael@0: enc += 2; michael@0: michael@0: // gzip and deflate are inherently acceptable in modern HTTP - always michael@0: // process them if a stream converter can also be found. michael@0: if (!PL_strcasecmp(enc, "gzip") || !PL_strcasecmp(enc, "deflate")) michael@0: return true; michael@0: michael@0: return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result) michael@0: { michael@0: if (!mStreamConvSvc) { michael@0: nsresult rv; michael@0: nsCOMPtr service = michael@0: do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: mStreamConvSvc = new nsMainThreadPtrHolder(service); michael@0: } michael@0: *result = mStreamConvSvc; michael@0: NS_ADDREF(*result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsISiteSecurityService* michael@0: nsHttpHandler::GetSSService() michael@0: { michael@0: if (!mSSService) { michael@0: nsCOMPtr service = do_GetService(NS_SSSERVICE_CONTRACTID); michael@0: mSSService = new nsMainThreadPtrHolder(service); michael@0: } michael@0: return mSSService; michael@0: } michael@0: michael@0: nsICookieService * michael@0: nsHttpHandler::GetCookieService() michael@0: { michael@0: if (!mCookieService) { michael@0: nsCOMPtr service = do_GetService(NS_COOKIESERVICE_CONTRACTID); michael@0: mCookieService = new nsMainThreadPtrHolder(service); michael@0: } michael@0: return mCookieService; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::GetIOService(nsIIOService** result) michael@0: { michael@0: NS_ADDREF(*result = mIOService); michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t michael@0: nsHttpHandler::Get32BitsOfPseudoRandom() michael@0: { michael@0: // only confirm rand seeding on socket thread michael@0: MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); michael@0: michael@0: // rand() provides different amounts of PRNG on different platforms. michael@0: // 15 or 31 bits are common amounts. michael@0: michael@0: PR_STATIC_ASSERT(RAND_MAX >= 0xfff); michael@0: michael@0: #if RAND_MAX < 0xffffU michael@0: return ((uint16_t) rand() << 20) | michael@0: (((uint16_t) rand() & 0xfff) << 8) | michael@0: ((uint16_t) rand() & 0xff); michael@0: #elif RAND_MAX < 0xffffffffU michael@0: return ((uint16_t) rand() << 16) | ((uint16_t) rand() & 0xffff); michael@0: #else michael@0: return (uint32_t) rand(); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsHttpHandler::NotifyObservers(nsIHttpChannel *chan, const char *event) michael@0: { michael@0: LOG(("nsHttpHandler::NotifyObservers [chan=%x event=\"%s\"]\n", chan, event)); michael@0: if (mObserverService) michael@0: mObserverService->NotifyObservers(chan, event, nullptr); michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, michael@0: uint32_t flags) michael@0: { michael@0: // TODO E10S This helper has to be initialized on the other process michael@0: nsRefPtr redirectCallbackHelper = michael@0: new nsAsyncRedirectVerifyHelper(); michael@0: michael@0: return redirectCallbackHelper->Init(oldChan, newChan, flags); michael@0: } michael@0: michael@0: /* static */ nsresult michael@0: nsHttpHandler::GenerateHostPort(const nsCString& host, int32_t port, michael@0: nsCString& hostLine) michael@0: { michael@0: return NS_GenerateHostPort(host, port, hostLine); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: const nsAFlatCString & michael@0: nsHttpHandler::UserAgent() michael@0: { michael@0: if (mUserAgentOverride) { michael@0: LOG(("using general.useragent.override : %s\n", mUserAgentOverride.get())); michael@0: return mUserAgentOverride; michael@0: } michael@0: michael@0: if (mUserAgentIsDirty) { michael@0: BuildUserAgent(); michael@0: mUserAgentIsDirty = false; michael@0: } michael@0: michael@0: return mUserAgent; michael@0: } michael@0: michael@0: void michael@0: nsHttpHandler::BuildUserAgent() michael@0: { michael@0: LOG(("nsHttpHandler::BuildUserAgent\n")); michael@0: michael@0: MOZ_ASSERT(!mLegacyAppName.IsEmpty() && michael@0: !mLegacyAppVersion.IsEmpty(), michael@0: "HTTP cannot send practical requests without this much"); michael@0: michael@0: // preallocate to worst-case size, which should always be better michael@0: // than if we didn't preallocate at all. michael@0: mUserAgent.SetCapacity(mLegacyAppName.Length() + michael@0: mLegacyAppVersion.Length() + michael@0: #ifndef UA_SPARE_PLATFORM michael@0: mPlatform.Length() + michael@0: #endif michael@0: mOscpu.Length() + michael@0: mMisc.Length() + michael@0: mProduct.Length() + michael@0: mProductSub.Length() + michael@0: mAppName.Length() + michael@0: mAppVersion.Length() + michael@0: mCompatFirefox.Length() + michael@0: mCompatDevice.Length() + michael@0: 13); michael@0: michael@0: // Application portion michael@0: mUserAgent.Assign(mLegacyAppName); michael@0: mUserAgent += '/'; michael@0: mUserAgent += mLegacyAppVersion; michael@0: mUserAgent += ' '; michael@0: michael@0: // Application comment michael@0: mUserAgent += '('; michael@0: #ifndef UA_SPARE_PLATFORM michael@0: if (!mPlatform.IsEmpty()) { michael@0: mUserAgent += mPlatform; michael@0: mUserAgent.AppendLiteral("; "); michael@0: } michael@0: #endif michael@0: if (!mCompatDevice.IsEmpty()) { michael@0: mUserAgent += mCompatDevice; michael@0: mUserAgent.AppendLiteral("; "); michael@0: } michael@0: else if (!mOscpu.IsEmpty()) { michael@0: mUserAgent += mOscpu; michael@0: mUserAgent.AppendLiteral("; "); michael@0: } michael@0: mUserAgent += mMisc; michael@0: mUserAgent += ')'; michael@0: michael@0: // Product portion michael@0: mUserAgent += ' '; michael@0: mUserAgent += mProduct; michael@0: mUserAgent += '/'; michael@0: mUserAgent += mProductSub; michael@0: michael@0: bool isFirefox = mAppName.EqualsLiteral("Firefox"); michael@0: if (isFirefox || mCompatFirefoxEnabled) { michael@0: // "Firefox/x.y" (compatibility) app token michael@0: mUserAgent += ' '; michael@0: mUserAgent += mCompatFirefox; michael@0: } michael@0: if (!isFirefox) { michael@0: // App portion michael@0: mUserAgent += ' '; michael@0: mUserAgent += mAppName; michael@0: mUserAgent += '/'; michael@0: mUserAgent += mAppVersion; michael@0: } michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: #define WNT_BASE "Windows NT %ld.%ld" michael@0: #define W64_PREFIX "; Win64" michael@0: #endif michael@0: michael@0: void michael@0: nsHttpHandler::InitUserAgentComponents() michael@0: { michael@0: #ifndef MOZ_UA_OS_AGNOSTIC michael@0: // Gather platform. michael@0: mPlatform.AssignLiteral( michael@0: #if defined(ANDROID) michael@0: "Android" michael@0: #elif defined(XP_WIN) michael@0: "Windows" michael@0: #elif defined(XP_MACOSX) michael@0: "Macintosh" michael@0: #elif defined(MOZ_X11) michael@0: "X11" michael@0: #endif michael@0: ); michael@0: #endif michael@0: michael@0: #if defined(ANDROID) || defined(MOZ_B2G) michael@0: nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); michael@0: MOZ_ASSERT(infoService, "Could not find a system info service"); michael@0: michael@0: bool isTablet; michael@0: nsresult rv = infoService->GetPropertyAsBool(NS_LITERAL_STRING("tablet"), &isTablet); michael@0: if (NS_SUCCEEDED(rv) && isTablet) michael@0: mCompatDevice.AssignLiteral("Tablet"); michael@0: else michael@0: mCompatDevice.AssignLiteral("Mobile"); michael@0: #endif michael@0: michael@0: #ifndef MOZ_UA_OS_AGNOSTIC michael@0: // Gather OS/CPU. michael@0: #if defined(XP_WIN) michael@0: OSVERSIONINFO info = { sizeof(OSVERSIONINFO) }; michael@0: #pragma warning(push) michael@0: #pragma warning(disable:4996) michael@0: if (GetVersionEx(&info)) { michael@0: #pragma warning(pop) michael@0: const char *format; michael@0: #if defined _M_IA64 michael@0: format = WNT_BASE W64_PREFIX "; IA64"; michael@0: #elif defined _M_X64 || defined _M_AMD64 michael@0: format = WNT_BASE W64_PREFIX "; x64"; michael@0: #else michael@0: BOOL isWow64 = FALSE; michael@0: if (!IsWow64Process(GetCurrentProcess(), &isWow64)) { michael@0: isWow64 = FALSE; michael@0: } michael@0: format = isWow64 michael@0: ? WNT_BASE "; WOW64" michael@0: : WNT_BASE; michael@0: #endif michael@0: char *buf = PR_smprintf(format, michael@0: info.dwMajorVersion, michael@0: info.dwMinorVersion); michael@0: if (buf) { michael@0: mOscpu = buf; michael@0: PR_smprintf_free(buf); michael@0: } michael@0: } michael@0: #elif defined (XP_MACOSX) michael@0: #if defined(__ppc__) michael@0: mOscpu.AssignLiteral("PPC Mac OS X"); michael@0: #elif defined(__i386__) || defined(__x86_64__) michael@0: mOscpu.AssignLiteral("Intel Mac OS X"); michael@0: #endif michael@0: SInt32 majorVersion = nsCocoaFeatures::OSXVersionMajor(); michael@0: SInt32 minorVersion = nsCocoaFeatures::OSXVersionMinor(); michael@0: mOscpu += nsPrintfCString(" %d.%d", majorVersion, minorVersion); michael@0: #elif defined (XP_UNIX) michael@0: struct utsname name; michael@0: michael@0: int ret = uname(&name); michael@0: if (ret >= 0) { michael@0: nsAutoCString buf; michael@0: buf = (char*)name.sysname; michael@0: michael@0: if (strcmp(name.machine, "x86_64") == 0 && michael@0: sizeof(void *) == sizeof(int32_t)) { michael@0: // We're running 32-bit code on x86_64. Make this browser michael@0: // look like it's running on i686 hardware, but append " michael@0: // (x86_64)" to the end of the oscpu identifier to be able michael@0: // to differentiate this from someone running 64-bit code michael@0: // on x86_64.. michael@0: michael@0: buf += " i686 on x86_64"; michael@0: } else { michael@0: buf += ' '; michael@0: michael@0: #ifdef AIX michael@0: // AIX uname returns machine specific info in the uname.machine michael@0: // field and does not return the cpu type like other platforms. michael@0: // We use the AIX version and release numbers instead. michael@0: buf += (char*)name.version; michael@0: buf += '.'; michael@0: buf += (char*)name.release; michael@0: #else michael@0: buf += (char*)name.machine; michael@0: #endif michael@0: } michael@0: michael@0: mOscpu.Assign(buf); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: mUserAgentIsDirty = true; michael@0: } michael@0: michael@0: uint32_t michael@0: nsHttpHandler::MaxSocketCount() michael@0: { michael@0: PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce, michael@0: nsSocketTransportService::DiscoverMaxCount); michael@0: // Don't use the full max count because sockets can be held in michael@0: // the persistent connection pool for a long time and that could michael@0: // starve other users. michael@0: michael@0: uint32_t maxCount = nsSocketTransportService::gMaxCount; michael@0: if (maxCount <= 8) michael@0: maxCount = 1; michael@0: else michael@0: maxCount -= 8; michael@0: michael@0: return maxCount; michael@0: } michael@0: michael@0: void michael@0: nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: int32_t val; michael@0: michael@0: LOG(("nsHttpHandler::PrefsChanged [pref=%s]\n", pref)); michael@0: michael@0: #define PREF_CHANGED(p) ((pref == nullptr) || !PL_strcmp(pref, p)) michael@0: #define MULTI_PREF_CHANGED(p) \ michael@0: ((pref == nullptr) || !PL_strncmp(pref, p, sizeof(p) - 1)) michael@0: michael@0: // michael@0: // UA components michael@0: // michael@0: michael@0: bool cVar = false; michael@0: michael@0: if (PREF_CHANGED(UA_PREF("compatMode.firefox"))) { michael@0: rv = prefs->GetBoolPref(UA_PREF("compatMode.firefox"), &cVar); michael@0: mCompatFirefoxEnabled = (NS_SUCCEEDED(rv) && cVar); michael@0: mUserAgentIsDirty = true; michael@0: } michael@0: michael@0: // general.useragent.override michael@0: if (PREF_CHANGED(UA_PREF("override"))) { michael@0: prefs->GetCharPref(UA_PREF("override"), michael@0: getter_Copies(mUserAgentOverride)); michael@0: mUserAgentIsDirty = true; michael@0: } michael@0: michael@0: // michael@0: // HTTP options michael@0: // michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("keep-alive.timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("keep-alive.timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mIdleTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff)); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("request.max-attempts"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("request.max-attempts"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mMaxRequestAttempts = (uint16_t) clamped(val, 1, 0xffff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("request.max-start-delay"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("request.max-start-delay"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mMaxRequestDelay = (uint16_t) clamped(val, 0, 0xffff); michael@0: if (mConnMgr) michael@0: mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_REQUEST_DELAY, michael@0: mMaxRequestDelay); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("response.timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("response.timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mResponseTimeout = PR_SecondsToInterval(clamped(val, 0, 0xffff)); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("max-connections"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("max-connections"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: michael@0: mMaxConnections = (uint16_t) clamped((uint32_t)val, michael@0: (uint32_t)1, MaxSocketCount()); michael@0: michael@0: if (mConnMgr) michael@0: mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS, michael@0: mMaxConnections); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-server"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("max-persistent-connections-per-server"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mMaxPersistentConnectionsPerServer = (uint8_t) clamped(val, 1, 0xff); michael@0: if (mConnMgr) michael@0: mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_HOST, michael@0: mMaxPersistentConnectionsPerServer); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-proxy"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("max-persistent-connections-per-proxy"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mMaxPersistentConnectionsPerProxy = (uint8_t) clamped(val, 1, 0xff); michael@0: if (mConnMgr) michael@0: mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_PROXY, michael@0: mMaxPersistentConnectionsPerProxy); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("sendRefererHeader"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("sendRefererHeader"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mReferrerLevel = (uint8_t) clamped(val, 0, 0xff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("referer.spoofSource"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("referer.spoofSource"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpoofReferrerSource = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("referer.trimmingPolicy"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("referer.trimmingPolicy"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mReferrerTrimmingPolicy = (uint8_t) clamped(val, 0, 0xff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("referer.XOriginPolicy"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("referer.XOriginPolicy"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mReferrerXOriginPolicy = (uint8_t) clamped(val, 0, 0xff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mRedirectionLimit = (uint8_t) clamped(val, 0, 0xff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("connection-retry-timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("connection-retry-timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mIdleSynTimeout = (uint16_t) clamped(val, 0, 3000); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("fast-fallback-to-IPv4"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("fast-fallback-to-IPv4"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mFastFallbackToIPv4 = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("version"))) { michael@0: nsXPIDLCString httpVersion; michael@0: prefs->GetCharPref(HTTP_PREF("version"), getter_Copies(httpVersion)); michael@0: if (httpVersion) { michael@0: if (!PL_strcmp(httpVersion, "1.1")) michael@0: mHttpVersion = NS_HTTP_VERSION_1_1; michael@0: else if (!PL_strcmp(httpVersion, "0.9")) michael@0: mHttpVersion = NS_HTTP_VERSION_0_9; michael@0: else michael@0: mHttpVersion = NS_HTTP_VERSION_1_0; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("proxy.version"))) { michael@0: nsXPIDLCString httpVersion; michael@0: prefs->GetCharPref(HTTP_PREF("proxy.version"), getter_Copies(httpVersion)); michael@0: if (httpVersion) { michael@0: if (!PL_strcmp(httpVersion, "1.1")) michael@0: mProxyHttpVersion = NS_HTTP_VERSION_1_1; michael@0: else michael@0: mProxyHttpVersion = NS_HTTP_VERSION_1_0; michael@0: // it does not make sense to issue a HTTP/0.9 request to a proxy server michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("pipelining"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (cVar) michael@0: mCapabilities |= NS_HTTP_ALLOW_PIPELINING; michael@0: else michael@0: mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING; michael@0: mPipeliningEnabled = cVar; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.maxrequests"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxrequests"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mMaxPipelinedRequests = clamped(val, 1, 0xffff); michael@0: if (mConnMgr) michael@0: mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PIPELINED_REQUESTS, michael@0: mMaxPipelinedRequests); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.max-optimistic-requests"))) { michael@0: rv = prefs-> michael@0: GetIntPref(HTTP_PREF("pipelining.max-optimistic-requests"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mMaxOptimisticPipelinedRequests = clamped(val, 1, 0xffff); michael@0: if (mConnMgr) michael@0: mConnMgr->UpdateParam michael@0: (nsHttpConnectionMgr::MAX_OPTIMISTIC_PIPELINED_REQUESTS, michael@0: mMaxOptimisticPipelinedRequests); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.aggressive"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("pipelining.aggressive"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mPipelineAggressive = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.maxsize"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxsize"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mMaxPipelineObjectSize = michael@0: static_cast(clamped(val, 1000, 100000000)); michael@0: } michael@0: } michael@0: michael@0: // Determines whether or not to actually reschedule after the michael@0: // reschedule-timeout has expired michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.reschedule-on-timeout"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("pipelining.reschedule-on-timeout"), michael@0: &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mPipelineRescheduleOnTimeout = cVar; michael@0: } michael@0: michael@0: // The amount of time head of line blocking is allowed (in ms) michael@0: // before the blocked transactions are moved to another pipeline michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.reschedule-timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pipelining.reschedule-timeout"), michael@0: &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPipelineRescheduleTimeout = michael@0: PR_MillisecondsToInterval((uint16_t) clamped(val, 500, 0xffff)); michael@0: } michael@0: } michael@0: michael@0: // The amount of time a pipelined transaction is allowed to wait before michael@0: // being canceled and retried in a non-pipeline connection michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.read-timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pipelining.read-timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPipelineReadTimeout = michael@0: PR_MillisecondsToInterval((uint16_t) clamped(val, 5000, michael@0: 0xffff)); michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pipelining.ssl"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("pipelining.ssl"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mPipeliningOverSSL = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("proxy.pipelining"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("proxy.pipelining"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mProxyPipelining = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("qos"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("qos"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mQoSBits = (uint8_t) clamped(val, 0, 0xff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("sendSecureXSiteReferrer"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("sendSecureXSiteReferrer"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSendSecureXSiteReferrer = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("accept.default"))) { michael@0: nsXPIDLCString accept; michael@0: rv = prefs->GetCharPref(HTTP_PREF("accept.default"), michael@0: getter_Copies(accept)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: SetAccept(accept); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("accept-encoding"))) { michael@0: nsXPIDLCString acceptEncodings; michael@0: rv = prefs->GetCharPref(HTTP_PREF("accept-encoding"), michael@0: getter_Copies(acceptEncodings)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: SetAcceptEncodings(acceptEncodings); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("use-cache"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("use-cache"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mUseCache = cVar; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("default-socket-type"))) { michael@0: nsXPIDLCString sval; michael@0: rv = prefs->GetCharPref(HTTP_PREF("default-socket-type"), michael@0: getter_Copies(sval)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (sval.IsEmpty()) michael@0: mDefaultSocketType.Adopt(0); michael@0: else { michael@0: // verify that this socket type is actually valid michael@0: nsCOMPtr sps( michael@0: do_GetService(NS_SOCKETPROVIDERSERVICE_CONTRACTID)); michael@0: if (sps) { michael@0: nsCOMPtr sp; michael@0: rv = sps->GetSocketProvider(sval, getter_AddRefs(sp)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // OK, this looks like a valid socket provider. michael@0: mDefaultSocketType.Assign(sval); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("prompt-temp-redirect"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("prompt-temp-redirect"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPromptTempRedirect = cVar; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("assoc-req.enforce"))) { michael@0: cVar = false; michael@0: rv = prefs->GetBoolPref(HTTP_PREF("assoc-req.enforce"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mEnforceAssocReq = cVar; michael@0: } michael@0: michael@0: // enable Persistent caching for HTTPS - bug#205921 michael@0: if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) { michael@0: cVar = false; michael@0: rv = prefs->GetBoolPref(BROWSER_PREF("disk_cache_ssl"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mEnablePersistentHttpsCaching = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("phishy-userpass-length"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("phishy-userpass-length"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mPhishyUserPassLength = (uint8_t) clamped(val, 0, 0xff); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.enabled"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mEnableSpdy = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v3"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v3"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdyV3 = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v3-1"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v3-1"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdyV31 = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.enabled.http2draft"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.http2draft"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mHttp2DraftEnabled = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.enforce-tls-profile"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.enforce-tls-profile"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mEnforceHttp2TlsProfile = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.coalesce-hostnames"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.coalesce-hostnames"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mCoalesceSpdy = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.persistent-settings"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.persistent-settings"), michael@0: &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdyPersistentSettings = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("spdy.timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdyTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff)); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.chunk-size"))) { michael@0: // keep this within http/2 ranges of 1 to 2^14-1 michael@0: rv = prefs->GetIntPref(HTTP_PREF("spdy.chunk-size"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdySendingChunkSize = (uint32_t) clamped(val, 1, 0x3fff); michael@0: } michael@0: michael@0: // The amount of idle seconds on a spdy connection before initiating a michael@0: // server ping. 0 will disable. michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.ping-threshold"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-threshold"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdyPingThreshold = michael@0: PR_SecondsToInterval((uint16_t) clamped(val, 0, 0x7fffffff)); michael@0: } michael@0: michael@0: // The amount of seconds to wait for a spdy ping response before michael@0: // closing the session. michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.ping-timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdyPingTimeout = michael@0: PR_SecondsToInterval((uint16_t) clamped(val, 0, 0x7fffffff)); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.allow-push"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("spdy.allow-push"), michael@0: &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mAllowPush = cVar; michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mSpdyPushAllowance = michael@0: static_cast michael@0: (clamped(val, 1024, static_cast(ASpdySession::kInitialRwin))); michael@0: } michael@0: } michael@0: michael@0: // The amount of seconds to wait for a spdy ping response before michael@0: // closing the session. michael@0: if (PREF_CHANGED(HTTP_PREF("spdy.send-buffer-size"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("spdy.send-buffer-size"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSpdySendBufferSize = (uint32_t) clamped(val, 1500, 0x7fffffff); michael@0: } michael@0: michael@0: // The maximum amount of time to wait for socket transport to be michael@0: // established michael@0: if (PREF_CHANGED(HTTP_PREF("connection-timeout"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("connection-timeout"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: // the pref is in seconds, but the variable is in milliseconds michael@0: mConnectTimeout = clamped(val, 1, 0xffff) * PR_MSEC_PER_SEC; michael@0: } michael@0: michael@0: // The maximum amount of time the cache session lock can be held michael@0: // before a new transaction bypasses the cache. In milliseconds. michael@0: if (PREF_CHANGED(HTTP_PREF("bypass-cachelock-threshold"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("bypass-cachelock-threshold"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: // the pref and variable are both in milliseconds michael@0: mBypassCacheLockThreshold = michael@0: static_cast(clamped(val, 0, 0x7ffffff)); michael@0: } michael@0: michael@0: // The maximum number of current global half open sockets allowable michael@0: // for starting a new speculative connection. michael@0: if (PREF_CHANGED(HTTP_PREF("speculative-parallel-limit"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("speculative-parallel-limit"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mParallelSpeculativeConnectLimit = (uint32_t) clamped(val, 0, 1024); michael@0: } michael@0: michael@0: // Whether or not to block requests for non head js/css items (e.g. media) michael@0: // while those elements load. michael@0: if (PREF_CHANGED(HTTP_PREF("rendering-critical-requests-prioritization"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("rendering-critical-requests-prioritization"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mCriticalRequestPrioritization = cVar; michael@0: } michael@0: michael@0: // on transition of network.http.diagnostics to true print michael@0: // a bunch of information to the console michael@0: if (pref && PREF_CHANGED(HTTP_PREF("diagnostics"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("diagnostics"), &cVar); michael@0: if (NS_SUCCEEDED(rv) && cVar) { michael@0: if (mConnMgr) michael@0: mConnMgr->PrintDiagnostics(); michael@0: } michael@0: } michael@0: // michael@0: // INTL options michael@0: // michael@0: michael@0: if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) { michael@0: nsCOMPtr pls; michael@0: prefs->GetComplexValue(INTL_ACCEPT_LANGUAGES, michael@0: NS_GET_IID(nsIPrefLocalizedString), michael@0: getter_AddRefs(pls)); michael@0: if (pls) { michael@0: nsXPIDLString uval; michael@0: pls->ToString(getter_Copies(uval)); michael@0: if (uval) michael@0: SetAcceptLanguages(NS_ConvertUTF16toUTF8(uval).get()); michael@0: } michael@0: } michael@0: michael@0: // michael@0: // Tracking options michael@0: // michael@0: michael@0: if (PREF_CHANGED(DONOTTRACK_HEADER_ENABLED)) { michael@0: cVar = false; michael@0: rv = prefs->GetBoolPref(DONOTTRACK_HEADER_ENABLED, &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mDoNotTrackEnabled = cVar; michael@0: } michael@0: } michael@0: if (PREF_CHANGED(DONOTTRACK_HEADER_VALUE)) { michael@0: val = 1; michael@0: rv = prefs->GetIntPref(DONOTTRACK_HEADER_VALUE, &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mDoNotTrackValue = val; michael@0: } michael@0: } michael@0: michael@0: // Hint option michael@0: if (PREF_CHANGED(SAFE_HINT_HEADER_VALUE)) { michael@0: cVar = false; michael@0: rv = prefs->GetBoolPref(SAFE_HINT_HEADER_VALUE, &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mSafeHintEnabled = cVar; michael@0: } michael@0: } michael@0: michael@0: // toggle to true anytime a token bucket related pref is changed.. that michael@0: // includes telemetry and allow-experiments because of the abtest profile michael@0: bool requestTokenBucketUpdated = false; michael@0: michael@0: // michael@0: // Telemetry michael@0: // michael@0: michael@0: if (PREF_CHANGED(TELEMETRY_ENABLED)) { michael@0: cVar = false; michael@0: requestTokenBucketUpdated = true; michael@0: rv = prefs->GetBoolPref(TELEMETRY_ENABLED, &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mTelemetryEnabled = cVar; michael@0: } michael@0: } michael@0: michael@0: // michael@0: // network.allow-experiments michael@0: // michael@0: if (PREF_CHANGED(ALLOW_EXPERIMENTS)) { michael@0: cVar = true; michael@0: requestTokenBucketUpdated = true; michael@0: rv = prefs->GetBoolPref(ALLOW_EXPERIMENTS, &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mAllowExperiments = cVar; michael@0: } michael@0: } michael@0: michael@0: // michael@0: // Test HTTP Pipelining (bug796192) michael@0: // If experiments are allowed and pipelining is Off, michael@0: // turn it On for just 10 minutes michael@0: // michael@0: if (mAllowExperiments && !mPipeliningEnabled && michael@0: PREF_CHANGED(HTTP_PREF("pipelining.abtest"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("pipelining.abtest"), &cVar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // If option is enabled, only test for ~1% of sessions michael@0: if (cVar && !(rand() % 128)) { michael@0: mCapabilities |= NS_HTTP_ALLOW_PIPELINING; michael@0: if (mPipelineTestTimer) michael@0: mPipelineTestTimer->Cancel(); michael@0: mPipelineTestTimer = michael@0: do_CreateInstance("@mozilla.org/timer;1", &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = mPipelineTestTimer->InitWithFuncCallback( michael@0: TimerCallback, this, 10*60*1000, // 10 minutes michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } else { michael@0: mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING; michael@0: if (mPipelineTestTimer) { michael@0: mPipelineTestTimer->Cancel(); michael@0: mPipelineTestTimer = nullptr; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: if (requestTokenBucketUpdated) { michael@0: MakeNewRequestTokenBucket(); michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pacing.requests.enabled"))) { michael@0: rv = prefs->GetBoolPref(HTTP_PREF("pacing.requests.enabled"), michael@0: &cVar); michael@0: if (NS_SUCCEEDED(rv)){ michael@0: requestTokenBucketUpdated = true; michael@0: mRequestTokenBucketEnabled = cVar; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("pacing.requests.min-parallelism"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.min-parallelism"), &val); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mRequestTokenBucketMinParallelism = static_cast(clamped(val, 1, 1024)); michael@0: } michael@0: if (PREF_CHANGED(HTTP_PREF("pacing.requests.hz"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.hz"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mRequestTokenBucketHz = static_cast(clamped(val, 1, 10000)); michael@0: requestTokenBucketUpdated = true; michael@0: } michael@0: } michael@0: if (PREF_CHANGED(HTTP_PREF("pacing.requests.burst"))) { michael@0: rv = prefs->GetIntPref(HTTP_PREF("pacing.requests.burst"), &val); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mRequestTokenBucketBurst = val ? val : 1; michael@0: requestTokenBucketUpdated = true; michael@0: } michael@0: } michael@0: if (requestTokenBucketUpdated) { michael@0: mRequestTokenBucket = michael@0: new EventTokenBucket(RequestTokenBucketHz(), michael@0: RequestTokenBucketBurst()); michael@0: } michael@0: michael@0: // Keepalive values for initial and idle connections. michael@0: if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_connections"))) { michael@0: rv = prefs->GetBoolPref( michael@0: HTTP_PREF("tcp_keepalive.short_lived_connections"), &cVar); michael@0: if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveShortLivedEnabled) { michael@0: mTCPKeepaliveShortLivedEnabled = cVar; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_time"))) { michael@0: rv = prefs->GetIntPref( michael@0: HTTP_PREF("tcp_keepalive.short_lived_time"), &val); michael@0: if (NS_SUCCEEDED(rv) && val > 0) michael@0: mTCPKeepaliveShortLivedTimeS = clamped(val, 1, 300); // Max 5 mins. michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_idle_time"))) { michael@0: rv = prefs->GetIntPref( michael@0: HTTP_PREF("tcp_keepalive.short_lived_idle_time"), &val); michael@0: if (NS_SUCCEEDED(rv) && val > 0) michael@0: mTCPKeepaliveShortLivedIdleTimeS = clamped(val, michael@0: 1, kMaxTCPKeepIdle); michael@0: } michael@0: michael@0: // Keepalive values for Long-lived Connections. michael@0: if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.long_lived_connections"))) { michael@0: rv = prefs->GetBoolPref( michael@0: HTTP_PREF("tcp_keepalive.long_lived_connections"), &cVar); michael@0: if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveLongLivedEnabled) { michael@0: mTCPKeepaliveLongLivedEnabled = cVar; michael@0: } michael@0: } michael@0: michael@0: if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.long_lived_idle_time"))) { michael@0: rv = prefs->GetIntPref( michael@0: HTTP_PREF("tcp_keepalive.long_lived_idle_time"), &val); michael@0: if (NS_SUCCEEDED(rv) && val > 0) michael@0: mTCPKeepaliveLongLivedIdleTimeS = clamped(val, michael@0: 1, kMaxTCPKeepIdle); michael@0: } michael@0: michael@0: // Enable HTTP response timeout if TCP Keepalives are disabled. michael@0: mResponseTimeoutEnabled = !mTCPKeepaliveShortLivedEnabled && michael@0: !mTCPKeepaliveLongLivedEnabled; michael@0: michael@0: #undef PREF_CHANGED michael@0: #undef MULTI_PREF_CHANGED michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Static method called by mPipelineTestTimer when it expires. michael@0: */ michael@0: void michael@0: nsHttpHandler::TimerCallback(nsITimer * aTimer, void * aClosure) michael@0: { michael@0: nsRefPtr thisObject = static_cast(aClosure); michael@0: if (!thisObject->mPipeliningEnabled) michael@0: thisObject->mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING; michael@0: } michael@0: michael@0: /** michael@0: * Allocates a C string into that contains a ISO 639 language list michael@0: * notated with HTTP "q" values for output with a HTTP Accept-Language michael@0: * header. Previous q values will be stripped because the order of michael@0: * the langs imply the q value. The q values are calculated by dividing michael@0: * 1.0 amongst the number of languages present. michael@0: * michael@0: * Ex: passing: "en, ja" michael@0: * returns: "en,ja;q=0.5" michael@0: * michael@0: * passing: "en, ja, fr_CA" michael@0: * returns: "en,ja;q=0.7,fr_CA;q=0.3" michael@0: */ michael@0: static nsresult michael@0: PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLanguages) michael@0: { michael@0: if (!i_AcceptLanguages) michael@0: return NS_OK; michael@0: michael@0: uint32_t n, count_n, size, wrote; michael@0: double q, dec; michael@0: char *p, *p2, *token, *q_Accept, *o_Accept; michael@0: const char *comma; michael@0: int32_t available; michael@0: michael@0: o_Accept = strdup(i_AcceptLanguages); michael@0: if (!o_Accept) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: for (p = o_Accept, n = size = 0; '\0' != *p; p++) { michael@0: if (*p == ',') n++; michael@0: size++; michael@0: } michael@0: michael@0: available = size + ++n * 11 + 1; michael@0: q_Accept = new char[available]; michael@0: if (!q_Accept) { michael@0: free(o_Accept); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: *q_Accept = '\0'; michael@0: q = 1.0; michael@0: dec = q / (double) n; michael@0: count_n = 0; michael@0: p2 = q_Accept; michael@0: for (token = nsCRT::strtok(o_Accept, ",", &p); michael@0: token != (char *) 0; michael@0: token = nsCRT::strtok(p, ",", &p)) michael@0: { michael@0: token = net_FindCharNotInSet(token, HTTP_LWS); michael@0: char* trim; michael@0: trim = net_FindCharInSet(token, ";" HTTP_LWS); michael@0: if (trim != (char*)0) // remove "; q=..." if present michael@0: *trim = '\0'; michael@0: michael@0: if (*token != '\0') { michael@0: comma = count_n++ != 0 ? "," : ""; // delimiter if not first item michael@0: uint32_t u = QVAL_TO_UINT(q); michael@0: michael@0: // Only display q-value if less than 1.00. michael@0: if (u < 100) { michael@0: const char *qval_str; michael@0: michael@0: // With a small number of languages, one decimal place is enough to prevent duplicate q-values. michael@0: // Also, trailing zeroes do not add any information, so they can be removed. michael@0: if ((n < 10) || ((u % 10) == 0)) { michael@0: u = (u + 5) / 10; michael@0: qval_str = "%s%s;q=0.%u"; michael@0: } else { michael@0: // Values below 10 require zero padding. michael@0: qval_str = "%s%s;q=0.%02u"; michael@0: } michael@0: michael@0: wrote = PR_snprintf(p2, available, qval_str, comma, token, u); michael@0: } else { michael@0: wrote = PR_snprintf(p2, available, "%s%s", comma, token); michael@0: } michael@0: michael@0: q -= dec; michael@0: p2 += wrote; michael@0: available -= wrote; michael@0: MOZ_ASSERT(available > 0, "allocated string not long enough"); michael@0: } michael@0: } michael@0: free(o_Accept); michael@0: michael@0: o_AcceptLanguages.Assign((const char *) q_Accept); michael@0: delete [] q_Accept; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages) michael@0: { michael@0: nsAutoCString buf; michael@0: nsresult rv = PrepareAcceptLanguages(aAcceptLanguages, buf); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mAcceptLanguages.Assign(buf); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::SetAccept(const char *aAccept) michael@0: { michael@0: mAccept = aAccept; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings) michael@0: { michael@0: mAcceptEncodings = aAcceptEncodings; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHttpHandler, michael@0: nsIHttpProtocolHandler, michael@0: nsIProxiedProtocolHandler, michael@0: nsIProtocolHandler, michael@0: nsIObserver, michael@0: nsISupportsWeakReference, michael@0: nsISpeculativeConnect) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler::nsIProtocolHandler michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetScheme(nsACString &aScheme) michael@0: { michael@0: aScheme.AssignLiteral("http"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetDefaultPort(int32_t *result) michael@0: { michael@0: *result = NS_HTTP_DEFAULT_PORT; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetProtocolFlags(uint32_t *result) michael@0: { michael@0: *result = NS_HTTP_PROTOCOL_FLAGS; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::NewURI(const nsACString &aSpec, michael@0: const char *aCharset, michael@0: nsIURI *aBaseURI, michael@0: nsIURI **aURI) michael@0: { michael@0: return mozilla::net::NewURI(aSpec, aCharset, aBaseURI, NS_HTTP_DEFAULT_PORT, aURI); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::NewChannel(nsIURI *uri, nsIChannel **result) michael@0: { michael@0: LOG(("nsHttpHandler::NewChannel\n")); michael@0: michael@0: NS_ENSURE_ARG_POINTER(uri); michael@0: NS_ENSURE_ARG_POINTER(result); michael@0: michael@0: bool isHttp = false, isHttps = false; michael@0: michael@0: // Verify that we have been given a valid scheme michael@0: nsresult rv = uri->SchemeIs("http", &isHttp); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (!isHttp) { michael@0: rv = uri->SchemeIs("https", &isHttps); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (!isHttps) { michael@0: NS_WARNING("Invalid URI scheme"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: return NewProxiedChannel(uri, nullptr, 0, nullptr, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) michael@0: { michael@0: // don't override anything. michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler::nsIProxiedProtocolHandler michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::NewProxiedChannel(nsIURI *uri, michael@0: nsIProxyInfo* givenProxyInfo, michael@0: uint32_t proxyResolveFlags, michael@0: nsIURI *proxyURI, michael@0: nsIChannel **result) michael@0: { michael@0: nsRefPtr httpChannel; michael@0: michael@0: LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n", michael@0: givenProxyInfo)); michael@0: michael@0: nsCOMPtr proxyInfo; michael@0: if (givenProxyInfo) { michael@0: proxyInfo = do_QueryInterface(givenProxyInfo); michael@0: NS_ENSURE_ARG(proxyInfo); michael@0: } michael@0: michael@0: bool https; michael@0: nsresult rv = uri->SchemeIs("https", &https); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (IsNeckoChild()) { michael@0: httpChannel = new HttpChannelChild(); michael@0: } else { michael@0: httpChannel = new nsHttpChannel(); michael@0: } michael@0: michael@0: uint32_t caps = mCapabilities; michael@0: michael@0: if (https) { michael@0: // enable pipelining over SSL if requested michael@0: if (mPipeliningOverSSL) michael@0: caps |= NS_HTTP_ALLOW_PIPELINING; michael@0: } michael@0: michael@0: if (!IsNeckoChild()) { michael@0: // HACK: make sure PSM gets initialized on the main thread. michael@0: net_EnsurePSMInit(); michael@0: } michael@0: michael@0: rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: httpChannel.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler::nsIHttpProtocolHandler michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetUserAgent(nsACString &value) michael@0: { michael@0: value = UserAgent(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetAppName(nsACString &value) michael@0: { michael@0: value = mLegacyAppName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetAppVersion(nsACString &value) michael@0: { michael@0: value = mLegacyAppVersion; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetPlatform(nsACString &value) michael@0: { michael@0: value = mPlatform; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetOscpu(nsACString &value) michael@0: { michael@0: value = mOscpu; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::GetMisc(nsACString &value) michael@0: { michael@0: value = mMisc; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /*static*/ void michael@0: nsHttpHandler::GetCacheSessionNameForStoragePolicy( michael@0: nsCacheStoragePolicy storagePolicy, michael@0: bool isPrivate, michael@0: uint32_t appId, michael@0: bool inBrowser, michael@0: nsACString& sessionName) michael@0: { michael@0: MOZ_ASSERT(!isPrivate || storagePolicy == nsICache::STORE_IN_MEMORY); michael@0: michael@0: switch (storagePolicy) { michael@0: case nsICache::STORE_IN_MEMORY: michael@0: sessionName.AssignASCII(isPrivate ? "HTTP-memory-only-PB" : "HTTP-memory-only"); michael@0: break; michael@0: case nsICache::STORE_OFFLINE: michael@0: sessionName.AssignLiteral("HTTP-offline"); michael@0: break; michael@0: default: michael@0: sessionName.AssignLiteral("HTTP"); michael@0: break; michael@0: } michael@0: if (appId != NECKO_NO_APP_ID || inBrowser) { michael@0: sessionName.Append('~'); michael@0: sessionName.AppendInt(appId); michael@0: sessionName.Append('~'); michael@0: sessionName.AppendInt(inBrowser); michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpHandler::nsIObserver michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::Observe(nsISupports *subject, michael@0: const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: LOG(("nsHttpHandler::Observe [topic=\"%s\"]\n", topic)); michael@0: michael@0: if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { michael@0: nsCOMPtr prefBranch = do_QueryInterface(subject); michael@0: if (prefBranch) michael@0: PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get()); michael@0: } michael@0: else if (strcmp(topic, "profile-change-net-teardown") == 0 || michael@0: strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { michael@0: michael@0: mHandlerActive = false; michael@0: michael@0: // clear cache of all authentication credentials. michael@0: mAuthCache.ClearAll(); michael@0: mPrivateAuthCache.ClearAll(); michael@0: if (mWifiTickler) michael@0: mWifiTickler->Cancel(); michael@0: michael@0: // ensure connection manager is shutdown michael@0: if (mConnMgr) michael@0: mConnMgr->Shutdown(); michael@0: michael@0: // need to reset the session start time since cache validation may michael@0: // depend on this value. michael@0: mSessionStartTime = NowInSeconds(); michael@0: michael@0: if (!mDoNotTrackEnabled) { michael@0: Telemetry::Accumulate(Telemetry::DNT_USAGE, DONOTTRACK_VALUE_UNSET); michael@0: } michael@0: else { michael@0: Telemetry::Accumulate(Telemetry::DNT_USAGE, mDoNotTrackValue); michael@0: } michael@0: } michael@0: else if (strcmp(topic, "profile-change-net-restore") == 0) { michael@0: // initialize connection manager michael@0: InitConnectionMgr(); michael@0: } michael@0: else if (strcmp(topic, "net:clear-active-logins") == 0) { michael@0: mAuthCache.ClearAll(); michael@0: mPrivateAuthCache.ClearAll(); michael@0: } michael@0: else if (strcmp(topic, "net:prune-dead-connections") == 0) { michael@0: if (mConnMgr) { michael@0: mConnMgr->PruneDeadConnections(); michael@0: } michael@0: } michael@0: else if (strcmp(topic, "net:prune-all-connections") == 0) { michael@0: if (mConnMgr) { michael@0: mConnMgr->DoShiftReloadConnectionCleanup(nullptr); michael@0: mConnMgr->PruneDeadConnections(); michael@0: } michael@0: } michael@0: else if (strcmp(topic, "net:failed-to-process-uri-content") == 0) { michael@0: nsCOMPtr uri = do_QueryInterface(subject); michael@0: if (uri && mConnMgr) michael@0: mConnMgr->ReportFailedToProcess(uri); michael@0: } michael@0: else if (strcmp(topic, "last-pb-context-exited") == 0) { michael@0: mPrivateAuthCache.ClearAll(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsISpeculativeConnect michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpHandler::SpeculativeConnect(nsIURI *aURI, michael@0: nsIInterfaceRequestor *aCallbacks) michael@0: { michael@0: if (!mHandlerActive) michael@0: return NS_OK; michael@0: michael@0: nsISiteSecurityService* sss = gHttpHandler->GetSSService(); michael@0: bool isStsHost = false; michael@0: if (!sss) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr loadContext = do_GetInterface(aCallbacks); michael@0: uint32_t flags = 0; michael@0: if (loadContext && loadContext->UsePrivateBrowsing()) michael@0: flags |= nsISocketProvider::NO_PERMANENT_STORAGE; michael@0: nsCOMPtr clone; michael@0: if (NS_SUCCEEDED(sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, michael@0: aURI, flags, &isStsHost)) && isStsHost) { michael@0: if (NS_SUCCEEDED(aURI->Clone(getter_AddRefs(clone)))) { michael@0: clone->SetScheme(NS_LITERAL_CSTRING("https")); michael@0: aURI = clone.get(); michael@0: } michael@0: } michael@0: michael@0: nsAutoCString scheme; michael@0: nsresult rv = aURI->GetScheme(scheme); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // If this is HTTPS, make sure PSM is initialized as the channel michael@0: // creation path may have been bypassed michael@0: if (scheme.EqualsLiteral("https")) { michael@0: if (!IsNeckoChild()) { michael@0: // make sure PSM gets initialized on the main thread. michael@0: net_EnsurePSMInit(); michael@0: } michael@0: } michael@0: // Ensure that this is HTTP or HTTPS, otherwise we don't do preconnect here michael@0: else if (!scheme.EqualsLiteral("http")) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Construct connection info object michael@0: bool usingSSL = false; michael@0: rv = aURI->SchemeIs("https", &usingSSL); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString host; michael@0: rv = aURI->GetAsciiHost(host); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: int32_t port = -1; michael@0: rv = aURI->GetPort(&port); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString username; michael@0: aURI->GetUsername(username); michael@0: michael@0: nsHttpConnectionInfo *ci = michael@0: new nsHttpConnectionInfo(host, port, username, nullptr, usingSSL); michael@0: michael@0: return SpeculativeConnect(ci, aCallbacks); michael@0: } michael@0: michael@0: void michael@0: nsHttpHandler::TickleWifi(nsIInterfaceRequestor *cb) michael@0: { michael@0: if (!cb || !mWifiTickler) michael@0: return; michael@0: michael@0: // If B2G requires a similar mechanism nsINetworkManager, currently only avail michael@0: // on B2G, contains the necessary information on wifi and gateway michael@0: michael@0: nsCOMPtr domWindow; michael@0: cb->GetInterface(NS_GET_IID(nsIDOMWindow), getter_AddRefs(domWindow)); michael@0: if (!domWindow) michael@0: return; michael@0: michael@0: nsCOMPtr domNavigator; michael@0: domWindow->GetNavigator(getter_AddRefs(domNavigator)); michael@0: nsCOMPtr networkNavigator = michael@0: do_QueryInterface(domNavigator); michael@0: if (!networkNavigator) michael@0: return; michael@0: michael@0: nsCOMPtr networkProperties; michael@0: networkNavigator->GetProperties(getter_AddRefs(networkProperties)); michael@0: if (!networkProperties) michael@0: return; michael@0: michael@0: uint32_t gwAddress; michael@0: bool isWifi; michael@0: nsresult rv; michael@0: michael@0: rv = networkProperties->GetDhcpGateway(&gwAddress); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = networkProperties->GetIsWifi(&isWifi); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: if (!gwAddress || !isWifi) michael@0: return; michael@0: michael@0: mWifiTickler->SetIPV4Address(gwAddress); michael@0: mWifiTickler->Tickle(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpsHandler implementation michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHttpsHandler, michael@0: nsIHttpProtocolHandler, michael@0: nsIProxiedProtocolHandler, michael@0: nsIProtocolHandler, michael@0: nsISupportsWeakReference, michael@0: nsISpeculativeConnect) michael@0: michael@0: nsresult michael@0: nsHttpsHandler::Init() michael@0: { michael@0: nsCOMPtr httpHandler( michael@0: do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http")); michael@0: MOZ_ASSERT(httpHandler.get() != nullptr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpsHandler::GetScheme(nsACString &aScheme) michael@0: { michael@0: aScheme.AssignLiteral("https"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpsHandler::GetDefaultPort(int32_t *aPort) michael@0: { michael@0: *aPort = NS_HTTPS_DEFAULT_PORT; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpsHandler::GetProtocolFlags(uint32_t *aProtocolFlags) michael@0: { michael@0: *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpsHandler::NewURI(const nsACString &aSpec, michael@0: const char *aOriginCharset, michael@0: nsIURI *aBaseURI, michael@0: nsIURI **_retval) michael@0: { michael@0: return mozilla::net::NewURI(aSpec, aOriginCharset, aBaseURI, NS_HTTPS_DEFAULT_PORT, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpsHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) michael@0: { michael@0: MOZ_ASSERT(gHttpHandler); michael@0: if (!gHttpHandler) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return gHttpHandler->NewChannel(aURI, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpsHandler::AllowPort(int32_t aPort, const char *aScheme, bool *_retval) michael@0: { michael@0: // don't override anything. michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace mozilla::net michael@0: } // namespace mozilla