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