netwerk/protocol/http/HttpChannelParent.cpp

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:0f37df5e0502
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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
9
10 #include "mozilla/dom/FileDescriptorSetParent.h"
11 #include "mozilla/net/HttpChannelParent.h"
12 #include "mozilla/dom/TabParent.h"
13 #include "mozilla/net/NeckoParent.h"
14 #include "mozilla/unused.h"
15 #include "HttpChannelParentListener.h"
16 #include "nsHttpHandler.h"
17 #include "nsNetUtil.h"
18 #include "nsISupportsPriority.h"
19 #include "nsIAuthPromptProvider.h"
20 #include "nsIScriptSecurityManager.h"
21 #include "nsSerializationHelper.h"
22 #include "nsISerializable.h"
23 #include "nsIAssociatedContentSecurity.h"
24 #include "nsIApplicationCacheService.h"
25 #include "mozilla/ipc/InputStreamUtils.h"
26 #include "mozilla/ipc/URIUtils.h"
27 #include "SerializedLoadContext.h"
28
29 using namespace mozilla::dom;
30 using namespace mozilla::ipc;
31
32 namespace mozilla {
33 namespace net {
34
35 HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding,
36 nsILoadContext* aLoadContext,
37 PBOverrideStatus aOverrideStatus)
38 : mIPCClosed(false)
39 , mStoredStatus(NS_OK)
40 , mStoredProgress(0)
41 , mStoredProgressMax(0)
42 , mSentRedirect1Begin(false)
43 , mSentRedirect1BeginFailed(false)
44 , mReceivedRedirect2Verify(false)
45 , mPBOverride(aOverrideStatus)
46 , mLoadContext(aLoadContext)
47 , mStatus(NS_OK)
48 , mDivertingFromChild(false)
49 , mDivertedOnStartRequest(false)
50 , mSuspendedForDiversion(false)
51 {
52 // Ensure gHttpHandler is initialized: we need the atom table up and running.
53 nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
54 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
55
56 MOZ_ASSERT(gHttpHandler);
57 mHttpHandler = gHttpHandler;
58
59 mTabParent = static_cast<mozilla::dom::TabParent*>(iframeEmbedding);
60 }
61
62 HttpChannelParent::~HttpChannelParent()
63 {
64 }
65
66 void
67 HttpChannelParent::ActorDestroy(ActorDestroyReason why)
68 {
69 // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
70 // yet, but child process has crashed. We must not try to send any more msgs
71 // to child, or IPDL will kill chrome process, too.
72 mIPCClosed = true;
73 }
74
75 bool
76 HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
77 {
78 switch (aArgs.type()) {
79 case HttpChannelCreationArgs::THttpChannelOpenArgs:
80 {
81 const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
82 return DoAsyncOpen(a.uri(), a.original(), a.doc(), a.referrer(),
83 a.apiRedirectTo(), a.loadFlags(), a.requestHeaders(),
84 a.requestMethod(), a.uploadStream(),
85 a.uploadStreamHasHeaders(), a.priority(),
86 a.redirectionLimit(), a.allowPipelining(),
87 a.forceAllowThirdPartyCookie(), a.resumeAt(),
88 a.startPos(), a.entityID(), a.chooseApplicationCache(),
89 a.appCacheClientID(), a.allowSpdy(), a.fds());
90 }
91 case HttpChannelCreationArgs::THttpChannelConnectArgs:
92 {
93 const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
94 return ConnectChannel(cArgs.channelId());
95 }
96 default:
97 NS_NOTREACHED("unknown open type");
98 return false;
99 }
100 }
101
102 //-----------------------------------------------------------------------------
103 // HttpChannelParent::nsISupports
104 //-----------------------------------------------------------------------------
105
106 NS_IMPL_ISUPPORTS(HttpChannelParent,
107 nsIInterfaceRequestor,
108 nsIProgressEventSink,
109 nsIRequestObserver,
110 nsIStreamListener,
111 nsIParentChannel,
112 nsIParentRedirectingChannel)
113
114 //-----------------------------------------------------------------------------
115 // HttpChannelParent::nsIInterfaceRequestor
116 //-----------------------------------------------------------------------------
117
118 NS_IMETHODIMP
119 HttpChannelParent::GetInterface(const nsIID& aIID, void **result)
120 {
121 if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
122 aIID.Equals(NS_GET_IID(nsISecureBrowserUI))) {
123 if (!mTabParent)
124 return NS_NOINTERFACE;
125
126 return mTabParent->QueryInterface(aIID, result);
127 }
128
129 // Only support nsILoadContext if child channel's callbacks did too
130 if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
131 NS_ADDREF(mLoadContext);
132 *result = static_cast<nsILoadContext*>(mLoadContext);
133 return NS_OK;
134 }
135
136 return QueryInterface(aIID, result);
137 }
138
139 //-----------------------------------------------------------------------------
140 // HttpChannelParent::PHttpChannelParent
141 //-----------------------------------------------------------------------------
142
143 bool
144 HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
145 const OptionalURIParams& aOriginalURI,
146 const OptionalURIParams& aDocURI,
147 const OptionalURIParams& aReferrerURI,
148 const OptionalURIParams& aAPIRedirectToURI,
149 const uint32_t& loadFlags,
150 const RequestHeaderTuples& requestHeaders,
151 const nsCString& requestMethod,
152 const OptionalInputStreamParams& uploadStream,
153 const bool& uploadStreamHasHeaders,
154 const uint16_t& priority,
155 const uint8_t& redirectionLimit,
156 const bool& allowPipelining,
157 const bool& forceAllowThirdPartyCookie,
158 const bool& doResumeAt,
159 const uint64_t& startPos,
160 const nsCString& entityID,
161 const bool& chooseApplicationCache,
162 const nsCString& appCacheClientID,
163 const bool& allowSpdy,
164 const OptionalFileDescriptorSet& aFds)
165 {
166 nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
167 if (!uri) {
168 // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
169 // null deref here.
170 return false;
171 }
172 nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
173 nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
174 nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
175 nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
176
177 nsCString uriSpec;
178 uri->GetSpec(uriSpec);
179 LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s]\n",
180 this, uriSpec.get()));
181
182 nsresult rv;
183
184 nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
185 if (NS_FAILED(rv))
186 return SendFailedAsyncOpen(rv);
187
188 nsCOMPtr<nsIChannel> channel;
189 rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, nullptr, nullptr, loadFlags);
190 if (NS_FAILED(rv))
191 return SendFailedAsyncOpen(rv);
192
193 mChannel = static_cast<nsHttpChannel *>(channel.get());
194 if (mPBOverride != kPBOverride_Unset) {
195 mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
196 }
197
198 if (doResumeAt)
199 mChannel->ResumeAt(startPos, entityID);
200
201 if (originalUri)
202 mChannel->SetOriginalURI(originalUri);
203 if (docUri)
204 mChannel->SetDocumentURI(docUri);
205 if (referrerUri)
206 mChannel->SetReferrerInternal(referrerUri);
207 if (apiRedirectToUri)
208 mChannel->RedirectTo(apiRedirectToUri);
209 if (loadFlags != nsIRequest::LOAD_NORMAL)
210 mChannel->SetLoadFlags(loadFlags);
211
212 for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
213 mChannel->SetRequestHeader(requestHeaders[i].mHeader,
214 requestHeaders[i].mValue,
215 requestHeaders[i].mMerge);
216 }
217
218 mParentListener = new HttpChannelParentListener(this);
219
220 mChannel->SetNotificationCallbacks(mParentListener);
221
222 mChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
223
224 nsTArray<mozilla::ipc::FileDescriptor> fds;
225 if (aFds.type() == OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
226 FileDescriptorSetParent* fdSetActor =
227 static_cast<FileDescriptorSetParent*>(aFds.get_PFileDescriptorSetParent());
228 MOZ_ASSERT(fdSetActor);
229
230 fdSetActor->ForgetFileDescriptors(fds);
231 MOZ_ASSERT(!fds.IsEmpty());
232
233 unused << fdSetActor->Send__delete__(fdSetActor);
234 }
235
236 nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds);
237 if (stream) {
238 mChannel->InternalSetUploadStream(stream);
239 mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
240 }
241
242 if (priority != nsISupportsPriority::PRIORITY_NORMAL)
243 mChannel->SetPriority(priority);
244 mChannel->SetRedirectionLimit(redirectionLimit);
245 mChannel->SetAllowPipelining(allowPipelining);
246 mChannel->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
247 mChannel->SetAllowSpdy(allowSpdy);
248
249 nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
250 do_QueryObject(mChannel);
251 nsCOMPtr<nsIApplicationCacheService> appCacheService =
252 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
253
254 bool setChooseApplicationCache = chooseApplicationCache;
255 if (appCacheChan && appCacheService) {
256 // We might potentially want to drop this flag (that is TRUE by default)
257 // after we successfully associate the channel with an application cache
258 // reported by the channel child. Dropping it here may be too early.
259 appCacheChan->SetInheritApplicationCache(false);
260 if (!appCacheClientID.IsEmpty()) {
261 nsCOMPtr<nsIApplicationCache> appCache;
262 rv = appCacheService->GetApplicationCache(appCacheClientID,
263 getter_AddRefs(appCache));
264 if (NS_SUCCEEDED(rv)) {
265 appCacheChan->SetApplicationCache(appCache);
266 setChooseApplicationCache = false;
267 }
268 }
269
270 if (setChooseApplicationCache) {
271 bool inBrowser = false;
272 uint32_t appId = NECKO_NO_APP_ID;
273 if (mLoadContext) {
274 mLoadContext->GetIsInBrowserElement(&inBrowser);
275 mLoadContext->GetAppId(&appId);
276 }
277
278 bool chooseAppCache = false;
279 nsCOMPtr<nsIScriptSecurityManager> secMan =
280 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
281 if (secMan) {
282 nsCOMPtr<nsIPrincipal> principal;
283 secMan->GetAppCodebasePrincipal(uri, appId, inBrowser, getter_AddRefs(principal));
284
285 // This works because we've already called SetNotificationCallbacks and
286 // done mPBOverride logic by this point.
287 chooseAppCache = NS_ShouldCheckAppCache(principal, NS_UsePrivateBrowsing(mChannel));
288 }
289
290 appCacheChan->SetChooseApplicationCache(chooseAppCache);
291 }
292 }
293
294 rv = mChannel->AsyncOpen(mParentListener, nullptr);
295 if (NS_FAILED(rv))
296 return SendFailedAsyncOpen(rv);
297
298 return true;
299 }
300
301 bool
302 HttpChannelParent::ConnectChannel(const uint32_t& channelId)
303 {
304 nsresult rv;
305
306 LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
307 nsCOMPtr<nsIChannel> channel;
308 rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
309 mChannel = static_cast<nsHttpChannel*>(channel.get());
310 LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
311
312 if (mPBOverride != kPBOverride_Unset) {
313 // redirected-to channel may not support PB
314 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
315 if (pbChannel) {
316 pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
317 }
318 }
319
320 return true;
321 }
322
323 bool
324 HttpChannelParent::RecvSetPriority(const uint16_t& priority)
325 {
326 if (mChannel) {
327 mChannel->SetPriority(priority);
328 }
329
330 nsCOMPtr<nsISupportsPriority> priorityRedirectChannel =
331 do_QueryInterface(mRedirectChannel);
332 if (priorityRedirectChannel)
333 priorityRedirectChannel->SetPriority(priority);
334
335 return true;
336 }
337
338 bool
339 HttpChannelParent::RecvSuspend()
340 {
341 if (mChannel) {
342 mChannel->Suspend();
343 }
344 return true;
345 }
346
347 bool
348 HttpChannelParent::RecvResume()
349 {
350 if (mChannel) {
351 mChannel->Resume();
352 }
353 return true;
354 }
355
356 bool
357 HttpChannelParent::RecvCancel(const nsresult& status)
358 {
359 // May receive cancel before channel has been constructed!
360 if (mChannel) {
361 mChannel->Cancel(status);
362 }
363 return true;
364 }
365
366
367 bool
368 HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
369 {
370 if (mCacheEntry)
371 mCacheEntry->SetMetaDataElement("charset", charset.get());
372 return true;
373 }
374
375 bool
376 HttpChannelParent::RecvUpdateAssociatedContentSecurity(const int32_t& broken,
377 const int32_t& no)
378 {
379 if (mAssociatedContentSecurity) {
380 mAssociatedContentSecurity->SetCountSubRequestsBrokenSecurity(broken);
381 mAssociatedContentSecurity->SetCountSubRequestsNoSecurity(no);
382 }
383 return true;
384 }
385
386 bool
387 HttpChannelParent::RecvRedirect2Verify(const nsresult& result,
388 const RequestHeaderTuples& changedHeaders,
389 const OptionalURIParams& aAPIRedirectURI)
390 {
391 if (NS_SUCCEEDED(result)) {
392 nsCOMPtr<nsIHttpChannel> newHttpChannel =
393 do_QueryInterface(mRedirectChannel);
394
395 if (newHttpChannel) {
396 nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI);
397
398 if (apiRedirectUri)
399 newHttpChannel->RedirectTo(apiRedirectUri);
400
401 for (uint32_t i = 0; i < changedHeaders.Length(); i++) {
402 newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
403 changedHeaders[i].mValue,
404 changedHeaders[i].mMerge);
405 }
406 }
407 }
408
409 if (!mRedirectCallback) {
410 // This should according the logic never happen, log the situation.
411 if (mReceivedRedirect2Verify)
412 LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
413 if (mSentRedirect1BeginFailed)
414 LOG(("RecvRedirect2Verify[%p]: Send to child failed", this));
415 if (mSentRedirect1Begin && NS_FAILED(result))
416 LOG(("RecvRedirect2Verify[%p]: Redirect failed", this));
417 if (mSentRedirect1Begin && NS_SUCCEEDED(result))
418 LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this));
419 if (!mRedirectChannel)
420 LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this));
421
422 NS_ERROR("Unexpcted call to HttpChannelParent::RecvRedirect2Verify, "
423 "mRedirectCallback null");
424 }
425
426 mReceivedRedirect2Verify = true;
427
428 if (mRedirectCallback) {
429 mRedirectCallback->OnRedirectVerifyCallback(result);
430 mRedirectCallback = nullptr;
431 }
432
433 return true;
434 }
435
436 bool
437 HttpChannelParent::RecvDocumentChannelCleanup()
438 {
439 // From now on only using mAssociatedContentSecurity. Free everything else.
440 mChannel = 0; // Reclaim some memory sooner.
441 mCacheEntry = 0; // Else we'll block other channels reading same URI
442 return true;
443 }
444
445 bool
446 HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign()
447 {
448 if (mOfflineForeignMarker) {
449 mOfflineForeignMarker->MarkAsForeign();
450 mOfflineForeignMarker = 0;
451 }
452
453 return true;
454 }
455
456 bool
457 HttpChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
458 const uint64_t& offset,
459 const uint32_t& count)
460 {
461 MOZ_ASSERT(mParentListener);
462 if (NS_WARN_IF(!mDivertingFromChild)) {
463 MOZ_ASSERT(mDivertingFromChild,
464 "Cannot RecvDivertOnDataAvailable if diverting is not set!");
465 FailDiversion(NS_ERROR_UNEXPECTED);
466 return false;
467 }
468
469 // Drop OnDataAvailables if the parent was canceled already.
470 if (NS_FAILED(mStatus)) {
471 return true;
472 }
473
474 nsCOMPtr<nsIInputStream> stringStream;
475 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
476 count, NS_ASSIGNMENT_DEPEND);
477 if (NS_FAILED(rv)) {
478 if (mChannel) {
479 mChannel->Cancel(rv);
480 }
481 mStatus = rv;
482 return true;
483 }
484
485 rv = mParentListener->OnDataAvailable(mChannel, nullptr, stringStream,
486 offset, count);
487 stringStream->Close();
488 if (NS_FAILED(rv)) {
489 if (mChannel) {
490 mChannel->Cancel(rv);
491 }
492 mStatus = rv;
493 return true;
494 }
495 return true;
496 }
497
498 bool
499 HttpChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
500 {
501 MOZ_ASSERT(mParentListener);
502 if (NS_WARN_IF(!mDivertingFromChild)) {
503 MOZ_ASSERT(mDivertingFromChild,
504 "Cannot RecvDivertOnStopRequest if diverting is not set!");
505 FailDiversion(NS_ERROR_UNEXPECTED);
506 return false;
507 }
508
509 // Honor the channel's status even if the underlying transaction completed.
510 nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
511
512 // Reset fake pending status in case OnStopRequest has already been called.
513 if (mChannel) {
514 mChannel->ForcePending(false);
515 }
516
517 mParentListener->OnStopRequest(mChannel, nullptr, status);
518 return true;
519 }
520
521 bool
522 HttpChannelParent::RecvDivertComplete()
523 {
524 MOZ_ASSERT(mParentListener);
525 mParentListener = nullptr;
526 if (NS_WARN_IF(!mDivertingFromChild)) {
527 MOZ_ASSERT(mDivertingFromChild,
528 "Cannot RecvDivertComplete if diverting is not set!");
529 FailDiversion(NS_ERROR_UNEXPECTED);
530 return false;
531 }
532
533 nsresult rv = ResumeForDiversion();
534 if (NS_WARN_IF(NS_FAILED(rv))) {
535 FailDiversion(NS_ERROR_UNEXPECTED);
536 return false;
537 }
538
539 return true;
540 }
541
542 //-----------------------------------------------------------------------------
543 // HttpChannelParent::nsIRequestObserver
544 //-----------------------------------------------------------------------------
545
546 NS_IMETHODIMP
547 HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
548 {
549 LOG(("HttpChannelParent::OnStartRequest [this=%p]\n", this));
550
551 MOZ_RELEASE_ASSERT(!mDivertingFromChild,
552 "Cannot call OnStartRequest if diverting is set!");
553
554 nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest);
555 nsHttpResponseHead *responseHead = chan->GetResponseHead();
556 nsHttpRequestHead *requestHead = chan->GetRequestHead();
557 bool isFromCache = false;
558 chan->IsFromCache(&isFromCache);
559 uint32_t expirationTime = nsICache::NO_EXPIRATION_TIME;
560 chan->GetCacheTokenExpirationTime(&expirationTime);
561 nsCString cachedCharset;
562 chan->GetCacheTokenCachedCharset(cachedCharset);
563
564 bool loadedFromApplicationCache;
565 chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache);
566 if (loadedFromApplicationCache) {
567 mOfflineForeignMarker = chan->GetOfflineCacheEntryAsForeignMarker();
568 nsCOMPtr<nsIApplicationCache> appCache;
569 chan->GetApplicationCache(getter_AddRefs(appCache));
570 nsCString appCacheGroupId;
571 nsCString appCacheClientId;
572 appCache->GetGroupID(appCacheGroupId);
573 appCache->GetClientID(appCacheClientId);
574 if (mIPCClosed ||
575 !SendAssociateApplicationCache(appCacheGroupId, appCacheClientId))
576 {
577 return NS_ERROR_UNEXPECTED;
578 }
579 }
580
581 nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(aRequest);
582 if (encodedChannel)
583 encodedChannel->SetApplyConversion(false);
584
585 // Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
586 // It could be already released by nsHttpChannel at that time.
587 nsCOMPtr<nsISupports> cacheEntry;
588 chan->GetCacheToken(getter_AddRefs(cacheEntry));
589 mCacheEntry = do_QueryInterface(cacheEntry);
590
591 nsresult channelStatus = NS_OK;
592 chan->GetStatus(&channelStatus);
593
594 nsCString secInfoSerialization;
595 nsCOMPtr<nsISupports> secInfoSupp;
596 chan->GetSecurityInfo(getter_AddRefs(secInfoSupp));
597 if (secInfoSupp) {
598 mAssociatedContentSecurity = do_QueryInterface(secInfoSupp);
599 nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfoSupp);
600 if (secInfoSer)
601 NS_SerializeToString(secInfoSer, secInfoSerialization);
602 }
603
604 uint16_t redirectCount = 0;
605 mChannel->GetRedirectCount(&redirectCount);
606 if (mIPCClosed ||
607 !SendOnStartRequest(channelStatus,
608 responseHead ? *responseHead : nsHttpResponseHead(),
609 !!responseHead,
610 requestHead->Headers(),
611 isFromCache,
612 mCacheEntry ? true : false,
613 expirationTime, cachedCharset, secInfoSerialization,
614 mChannel->GetSelfAddr(), mChannel->GetPeerAddr(),
615 redirectCount))
616 {
617 return NS_ERROR_UNEXPECTED;
618 }
619 return NS_OK;
620 }
621
622 NS_IMETHODIMP
623 HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
624 nsISupports *aContext,
625 nsresult aStatusCode)
626 {
627 LOG(("HttpChannelParent::OnStopRequest: [this=%p status=%x]\n",
628 this, aStatusCode));
629
630 MOZ_RELEASE_ASSERT(!mDivertingFromChild,
631 "Cannot call OnStopRequest if diverting is set!");
632
633 if (mIPCClosed || !SendOnStopRequest(aStatusCode))
634 return NS_ERROR_UNEXPECTED;
635 return NS_OK;
636 }
637
638 //-----------------------------------------------------------------------------
639 // HttpChannelParent::nsIStreamListener
640 //-----------------------------------------------------------------------------
641
642 NS_IMETHODIMP
643 HttpChannelParent::OnDataAvailable(nsIRequest *aRequest,
644 nsISupports *aContext,
645 nsIInputStream *aInputStream,
646 uint64_t aOffset,
647 uint32_t aCount)
648 {
649 LOG(("HttpChannelParent::OnDataAvailable [this=%p]\n", this));
650
651 MOZ_RELEASE_ASSERT(!mDivertingFromChild,
652 "Cannot call OnDataAvailable if diverting is set!");
653
654 nsCString data;
655 nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
656 if (NS_FAILED(rv))
657 return rv;
658
659 nsresult channelStatus = NS_OK;
660 mChannel->GetStatus(&channelStatus);
661
662 // OnDataAvailable is always preceded by OnStatus/OnProgress calls that set
663 // mStoredStatus/mStoredProgress(Max) to appropriate values, unless
664 // LOAD_BACKGROUND set. In that case, they'll have garbage values, but
665 // child doesn't use them.
666 if (mIPCClosed || !SendOnTransportAndData(channelStatus, mStoredStatus,
667 mStoredProgress, mStoredProgressMax,
668 data, aOffset, aCount)) {
669 return NS_ERROR_UNEXPECTED;
670 }
671 return NS_OK;
672 }
673
674 //-----------------------------------------------------------------------------
675 // HttpChannelParent::nsIProgressEventSink
676 //-----------------------------------------------------------------------------
677
678 NS_IMETHODIMP
679 HttpChannelParent::OnProgress(nsIRequest *aRequest,
680 nsISupports *aContext,
681 uint64_t aProgress,
682 uint64_t aProgressMax)
683 {
684 // OnStatus has always just set mStoredStatus. If it indicates this precedes
685 // OnDataAvailable, store and ODA will send to child.
686 if (mStoredStatus == NS_NET_STATUS_RECEIVING_FROM ||
687 mStoredStatus == NS_NET_STATUS_READING)
688 {
689 mStoredProgress = aProgress;
690 mStoredProgressMax = aProgressMax;
691 } else {
692 // Send to child now. The only case I've observed that this handles (i.e.
693 // non-ODA status with progress > 0) is data upload progress notification
694 // (status == NS_NET_STATUS_SENDING_TO)
695 if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax))
696 return NS_ERROR_UNEXPECTED;
697 }
698
699 return NS_OK;
700 }
701
702 NS_IMETHODIMP
703 HttpChannelParent::OnStatus(nsIRequest *aRequest,
704 nsISupports *aContext,
705 nsresult aStatus,
706 const char16_t *aStatusArg)
707 {
708 // If this precedes OnDataAvailable, store and ODA will send to child.
709 if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
710 aStatus == NS_NET_STATUS_READING)
711 {
712 mStoredStatus = aStatus;
713 return NS_OK;
714 }
715 // Otherwise, send to child now
716 if (mIPCClosed || !SendOnStatus(aStatus))
717 return NS_ERROR_UNEXPECTED;
718 return NS_OK;
719 }
720
721 //-----------------------------------------------------------------------------
722 // HttpChannelParent::nsIParentChannel
723 //-----------------------------------------------------------------------------
724
725 NS_IMETHODIMP
726 HttpChannelParent::SetParentListener(HttpChannelParentListener* aListener)
727 {
728 MOZ_ASSERT(aListener);
729 MOZ_ASSERT(!mParentListener, "SetParentListener should only be called for "
730 "new HttpChannelParents after a redirect, when "
731 "mParentListener is null.");
732 mParentListener = aListener;
733 return NS_OK;
734 }
735
736 NS_IMETHODIMP
737 HttpChannelParent::Delete()
738 {
739 if (!mIPCClosed)
740 unused << SendDeleteSelf();
741
742 return NS_OK;
743 }
744
745 //-----------------------------------------------------------------------------
746 // HttpChannelParent::nsIParentRedirectingChannel
747 //-----------------------------------------------------------------------------
748
749 NS_IMETHODIMP
750 HttpChannelParent::StartRedirect(uint32_t newChannelId,
751 nsIChannel* newChannel,
752 uint32_t redirectFlags,
753 nsIAsyncVerifyRedirectCallback* callback)
754 {
755 if (mIPCClosed)
756 return NS_BINDING_ABORTED;
757
758 nsCOMPtr<nsIURI> newURI;
759 newChannel->GetURI(getter_AddRefs(newURI));
760
761 URIParams uriParams;
762 SerializeURI(newURI, uriParams);
763
764 nsHttpResponseHead *responseHead = mChannel->GetResponseHead();
765 bool result = SendRedirect1Begin(newChannelId, uriParams, redirectFlags,
766 responseHead ? *responseHead
767 : nsHttpResponseHead());
768 if (!result) {
769 // Bug 621446 investigation
770 mSentRedirect1BeginFailed = true;
771 return NS_BINDING_ABORTED;
772 }
773
774 // Bug 621446 investigation
775 mSentRedirect1Begin = true;
776
777 // Result is handled in RecvRedirect2Verify above
778
779 mRedirectChannel = newChannel;
780 mRedirectCallback = callback;
781 return NS_OK;
782 }
783
784 NS_IMETHODIMP
785 HttpChannelParent::CompleteRedirect(bool succeeded)
786 {
787 if (succeeded && !mIPCClosed) {
788 // TODO: check return value: assume child dead if failed
789 unused << SendRedirect3Complete();
790 }
791
792 mRedirectChannel = nullptr;
793 return NS_OK;
794 }
795
796 //-----------------------------------------------------------------------------
797 // HttpChannelParent::ADivertableParentChannel
798 //-----------------------------------------------------------------------------
799 nsresult
800 HttpChannelParent::SuspendForDiversion()
801 {
802 MOZ_ASSERT(mChannel);
803 MOZ_ASSERT(mParentListener);
804 if (NS_WARN_IF(mDivertingFromChild)) {
805 MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
806 return NS_ERROR_UNEXPECTED;
807 }
808
809 // Try suspending the channel. Allow it to fail, since OnStopRequest may have
810 // been called and thus the channel may not be pending.
811 nsresult rv = mChannel->Suspend();
812 MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
813 mSuspendedForDiversion = NS_SUCCEEDED(rv);
814
815 rv = mParentListener->SuspendForDiversion();
816 MOZ_ASSERT(NS_SUCCEEDED(rv));
817
818 // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
819 // to the child.
820 mDivertingFromChild = true;
821
822 return NS_OK;
823 }
824
825 /* private, supporting function for ADivertableParentChannel */
826 nsresult
827 HttpChannelParent::ResumeForDiversion()
828 {
829 MOZ_ASSERT(mChannel);
830 if (NS_WARN_IF(!mDivertingFromChild)) {
831 MOZ_ASSERT(mDivertingFromChild,
832 "Cannot ResumeForDiversion if not diverting!");
833 return NS_ERROR_UNEXPECTED;
834 }
835
836 if (mSuspendedForDiversion) {
837 // The nsHttpChannel will deliver remaining OnData/OnStop for the transfer.
838 nsresult rv = mChannel->Resume();
839 if (NS_WARN_IF(NS_FAILED(rv))) {
840 FailDiversion(NS_ERROR_UNEXPECTED, true);
841 return rv;
842 }
843 mSuspendedForDiversion = false;
844 }
845
846 if (NS_WARN_IF(mIPCClosed || !SendDeleteSelf())) {
847 FailDiversion(NS_ERROR_UNEXPECTED);
848 return NS_ERROR_UNEXPECTED;
849 }
850 return NS_OK;
851 }
852
853 void
854 HttpChannelParent::DivertTo(nsIStreamListener *aListener)
855 {
856 MOZ_ASSERT(mParentListener);
857 if (NS_WARN_IF(!mDivertingFromChild)) {
858 MOZ_ASSERT(mDivertingFromChild,
859 "Cannot DivertTo new listener if diverting is not set!");
860 return;
861 }
862
863 DebugOnly<nsresult> rv = mParentListener->DivertTo(aListener);
864 MOZ_ASSERT(NS_SUCCEEDED(rv));
865
866 if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
867 FailDiversion(NS_ERROR_UNEXPECTED);
868 return;
869 }
870
871 // Call OnStartRequest and SendDivertMessages asynchronously to avoid
872 // reentering client context.
873 NS_DispatchToCurrentThread(
874 NS_NewRunnableMethod(this, &HttpChannelParent::StartDiversion));
875 return;
876 }
877
878 void
879 HttpChannelParent::StartDiversion()
880 {
881 if (NS_WARN_IF(!mDivertingFromChild)) {
882 MOZ_ASSERT(mDivertingFromChild,
883 "Cannot StartDiversion if diverting is not set!");
884 return;
885 }
886
887 // Fake pending status in case OnStopRequest has already been called.
888 if (mChannel) {
889 mChannel->ForcePending(true);
890 }
891
892 // Call OnStartRequest for the "DivertTo" listener.
893 nsresult rv = mParentListener->OnStartRequest(mChannel, nullptr);
894 if (NS_FAILED(rv)) {
895 if (mChannel) {
896 mChannel->Cancel(rv);
897 }
898 mStatus = rv;
899 }
900 mDivertedOnStartRequest = true;
901
902 // After OnStartRequest has been called, tell HttpChannelChild to divert the
903 // OnDataAvailables and OnStopRequest to this HttpChannelParent.
904 if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
905 FailDiversion(NS_ERROR_UNEXPECTED);
906 return;
907 }
908 }
909
910 class HTTPFailDiversionEvent : public nsRunnable
911 {
912 public:
913 HTTPFailDiversionEvent(HttpChannelParent *aChannelParent,
914 nsresult aErrorCode,
915 bool aSkipResume)
916 : mChannelParent(aChannelParent)
917 , mErrorCode(aErrorCode)
918 , mSkipResume(aSkipResume)
919 {
920 MOZ_RELEASE_ASSERT(aChannelParent);
921 MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
922 }
923 NS_IMETHOD Run()
924 {
925 mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
926 return NS_OK;
927 }
928 private:
929 nsRefPtr<HttpChannelParent> mChannelParent;
930 nsresult mErrorCode;
931 bool mSkipResume;
932 };
933
934 void
935 HttpChannelParent::FailDiversion(nsresult aErrorCode,
936 bool aSkipResume)
937 {
938 MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
939 MOZ_RELEASE_ASSERT(mDivertingFromChild);
940 MOZ_RELEASE_ASSERT(mParentListener);
941 MOZ_RELEASE_ASSERT(mChannel);
942
943 NS_DispatchToCurrentThread(
944 new HTTPFailDiversionEvent(this, aErrorCode, aSkipResume));
945 }
946
947 void
948 HttpChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
949 bool aSkipResume)
950 {
951 MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
952 MOZ_RELEASE_ASSERT(mDivertingFromChild);
953 MOZ_RELEASE_ASSERT(mParentListener);
954 MOZ_RELEASE_ASSERT(mChannel);
955
956 mChannel->Cancel(aErrorCode);
957
958 mChannel->ForcePending(false);
959
960 bool isPending = false;
961 nsresult rv = mChannel->IsPending(&isPending);
962 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
963
964 // Resume only if we suspended earlier.
965 if (mSuspendedForDiversion) {
966 mChannel->Resume();
967 }
968 // Channel has already sent OnStartRequest to the child, so ensure that we
969 // call it here if it hasn't already been called.
970 if (!mDivertedOnStartRequest) {
971 mChannel->ForcePending(true);
972 mParentListener->OnStartRequest(mChannel, nullptr);
973 mChannel->ForcePending(false);
974 }
975 // If the channel is pending, it will call OnStopRequest itself; otherwise, do
976 // it here.
977 if (!isPending) {
978 mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode);
979 }
980 mParentListener = nullptr;
981 mChannel = nullptr;
982
983 if (!mIPCClosed) {
984 unused << SendDeleteSelf();
985 }
986 }
987
988 }} // mozilla::net

mercurial