|
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 |