Bug 722034 - Part 5 - Move cache validation to the cache thread, r=honzab
[gecko.git] / netwerk / protocol / http / nsHttpHandler.cpp
blob71c948fa77a764e0aabd67cada39a96c0a8c8f15
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsHttp.h"
8 #include "nsHttpHandler.h"
9 #include "nsHttpChannel.h"
10 #include "nsHttpConnection.h"
11 #include "nsHttpResponseHead.h"
12 #include "nsHttpTransaction.h"
13 #include "nsHttpAuthCache.h"
14 #include "nsStandardURL.h"
15 #include "nsIHttpChannel.h"
16 #include "nsIURL.h"
17 #include "nsIStandardURL.h"
18 #include "nsICacheService.h"
19 #include "nsICategoryManager.h"
20 #include "nsCategoryManagerUtils.h"
21 #include "nsICacheService.h"
22 #include "nsIPrefService.h"
23 #include "nsIPrefBranch.h"
24 #include "nsIPrefLocalizedString.h"
25 #include "nsISocketProviderService.h"
26 #include "nsISocketProvider.h"
27 #include "nsPrintfCString.h"
28 #include "nsCOMPtr.h"
29 #include "nsNetCID.h"
30 #include "prprf.h"
31 #include "nsReadableUtils.h"
32 #include "nsQuickSort.h"
33 #include "nsNetUtil.h"
34 #include "nsIOService.h"
35 #include "nsAsyncRedirectVerifyHelper.h"
36 #include "nsSocketTransportService2.h"
37 #include "nsAlgorithm.h"
38 #include "ASpdySession.h"
40 #include "nsIXULAppInfo.h"
42 #include "mozilla/net/NeckoChild.h"
44 #if defined(XP_UNIX)
45 #include <sys/utsname.h>
46 #endif
48 #if defined(XP_WIN)
49 #include <windows.h>
50 #endif
52 #if defined(XP_MACOSX)
53 #include <CoreServices/CoreServices.h>
54 #endif
56 #if defined(XP_OS2)
57 #define INCL_DOSMISC
58 #include <os2.h>
59 #endif
61 //-----------------------------------------------------------------------------
62 using namespace mozilla;
63 using namespace mozilla::net;
64 #include "mozilla/net/HttpChannelChild.h"
66 #include "mozilla/FunctionTimer.h"
68 #ifdef DEBUG
69 // defined by the socket transport service while active
70 extern PRThread *gSocketThread;
71 #endif
73 static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
74 static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
75 static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID);
76 static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
77 static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
79 #define UA_PREF_PREFIX "general.useragent."
80 #ifdef XP_WIN
81 #define UA_SPARE_PLATFORM
82 #endif
84 #define HTTP_PREF_PREFIX "network.http."
85 #define INTL_ACCEPT_LANGUAGES "intl.accept_languages"
86 #define NETWORK_ENABLEIDN "network.enableIDN"
87 #define BROWSER_PREF_PREFIX "browser.cache."
88 #define DONOTTRACK_HEADER_ENABLED "privacy.donottrackheader.enabled"
89 #define TELEMETRY_ENABLED "toolkit.telemetry.enabled"
90 #define ALLOW_EXPERIMENTS "network.allow-experiments"
92 #define UA_PREF(_pref) UA_PREF_PREFIX _pref
93 #define HTTP_PREF(_pref) HTTP_PREF_PREFIX _pref
94 #define BROWSER_PREF(_pref) BROWSER_PREF_PREFIX _pref
96 #define NS_HTTP_PROTOCOL_FLAGS (URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_LOADABLE_BY_ANYONE)
98 //-----------------------------------------------------------------------------
100 static nsresult
101 NewURI(const nsACString &aSpec,
102 const char *aCharset,
103 nsIURI *aBaseURI,
104 PRInt32 aDefaultPort,
105 nsIURI **aURI)
107 nsStandardURL *url = new nsStandardURL();
108 if (!url)
109 return NS_ERROR_OUT_OF_MEMORY;
110 NS_ADDREF(url);
112 nsresult rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
113 aDefaultPort, aSpec, aCharset, aBaseURI);
114 if (NS_FAILED(rv)) {
115 NS_RELEASE(url);
116 return rv;
119 *aURI = url; // no QI needed
120 return NS_OK;
123 //-----------------------------------------------------------------------------
124 // nsHttpHandler <public>
125 //-----------------------------------------------------------------------------
127 nsHttpHandler *gHttpHandler = nsnull;
129 nsHttpHandler::nsHttpHandler()
130 : mConnMgr(nsnull)
131 , mHttpVersion(NS_HTTP_VERSION_1_1)
132 , mProxyHttpVersion(NS_HTTP_VERSION_1_1)
133 , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
134 , mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
135 , mReferrerLevel(0xff) // by default we always send a referrer
136 , mFastFallbackToIPv4(false)
137 , mIdleTimeout(PR_SecondsToInterval(10))
138 , mSpdyTimeout(PR_SecondsToInterval(180))
139 , mMaxRequestAttempts(10)
140 , mMaxRequestDelay(10)
141 , mIdleSynTimeout(250)
142 , mMaxConnections(24)
143 , mMaxConnectionsPerServer(8)
144 , mMaxPersistentConnectionsPerServer(2)
145 , mMaxPersistentConnectionsPerProxy(4)
146 , mMaxPipelinedRequests(32)
147 , mMaxOptimisticPipelinedRequests(4)
148 , mPipelineAggressive(false)
149 , mMaxPipelineObjectSize(300000)
150 , mPipelineRescheduleOnTimeout(true)
151 , mPipelineRescheduleTimeout(PR_MillisecondsToInterval(1500))
152 , mPipelineReadTimeout(PR_MillisecondsToInterval(30000))
153 , mRedirectionLimit(10)
154 , mPhishyUserPassLength(1)
155 , mQoSBits(0x00)
156 , mPipeliningOverSSL(false)
157 , mEnforceAssocReq(false)
158 , mLastUniqueID(NowInSeconds())
159 , mSessionStartTime(0)
160 , mLegacyAppName("Mozilla")
161 , mLegacyAppVersion("5.0")
162 , mProduct("Gecko")
163 , mUserAgentIsDirty(true)
164 , mUseCache(true)
165 , mPromptTempRedirect(true)
166 , mSendSecureXSiteReferrer(true)
167 , mEnablePersistentHttpsCaching(false)
168 , mDoNotTrackEnabled(false)
169 , mTelemetryEnabled(false)
170 , mAllowExperiments(true)
171 , mEnableSpdy(false)
172 , mSpdyV2(true)
173 , mSpdyV3(true)
174 , mCoalesceSpdy(true)
175 , mUseAlternateProtocol(false)
176 , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
177 , mSpdyPingThreshold(PR_SecondsToInterval(44))
178 , mSpdyPingTimeout(PR_SecondsToInterval(8))
180 #if defined(PR_LOGGING)
181 gHttpLog = PR_NewLogModule("nsHttp");
182 #endif
184 LOG(("Creating nsHttpHandler [this=%x].\n", this));
186 NS_ASSERTION(!gHttpHandler, "HTTP handler already created!");
187 gHttpHandler = this;
190 nsHttpHandler::~nsHttpHandler()
192 LOG(("Deleting nsHttpHandler [this=%x]\n", this));
194 // make sure the connection manager is shutdown
195 if (mConnMgr) {
196 mConnMgr->Shutdown();
197 NS_RELEASE(mConnMgr);
200 // Note: don't call NeckoChild::DestroyNeckoChild() here, as it's too late
201 // and it'll segfault. NeckoChild will get cleaned up by process exit.
203 nsHttp::DestroyAtomTable();
205 gHttpHandler = nsnull;
208 nsresult
209 nsHttpHandler::Init()
211 NS_TIME_FUNCTION;
213 nsresult rv;
215 LOG(("nsHttpHandler::Init\n"));
217 rv = nsHttp::CreateAtomTable();
218 if (NS_FAILED(rv))
219 return rv;
221 mIOService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
222 if (NS_FAILED(rv)) {
223 NS_WARNING("unable to continue without io service");
224 return rv;
227 if (IsNeckoChild())
228 NeckoChild::InitNeckoChild();
230 InitUserAgentComponents();
232 // monitor some preference changes
233 nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
234 if (prefBranch) {
235 prefBranch->AddObserver(HTTP_PREF_PREFIX, this, true);
236 prefBranch->AddObserver(UA_PREF_PREFIX, this, true);
237 prefBranch->AddObserver(INTL_ACCEPT_LANGUAGES, this, true);
238 prefBranch->AddObserver(NETWORK_ENABLEIDN, this, true);
239 prefBranch->AddObserver(BROWSER_PREF("disk_cache_ssl"), this, true);
240 prefBranch->AddObserver(DONOTTRACK_HEADER_ENABLED, this, true);
241 prefBranch->AddObserver(TELEMETRY_ENABLED, this, true);
243 PrefsChanged(prefBranch, nsnull);
246 mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);
248 nsCOMPtr<nsIXULAppInfo> appInfo =
249 do_GetService("@mozilla.org/xre/app-info;1");
251 mAppName.AssignLiteral(MOZ_APP_UA_NAME);
252 if (mAppName.Length() == 0 && appInfo) {
253 // Try to get the UA name from appInfo, falling back to the name
254 appInfo->GetUAName(mAppName);
255 if (mAppName.Length() == 0) {
256 appInfo->GetName(mAppName);
258 appInfo->GetVersion(mAppVersion);
259 mAppName.StripChars(" ()<>@,;:\\\"/[]?={}");
260 } else {
261 mAppVersion.AssignLiteral(MOZ_APP_UA_VERSION);
264 #if DEBUG
265 // dump user agent prefs
266 LOG(("> legacy-app-name = %s\n", mLegacyAppName.get()));
267 LOG(("> legacy-app-version = %s\n", mLegacyAppVersion.get()));
268 LOG(("> platform = %s\n", mPlatform.get()));
269 LOG(("> oscpu = %s\n", mOscpu.get()));
270 LOG(("> misc = %s\n", mMisc.get()));
271 LOG(("> product = %s\n", mProduct.get()));
272 LOG(("> product-sub = %s\n", mProductSub.get()));
273 LOG(("> app-name = %s\n", mAppName.get()));
274 LOG(("> app-version = %s\n", mAppVersion.get()));
275 LOG(("> compat-firefox = %s\n", mCompatFirefox.get()));
276 LOG(("> user-agent = %s\n", UserAgent().get()));
277 #endif
279 mSessionStartTime = NowInSeconds();
281 rv = mAuthCache.Init();
282 if (NS_FAILED(rv)) return rv;
284 rv = InitConnectionMgr();
285 if (NS_FAILED(rv)) return rv;
287 mProductSub.AssignLiteral(MOZILLA_UAVERSION);
289 // Startup the http category
290 // Bring alive the objects in the http-protocol-startup category
291 NS_CreateServicesFromCategory(NS_HTTP_STARTUP_CATEGORY,
292 static_cast<nsISupports*>(static_cast<void*>(this)),
293 NS_HTTP_STARTUP_TOPIC);
295 mObserverService = mozilla::services::GetObserverService();
296 if (mObserverService) {
297 mObserverService->AddObserver(this, "profile-change-net-teardown", true);
298 mObserverService->AddObserver(this, "profile-change-net-restore", true);
299 mObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
300 mObserverService->AddObserver(this, "net:clear-active-logins", true);
301 mObserverService->AddObserver(this, "net:prune-dead-connections", true);
302 mObserverService->AddObserver(this, "net:failed-to-process-uri-content", true);
305 return NS_OK;
308 nsresult
309 nsHttpHandler::InitConnectionMgr()
311 NS_TIME_FUNCTION;
313 nsresult rv;
315 if (!mConnMgr) {
316 mConnMgr = new nsHttpConnectionMgr();
317 if (!mConnMgr)
318 return NS_ERROR_OUT_OF_MEMORY;
319 NS_ADDREF(mConnMgr);
322 rv = mConnMgr->Init(mMaxConnections,
323 mMaxConnectionsPerServer,
324 mMaxConnectionsPerServer,
325 mMaxPersistentConnectionsPerServer,
326 mMaxPersistentConnectionsPerProxy,
327 mMaxRequestDelay,
328 mMaxPipelinedRequests,
329 mMaxOptimisticPipelinedRequests);
330 return rv;
333 nsresult
334 nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request,
335 PRUint8 caps,
336 bool useProxy)
338 nsresult rv;
340 // Add the "User-Agent" header
341 rv = request->SetHeader(nsHttp::User_Agent, UserAgent());
342 if (NS_FAILED(rv)) return rv;
344 // MIME based content negotiation lives!
345 // Add the "Accept" header
346 rv = request->SetHeader(nsHttp::Accept, mAccept);
347 if (NS_FAILED(rv)) return rv;
349 // Add the "Accept-Language" header
350 if (!mAcceptLanguages.IsEmpty()) {
351 // Add the "Accept-Language" header
352 rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages);
353 if (NS_FAILED(rv)) return rv;
356 // Add the "Accept-Encoding" header
357 rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings);
358 if (NS_FAILED(rv)) return rv;
360 // RFC2616 section 19.6.2 states that the "Connection: keep-alive"
361 // and "Keep-alive" request headers should not be sent by HTTP/1.1
362 // user-agents. Otherwise, problems with proxy servers (especially
363 // transparent proxies) can result.
365 // However, we need to send something so that we can use keepalive
366 // with HTTP/1.0 servers/proxies. We use "Proxy-Connection:" when
367 // we're talking to an http proxy, and "Connection:" otherwise.
368 // We no longer send the Keep-Alive request header.
370 NS_NAMED_LITERAL_CSTRING(close, "close");
371 NS_NAMED_LITERAL_CSTRING(keepAlive, "keep-alive");
373 const nsACString *connectionType = &close;
374 if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
375 connectionType = &keepAlive;
376 } else if (useProxy) {
377 // Bug 92006
378 request->SetHeader(nsHttp::Connection, close);
381 // Add the "Do-Not-Track" header
382 if (mDoNotTrackEnabled) {
383 rv = request->SetHeader(nsHttp::DoNotTrack,
384 NS_LITERAL_CSTRING("1"));
385 if (NS_FAILED(rv)) return rv;
388 const nsHttpAtom &header = useProxy ? nsHttp::Proxy_Connection
389 : nsHttp::Connection;
390 return request->SetHeader(header, *connectionType);
393 bool
394 nsHttpHandler::IsAcceptableEncoding(const char *enc)
396 if (!enc)
397 return false;
399 // HTTP 1.1 allows servers to send x-gzip and x-compress instead
400 // of gzip and compress, for example. So, we'll always strip off
401 // an "x-" prefix before matching the encoding to one we claim
402 // to accept.
403 if (!PL_strncasecmp(enc, "x-", 2))
404 enc += 2;
406 return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nsnull;
409 nsresult
410 nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
412 if (!mStreamConvSvc) {
413 nsresult rv;
414 mStreamConvSvc = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
415 if (NS_FAILED(rv)) return rv;
417 *result = mStreamConvSvc;
418 NS_ADDREF(*result);
419 return NS_OK;
422 nsIStrictTransportSecurityService*
423 nsHttpHandler::GetSTSService()
425 if (!mSTSService)
426 mSTSService = do_GetService(NS_STSSERVICE_CONTRACTID);
427 return mSTSService;
430 nsICookieService *
431 nsHttpHandler::GetCookieService()
433 if (!mCookieService)
434 mCookieService = do_GetService(NS_COOKIESERVICE_CONTRACTID);
435 return mCookieService;
438 nsresult
439 nsHttpHandler::GetIOService(nsIIOService** result)
441 NS_ADDREF(*result = mIOService);
442 return NS_OK;
445 PRUint32
446 nsHttpHandler::Get32BitsOfPseudoRandom()
448 // only confirm rand seeding on socket thread
449 NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
451 // rand() provides different amounts of PRNG on different platforms.
452 // 15 or 31 bits are common amounts.
454 PR_STATIC_ASSERT(RAND_MAX >= 0xfff);
456 #if RAND_MAX < 0xffffU
457 return ((PRUint16) rand() << 20) |
458 (((PRUint16) rand() & 0xfff) << 8) |
459 ((PRUint16) rand() & 0xff);
460 #elif RAND_MAX < 0xffffffffU
461 return ((PRUint16) rand() << 16) | ((PRUint16) rand() & 0xffff);
462 #else
463 return (PRUint32) rand();
464 #endif
467 void
468 nsHttpHandler::NotifyObservers(nsIHttpChannel *chan, const char *event)
470 LOG(("nsHttpHandler::NotifyObservers [chan=%x event=\"%s\"]\n", chan, event));
471 if (mObserverService)
472 mObserverService->NotifyObservers(chan, event, nsnull);
475 nsresult
476 nsHttpHandler::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
477 PRUint32 flags)
479 // TODO E10S This helper has to be initialized on the other process
480 nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
481 new nsAsyncRedirectVerifyHelper();
483 return redirectCallbackHelper->Init(oldChan, newChan, flags);
486 /* static */ nsresult
487 nsHttpHandler::GenerateHostPort(const nsCString& host, PRInt32 port,
488 nsCString& hostLine)
490 return NS_GenerateHostPort(host, port, hostLine);
493 //-----------------------------------------------------------------------------
494 // nsHttpHandler <private>
495 //-----------------------------------------------------------------------------
497 const nsAFlatCString &
498 nsHttpHandler::UserAgent()
500 if (mUserAgentOverride) {
501 LOG(("using general.useragent.override : %s\n", mUserAgentOverride.get()));
502 return mUserAgentOverride;
505 if (mUserAgentIsDirty) {
506 BuildUserAgent();
507 mUserAgentIsDirty = false;
510 return mUserAgent;
513 void
514 nsHttpHandler::BuildUserAgent()
516 LOG(("nsHttpHandler::BuildUserAgent\n"));
518 NS_ASSERTION(!mLegacyAppName.IsEmpty() &&
519 !mLegacyAppVersion.IsEmpty() &&
520 !mPlatform.IsEmpty() &&
521 !mOscpu.IsEmpty(),
522 "HTTP cannot send practical requests without this much");
524 // preallocate to worst-case size, which should always be better
525 // than if we didn't preallocate at all.
526 mUserAgent.SetCapacity(mLegacyAppName.Length() +
527 mLegacyAppVersion.Length() +
528 #ifndef UA_SPARE_PLATFORM
529 mPlatform.Length() +
530 #endif
531 mOscpu.Length() +
532 mMisc.Length() +
533 mProduct.Length() +
534 mProductSub.Length() +
535 mAppName.Length() +
536 mAppVersion.Length() +
537 mCompatFirefox.Length() +
538 mCompatDevice.Length() +
539 13);
541 // Application portion
542 mUserAgent.Assign(mLegacyAppName);
543 mUserAgent += '/';
544 mUserAgent += mLegacyAppVersion;
545 mUserAgent += ' ';
547 // Application comment
548 mUserAgent += '(';
549 #ifndef UA_SPARE_PLATFORM
550 mUserAgent += mPlatform;
551 mUserAgent.AppendLiteral("; ");
552 #endif
553 #if defined(ANDROID) || defined(MOZ_PLATFORM_MAEMO)
554 if (!mCompatDevice.IsEmpty()) {
555 mUserAgent += mCompatDevice;
556 mUserAgent.AppendLiteral("; ");
558 #else
559 mUserAgent += mOscpu;
560 mUserAgent.AppendLiteral("; ");
561 #endif
562 mUserAgent += mMisc;
563 mUserAgent += ')';
565 // Product portion
566 mUserAgent += ' ';
567 mUserAgent += mProduct;
568 mUserAgent += '/';
569 mUserAgent += mProductSub;
571 // "Firefox/x.y.z" compatibility token
572 if (!mCompatFirefox.IsEmpty()) {
573 mUserAgent += ' ';
574 mUserAgent += mCompatFirefox;
577 // App portion
578 mUserAgent += ' ';
579 mUserAgent += mAppName;
580 mUserAgent += '/';
581 mUserAgent += mAppVersion;
584 #ifdef XP_WIN
585 #define WNT_BASE "Windows NT %ld.%ld"
586 #define W64_PREFIX "; Win64"
587 #endif
589 void
590 nsHttpHandler::InitUserAgentComponents()
592 // Gather platform.
593 mPlatform.AssignLiteral(
594 #if defined(ANDROID)
595 "Android"
596 #elif defined(XP_OS2)
597 "OS/2"
598 #elif defined(XP_WIN)
599 "Windows"
600 #elif defined(XP_MACOSX)
601 "Macintosh"
602 #elif defined(MOZ_PLATFORM_MAEMO)
603 "Maemo"
604 #elif defined(MOZ_X11)
605 "X11"
606 #else
608 #endif
611 #if defined(ANDROID) || defined(MOZ_PLATFORM_MAEMO)
612 nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
613 NS_ASSERTION(infoService, "Could not find a system info service");
615 bool isTablet = false;
616 infoService->GetPropertyAsBool(NS_LITERAL_STRING("tablet"), &isTablet);
617 if (isTablet)
618 mCompatDevice.AssignLiteral("Tablet");
619 else
620 mCompatDevice.AssignLiteral("Mobile");
621 #endif
623 // Gather OS/CPU.
624 #if defined(XP_OS2)
625 ULONG os2ver = 0;
626 DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_MINOR,
627 &os2ver, sizeof(os2ver));
628 if (os2ver == 11)
629 mOscpu.AssignLiteral("2.11");
630 else if (os2ver == 30)
631 mOscpu.AssignLiteral("Warp 3");
632 else if (os2ver == 40)
633 mOscpu.AssignLiteral("Warp 4");
634 else if (os2ver == 45)
635 mOscpu.AssignLiteral("Warp 4.5");
637 #elif defined(XP_WIN)
638 OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
639 if (GetVersionEx(&info)) {
640 const char *format;
641 #if defined _M_IA64
642 format = WNT_BASE W64_PREFIX "; IA64";
643 #elif defined _M_X64 || defined _M_AMD64
644 format = WNT_BASE W64_PREFIX "; x64";
645 #else
646 BOOL isWow64 = FALSE;
647 if (!IsWow64Process(GetCurrentProcess(), &isWow64)) {
648 isWow64 = FALSE;
650 format = isWow64
651 ? WNT_BASE "; WOW64"
652 : WNT_BASE;
653 #endif
654 char *buf = PR_smprintf(format,
655 info.dwMajorVersion,
656 info.dwMinorVersion);
657 if (buf) {
658 mOscpu = buf;
659 PR_smprintf_free(buf);
662 #elif defined (XP_MACOSX)
663 #if defined(__ppc__)
664 mOscpu.AssignLiteral("PPC Mac OS X");
665 #elif defined(__i386__) || defined(__x86_64__)
666 mOscpu.AssignLiteral("Intel Mac OS X");
667 #endif
668 SInt32 majorVersion, minorVersion;
669 if ((::Gestalt(gestaltSystemVersionMajor, &majorVersion) == noErr) &&
670 (::Gestalt(gestaltSystemVersionMinor, &minorVersion) == noErr)) {
671 mOscpu += nsPrintfCString(" %d.%d", majorVersion, minorVersion);
673 #elif defined (XP_UNIX)
674 struct utsname name;
676 int ret = uname(&name);
677 if (ret >= 0) {
678 nsCAutoString buf;
679 buf = (char*)name.sysname;
681 if (strcmp(name.machine, "x86_64") == 0 &&
682 sizeof(void *) == sizeof(PRInt32)) {
683 // We're running 32-bit code on x86_64. Make this browser
684 // look like it's running on i686 hardware, but append "
685 // (x86_64)" to the end of the oscpu identifier to be able
686 // to differentiate this from someone running 64-bit code
687 // on x86_64..
689 buf += " i686 on x86_64";
690 } else {
691 buf += ' ';
693 #ifdef AIX
694 // AIX uname returns machine specific info in the uname.machine
695 // field and does not return the cpu type like other platforms.
696 // We use the AIX version and release numbers instead.
697 buf += (char*)name.version;
698 buf += '.';
699 buf += (char*)name.release;
700 #else
701 buf += (char*)name.machine;
702 #endif
705 mOscpu.Assign(buf);
707 #endif
709 mUserAgentIsDirty = true;
712 PRUint32
713 nsHttpHandler::MaxSocketCount()
715 PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce,
716 nsSocketTransportService::DiscoverMaxCount);
717 // Don't use the full max count because sockets can be held in
718 // the persistent connection pool for a long time and that could
719 // starve other users.
721 PRUint32 maxCount = nsSocketTransportService::gMaxCount;
722 if (maxCount <= 8)
723 maxCount = 1;
724 else
725 maxCount -= 8;
727 return maxCount;
730 void
731 nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
733 nsresult rv = NS_OK;
734 PRInt32 val;
736 LOG(("nsHttpHandler::PrefsChanged [pref=%s]\n", pref));
738 #define PREF_CHANGED(p) ((pref == nsnull) || !PL_strcmp(pref, p))
739 #define MULTI_PREF_CHANGED(p) \
740 ((pref == nsnull) || !PL_strncmp(pref, p, sizeof(p) - 1))
743 // UA components
746 bool cVar = false;
748 if (PREF_CHANGED(UA_PREF("compatMode.firefox"))) {
749 rv = prefs->GetBoolPref(UA_PREF("compatMode.firefox"), &cVar);
750 if (NS_SUCCEEDED(rv) && cVar) {
751 mCompatFirefox.AssignLiteral("Firefox/" MOZ_UA_FIREFOX_VERSION);
752 } else {
753 mCompatFirefox.Truncate();
755 mUserAgentIsDirty = true;
758 // general.useragent.override
759 if (PREF_CHANGED(UA_PREF("override"))) {
760 prefs->GetCharPref(UA_PREF("override"),
761 getter_Copies(mUserAgentOverride));
762 mUserAgentIsDirty = true;
766 // HTTP options
769 if (PREF_CHANGED(HTTP_PREF("keep-alive.timeout"))) {
770 rv = prefs->GetIntPref(HTTP_PREF("keep-alive.timeout"), &val);
771 if (NS_SUCCEEDED(rv))
772 mIdleTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
775 if (PREF_CHANGED(HTTP_PREF("request.max-attempts"))) {
776 rv = prefs->GetIntPref(HTTP_PREF("request.max-attempts"), &val);
777 if (NS_SUCCEEDED(rv))
778 mMaxRequestAttempts = (PRUint16) clamped(val, 1, 0xffff);
781 if (PREF_CHANGED(HTTP_PREF("request.max-start-delay"))) {
782 rv = prefs->GetIntPref(HTTP_PREF("request.max-start-delay"), &val);
783 if (NS_SUCCEEDED(rv)) {
784 mMaxRequestDelay = (PRUint16) clamped(val, 0, 0xffff);
785 if (mConnMgr)
786 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_REQUEST_DELAY,
787 mMaxRequestDelay);
791 if (PREF_CHANGED(HTTP_PREF("max-connections"))) {
792 rv = prefs->GetIntPref(HTTP_PREF("max-connections"), &val);
793 if (NS_SUCCEEDED(rv)) {
795 mMaxConnections = (PRUint16) clamped((PRUint32)val,
796 (PRUint32)1, MaxSocketCount());
798 if (mConnMgr)
799 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS,
800 mMaxConnections);
804 if (PREF_CHANGED(HTTP_PREF("max-connections-per-server"))) {
805 rv = prefs->GetIntPref(HTTP_PREF("max-connections-per-server"), &val);
806 if (NS_SUCCEEDED(rv)) {
807 mMaxConnectionsPerServer = (PRUint8) clamped(val, 1, 0xff);
808 if (mConnMgr) {
809 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS_PER_HOST,
810 mMaxConnectionsPerServer);
811 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS_PER_PROXY,
812 mMaxConnectionsPerServer);
817 if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-server"))) {
818 rv = prefs->GetIntPref(HTTP_PREF("max-persistent-connections-per-server"), &val);
819 if (NS_SUCCEEDED(rv)) {
820 mMaxPersistentConnectionsPerServer = (PRUint8) clamped(val, 1, 0xff);
821 if (mConnMgr)
822 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_HOST,
823 mMaxPersistentConnectionsPerServer);
827 if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-proxy"))) {
828 rv = prefs->GetIntPref(HTTP_PREF("max-persistent-connections-per-proxy"), &val);
829 if (NS_SUCCEEDED(rv)) {
830 mMaxPersistentConnectionsPerProxy = (PRUint8) clamped(val, 1, 0xff);
831 if (mConnMgr)
832 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
833 mMaxPersistentConnectionsPerProxy);
837 if (PREF_CHANGED(HTTP_PREF("sendRefererHeader"))) {
838 rv = prefs->GetIntPref(HTTP_PREF("sendRefererHeader"), &val);
839 if (NS_SUCCEEDED(rv))
840 mReferrerLevel = (PRUint8) clamped(val, 0, 0xff);
843 if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) {
844 rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val);
845 if (NS_SUCCEEDED(rv))
846 mRedirectionLimit = (PRUint8) clamped(val, 0, 0xff);
849 if (PREF_CHANGED(HTTP_PREF("connection-retry-timeout"))) {
850 rv = prefs->GetIntPref(HTTP_PREF("connection-retry-timeout"), &val);
851 if (NS_SUCCEEDED(rv))
852 mIdleSynTimeout = (PRUint16) clamped(val, 0, 3000);
855 if (PREF_CHANGED(HTTP_PREF("fast-fallback-to-IPv4"))) {
856 rv = prefs->GetBoolPref(HTTP_PREF("fast-fallback-to-IPv4"), &cVar);
857 if (NS_SUCCEEDED(rv))
858 mFastFallbackToIPv4 = cVar;
861 if (PREF_CHANGED(HTTP_PREF("version"))) {
862 nsXPIDLCString httpVersion;
863 prefs->GetCharPref(HTTP_PREF("version"), getter_Copies(httpVersion));
864 if (httpVersion) {
865 if (!PL_strcmp(httpVersion, "1.1"))
866 mHttpVersion = NS_HTTP_VERSION_1_1;
867 else if (!PL_strcmp(httpVersion, "0.9"))
868 mHttpVersion = NS_HTTP_VERSION_0_9;
869 else
870 mHttpVersion = NS_HTTP_VERSION_1_0;
874 if (PREF_CHANGED(HTTP_PREF("proxy.version"))) {
875 nsXPIDLCString httpVersion;
876 prefs->GetCharPref(HTTP_PREF("proxy.version"), getter_Copies(httpVersion));
877 if (httpVersion) {
878 if (!PL_strcmp(httpVersion, "1.1"))
879 mProxyHttpVersion = NS_HTTP_VERSION_1_1;
880 else
881 mProxyHttpVersion = NS_HTTP_VERSION_1_0;
882 // it does not make sense to issue a HTTP/0.9 request to a proxy server
886 if (PREF_CHANGED(HTTP_PREF("keep-alive"))) {
887 rv = prefs->GetBoolPref(HTTP_PREF("keep-alive"), &cVar);
888 if (NS_SUCCEEDED(rv)) {
889 if (cVar)
890 mCapabilities |= NS_HTTP_ALLOW_KEEPALIVE;
891 else
892 mCapabilities &= ~NS_HTTP_ALLOW_KEEPALIVE;
896 if (PREF_CHANGED(HTTP_PREF("proxy.keep-alive"))) {
897 rv = prefs->GetBoolPref(HTTP_PREF("proxy.keep-alive"), &cVar);
898 if (NS_SUCCEEDED(rv)) {
899 if (cVar)
900 mProxyCapabilities |= NS_HTTP_ALLOW_KEEPALIVE;
901 else
902 mProxyCapabilities &= ~NS_HTTP_ALLOW_KEEPALIVE;
906 if (PREF_CHANGED(HTTP_PREF("pipelining"))) {
907 rv = prefs->GetBoolPref(HTTP_PREF("pipelining"), &cVar);
908 if (NS_SUCCEEDED(rv)) {
909 if (cVar)
910 mCapabilities |= NS_HTTP_ALLOW_PIPELINING;
911 else
912 mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
916 if (PREF_CHANGED(HTTP_PREF("pipelining.maxrequests"))) {
917 rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxrequests"), &val);
918 if (NS_SUCCEEDED(rv)) {
919 mMaxPipelinedRequests = clamped(val, 1, 0xffff);
920 if (mConnMgr)
921 mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_PIPELINED_REQUESTS,
922 mMaxPipelinedRequests);
926 if (PREF_CHANGED(HTTP_PREF("pipelining.max-optimistic-requests"))) {
927 rv = prefs->
928 GetIntPref(HTTP_PREF("pipelining.max-optimistic-requests"), &val);
929 if (NS_SUCCEEDED(rv)) {
930 mMaxOptimisticPipelinedRequests = clamped(val, 1, 0xffff);
931 if (mConnMgr)
932 mConnMgr->UpdateParam
933 (nsHttpConnectionMgr::MAX_OPTIMISTIC_PIPELINED_REQUESTS,
934 mMaxOptimisticPipelinedRequests);
938 if (PREF_CHANGED(HTTP_PREF("pipelining.aggressive"))) {
939 rv = prefs->GetBoolPref(HTTP_PREF("pipelining.aggressive"), &cVar);
940 if (NS_SUCCEEDED(rv))
941 mPipelineAggressive = cVar;
944 if (PREF_CHANGED(HTTP_PREF("pipelining.maxsize"))) {
945 rv = prefs->GetIntPref(HTTP_PREF("pipelining.maxsize"), &val);
946 if (NS_SUCCEEDED(rv)) {
947 mMaxPipelineObjectSize =
948 static_cast<PRInt64>(clamped(val, 1000, 100000000));
952 // Determines whether or not to actually reschedule after the
953 // reschedule-timeout has expired
954 if (PREF_CHANGED(HTTP_PREF("pipelining.reschedule-on-timeout"))) {
955 rv = prefs->GetBoolPref(HTTP_PREF("pipelining.reschedule-on-timeout"),
956 &cVar);
957 if (NS_SUCCEEDED(rv))
958 mPipelineRescheduleOnTimeout = cVar;
961 // The amount of time head of line blocking is allowed (in ms)
962 // before the blocked transactions are moved to another pipeline
963 if (PREF_CHANGED(HTTP_PREF("pipelining.reschedule-timeout"))) {
964 rv = prefs->GetIntPref(HTTP_PREF("pipelining.reschedule-timeout"),
965 &val);
966 if (NS_SUCCEEDED(rv)) {
967 mPipelineRescheduleTimeout =
968 PR_MillisecondsToInterval((PRUint16) clamped(val, 500, 0xffff));
972 // The amount of time a pipelined transaction is allowed to wait before
973 // being canceled and retried in a non-pipeline connection
974 if (PREF_CHANGED(HTTP_PREF("pipelining.read-timeout"))) {
975 rv = prefs->GetIntPref(HTTP_PREF("pipelining.read-timeout"), &val);
976 if (NS_SUCCEEDED(rv)) {
977 mPipelineReadTimeout =
978 PR_MillisecondsToInterval((PRUint16) clamped(val, 5000,
979 0xffff));
983 if (PREF_CHANGED(HTTP_PREF("pipelining.ssl"))) {
984 rv = prefs->GetBoolPref(HTTP_PREF("pipelining.ssl"), &cVar);
985 if (NS_SUCCEEDED(rv))
986 mPipeliningOverSSL = cVar;
989 if (PREF_CHANGED(HTTP_PREF("proxy.pipelining"))) {
990 rv = prefs->GetBoolPref(HTTP_PREF("proxy.pipelining"), &cVar);
991 if (NS_SUCCEEDED(rv)) {
992 if (cVar)
993 mProxyCapabilities |= NS_HTTP_ALLOW_PIPELINING;
994 else
995 mProxyCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
999 if (PREF_CHANGED(HTTP_PREF("qos"))) {
1000 rv = prefs->GetIntPref(HTTP_PREF("qos"), &val);
1001 if (NS_SUCCEEDED(rv))
1002 mQoSBits = (PRUint8) clamped(val, 0, 0xff);
1005 if (PREF_CHANGED(HTTP_PREF("sendSecureXSiteReferrer"))) {
1006 rv = prefs->GetBoolPref(HTTP_PREF("sendSecureXSiteReferrer"), &cVar);
1007 if (NS_SUCCEEDED(rv))
1008 mSendSecureXSiteReferrer = cVar;
1011 if (PREF_CHANGED(HTTP_PREF("accept.default"))) {
1012 nsXPIDLCString accept;
1013 rv = prefs->GetCharPref(HTTP_PREF("accept.default"),
1014 getter_Copies(accept));
1015 if (NS_SUCCEEDED(rv))
1016 SetAccept(accept);
1019 if (PREF_CHANGED(HTTP_PREF("accept-encoding"))) {
1020 nsXPIDLCString acceptEncodings;
1021 rv = prefs->GetCharPref(HTTP_PREF("accept-encoding"),
1022 getter_Copies(acceptEncodings));
1023 if (NS_SUCCEEDED(rv))
1024 SetAcceptEncodings(acceptEncodings);
1027 if (PREF_CHANGED(HTTP_PREF("use-cache"))) {
1028 rv = prefs->GetBoolPref(HTTP_PREF("use-cache"), &cVar);
1029 if (NS_SUCCEEDED(rv)) {
1030 mUseCache = cVar;
1034 if (PREF_CHANGED(HTTP_PREF("default-socket-type"))) {
1035 nsXPIDLCString sval;
1036 rv = prefs->GetCharPref(HTTP_PREF("default-socket-type"),
1037 getter_Copies(sval));
1038 if (NS_SUCCEEDED(rv)) {
1039 if (sval.IsEmpty())
1040 mDefaultSocketType.Adopt(0);
1041 else {
1042 // verify that this socket type is actually valid
1043 nsCOMPtr<nsISocketProviderService> sps(
1044 do_GetService(NS_SOCKETPROVIDERSERVICE_CONTRACTID));
1045 if (sps) {
1046 nsCOMPtr<nsISocketProvider> sp;
1047 rv = sps->GetSocketProvider(sval, getter_AddRefs(sp));
1048 if (NS_SUCCEEDED(rv)) {
1049 // OK, this looks like a valid socket provider.
1050 mDefaultSocketType.Assign(sval);
1057 if (PREF_CHANGED(HTTP_PREF("prompt-temp-redirect"))) {
1058 rv = prefs->GetBoolPref(HTTP_PREF("prompt-temp-redirect"), &cVar);
1059 if (NS_SUCCEEDED(rv)) {
1060 mPromptTempRedirect = cVar;
1064 if (PREF_CHANGED(HTTP_PREF("assoc-req.enforce"))) {
1065 cVar = false;
1066 rv = prefs->GetBoolPref(HTTP_PREF("assoc-req.enforce"), &cVar);
1067 if (NS_SUCCEEDED(rv))
1068 mEnforceAssocReq = cVar;
1071 // enable Persistent caching for HTTPS - bug#205921
1072 if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
1073 cVar = false;
1074 rv = prefs->GetBoolPref(BROWSER_PREF("disk_cache_ssl"), &cVar);
1075 if (NS_SUCCEEDED(rv))
1076 mEnablePersistentHttpsCaching = cVar;
1079 if (PREF_CHANGED(HTTP_PREF("phishy-userpass-length"))) {
1080 rv = prefs->GetIntPref(HTTP_PREF("phishy-userpass-length"), &val);
1081 if (NS_SUCCEEDED(rv))
1082 mPhishyUserPassLength = (PRUint8) clamped(val, 0, 0xff);
1085 if (PREF_CHANGED(HTTP_PREF("spdy.enabled"))) {
1086 rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled"), &cVar);
1087 if (NS_SUCCEEDED(rv))
1088 mEnableSpdy = cVar;
1091 if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v2"))) {
1092 rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v2"), &cVar);
1093 if (NS_SUCCEEDED(rv))
1094 mSpdyV2 = cVar;
1097 if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v3"))) {
1098 rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v3"), &cVar);
1099 if (NS_SUCCEEDED(rv))
1100 mSpdyV3 = cVar;
1103 if (PREF_CHANGED(HTTP_PREF("spdy.coalesce-hostnames"))) {
1104 rv = prefs->GetBoolPref(HTTP_PREF("spdy.coalesce-hostnames"), &cVar);
1105 if (NS_SUCCEEDED(rv))
1106 mCoalesceSpdy = cVar;
1109 if (PREF_CHANGED(HTTP_PREF("spdy.use-alternate-protocol"))) {
1110 rv = prefs->GetBoolPref(HTTP_PREF("spdy.use-alternate-protocol"),
1111 &cVar);
1112 if (NS_SUCCEEDED(rv))
1113 mUseAlternateProtocol = cVar;
1116 if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) {
1117 rv = prefs->GetIntPref(HTTP_PREF("spdy.timeout"), &val);
1118 if (NS_SUCCEEDED(rv))
1119 mSpdyTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
1122 if (PREF_CHANGED(HTTP_PREF("spdy.chunk-size"))) {
1123 rv = prefs->GetIntPref(HTTP_PREF("spdy.chunk-size"), &val);
1124 if (NS_SUCCEEDED(rv))
1125 mSpdySendingChunkSize = (PRUint32) clamped(val, 1, 0x7fffffff);
1128 // The amount of idle seconds on a spdy connection before initiating a
1129 // server ping. 0 will disable.
1130 if (PREF_CHANGED(HTTP_PREF("spdy.ping-threshold"))) {
1131 rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-threshold"), &val);
1132 if (NS_SUCCEEDED(rv))
1133 mSpdyPingThreshold =
1134 PR_SecondsToInterval((PRUint16) clamped(val, 0, 0x7fffffff));
1137 // The amount of seconds to wait for a spdy ping response before
1138 // closing the session.
1139 if (PREF_CHANGED(HTTP_PREF("spdy.ping-timeout"))) {
1140 rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-timeout"), &val);
1141 if (NS_SUCCEEDED(rv))
1142 mSpdyPingTimeout =
1143 PR_SecondsToInterval((PRUint16) clamped(val, 0, 0x7fffffff));
1147 // INTL options
1150 if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) {
1151 nsCOMPtr<nsIPrefLocalizedString> pls;
1152 prefs->GetComplexValue(INTL_ACCEPT_LANGUAGES,
1153 NS_GET_IID(nsIPrefLocalizedString),
1154 getter_AddRefs(pls));
1155 if (pls) {
1156 nsXPIDLString uval;
1157 pls->ToString(getter_Copies(uval));
1158 if (uval)
1159 SetAcceptLanguages(NS_ConvertUTF16toUTF8(uval).get());
1164 // IDN options
1167 if (PREF_CHANGED(NETWORK_ENABLEIDN)) {
1168 bool enableIDN = false;
1169 prefs->GetBoolPref(NETWORK_ENABLEIDN, &enableIDN);
1170 // No locking is required here since this method runs in the main
1171 // UI thread, and so do all the methods in nsHttpChannel.cpp
1172 // (mIDNConverter is used by nsHttpChannel)
1173 if (enableIDN && !mIDNConverter) {
1174 mIDNConverter = do_GetService(NS_IDNSERVICE_CONTRACTID);
1175 NS_ASSERTION(mIDNConverter, "idnSDK not installed");
1177 else if (!enableIDN && mIDNConverter)
1178 mIDNConverter = nsnull;
1182 // Tracking options
1185 if (PREF_CHANGED(DONOTTRACK_HEADER_ENABLED)) {
1186 cVar = false;
1187 rv = prefs->GetBoolPref(DONOTTRACK_HEADER_ENABLED, &cVar);
1188 if (NS_SUCCEEDED(rv)) {
1189 mDoNotTrackEnabled = cVar;
1194 // Telemetry
1197 if (PREF_CHANGED(TELEMETRY_ENABLED)) {
1198 cVar = false;
1199 rv = prefs->GetBoolPref(TELEMETRY_ENABLED, &cVar);
1200 if (NS_SUCCEEDED(rv)) {
1201 mTelemetryEnabled = cVar;
1206 // network.allow-experiments
1209 if (PREF_CHANGED(ALLOW_EXPERIMENTS)) {
1210 cVar = true;
1211 rv = prefs->GetBoolPref(ALLOW_EXPERIMENTS, &cVar);
1212 if (NS_SUCCEEDED(rv)) {
1213 mAllowExperiments = cVar;
1217 #undef PREF_CHANGED
1218 #undef MULTI_PREF_CHANGED
1222 * Allocates a C string into that contains a ISO 639 language list
1223 * notated with HTTP "q" values for output with a HTTP Accept-Language
1224 * header. Previous q values will be stripped because the order of
1225 * the langs imply the q value. The q values are calculated by dividing
1226 * 1.0 amongst the number of languages present.
1228 * Ex: passing: "en, ja"
1229 * returns: "en,ja;q=0.5"
1231 * passing: "en, ja, fr_CA"
1232 * returns: "en,ja;q=0.7,fr_CA;q=0.3"
1234 static nsresult
1235 PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLanguages)
1237 if (!i_AcceptLanguages)
1238 return NS_OK;
1240 PRUint32 n, size, wrote;
1241 double q, dec;
1242 char *p, *p2, *token, *q_Accept, *o_Accept;
1243 const char *comma;
1244 PRInt32 available;
1246 o_Accept = nsCRT::strdup(i_AcceptLanguages);
1247 if (!o_Accept)
1248 return NS_ERROR_OUT_OF_MEMORY;
1249 for (p = o_Accept, n = size = 0; '\0' != *p; p++) {
1250 if (*p == ',') n++;
1251 size++;
1254 available = size + ++n * 11 + 1;
1255 q_Accept = new char[available];
1256 if (!q_Accept) {
1257 nsCRT::free(o_Accept);
1258 return NS_ERROR_OUT_OF_MEMORY;
1260 *q_Accept = '\0';
1261 q = 1.0;
1262 dec = q / (double) n;
1263 n = 0;
1264 p2 = q_Accept;
1265 for (token = nsCRT::strtok(o_Accept, ",", &p);
1266 token != (char *) 0;
1267 token = nsCRT::strtok(p, ",", &p))
1269 token = net_FindCharNotInSet(token, HTTP_LWS);
1270 char* trim;
1271 trim = net_FindCharInSet(token, ";" HTTP_LWS);
1272 if (trim != (char*)0) // remove "; q=..." if present
1273 *trim = '\0';
1275 if (*token != '\0') {
1276 comma = n++ != 0 ? "," : ""; // delimiter if not first item
1277 PRUint32 u = QVAL_TO_UINT(q);
1278 if (u < 10)
1279 wrote = PR_snprintf(p2, available, "%s%s;q=0.%u", comma, token, u);
1280 else
1281 wrote = PR_snprintf(p2, available, "%s%s", comma, token);
1282 q -= dec;
1283 p2 += wrote;
1284 available -= wrote;
1285 NS_ASSERTION(available > 0, "allocated string not long enough");
1288 nsCRT::free(o_Accept);
1290 o_AcceptLanguages.Assign((const char *) q_Accept);
1291 delete [] q_Accept;
1293 return NS_OK;
1296 nsresult
1297 nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages)
1299 nsCAutoString buf;
1300 nsresult rv = PrepareAcceptLanguages(aAcceptLanguages, buf);
1301 if (NS_SUCCEEDED(rv))
1302 mAcceptLanguages.Assign(buf);
1303 return rv;
1306 nsresult
1307 nsHttpHandler::SetAccept(const char *aAccept)
1309 mAccept = aAccept;
1310 return NS_OK;
1313 nsresult
1314 nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings)
1316 mAcceptEncodings = aAcceptEncodings;
1317 return NS_OK;
1320 //-----------------------------------------------------------------------------
1321 // nsHttpHandler::nsISupports
1322 //-----------------------------------------------------------------------------
1324 NS_IMPL_THREADSAFE_ISUPPORTS6(nsHttpHandler,
1325 nsIHttpProtocolHandler,
1326 nsIProxiedProtocolHandler,
1327 nsIProtocolHandler,
1328 nsIObserver,
1329 nsISupportsWeakReference,
1330 nsISpeculativeConnect)
1332 //-----------------------------------------------------------------------------
1333 // nsHttpHandler::nsIProtocolHandler
1334 //-----------------------------------------------------------------------------
1336 NS_IMETHODIMP
1337 nsHttpHandler::GetScheme(nsACString &aScheme)
1339 aScheme.AssignLiteral("http");
1340 return NS_OK;
1343 NS_IMETHODIMP
1344 nsHttpHandler::GetDefaultPort(PRInt32 *result)
1346 *result = NS_HTTP_DEFAULT_PORT;
1347 return NS_OK;
1350 NS_IMETHODIMP
1351 nsHttpHandler::GetProtocolFlags(PRUint32 *result)
1353 *result = NS_HTTP_PROTOCOL_FLAGS;
1354 return NS_OK;
1357 NS_IMETHODIMP
1358 nsHttpHandler::NewURI(const nsACString &aSpec,
1359 const char *aCharset,
1360 nsIURI *aBaseURI,
1361 nsIURI **aURI)
1363 LOG(("nsHttpHandler::NewURI\n"));
1364 return ::NewURI(aSpec, aCharset, aBaseURI, NS_HTTP_DEFAULT_PORT, aURI);
1367 NS_IMETHODIMP
1368 nsHttpHandler::NewChannel(nsIURI *uri, nsIChannel **result)
1370 LOG(("nsHttpHandler::NewChannel\n"));
1372 NS_ENSURE_ARG_POINTER(uri);
1373 NS_ENSURE_ARG_POINTER(result);
1375 bool isHttp = false, isHttps = false;
1377 // Verify that we have been given a valid scheme
1378 nsresult rv = uri->SchemeIs("http", &isHttp);
1379 if (NS_FAILED(rv)) return rv;
1380 if (!isHttp) {
1381 rv = uri->SchemeIs("https", &isHttps);
1382 if (NS_FAILED(rv)) return rv;
1383 if (!isHttps) {
1384 NS_WARNING("Invalid URI scheme");
1385 return NS_ERROR_UNEXPECTED;
1389 return NewProxiedChannel(uri, nsnull, result);
1392 NS_IMETHODIMP
1393 nsHttpHandler::AllowPort(PRInt32 port, const char *scheme, bool *_retval)
1395 // don't override anything.
1396 *_retval = false;
1397 return NS_OK;
1400 //-----------------------------------------------------------------------------
1401 // nsHttpHandler::nsIProxiedProtocolHandler
1402 //-----------------------------------------------------------------------------
1404 NS_IMETHODIMP
1405 nsHttpHandler::NewProxiedChannel(nsIURI *uri,
1406 nsIProxyInfo* givenProxyInfo,
1407 nsIChannel **result)
1409 nsRefPtr<HttpBaseChannel> httpChannel;
1411 LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n",
1412 givenProxyInfo));
1414 nsCOMPtr<nsProxyInfo> proxyInfo;
1415 if (givenProxyInfo) {
1416 proxyInfo = do_QueryInterface(givenProxyInfo);
1417 NS_ENSURE_ARG(proxyInfo);
1420 bool https;
1421 nsresult rv = uri->SchemeIs("https", &https);
1422 if (NS_FAILED(rv))
1423 return rv;
1425 if (IsNeckoChild()) {
1426 httpChannel = new HttpChannelChild();
1427 } else {
1428 httpChannel = new nsHttpChannel();
1431 // select proxy caps if using a non-transparent proxy. SSL tunneling
1432 // should not use proxy settings.
1433 PRInt8 caps;
1434 if (proxyInfo && !nsCRT::strcmp(proxyInfo->Type(), "http") && !https)
1435 caps = mProxyCapabilities;
1436 else
1437 caps = mCapabilities;
1439 if (https) {
1440 // enable pipelining over SSL if requested
1441 if (mPipeliningOverSSL)
1442 caps |= NS_HTTP_ALLOW_PIPELINING;
1444 if (!IsNeckoChild()) {
1445 // HACK: make sure PSM gets initialized on the main thread.
1446 net_EnsurePSMInit();
1450 rv = httpChannel->Init(uri, caps, proxyInfo);
1451 if (NS_FAILED(rv))
1452 return rv;
1454 httpChannel.forget(result);
1455 return NS_OK;
1458 //-----------------------------------------------------------------------------
1459 // nsHttpHandler::nsIHttpProtocolHandler
1460 //-----------------------------------------------------------------------------
1462 NS_IMETHODIMP
1463 nsHttpHandler::GetUserAgent(nsACString &value)
1465 value = UserAgent();
1466 return NS_OK;
1469 NS_IMETHODIMP
1470 nsHttpHandler::GetAppName(nsACString &value)
1472 value = mLegacyAppName;
1473 return NS_OK;
1476 NS_IMETHODIMP
1477 nsHttpHandler::GetAppVersion(nsACString &value)
1479 value = mLegacyAppVersion;
1480 return NS_OK;
1483 NS_IMETHODIMP
1484 nsHttpHandler::GetProduct(nsACString &value)
1486 value = mProduct;
1487 return NS_OK;
1490 NS_IMETHODIMP
1491 nsHttpHandler::GetProductSub(nsACString &value)
1493 value = mProductSub;
1494 return NS_OK;
1497 NS_IMETHODIMP
1498 nsHttpHandler::GetPlatform(nsACString &value)
1500 value = mPlatform;
1501 return NS_OK;
1504 NS_IMETHODIMP
1505 nsHttpHandler::GetOscpu(nsACString &value)
1507 value = mOscpu;
1508 return NS_OK;
1511 NS_IMETHODIMP
1512 nsHttpHandler::GetMisc(nsACString &value)
1514 value = mMisc;
1515 return NS_OK;
1518 //-----------------------------------------------------------------------------
1519 // nsHttpHandler::nsIObserver
1520 //-----------------------------------------------------------------------------
1522 NS_IMETHODIMP
1523 nsHttpHandler::Observe(nsISupports *subject,
1524 const char *topic,
1525 const PRUnichar *data)
1527 LOG(("nsHttpHandler::Observe [topic=\"%s\"]\n", topic));
1529 if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
1530 nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
1531 if (prefBranch)
1532 PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
1534 else if (strcmp(topic, "profile-change-net-teardown") == 0 ||
1535 strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
1537 // clear cache of all authentication credentials.
1538 mAuthCache.ClearAll();
1540 // ensure connection manager is shutdown
1541 if (mConnMgr)
1542 mConnMgr->Shutdown();
1544 // need to reset the session start time since cache validation may
1545 // depend on this value.
1546 mSessionStartTime = NowInSeconds();
1548 else if (strcmp(topic, "profile-change-net-restore") == 0) {
1549 // initialize connection manager
1550 InitConnectionMgr();
1552 else if (strcmp(topic, "net:clear-active-logins") == 0) {
1553 mAuthCache.ClearAll();
1555 else if (strcmp(topic, "net:prune-dead-connections") == 0) {
1556 if (mConnMgr) {
1557 mConnMgr->PruneDeadConnections();
1560 else if (strcmp(topic, "net:failed-to-process-uri-content") == 0) {
1561 nsCOMPtr<nsIURI> uri = do_QueryInterface(subject);
1562 if (uri && mConnMgr)
1563 mConnMgr->ReportFailedToProcess(uri);
1566 return NS_OK;
1569 // nsISpeculativeConnect
1571 NS_IMETHODIMP
1572 nsHttpHandler::SpeculativeConnect(nsIURI *aURI,
1573 nsIInterfaceRequestor *aCallbacks,
1574 nsIEventTarget *aTarget)
1576 nsIStrictTransportSecurityService* stss = gHttpHandler->GetSTSService();
1577 bool isStsHost = false;
1578 if (!stss)
1579 return NS_OK;
1581 nsCOMPtr<nsIURI> clone;
1582 if (NS_SUCCEEDED(stss->IsStsURI(aURI, &isStsHost)) && isStsHost) {
1583 if (NS_SUCCEEDED(aURI->Clone(getter_AddRefs(clone)))) {
1584 clone->SetScheme(NS_LITERAL_CSTRING("https"));
1585 aURI = clone.get();
1589 nsCAutoString scheme;
1590 nsresult rv = aURI->GetScheme(scheme);
1591 if (NS_FAILED(rv))
1592 return rv;
1594 // If this is HTTPS, make sure PSM is initialized as the channel
1595 // creation path may have been bypassed
1596 if (scheme.EqualsLiteral("https")) {
1597 if (!IsNeckoChild()) {
1598 // make sure PSM gets initialized on the main thread.
1599 net_EnsurePSMInit();
1602 // Ensure that this is HTTP or HTTPS, otherwise we don't do preconnect here
1603 else if (!scheme.EqualsLiteral("http"))
1604 return NS_ERROR_UNEXPECTED;
1606 // Construct connection info object
1607 bool usingSSL = false;
1608 rv = aURI->SchemeIs("https", &usingSSL);
1609 if (NS_FAILED(rv))
1610 return rv;
1612 nsCAutoString host;
1613 rv = aURI->GetAsciiHost(host);
1614 if (NS_FAILED(rv))
1615 return rv;
1617 PRInt32 port = -1;
1618 rv = aURI->GetPort(&port);
1619 if (NS_FAILED(rv))
1620 return rv;
1622 nsHttpConnectionInfo *ci =
1623 new nsHttpConnectionInfo(host, port, nsnull, usingSSL);
1625 return SpeculativeConnect(ci, aCallbacks, aTarget);
1628 //-----------------------------------------------------------------------------
1629 // nsHttpsHandler implementation
1630 //-----------------------------------------------------------------------------
1632 NS_IMPL_THREADSAFE_ISUPPORTS5(nsHttpsHandler,
1633 nsIHttpProtocolHandler,
1634 nsIProxiedProtocolHandler,
1635 nsIProtocolHandler,
1636 nsISupportsWeakReference,
1637 nsISpeculativeConnect)
1639 nsresult
1640 nsHttpsHandler::Init()
1642 nsCOMPtr<nsIProtocolHandler> httpHandler(
1643 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http"));
1644 NS_ASSERTION(httpHandler.get() != nsnull, "no http handler?");
1645 return NS_OK;
1648 NS_IMETHODIMP
1649 nsHttpsHandler::GetScheme(nsACString &aScheme)
1651 aScheme.AssignLiteral("https");
1652 return NS_OK;
1655 NS_IMETHODIMP
1656 nsHttpsHandler::GetDefaultPort(PRInt32 *aPort)
1658 *aPort = NS_HTTPS_DEFAULT_PORT;
1659 return NS_OK;
1662 NS_IMETHODIMP
1663 nsHttpsHandler::GetProtocolFlags(PRUint32 *aProtocolFlags)
1665 *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS;
1666 return NS_OK;
1669 NS_IMETHODIMP
1670 nsHttpsHandler::NewURI(const nsACString &aSpec,
1671 const char *aOriginCharset,
1672 nsIURI *aBaseURI,
1673 nsIURI **_retval)
1675 return ::NewURI(aSpec, aOriginCharset, aBaseURI, NS_HTTPS_DEFAULT_PORT, _retval);
1678 NS_IMETHODIMP
1679 nsHttpsHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
1681 NS_ABORT_IF_FALSE(gHttpHandler, "Should have a HTTP handler by now.");
1682 if (!gHttpHandler)
1683 return NS_ERROR_UNEXPECTED;
1684 return gHttpHandler->NewChannel(aURI, _retval);
1687 NS_IMETHODIMP
1688 nsHttpsHandler::AllowPort(PRInt32 aPort, const char *aScheme, bool *_retval)
1690 // don't override anything.
1691 *_retval = false;
1692 return NS_OK;