TRRServiceChannel.cpp (48814B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 : */ 3 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include "TRRServiceChannel.h" 9 10 #include "HttpLog.h" 11 #include "AltServiceChild.h" 12 #include "mozilla/glean/NetwerkMetrics.h" 13 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 14 #include "mozilla/ScopeExit.h" 15 #include "mozilla/StaticPrefs_network.h" 16 #include "nsDNSPrefetch.h" 17 #include "nsEscape.h" 18 #include "nsHttpTransaction.h" 19 #include "nsICancelable.h" 20 #include "nsICachingChannel.h" 21 #include "nsIProtocolProxyService2.h" 22 #include "nsIOService.h" 23 #include "nsISeekableStream.h" 24 #include "nsURLHelper.h" 25 #include "ProxyConfigLookup.h" 26 #include "TRRLoadInfo.h" 27 #include "ReferrerInfo.h" 28 #include "TRR.h" 29 #include "TRRService.h" 30 31 namespace mozilla::net { 32 33 NS_IMPL_ADDREF(TRRServiceChannel) 34 35 // Because nsSupportsWeakReference isn't thread-safe we must ensure that 36 // TRRServiceChannel is destroyed on the target thread. Any Release() called 37 // on a different thread is dispatched to the target thread. 38 bool TRRServiceChannel::DispatchRelease() { 39 if (mCurrentEventTarget->IsOnCurrentThread()) { 40 return false; 41 } 42 43 mCurrentEventTarget->Dispatch( 44 NewNonOwningRunnableMethod("net::TRRServiceChannel::Release", this, 45 &TRRServiceChannel::Release), 46 NS_DISPATCH_NORMAL); 47 48 return true; 49 } 50 51 NS_IMETHODIMP_(MozExternalRefCountType) 52 TRRServiceChannel::Release() { 53 nsrefcnt count = mRefCnt - 1; 54 if (DispatchRelease()) { 55 // Redispatched to the target thread. 56 return count; 57 } 58 59 MOZ_ASSERT(0 != mRefCnt, "dup release"); 60 count = --mRefCnt; 61 NS_LOG_RELEASE(this, count, "TRRServiceChannel"); 62 63 if (0 == count) { 64 mRefCnt = 1; 65 delete (this); 66 return 0; 67 } 68 69 return count; 70 } 71 72 NS_INTERFACE_MAP_BEGIN(TRRServiceChannel) 73 NS_INTERFACE_MAP_ENTRY(nsIRequest) 74 NS_INTERFACE_MAP_ENTRY(nsIChannel) 75 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) 76 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) 77 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) 78 NS_INTERFACE_MAP_ENTRY(nsIClassOfService) 79 NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) 80 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback) 81 NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 82 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 83 NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) 84 NS_INTERFACE_MAP_ENTRY(nsIDNSListener) 85 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 86 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2) 87 NS_INTERFACE_MAP_ENTRY_CONCRETE(TRRServiceChannel) 88 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) 89 90 TRRServiceChannel::TRRServiceChannel() 91 : HttpAsyncAborter<TRRServiceChannel>(this), 92 mProxyRequest(nullptr, "TRRServiceChannel::mProxyRequest"), 93 mCurrentEventTarget(GetCurrentSerialEventTarget()) { 94 LOG(("TRRServiceChannel ctor [this=%p]\n", this)); 95 } 96 97 TRRServiceChannel::~TRRServiceChannel() { 98 LOG(("TRRServiceChannel dtor [this=%p]\n", this)); 99 } 100 101 NS_IMETHODIMP TRRServiceChannel::SetCanceledReason(const nsACString& aReason) { 102 return SetCanceledReasonImpl(aReason); 103 } 104 105 NS_IMETHODIMP TRRServiceChannel::GetCanceledReason(nsACString& aReason) { 106 return GetCanceledReasonImpl(aReason); 107 } 108 109 NS_IMETHODIMP 110 TRRServiceChannel::CancelWithReason(nsresult aStatus, 111 const nsACString& aReason) { 112 return CancelWithReasonImpl(aStatus, aReason); 113 } 114 115 NS_IMETHODIMP 116 TRRServiceChannel::Cancel(nsresult status) { 117 LOG(("TRRServiceChannel::Cancel [this=%p status=%" PRIx32 "]\n", this, 118 static_cast<uint32_t>(status))); 119 if (mCanceled) { 120 LOG((" ignoring; already canceled\n")); 121 return NS_OK; 122 } 123 124 mCanceled = true; 125 mStatus = status; 126 127 nsCOMPtr<nsICancelable> proxyRequest; 128 { 129 auto req = mProxyRequest.Lock(); 130 proxyRequest.swap(*req); 131 } 132 133 if (proxyRequest) { 134 NS_DispatchToMainThread( 135 NS_NewRunnableFunction( 136 "CancelProxyRequest", 137 [proxyRequest, status]() { proxyRequest->Cancel(status); }), 138 NS_DISPATCH_NORMAL); 139 } 140 141 CancelNetworkRequest(status); 142 return NS_OK; 143 } 144 145 void TRRServiceChannel::CancelNetworkRequest(nsresult aStatus) { 146 if (mTransaction) { 147 nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus); 148 if (NS_FAILED(rv)) { 149 LOG(("failed to cancel the transaction\n")); 150 } 151 } 152 if (mTransactionPump) mTransactionPump->Cancel(aStatus); 153 } 154 155 NS_IMETHODIMP 156 TRRServiceChannel::Suspend() { 157 LOG(("TRRServiceChannel::SuspendInternal [this=%p]\n", this)); 158 159 if (mTransactionPump) { 160 return mTransactionPump->Suspend(); 161 } 162 163 return NS_OK; 164 } 165 166 NS_IMETHODIMP 167 TRRServiceChannel::Resume() { 168 LOG(("TRRServiceChannel::Resume [this=%p]\n", this)); 169 170 if (mTransactionPump) { 171 return mTransactionPump->Resume(); 172 } 173 174 return NS_OK; 175 } 176 177 NS_IMETHODIMP 178 TRRServiceChannel::GetSecurityInfo(nsITransportSecurityInfo** securityInfo) { 179 NS_ENSURE_ARG_POINTER(securityInfo); 180 *securityInfo = do_AddRef(mSecurityInfo).take(); 181 return NS_OK; 182 } 183 184 NS_IMETHODIMP 185 TRRServiceChannel::AsyncOpen(nsIStreamListener* aListener) { 186 NS_ENSURE_ARG_POINTER(aListener); 187 NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS); 188 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED); 189 190 if (mCanceled) { 191 ReleaseListeners(); 192 return mStatus; 193 } 194 195 // HttpBaseChannel::MaybeWaitForUploadStreamNormalization can only be used on 196 // main thread, so we can only return an error here. 197 #ifdef NIGHTLY_BUILD 198 MOZ_ASSERT(!LoadPendingUploadStreamNormalization()); 199 #endif 200 if (LoadPendingUploadStreamNormalization()) { 201 return NS_ERROR_FAILURE; 202 } 203 204 if (!gHttpHandler->Active()) { 205 LOG((" after HTTP shutdown...")); 206 ReleaseListeners(); 207 return NS_ERROR_NOT_AVAILABLE; 208 } 209 210 nsresult rv = NS_CheckPortSafety(mURI); 211 if (NS_FAILED(rv)) { 212 ReleaseListeners(); 213 return rv; 214 } 215 216 StoreIsPending(true); 217 StoreWasOpened(true); 218 219 mListener = aListener; 220 221 mAsyncOpenTime = TimeStamp::Now(); 222 223 rv = MaybeResolveProxyAndBeginConnect(); 224 if (NS_FAILED(rv)) { 225 (void)AsyncAbort(rv); 226 } 227 228 return NS_OK; 229 } 230 231 nsresult TRRServiceChannel::MaybeResolveProxyAndBeginConnect() { 232 nsresult rv; 233 234 // The common case for HTTP channels is to begin proxy resolution and return 235 // at this point. The only time we know mProxyInfo already is if we're 236 // proxying a non-http protocol like ftp. We don't need to discover proxy 237 // settings if we are never going to make a network connection. 238 // If mConnectionInfo is already supplied, we don't need to do proxy 239 // resolution again. 240 if (!mProxyInfo && !mConnectionInfo && 241 !(mLoadFlags & (nsICachingChannel::LOAD_ONLY_FROM_CACHE | 242 nsICachingChannel::LOAD_NO_NETWORK_IO)) && 243 NS_SUCCEEDED(ResolveProxy())) { 244 return NS_OK; 245 } 246 247 rv = BeginConnect(); 248 if (NS_FAILED(rv)) { 249 (void)AsyncAbort(rv); 250 } 251 252 return NS_OK; 253 } 254 255 nsresult TRRServiceChannel::ResolveProxy() { 256 LOG(("TRRServiceChannel::ResolveProxy [this=%p]\n", this)); 257 if (!NS_IsMainThread()) { 258 return NS_DispatchToMainThread( 259 NewRunnableMethod("TRRServiceChannel::ResolveProxy", this, 260 &TRRServiceChannel::ResolveProxy), 261 NS_DISPATCH_NORMAL); 262 } 263 264 MOZ_ASSERT(NS_IsMainThread()); 265 266 // TODO: bug 1625171. Consider moving proxy resolution to socket process. 267 RefPtr<TRRServiceChannel> self = this; 268 nsCOMPtr<nsICancelable> proxyRequest; 269 nsresult rv = ProxyConfigLookup::Create( 270 [self](nsIProxyInfo* aProxyInfo, nsresult aStatus) { 271 self->OnProxyAvailable(nullptr, nullptr, aProxyInfo, aStatus); 272 }, 273 mURI, mProxyResolveFlags, getter_AddRefs(proxyRequest)); 274 275 if (NS_FAILED(rv)) { 276 if (!mCurrentEventTarget->IsOnCurrentThread()) { 277 return mCurrentEventTarget->Dispatch( 278 NewRunnableMethod<nsresult>("TRRServiceChannel::AsyncAbort", this, 279 &TRRServiceChannel::AsyncAbort, rv), 280 NS_DISPATCH_NORMAL); 281 } 282 } 283 284 { 285 auto req = mProxyRequest.Lock(); 286 // We only set mProxyRequest if the channel hasn't already been cancelled 287 // on another thread. 288 if (!mCanceled) { 289 *req = proxyRequest.forget(); 290 } 291 } 292 293 // If the channel has been cancelled, we go ahead and cancel the proxy 294 // request right here. 295 if (proxyRequest) { 296 proxyRequest->Cancel(mStatus); 297 } 298 299 return rv; 300 } 301 302 NS_IMETHODIMP 303 TRRServiceChannel::OnProxyAvailable(nsICancelable* request, nsIChannel* channel, 304 nsIProxyInfo* pi, nsresult status) { 305 LOG(("TRRServiceChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32 306 " mStatus=%" PRIx32 "]\n", 307 this, pi, static_cast<uint32_t>(status), 308 static_cast<uint32_t>(static_cast<nsresult>(mStatus)))); 309 310 if (!mCurrentEventTarget->IsOnCurrentThread()) { 311 RefPtr<TRRServiceChannel> self = this; 312 nsCOMPtr<nsIProxyInfo> info = pi; 313 return mCurrentEventTarget->Dispatch( 314 NS_NewRunnableFunction("TRRServiceChannel::OnProxyAvailable", 315 [self, info, status]() { 316 self->OnProxyAvailable(nullptr, nullptr, info, 317 status); 318 }), 319 NS_DISPATCH_NORMAL); 320 } 321 322 MOZ_ASSERT(mCurrentEventTarget->IsOnCurrentThread()); 323 324 { 325 auto proxyRequest = mProxyRequest.Lock(); 326 *proxyRequest = nullptr; 327 } 328 329 nsresult rv; 330 331 // If status is a failure code, then it means that we failed to resolve 332 // proxy info. That is a non-fatal error assuming it wasn't because the 333 // request was canceled. We just failover to DIRECT when proxy resolution 334 // fails (failure can mean that the PAC URL could not be loaded). 335 336 if (NS_SUCCEEDED(status)) mProxyInfo = pi; 337 338 if (!gHttpHandler->Active()) { 339 LOG( 340 ("nsHttpChannel::OnProxyAvailable [this=%p] " 341 "Handler no longer active.\n", 342 this)); 343 rv = NS_ERROR_NOT_AVAILABLE; 344 } else { 345 rv = BeginConnect(); 346 } 347 348 if (NS_FAILED(rv)) { 349 (void)AsyncAbort(rv); 350 } 351 return rv; 352 } 353 354 nsresult TRRServiceChannel::BeginConnect() { 355 LOG(("TRRServiceChannel::BeginConnect [this=%p]\n", this)); 356 nsresult rv; 357 358 // Construct connection info object 359 nsAutoCString host; 360 nsAutoCString scheme; 361 int32_t port = -1; 362 bool isHttps = mURI->SchemeIs("https"); 363 364 rv = mURI->GetScheme(scheme); 365 if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host); 366 if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port); 367 if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec); 368 if (NS_FAILED(rv)) { 369 return rv; 370 } 371 372 // Just a warning here because some nsIURIs do not implement this method. 373 (void)NS_WARN_IF(NS_FAILED(mURI->GetUsername(mUsername))); 374 375 // Reject the URL if it doesn't specify a host 376 if (host.IsEmpty()) { 377 rv = NS_ERROR_MALFORMED_URI; 378 return rv; 379 } 380 LOG(("host=%s port=%d\n", host.get(), port)); 381 LOG(("uri=%s\n", mSpec.get())); 382 383 nsCOMPtr<nsProxyInfo> proxyInfo; 384 if (mConnectionInfo) { 385 proxyInfo = mConnectionInfo->ProxyInfo(); 386 } else if (mProxyInfo) { 387 proxyInfo = do_QueryInterface(mProxyInfo); 388 } 389 390 mRequestHead.SetHTTPS(isHttps); 391 mRequestHead.SetOrigin(scheme, host, port); 392 393 gHttpHandler->MaybeAddAltSvcForTesting(mURI, mUsername, mPrivateBrowsing, 394 mCallbacks, OriginAttributes()); 395 396 RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo( 397 host, port, ""_ns, mUsername, proxyInfo, OriginAttributes(), isHttps); 398 // TODO: Bug 1622778 for using AltService in socket process. 399 StoreAllowAltSvc(XRE_IsParentProcess() && LoadAllowAltSvc()); 400 bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo); 401 bool http3Allowed = Http3Allowed(); 402 if (!http3Allowed) { 403 mCaps |= NS_HTTP_DISALLOW_HTTP3; 404 } 405 406 RefPtr<AltSvcMapping> mapping; 407 if (LoadAllowAltSvc() && // per channel 408 (http2Allowed || http3Allowed) && !(mLoadFlags & LOAD_FRESH_CONNECTION) && 409 AltSvcMapping::AcceptableProxy(proxyInfo) && 410 (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) && 411 (mapping = gHttpHandler->GetAltServiceMapping( 412 scheme, host, port, mPrivateBrowsing, OriginAttributes(), 413 http2Allowed, http3Allowed))) { 414 LOG(("TRRServiceChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", 415 this, scheme.get(), mapping->AlternateHost().get(), 416 mapping->AlternatePort(), mapping->HashKey().get())); 417 418 if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) { 419 nsAutoCString altUsedLine(mapping->AlternateHost()); 420 bool defaultPort = 421 mapping->AlternatePort() == 422 (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT); 423 if (!defaultPort) { 424 altUsedLine.AppendLiteral(":"); 425 altUsedLine.AppendInt(mapping->AlternatePort()); 426 } 427 rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine); 428 MOZ_ASSERT(NS_SUCCEEDED(rv)); 429 } 430 431 LOG(("TRRServiceChannel %p Using connection info from altsvc mapping", 432 this)); 433 mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo, 434 OriginAttributes()); 435 glean::http::transaction_use_altsvc 436 .EnumGet(glean::http::TransactionUseAltsvcLabel::eTrue) 437 .Add(); 438 } else if (mConnectionInfo) { 439 LOG(("TRRServiceChannel %p Using channel supplied connection info", this)); 440 } else { 441 LOG(("TRRServiceChannel %p Using default connection info", this)); 442 443 mConnectionInfo = connInfo; 444 glean::http::transaction_use_altsvc 445 .EnumGet(glean::http::TransactionUseAltsvcLabel::eFalse) 446 .Add(); 447 } 448 449 // Need to re-ask the handler, since mConnectionInfo may not be the connInfo 450 // we used earlier 451 if (gHttpHandler->IsHttp2Excluded(mConnectionInfo)) { 452 StoreAllowSpdy(0); 453 mCaps |= NS_HTTP_DISALLOW_SPDY; 454 mConnectionInfo->SetNoSpdy(true); 455 } 456 457 // if this somehow fails we can go on without it 458 (void)gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps); 459 460 // Adjust mCaps according to our request headers: 461 // - If "Connection: close" is set as a request header, then do not bother 462 // trying to establish a keep-alive connection. 463 if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) { 464 mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE); 465 } 466 467 // TRR requests should never be blocked. 468 mCaps |= (NS_HTTP_LOAD_UNBLOCKED | NS_HTTP_URGENT_START); 469 SetPriority(nsISupportsPriority::PRIORITY_HIGHEST); 470 471 if (mCanceled) { 472 return mStatus; 473 } 474 475 MaybeStartDNSPrefetch(); 476 477 rv = ContinueOnBeforeConnect(); 478 if (NS_FAILED(rv)) { 479 return rv; 480 } 481 482 return NS_OK; 483 } 484 485 nsresult TRRServiceChannel::ContinueOnBeforeConnect() { 486 LOG(("TRRServiceChannel::ContinueOnBeforeConnect [this=%p]\n", this)); 487 488 // ensure that we are using a valid hostname 489 if (!net_IsValidDNSHost(nsDependentCString(mConnectionInfo->Origin()))) { 490 return NS_ERROR_UNKNOWN_HOST; 491 } 492 493 if (LoadIsTRRServiceChannel()) { 494 mCaps |= NS_HTTP_LARGE_KEEPALIVE; 495 DisallowHTTPSRR(mCaps); 496 } 497 498 mCaps |= NS_HTTP_TRR_FLAGS_FROM_MODE(nsIRequest::GetTRRMode()); 499 500 // Finalize ConnectionInfo flags before SpeculativeConnect 501 mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0); 502 mConnectionInfo->SetPrivate(mPrivateBrowsing); 503 mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY); 504 mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) || 505 LoadBeConservative()); 506 mConnectionInfo->SetTlsFlags(mTlsFlags); 507 mConnectionInfo->SetIsTrrServiceChannel(LoadIsTRRServiceChannel()); 508 mConnectionInfo->SetTRRMode(nsIRequest::GetTRRMode()); 509 mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4); 510 mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6); 511 512 if (mLoadFlags & LOAD_FRESH_CONNECTION) { 513 glean::networking::trr_connection_cycle_count.Get(TRRService::ProviderKey()) 514 .Add(1); 515 nsresult rv = 516 gHttpHandler->ConnMgr()->DoSingleConnectionCleanup(mConnectionInfo); 517 LOG( 518 ("TRRServiceChannel::BeginConnect " 519 "DoSingleConnectionCleanup succeeded=%d %08x [this=%p]", 520 NS_SUCCEEDED(rv), static_cast<uint32_t>(rv), this)); 521 } 522 523 return Connect(); 524 } 525 526 nsresult TRRServiceChannel::Connect() { 527 LOG(("TRRServiceChannel::Connect [this=%p]\n", this)); 528 529 nsresult rv = SetupTransaction(); 530 if (NS_FAILED(rv)) { 531 return rv; 532 } 533 534 rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); 535 if (NS_FAILED(rv)) { 536 return rv; 537 } 538 539 return mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump)); 540 } 541 542 nsresult TRRServiceChannel::SetupTransaction() { 543 LOG(( 544 "TRRServiceChannel::SetupTransaction " 545 "[this=%p, cos=%lu, inc=%d, prio=%d]\n", 546 this, mClassOfService.Flags(), mClassOfService.Incremental(), mPriority)); 547 548 NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED); 549 550 nsresult rv; 551 552 if (!LoadAllowSpdy()) { 553 mCaps |= NS_HTTP_DISALLOW_SPDY; 554 } 555 // Check a proxy info from mConnectionInfo. TRR channel may use a proxy that 556 // is set in mConnectionInfo but acutally the channel do not have mProxyInfo 557 // set. This can happend when network.trr.async_connInfo is true. 558 bool useNonDirectProxy = mConnectionInfo->ProxyInfo() 559 ? !mConnectionInfo->ProxyInfo()->IsDirect() 560 : false; 561 if (!Http3Allowed() || useNonDirectProxy) { 562 mCaps |= NS_HTTP_DISALLOW_HTTP3; 563 } 564 if (LoadBeConservative()) { 565 mCaps |= NS_HTTP_BE_CONSERVATIVE; 566 } 567 568 // Use the URI path if not proxying (transparent proxying such as proxy 569 // CONNECT does not count here). Also figure out what HTTP version to use. 570 nsAutoCString buf, path; 571 nsCString* requestURI; 572 573 // This is the normal e2e H1 path syntax "/index.html" 574 rv = mURI->GetPathQueryRef(path); 575 if (NS_FAILED(rv)) { 576 return rv; 577 } 578 579 // path may contain UTF-8 characters, so ensure that they're escaped. 580 if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces, 581 buf)) { 582 requestURI = &buf; 583 } else { 584 requestURI = &path; 585 } 586 587 // trim off the #ref portion if any... 588 int32_t ref1 = requestURI->FindChar('#'); 589 if (ref1 != kNotFound) { 590 requestURI->SetLength(ref1); 591 } 592 593 if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) { 594 mRequestHead.SetVersion(gHttpHandler->HttpVersion()); 595 } else { 596 mRequestHead.SetPath(*requestURI); 597 598 // RequestURI should be the absolute uri H1 proxy syntax 599 // "http://foo/index.html" so we will overwrite the relative version in 600 // requestURI 601 rv = mURI->GetUserPass(buf); 602 if (NS_FAILED(rv)) return rv; 603 if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) || 604 strncmp(mSpec.get(), "https:", 6) == 0)) { 605 nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI); 606 rv = tempURI->GetAsciiSpec(path); 607 if (NS_FAILED(rv)) return rv; 608 requestURI = &path; 609 } else { 610 requestURI = &mSpec; 611 } 612 613 // trim off the #ref portion if any... 614 int32_t ref2 = requestURI->FindChar('#'); 615 if (ref2 != kNotFound) { 616 requestURI->SetLength(ref2); 617 } 618 619 mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion()); 620 } 621 622 mRequestHead.SetRequestURI(*requestURI); 623 624 // Force setting no-cache header for TRRServiceChannel. 625 // We need to send 'Pragma:no-cache' to inhibit proxy caching even if 626 // no proxy is configured since we might be talking with a transparent 627 // proxy, i.e. one that operates at the network level. See bug #14772. 628 rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); 629 MOZ_ASSERT(NS_SUCCEEDED(rv)); 630 // If we're configured to speak HTTP/1.1 then also send 'Cache-control: 631 // no-cache' 632 if (mRequestHead.Version() >= HttpVersion::v1_1) { 633 rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true); 634 MOZ_ASSERT(NS_SUCCEEDED(rv)); 635 } 636 637 // create wrapper for this channel's notification callbacks 638 nsCOMPtr<nsIInterfaceRequestor> callbacks; 639 NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 640 getter_AddRefs(callbacks)); 641 642 // create the transaction object 643 mTransaction = new nsHttpTransaction(); 644 LOG1(("TRRServiceChannel %p created nsHttpTransaction %p\n", this, 645 mTransaction.get())); 646 647 // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. 648 if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS; 649 650 EnsureRequestContext(); 651 652 struct LNAPerms perms{}; 653 654 rv = mTransaction->Init( 655 mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength, 656 LoadUploadStreamHasHeaders(), mCurrentEventTarget, callbacks, this, 657 mBrowserId, HttpTrafficCategory::eInvalid, mRequestContext, 658 mClassOfService, mInitialRwin, LoadResponseTimeoutEnabled(), mChannelId, 659 nullptr, nsILoadInfo::IPAddressSpace::Unknown, perms); 660 661 if (NS_FAILED(rv)) { 662 mTransaction = nullptr; 663 return rv; 664 } 665 666 return rv; 667 } 668 669 void TRRServiceChannel::MaybeStartDNSPrefetch() { 670 if (mConnectionInfo->UsingHttpProxy() || 671 (mLoadFlags & (nsICachingChannel::LOAD_NO_NETWORK_IO | 672 nsICachingChannel::LOAD_ONLY_FROM_CACHE))) { 673 return; 674 } 675 676 LOG( 677 ("TRRServiceChannel::MaybeStartDNSPrefetch [this=%p] " 678 "prefetching%s\n", 679 this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "")); 680 681 OriginAttributes originAttributes; 682 mDNSPrefetch = new nsDNSPrefetch(mURI, originAttributes, 683 nsIRequest::GetTRRMode(), this, true); 684 nsIDNSService::DNSFlags dnsFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; 685 if (mCaps & NS_HTTP_REFRESH_DNS) { 686 dnsFlags |= nsIDNSService::RESOLVE_BYPASS_CACHE; 687 } 688 nsresult rv = mDNSPrefetch->PrefetchHigh(dnsFlags); 689 NS_ENSURE_SUCCESS_VOID(rv); 690 } 691 692 NS_IMETHODIMP 693 TRRServiceChannel::OnTransportStatus(nsITransport* trans, nsresult status, 694 int64_t progress, int64_t progressMax) { 695 return NS_OK; 696 } 697 698 nsresult TRRServiceChannel::CallOnStartRequest() { 699 LOG(("TRRServiceChannel::CallOnStartRequest [this=%p]", this)); 700 701 if (LoadOnStartRequestCalled()) { 702 LOG(("CallOnStartRequest already invoked before")); 703 return mStatus; 704 } 705 706 nsresult rv = NS_OK; 707 StoreTracingEnabled(false); 708 709 // Ensure mListener->OnStartRequest will be invoked before exiting 710 // this function. 711 auto onStartGuard = MakeScopeExit([&] { 712 LOG( 713 (" calling mListener->OnStartRequest by ScopeExit [this=%p, " 714 "listener=%p]\n", 715 this, mListener.get())); 716 MOZ_ASSERT(!LoadOnStartRequestCalled()); 717 718 if (mListener) { 719 nsCOMPtr<nsIStreamListener> deleteProtector(mListener); 720 StoreOnStartRequestCalled(true); 721 deleteProtector->OnStartRequest(this); 722 } 723 StoreOnStartRequestCalled(true); 724 }); 725 726 if (mResponseHead && !mResponseHead->HasContentCharset()) { 727 mResponseHead->SetContentCharset(mContentCharsetHint); 728 } 729 730 LOG((" calling mListener->OnStartRequest [this=%p, listener=%p]\n", this, 731 mListener.get())); 732 733 // About to call OnStartRequest, dismiss the guard object. 734 onStartGuard.release(); 735 736 if (mListener) { 737 MOZ_ASSERT(!LoadOnStartRequestCalled(), 738 "We should not call OsStartRequest twice"); 739 nsCOMPtr<nsIStreamListener> deleteProtector(mListener); 740 StoreOnStartRequestCalled(true); 741 rv = deleteProtector->OnStartRequest(this); 742 if (NS_FAILED(rv)) return rv; 743 } else { 744 NS_WARNING("OnStartRequest skipped because of null listener"); 745 StoreOnStartRequestCalled(true); 746 } 747 748 if (!mResponseHead) { 749 return NS_OK; 750 } 751 752 nsAutoCString contentEncoding; 753 rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 754 if (NS_FAILED(rv) || contentEncoding.IsEmpty()) { 755 return NS_OK; 756 } 757 758 // DoApplyContentConversions can only be called on the main thread. 759 if (NS_IsMainThread()) { 760 nsCOMPtr<nsIStreamListener> listener; 761 rv = 762 DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr); 763 if (NS_FAILED(rv)) { 764 return rv; 765 } 766 767 AfterApplyContentConversions(rv, listener); 768 return NS_OK; 769 } 770 771 Suspend(); 772 773 RefPtr<TRRServiceChannel> self = this; 774 rv = NS_DispatchToMainThread( 775 NS_NewRunnableFunction("TRRServiceChannel::DoApplyContentConversions", 776 [self]() { 777 nsCOMPtr<nsIStreamListener> listener; 778 nsresult rv = self->DoApplyContentConversions( 779 self->mListener, getter_AddRefs(listener), 780 nullptr); 781 self->AfterApplyContentConversions(rv, listener); 782 }), 783 NS_DISPATCH_NORMAL); 784 if (NS_FAILED(rv)) { 785 Resume(); 786 return rv; 787 } 788 789 return NS_OK; 790 } 791 792 void TRRServiceChannel::AfterApplyContentConversions( 793 nsresult aResult, nsIStreamListener* aListener) { 794 LOG(("TRRServiceChannel::AfterApplyContentConversions [this=%p]", this)); 795 if (!mCurrentEventTarget->IsOnCurrentThread()) { 796 RefPtr<TRRServiceChannel> self = this; 797 nsCOMPtr<nsIStreamListener> listener = aListener; 798 self->mCurrentEventTarget->Dispatch( 799 NS_NewRunnableFunction( 800 "TRRServiceChannel::AfterApplyContentConversions", 801 [self, aResult, listener]() { 802 self->Resume(); 803 self->AfterApplyContentConversions(aResult, listener); 804 }), 805 NS_DISPATCH_NORMAL); 806 return; 807 } 808 809 if (mCanceled) { 810 return; 811 } 812 813 if (NS_FAILED(aResult)) { 814 (void)AsyncAbort(aResult); 815 return; 816 } 817 818 if (aListener) { 819 mListener = aListener; 820 mCompressListener = aListener; 821 StoreHasAppliedConversion(true); 822 } 823 } 824 825 void TRRServiceChannel::ProcessAltService( 826 nsHttpConnectionInfo* aTransConnInfo) { 827 // e.g. Alt-Svc: h2=":443"; ma=60 828 // e.g. Alt-Svc: h2="otherhost:443" 829 // Alt-Svc = 1#( alternative *( OWS ";" OWS parameter ) ) 830 // alternative = protocol-id "=" alt-authority 831 // protocol-id = token ; percent-encoded ALPN protocol identifier 832 // alt-authority = quoted-string ; containing [ uri-host ] ":" port 833 834 if (!LoadAllowAltSvc()) { // per channel opt out 835 return; 836 } 837 838 if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) { 839 return; 840 } 841 842 nsCString scheme; 843 mURI->GetScheme(scheme); 844 bool isHttp = scheme.EqualsLiteral("http"); 845 if (!isHttp && !scheme.EqualsLiteral("https")) { 846 return; 847 } 848 849 nsCString altSvc; 850 (void)mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc); 851 if (altSvc.IsEmpty()) { 852 return; 853 } 854 855 if (!nsHttp::IsReasonableHeaderValue(altSvc)) { 856 LOG(("Alt-Svc Response Header seems unreasonable - skipping\n")); 857 return; 858 } 859 860 nsCString originHost; 861 int32_t originPort = 80; 862 mURI->GetPort(&originPort); 863 if (NS_FAILED(mURI->GetAsciiHost(originHost))) { 864 return; 865 } 866 867 nsCOMPtr<nsIInterfaceRequestor> callbacks; 868 nsCOMPtr<nsProxyInfo> proxyInfo; 869 NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 870 getter_AddRefs(callbacks)); 871 if (mProxyInfo) { 872 proxyInfo = do_QueryInterface(mProxyInfo); 873 } 874 875 RefPtr<nsHttpConnectionInfo> connectionInfo = aTransConnInfo; 876 auto processHeaderTask = [altSvc, scheme, originHost, originPort, 877 userName(mUsername), 878 privateBrowsing(mPrivateBrowsing), callbacks, 879 proxyInfo, caps(mCaps), connectionInfo]() { 880 if (XRE_IsSocketProcess()) { 881 AltServiceChild::ProcessHeader(altSvc, scheme, originHost, originPort, 882 userName, privateBrowsing, callbacks, 883 proxyInfo, caps & NS_HTTP_DISALLOW_SPDY, 884 OriginAttributes(), connectionInfo); 885 return; 886 } 887 888 AltSvcMapping::ProcessHeader(altSvc, scheme, originHost, originPort, 889 userName, privateBrowsing, callbacks, 890 proxyInfo, caps & NS_HTTP_DISALLOW_SPDY, 891 OriginAttributes(), connectionInfo); 892 }; 893 894 if (NS_IsMainThread()) { 895 processHeaderTask(); 896 return; 897 } 898 899 NS_DispatchToMainThread(NS_NewRunnableFunction( 900 "TRRServiceChannel::ProcessAltService", std::move(processHeaderTask))); 901 } 902 903 NS_IMETHODIMP 904 TRRServiceChannel::OnStartRequest(nsIRequest* request) { 905 LOG(("TRRServiceChannel::OnStartRequest [this=%p request=%p status=%" PRIx32 906 "]\n", 907 this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus)))); 908 909 if (!(mCanceled || NS_FAILED(mStatus))) { 910 // capture the request's status, so our consumers will know ASAP of any 911 // connection failures, etc - bug 93581 912 nsresult status; 913 request->GetStatus(&status); 914 mStatus = status; 915 } 916 917 MOZ_ASSERT(request == mTransactionPump, "Unexpected request"); 918 919 StoreAfterOnStartRequestBegun(true); 920 if (mTransaction) { 921 if (!mSecurityInfo) { 922 // grab the security info from the connection object; the transaction 923 // is guaranteed to own a reference to the connection. 924 mSecurityInfo = mTransaction->SecurityInfo(); 925 } 926 } 927 928 if (NS_SUCCEEDED(mStatus) && mTransaction) { 929 // mTransactionPump doesn't hit OnInputStreamReady and call this until 930 // all of the response headers have been acquired, so we can take 931 // ownership of them from the transaction. 932 RefPtr<nsHttpConnectionInfo> connInfo; 933 mResponseHead = 934 mTransaction->TakeResponseHeadAndConnInfo(getter_AddRefs(connInfo)); 935 if (mResponseHead) { 936 uint32_t httpStatus = mResponseHead->Status(); 937 if (mTransaction->ProxyConnectFailed()) { 938 LOG(("TRRServiceChannel proxy connect failed httpStatus: %d", 939 httpStatus)); 940 MOZ_ASSERT(mConnectionInfo->UsingConnect(), 941 "proxy connect failed but not using CONNECT?"); 942 nsresult rv = HttpProxyResponseToErrorCode(httpStatus); 943 mTransaction->DontReuseConnection(); 944 Cancel(rv); 945 return CallOnStartRequest(); 946 } 947 948 if ((httpStatus < 500) && (httpStatus != 421) && (httpStatus != 407)) { 949 ProcessAltService(connInfo); 950 } 951 952 if (httpStatus == 300 || httpStatus == 301 || httpStatus == 302 || 953 httpStatus == 303 || httpStatus == 307 || httpStatus == 308) { 954 nsresult rv = SyncProcessRedirection(httpStatus); 955 if (NS_SUCCEEDED(rv)) { 956 return rv; 957 } 958 959 mStatus = rv; 960 DoNotifyListener(); 961 return rv; 962 } 963 } else { 964 NS_WARNING("No response head in OnStartRequest"); 965 } 966 } 967 968 // avoid crashing if mListener happens to be null... 969 if (!mListener) { 970 MOZ_ASSERT_UNREACHABLE("mListener is null"); 971 return NS_OK; 972 } 973 974 return CallOnStartRequest(); 975 } 976 977 nsresult TRRServiceChannel::SyncProcessRedirection(uint32_t aHttpStatus) { 978 nsAutoCString location; 979 980 // if a location header was not given, then we can't perform the redirect, 981 // so just carry on as though this were a normal response. 982 if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location))) { 983 return NS_ERROR_FAILURE; 984 } 985 986 // make sure non-ASCII characters in the location header are escaped. 987 nsAutoCString locationBuf; 988 if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces, 989 locationBuf)) { 990 location = locationBuf; 991 } 992 993 LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(), 994 uint32_t(mRedirectionLimit))); 995 996 nsCOMPtr<nsIURI> redirectURI; 997 nsresult rv = NS_NewURI(getter_AddRefs(redirectURI), location); 998 999 if (NS_FAILED(rv)) { 1000 LOG(("Invalid URI for redirect: Location: %s\n", location.get())); 1001 return NS_ERROR_CORRUPTED_CONTENT; 1002 } 1003 1004 // move the reference of the old location to the new one if the new 1005 // one has none. 1006 PropagateReferenceIfNeeded(mURI, redirectURI); 1007 1008 bool rewriteToGET = 1009 ShouldRewriteRedirectToGET(aHttpStatus, mRequestHead.ParsedMethod()); 1010 1011 // Let's not rewrite the method to GET for TRR requests. 1012 if (rewriteToGET) { 1013 return NS_ERROR_FAILURE; 1014 } 1015 1016 // If the method is not safe (such as POST, PUT, DELETE, ...) 1017 if (!mRequestHead.IsSafeMethod()) { 1018 LOG(("TRRServiceChannel: unsafe redirect to:%s\n", location.get())); 1019 } 1020 1021 uint32_t redirectFlags; 1022 if (nsHttp::IsPermanentRedirect(aHttpStatus)) { 1023 redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT; 1024 } else { 1025 redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY; 1026 } 1027 1028 nsCOMPtr<nsIChannel> newChannel; 1029 nsCOMPtr<nsILoadInfo> redirectLoadInfo = 1030 static_cast<TRRLoadInfo*>(mLoadInfo.get())->Clone(); 1031 rv = gHttpHandler->CreateTRRServiceChannel(redirectURI, nullptr, 0, nullptr, 1032 redirectLoadInfo, 1033 getter_AddRefs(newChannel)); 1034 if (NS_FAILED(rv)) { 1035 return rv; 1036 } 1037 1038 rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET, 1039 redirectFlags); 1040 if (NS_FAILED(rv)) { 1041 return rv; 1042 } 1043 1044 // Make sure to do this after we received redirect veto answer, 1045 // i.e. after all sinks had been notified 1046 newChannel->SetOriginalURI(mOriginalURI); 1047 1048 rv = newChannel->AsyncOpen(mListener); 1049 LOG((" new channel AsyncOpen returned %" PRIX32, static_cast<uint32_t>(rv))); 1050 1051 // close down this channel 1052 Cancel(NS_BINDING_REDIRECTED); 1053 1054 ReleaseListeners(); 1055 1056 return NS_OK; 1057 } 1058 1059 nsresult TRRServiceChannel::SetupReplacementChannel(nsIURI* aNewURI, 1060 nsIChannel* aNewChannel, 1061 bool aPreserveMethod, 1062 uint32_t aRedirectFlags) { 1063 LOG( 1064 ("TRRServiceChannel::SetupReplacementChannel " 1065 "[this=%p newChannel=%p preserveMethod=%d]", 1066 this, aNewChannel, aPreserveMethod)); 1067 1068 nsresult rv = HttpBaseChannel::SetupReplacementChannel( 1069 aNewURI, aNewChannel, aPreserveMethod, aRedirectFlags); 1070 if (NS_FAILED(rv)) { 1071 return rv; 1072 } 1073 1074 rv = CheckRedirectLimit(aNewURI, aRedirectFlags); 1075 NS_ENSURE_SUCCESS(rv, rv); 1076 1077 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel); 1078 if (!httpChannel) { 1079 MOZ_ASSERT(false); 1080 return NS_ERROR_FAILURE; 1081 } 1082 1083 // convey the ApplyConversion flag (bug 91862) 1084 nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel); 1085 if (encodedChannel) { 1086 encodedChannel->SetApplyConversion(LoadApplyConversion()); 1087 } 1088 1089 if (mContentTypeHint.IsEmpty()) { 1090 return NS_OK; 1091 } 1092 1093 // Make sure we set content-type on the old channel properly. 1094 MOZ_ASSERT(mContentTypeHint.Equals("application/dns-message")); 1095 1096 // Apply TRR specific settings. Note that we already know mContentTypeHint is 1097 // "application/dns-message" here. 1098 return TRR::SetupTRRServiceChannelInternal( 1099 httpChannel, 1100 mRequestHead.ParsedMethod() == nsHttpRequestHead::kMethod_Get, 1101 mContentTypeHint); 1102 } 1103 1104 NS_IMETHODIMP 1105 TRRServiceChannel::OnDataAvailable(nsIRequest* request, nsIInputStream* input, 1106 uint64_t offset, uint32_t count) { 1107 LOG(("TRRServiceChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64 1108 " count=%" PRIu32 "]\n", 1109 this, request, offset, count)); 1110 1111 // don't send out OnDataAvailable notifications if we've been canceled. 1112 if (mCanceled) return mStatus; 1113 1114 MOZ_ASSERT(mResponseHead, "No response head in ODA!!"); 1115 1116 if (mListener) { 1117 return mListener->OnDataAvailable(this, input, offset, count); 1118 } 1119 1120 return NS_ERROR_ABORT; 1121 } 1122 1123 static void TelemetryReport(nsITimedChannel* aTimedChannel, 1124 uint64_t aRequestSize, uint64_t aTransferSize) { 1125 TimeStamp asyncOpen; 1126 nsresult rv = aTimedChannel->GetAsyncOpen(&asyncOpen); 1127 if (NS_FAILED(rv)) { 1128 return; 1129 } 1130 1131 TimeStamp domainLookupStart; 1132 rv = aTimedChannel->GetDomainLookupStart(&domainLookupStart); 1133 if (NS_FAILED(rv)) { 1134 return; 1135 } 1136 1137 TimeStamp domainLookupEnd; 1138 rv = aTimedChannel->GetDomainLookupEnd(&domainLookupEnd); 1139 if (NS_FAILED(rv)) { 1140 return; 1141 } 1142 1143 TimeStamp connectStart; 1144 rv = aTimedChannel->GetConnectStart(&connectStart); 1145 if (NS_FAILED(rv)) { 1146 return; 1147 } 1148 1149 TimeStamp secureConnectionStart; 1150 rv = aTimedChannel->GetSecureConnectionStart(&secureConnectionStart); 1151 if (NS_FAILED(rv)) { 1152 return; 1153 } 1154 1155 TimeStamp connectEnd; 1156 rv = aTimedChannel->GetConnectEnd(&connectEnd); 1157 if (NS_FAILED(rv)) { 1158 return; 1159 } 1160 1161 TimeStamp requestStart; 1162 rv = aTimedChannel->GetRequestStart(&requestStart); 1163 if (NS_FAILED(rv)) { 1164 return; 1165 } 1166 1167 TimeStamp responseStart; 1168 rv = aTimedChannel->GetResponseStart(&responseStart); 1169 if (NS_FAILED(rv)) { 1170 return; 1171 } 1172 1173 TimeStamp responseEnd; 1174 rv = aTimedChannel->GetResponseEnd(&responseEnd); 1175 if (NS_FAILED(rv)) { 1176 return; 1177 } 1178 1179 const nsCString& key = TRRService::ProviderKey(); 1180 if (!domainLookupStart.IsNull()) { 1181 mozilla::glean::networking::trr_dns_start.Get(key).AccumulateRawDuration( 1182 domainLookupStart - asyncOpen); 1183 if (!domainLookupEnd.IsNull()) { 1184 mozilla::glean::networking::trr_dns_end.Get(key).AccumulateRawDuration( 1185 domainLookupEnd - domainLookupStart); 1186 } 1187 } 1188 if (!connectEnd.IsNull()) { 1189 if (!connectStart.IsNull()) { 1190 mozilla::glean::networking::trr_tcp_connection.Get(key) 1191 .AccumulateRawDuration(connectEnd - connectStart); 1192 } 1193 if (!secureConnectionStart.IsNull()) { 1194 mozilla::glean::networking::trr_tls_handshake.Get(key) 1195 .AccumulateRawDuration(connectEnd - secureConnectionStart); 1196 } 1197 } 1198 if (!requestStart.IsNull() && !responseEnd.IsNull()) { 1199 mozilla::glean::networking::trr_open_to_first_sent.Get(key) 1200 .AccumulateRawDuration(requestStart - asyncOpen); 1201 mozilla::glean::networking::trr_first_sent_to_last_received.Get(key) 1202 .AccumulateRawDuration(responseEnd - requestStart); 1203 mozilla::glean::networking::trr_complete_load.Get(key) 1204 .AccumulateRawDuration(responseEnd - asyncOpen); 1205 if (!responseStart.IsNull()) { 1206 mozilla::glean::networking::trr_open_to_first_received.Get(key) 1207 .AccumulateRawDuration(responseStart - asyncOpen); 1208 } 1209 } 1210 glean::networking::trr_request_size.Get(key).Accumulate(aRequestSize); 1211 glean::networking::trr_response_size.Get(key).Accumulate(aTransferSize); 1212 } 1213 1214 NS_IMETHODIMP 1215 TRRServiceChannel::OnStopRequest(nsIRequest* request, nsresult status) { 1216 LOG(("TRRServiceChannel::OnStopRequest [this=%p request=%p status=%" PRIx32 1217 "]\n", 1218 this, request, static_cast<uint32_t>(status))); 1219 1220 if (mCanceled || NS_FAILED(mStatus)) status = mStatus; 1221 1222 mTransactionTimings = mTransaction->Timings(); 1223 mRequestSize = mTransaction->GetRequestSize(); 1224 mTransferSize = mTransaction->GetTransferSize(); 1225 mTransaction = nullptr; 1226 mTransactionPump = nullptr; 1227 1228 if (mListener) { 1229 LOG(("TRRServiceChannel %p calling OnStopRequest\n", this)); 1230 MOZ_ASSERT(LoadOnStartRequestCalled(), 1231 "OnStartRequest should be called before OnStopRequest"); 1232 MOZ_ASSERT(!LoadOnStopRequestCalled(), 1233 "We should not call OnStopRequest twice"); 1234 StoreOnStopRequestCalled(true); 1235 mListener->OnStopRequest(this, status); 1236 } 1237 StoreOnStopRequestCalled(true); 1238 1239 mDNSPrefetch = nullptr; 1240 1241 if (mLoadGroup) { 1242 mLoadGroup->RemoveRequest(this, nullptr, status); 1243 } 1244 1245 ReleaseListeners(); 1246 TelemetryReport(this, mRequestSize, mTransferSize); 1247 return NS_OK; 1248 } 1249 1250 NS_IMETHODIMP 1251 TRRServiceChannel::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec, 1252 nsresult status) { 1253 LOG( 1254 ("TRRServiceChannel::OnLookupComplete [this=%p] prefetch complete%s: " 1255 "%s status[0x%" PRIx32 "]\n", 1256 this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "", 1257 NS_SUCCEEDED(status) ? "success" : "failure", 1258 static_cast<uint32_t>(status))); 1259 1260 // We no longer need the dns prefetch object. Note: mDNSPrefetch could be 1261 // validly null if OnStopRequest has already been called. 1262 // We only need the domainLookup timestamps when not loading from cache 1263 if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) { 1264 TimeStamp connectStart = mTransaction->GetConnectStart(); 1265 TimeStamp requestStart = mTransaction->GetRequestStart(); 1266 // We only set the domainLookup timestamps if we're not using a 1267 // persistent connection. 1268 if (requestStart.IsNull() && connectStart.IsNull()) { 1269 mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp()); 1270 mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp()); 1271 } 1272 } 1273 mDNSPrefetch = nullptr; 1274 1275 // Unset DNS cache refresh if it was requested, 1276 if (mCaps & NS_HTTP_REFRESH_DNS) { 1277 mCaps &= ~NS_HTTP_REFRESH_DNS; 1278 if (mTransaction) { 1279 mTransaction->SetDNSWasRefreshed(); 1280 } 1281 } 1282 1283 return NS_OK; 1284 } 1285 1286 NS_IMETHODIMP 1287 TRRServiceChannel::LogBlockedCORSRequest(const nsAString& aMessage, 1288 const nsACString& aCategory, 1289 bool aIsWarning) { 1290 return NS_ERROR_NOT_IMPLEMENTED; 1291 } 1292 1293 NS_IMETHODIMP 1294 TRRServiceChannel::LogMimeTypeMismatch(const nsACString& aMessageName, 1295 bool aWarning, const nsAString& aURL, 1296 const nsAString& aContentType) { 1297 return NS_ERROR_NOT_IMPLEMENTED; 1298 } 1299 1300 NS_IMETHODIMP 1301 TRRServiceChannel::GetIsAuthChannel(bool* aIsAuthChannel) { 1302 return NS_ERROR_NOT_IMPLEMENTED; 1303 } 1304 1305 NS_IMETHODIMP 1306 TRRServiceChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 1307 mCallbacks = aCallbacks; 1308 return NS_OK; 1309 } 1310 1311 NS_IMETHODIMP 1312 TRRServiceChannel::SetPriority(int32_t value) { 1313 return NS_ERROR_NOT_IMPLEMENTED; 1314 } 1315 1316 void TRRServiceChannel::OnClassOfServiceUpdated() { 1317 LOG(("TRRServiceChannel::OnClassOfServiceUpdated this=%p, cos=%lu inc=%d", 1318 this, mClassOfService.Flags(), mClassOfService.Incremental())); 1319 1320 if (mTransaction) { 1321 gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction, 1322 mClassOfService); 1323 } 1324 } 1325 1326 NS_IMETHODIMP 1327 TRRServiceChannel::SetClassFlags(uint32_t inFlags) { 1328 uint32_t previous = mClassOfService.Flags(); 1329 mClassOfService.SetFlags(inFlags); 1330 if (previous != mClassOfService.Flags()) { 1331 OnClassOfServiceUpdated(); 1332 } 1333 return NS_OK; 1334 } 1335 1336 NS_IMETHODIMP 1337 TRRServiceChannel::SetIncremental(bool inFlag) { 1338 bool previous = mClassOfService.Incremental(); 1339 mClassOfService.SetIncremental(inFlag); 1340 if (previous != mClassOfService.Incremental()) { 1341 OnClassOfServiceUpdated(); 1342 } 1343 return NS_OK; 1344 } 1345 1346 NS_IMETHODIMP 1347 TRRServiceChannel::SetClassOfService(ClassOfService cos) { 1348 ClassOfService previous = mClassOfService; 1349 mClassOfService = cos; 1350 if (previous != mClassOfService) { 1351 OnClassOfServiceUpdated(); 1352 } 1353 return NS_OK; 1354 } 1355 1356 NS_IMETHODIMP 1357 TRRServiceChannel::AddClassFlags(uint32_t inFlags) { 1358 uint32_t previous = mClassOfService.Flags(); 1359 mClassOfService.SetFlags(inFlags | mClassOfService.Flags()); 1360 if (previous != mClassOfService.Flags()) { 1361 OnClassOfServiceUpdated(); 1362 } 1363 return NS_OK; 1364 } 1365 1366 NS_IMETHODIMP 1367 TRRServiceChannel::ClearClassFlags(uint32_t inFlags) { 1368 uint32_t previous = mClassOfService.Flags(); 1369 mClassOfService.SetFlags(~inFlags & mClassOfService.Flags()); 1370 if (previous != mClassOfService.Flags()) { 1371 OnClassOfServiceUpdated(); 1372 } 1373 return NS_OK; 1374 } 1375 1376 NS_IMETHODIMP 1377 TRRServiceChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) { 1378 return NS_ERROR_NOT_IMPLEMENTED; 1379 } 1380 1381 void TRRServiceChannel::DoAsyncAbort(nsresult aStatus) { 1382 (void)AsyncAbort(aStatus); 1383 } 1384 1385 NS_IMETHODIMP 1386 TRRServiceChannel::GetProxyInfo(nsIProxyInfo** result) { 1387 if (!mConnectionInfo) { 1388 *result = do_AddRef(mProxyInfo).take(); 1389 } else { 1390 *result = do_AddRef(mConnectionInfo->ProxyInfo()).take(); 1391 } 1392 return NS_OK; 1393 } 1394 1395 NS_IMETHODIMP TRRServiceChannel::GetHttpProxyConnectResponseCode( 1396 int32_t* aResponseCode) { 1397 NS_ENSURE_ARG_POINTER(aResponseCode); 1398 1399 *aResponseCode = -1; 1400 return NS_OK; 1401 } 1402 1403 NS_IMETHODIMP 1404 TRRServiceChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 1405 return HttpBaseChannel::GetLoadFlags(aLoadFlags); 1406 } 1407 1408 NS_IMETHODIMP 1409 TRRServiceChannel::SetLoadFlags(nsLoadFlags aLoadFlags) { 1410 if (aLoadFlags & (nsICachingChannel::LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE | 1411 nsICachingChannel::LOAD_NO_NETWORK_IO)) { 1412 MOZ_ASSERT(false, "Wrong load flags!"); 1413 return NS_ERROR_FAILURE; 1414 } 1415 1416 return HttpBaseChannel::SetLoadFlags(aLoadFlags); 1417 } 1418 1419 NS_IMETHODIMP 1420 TRRServiceChannel::GetURI(nsIURI** aURI) { 1421 return HttpBaseChannel::GetURI(aURI); 1422 } 1423 1424 NS_IMETHODIMP 1425 TRRServiceChannel::GetNotificationCallbacks( 1426 nsIInterfaceRequestor** aCallbacks) { 1427 return HttpBaseChannel::GetNotificationCallbacks(aCallbacks); 1428 } 1429 1430 NS_IMETHODIMP 1431 TRRServiceChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 1432 return HttpBaseChannel::GetLoadGroup(aLoadGroup); 1433 } 1434 1435 NS_IMETHODIMP 1436 TRRServiceChannel::GetRequestMethod(nsACString& aMethod) { 1437 return HttpBaseChannel::GetRequestMethod(aMethod); 1438 } 1439 1440 void TRRServiceChannel::DoNotifyListener() { 1441 LOG(("TRRServiceChannel::DoNotifyListener this=%p", this)); 1442 1443 // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag 1444 // LOAD_ONLY_IF_MODIFIED) we want to set AfterOnStartRequestBegun to true 1445 // before notifying listener. 1446 if (!LoadAfterOnStartRequestBegun()) { 1447 StoreAfterOnStartRequestBegun(true); 1448 } 1449 1450 if (mListener && !LoadOnStartRequestCalled()) { 1451 nsCOMPtr<nsIStreamListener> listener = mListener; 1452 StoreOnStartRequestCalled(true); 1453 listener->OnStartRequest(this); 1454 } 1455 StoreOnStartRequestCalled(true); 1456 1457 // Make sure IsPending is set to false. At this moment we are done from 1458 // the point of view of our consumer and we have to report our self 1459 // as not-pending. 1460 StoreIsPending(false); 1461 1462 if (mListener && !LoadOnStopRequestCalled()) { 1463 nsCOMPtr<nsIStreamListener> listener = mListener; 1464 StoreOnStopRequestCalled(true); 1465 listener->OnStopRequest(this, mStatus); 1466 } 1467 StoreOnStopRequestCalled(true); 1468 1469 // We have to make sure to drop the references to listeners and callbacks 1470 // no longer needed. 1471 ReleaseListeners(); 1472 1473 DoNotifyListenerCleanup(); 1474 } 1475 1476 void TRRServiceChannel::DoNotifyListenerCleanup() {} 1477 1478 NS_IMETHODIMP 1479 TRRServiceChannel::GetDomainLookupStart(TimeStamp* _retval) { 1480 if (mTransaction) { 1481 *_retval = mTransaction->GetDomainLookupStart(); 1482 } else { 1483 *_retval = mTransactionTimings.domainLookupStart; 1484 } 1485 return NS_OK; 1486 } 1487 1488 NS_IMETHODIMP 1489 TRRServiceChannel::GetDomainLookupEnd(TimeStamp* _retval) { 1490 if (mTransaction) { 1491 *_retval = mTransaction->GetDomainLookupEnd(); 1492 } else { 1493 *_retval = mTransactionTimings.domainLookupEnd; 1494 } 1495 return NS_OK; 1496 } 1497 1498 NS_IMETHODIMP 1499 TRRServiceChannel::GetConnectStart(TimeStamp* _retval) { 1500 if (mTransaction) { 1501 *_retval = mTransaction->GetConnectStart(); 1502 } else { 1503 *_retval = mTransactionTimings.connectStart; 1504 } 1505 return NS_OK; 1506 } 1507 1508 NS_IMETHODIMP 1509 TRRServiceChannel::GetTcpConnectEnd(TimeStamp* _retval) { 1510 if (mTransaction) { 1511 *_retval = mTransaction->GetTcpConnectEnd(); 1512 } else { 1513 *_retval = mTransactionTimings.tcpConnectEnd; 1514 } 1515 return NS_OK; 1516 } 1517 1518 NS_IMETHODIMP 1519 TRRServiceChannel::GetSecureConnectionStart(TimeStamp* _retval) { 1520 if (mTransaction) { 1521 *_retval = mTransaction->GetSecureConnectionStart(); 1522 } else { 1523 *_retval = mTransactionTimings.secureConnectionStart; 1524 } 1525 return NS_OK; 1526 } 1527 1528 NS_IMETHODIMP 1529 TRRServiceChannel::GetConnectEnd(TimeStamp* _retval) { 1530 if (mTransaction) { 1531 *_retval = mTransaction->GetConnectEnd(); 1532 } else { 1533 *_retval = mTransactionTimings.connectEnd; 1534 } 1535 return NS_OK; 1536 } 1537 1538 NS_IMETHODIMP 1539 TRRServiceChannel::GetRequestStart(TimeStamp* _retval) { 1540 if (mTransaction) { 1541 *_retval = mTransaction->GetRequestStart(); 1542 } else { 1543 *_retval = mTransactionTimings.requestStart; 1544 } 1545 return NS_OK; 1546 } 1547 1548 NS_IMETHODIMP 1549 TRRServiceChannel::GetResponseStart(TimeStamp* _retval) { 1550 if (mTransaction) { 1551 *_retval = mTransaction->GetResponseStart(); 1552 } else { 1553 *_retval = mTransactionTimings.responseStart; 1554 } 1555 return NS_OK; 1556 } 1557 1558 NS_IMETHODIMP 1559 TRRServiceChannel::GetResponseEnd(TimeStamp* _retval) { 1560 if (mTransaction) { 1561 *_retval = mTransaction->GetResponseEnd(); 1562 } else { 1563 *_retval = mTransactionTimings.responseEnd; 1564 } 1565 return NS_OK; 1566 } 1567 1568 NS_IMETHODIMP TRRServiceChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 1569 return NS_OK; 1570 } 1571 1572 NS_IMETHODIMP 1573 TRRServiceChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* aResult) { 1574 NS_ENSURE_ARG_POINTER(aResult); 1575 *aResult = true; 1576 return NS_OK; 1577 } 1578 1579 bool TRRServiceChannel::SameOriginWithOriginalUri(nsIURI* aURI) { return true; } 1580 1581 } // namespace mozilla::net