1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/HttpChannelParent.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,988 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +#include "mozilla/dom/FileDescriptorSetParent.h" 1.14 +#include "mozilla/net/HttpChannelParent.h" 1.15 +#include "mozilla/dom/TabParent.h" 1.16 +#include "mozilla/net/NeckoParent.h" 1.17 +#include "mozilla/unused.h" 1.18 +#include "HttpChannelParentListener.h" 1.19 +#include "nsHttpHandler.h" 1.20 +#include "nsNetUtil.h" 1.21 +#include "nsISupportsPriority.h" 1.22 +#include "nsIAuthPromptProvider.h" 1.23 +#include "nsIScriptSecurityManager.h" 1.24 +#include "nsSerializationHelper.h" 1.25 +#include "nsISerializable.h" 1.26 +#include "nsIAssociatedContentSecurity.h" 1.27 +#include "nsIApplicationCacheService.h" 1.28 +#include "mozilla/ipc/InputStreamUtils.h" 1.29 +#include "mozilla/ipc/URIUtils.h" 1.30 +#include "SerializedLoadContext.h" 1.31 + 1.32 +using namespace mozilla::dom; 1.33 +using namespace mozilla::ipc; 1.34 + 1.35 +namespace mozilla { 1.36 +namespace net { 1.37 + 1.38 +HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding, 1.39 + nsILoadContext* aLoadContext, 1.40 + PBOverrideStatus aOverrideStatus) 1.41 + : mIPCClosed(false) 1.42 + , mStoredStatus(NS_OK) 1.43 + , mStoredProgress(0) 1.44 + , mStoredProgressMax(0) 1.45 + , mSentRedirect1Begin(false) 1.46 + , mSentRedirect1BeginFailed(false) 1.47 + , mReceivedRedirect2Verify(false) 1.48 + , mPBOverride(aOverrideStatus) 1.49 + , mLoadContext(aLoadContext) 1.50 + , mStatus(NS_OK) 1.51 + , mDivertingFromChild(false) 1.52 + , mDivertedOnStartRequest(false) 1.53 + , mSuspendedForDiversion(false) 1.54 +{ 1.55 + // Ensure gHttpHandler is initialized: we need the atom table up and running. 1.56 + nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer = 1.57 + do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http"); 1.58 + 1.59 + MOZ_ASSERT(gHttpHandler); 1.60 + mHttpHandler = gHttpHandler; 1.61 + 1.62 + mTabParent = static_cast<mozilla::dom::TabParent*>(iframeEmbedding); 1.63 +} 1.64 + 1.65 +HttpChannelParent::~HttpChannelParent() 1.66 +{ 1.67 +} 1.68 + 1.69 +void 1.70 +HttpChannelParent::ActorDestroy(ActorDestroyReason why) 1.71 +{ 1.72 + // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest 1.73 + // yet, but child process has crashed. We must not try to send any more msgs 1.74 + // to child, or IPDL will kill chrome process, too. 1.75 + mIPCClosed = true; 1.76 +} 1.77 + 1.78 +bool 1.79 +HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) 1.80 +{ 1.81 + switch (aArgs.type()) { 1.82 + case HttpChannelCreationArgs::THttpChannelOpenArgs: 1.83 + { 1.84 + const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs(); 1.85 + return DoAsyncOpen(a.uri(), a.original(), a.doc(), a.referrer(), 1.86 + a.apiRedirectTo(), a.loadFlags(), a.requestHeaders(), 1.87 + a.requestMethod(), a.uploadStream(), 1.88 + a.uploadStreamHasHeaders(), a.priority(), 1.89 + a.redirectionLimit(), a.allowPipelining(), 1.90 + a.forceAllowThirdPartyCookie(), a.resumeAt(), 1.91 + a.startPos(), a.entityID(), a.chooseApplicationCache(), 1.92 + a.appCacheClientID(), a.allowSpdy(), a.fds()); 1.93 + } 1.94 + case HttpChannelCreationArgs::THttpChannelConnectArgs: 1.95 + { 1.96 + const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs(); 1.97 + return ConnectChannel(cArgs.channelId()); 1.98 + } 1.99 + default: 1.100 + NS_NOTREACHED("unknown open type"); 1.101 + return false; 1.102 + } 1.103 +} 1.104 + 1.105 +//----------------------------------------------------------------------------- 1.106 +// HttpChannelParent::nsISupports 1.107 +//----------------------------------------------------------------------------- 1.108 + 1.109 +NS_IMPL_ISUPPORTS(HttpChannelParent, 1.110 + nsIInterfaceRequestor, 1.111 + nsIProgressEventSink, 1.112 + nsIRequestObserver, 1.113 + nsIStreamListener, 1.114 + nsIParentChannel, 1.115 + nsIParentRedirectingChannel) 1.116 + 1.117 +//----------------------------------------------------------------------------- 1.118 +// HttpChannelParent::nsIInterfaceRequestor 1.119 +//----------------------------------------------------------------------------- 1.120 + 1.121 +NS_IMETHODIMP 1.122 +HttpChannelParent::GetInterface(const nsIID& aIID, void **result) 1.123 +{ 1.124 + if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider)) || 1.125 + aIID.Equals(NS_GET_IID(nsISecureBrowserUI))) { 1.126 + if (!mTabParent) 1.127 + return NS_NOINTERFACE; 1.128 + 1.129 + return mTabParent->QueryInterface(aIID, result); 1.130 + } 1.131 + 1.132 + // Only support nsILoadContext if child channel's callbacks did too 1.133 + if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) { 1.134 + NS_ADDREF(mLoadContext); 1.135 + *result = static_cast<nsILoadContext*>(mLoadContext); 1.136 + return NS_OK; 1.137 + } 1.138 + 1.139 + return QueryInterface(aIID, result); 1.140 +} 1.141 + 1.142 +//----------------------------------------------------------------------------- 1.143 +// HttpChannelParent::PHttpChannelParent 1.144 +//----------------------------------------------------------------------------- 1.145 + 1.146 +bool 1.147 +HttpChannelParent::DoAsyncOpen( const URIParams& aURI, 1.148 + const OptionalURIParams& aOriginalURI, 1.149 + const OptionalURIParams& aDocURI, 1.150 + const OptionalURIParams& aReferrerURI, 1.151 + const OptionalURIParams& aAPIRedirectToURI, 1.152 + const uint32_t& loadFlags, 1.153 + const RequestHeaderTuples& requestHeaders, 1.154 + const nsCString& requestMethod, 1.155 + const OptionalInputStreamParams& uploadStream, 1.156 + const bool& uploadStreamHasHeaders, 1.157 + const uint16_t& priority, 1.158 + const uint8_t& redirectionLimit, 1.159 + const bool& allowPipelining, 1.160 + const bool& forceAllowThirdPartyCookie, 1.161 + const bool& doResumeAt, 1.162 + const uint64_t& startPos, 1.163 + const nsCString& entityID, 1.164 + const bool& chooseApplicationCache, 1.165 + const nsCString& appCacheClientID, 1.166 + const bool& allowSpdy, 1.167 + const OptionalFileDescriptorSet& aFds) 1.168 +{ 1.169 + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); 1.170 + if (!uri) { 1.171 + // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from 1.172 + // null deref here. 1.173 + return false; 1.174 + } 1.175 + nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI); 1.176 + nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI); 1.177 + nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI); 1.178 + nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI); 1.179 + 1.180 + nsCString uriSpec; 1.181 + uri->GetSpec(uriSpec); 1.182 + LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s]\n", 1.183 + this, uriSpec.get())); 1.184 + 1.185 + nsresult rv; 1.186 + 1.187 + nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv)); 1.188 + if (NS_FAILED(rv)) 1.189 + return SendFailedAsyncOpen(rv); 1.190 + 1.191 + nsCOMPtr<nsIChannel> channel; 1.192 + rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, nullptr, nullptr, loadFlags); 1.193 + if (NS_FAILED(rv)) 1.194 + return SendFailedAsyncOpen(rv); 1.195 + 1.196 + mChannel = static_cast<nsHttpChannel *>(channel.get()); 1.197 + if (mPBOverride != kPBOverride_Unset) { 1.198 + mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false); 1.199 + } 1.200 + 1.201 + if (doResumeAt) 1.202 + mChannel->ResumeAt(startPos, entityID); 1.203 + 1.204 + if (originalUri) 1.205 + mChannel->SetOriginalURI(originalUri); 1.206 + if (docUri) 1.207 + mChannel->SetDocumentURI(docUri); 1.208 + if (referrerUri) 1.209 + mChannel->SetReferrerInternal(referrerUri); 1.210 + if (apiRedirectToUri) 1.211 + mChannel->RedirectTo(apiRedirectToUri); 1.212 + if (loadFlags != nsIRequest::LOAD_NORMAL) 1.213 + mChannel->SetLoadFlags(loadFlags); 1.214 + 1.215 + for (uint32_t i = 0; i < requestHeaders.Length(); i++) { 1.216 + mChannel->SetRequestHeader(requestHeaders[i].mHeader, 1.217 + requestHeaders[i].mValue, 1.218 + requestHeaders[i].mMerge); 1.219 + } 1.220 + 1.221 + mParentListener = new HttpChannelParentListener(this); 1.222 + 1.223 + mChannel->SetNotificationCallbacks(mParentListener); 1.224 + 1.225 + mChannel->SetRequestMethod(nsDependentCString(requestMethod.get())); 1.226 + 1.227 + nsTArray<mozilla::ipc::FileDescriptor> fds; 1.228 + if (aFds.type() == OptionalFileDescriptorSet::TPFileDescriptorSetParent) { 1.229 + FileDescriptorSetParent* fdSetActor = 1.230 + static_cast<FileDescriptorSetParent*>(aFds.get_PFileDescriptorSetParent()); 1.231 + MOZ_ASSERT(fdSetActor); 1.232 + 1.233 + fdSetActor->ForgetFileDescriptors(fds); 1.234 + MOZ_ASSERT(!fds.IsEmpty()); 1.235 + 1.236 + unused << fdSetActor->Send__delete__(fdSetActor); 1.237 + } 1.238 + 1.239 + nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds); 1.240 + if (stream) { 1.241 + mChannel->InternalSetUploadStream(stream); 1.242 + mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders); 1.243 + } 1.244 + 1.245 + if (priority != nsISupportsPriority::PRIORITY_NORMAL) 1.246 + mChannel->SetPriority(priority); 1.247 + mChannel->SetRedirectionLimit(redirectionLimit); 1.248 + mChannel->SetAllowPipelining(allowPipelining); 1.249 + mChannel->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie); 1.250 + mChannel->SetAllowSpdy(allowSpdy); 1.251 + 1.252 + nsCOMPtr<nsIApplicationCacheChannel> appCacheChan = 1.253 + do_QueryObject(mChannel); 1.254 + nsCOMPtr<nsIApplicationCacheService> appCacheService = 1.255 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID); 1.256 + 1.257 + bool setChooseApplicationCache = chooseApplicationCache; 1.258 + if (appCacheChan && appCacheService) { 1.259 + // We might potentially want to drop this flag (that is TRUE by default) 1.260 + // after we successfully associate the channel with an application cache 1.261 + // reported by the channel child. Dropping it here may be too early. 1.262 + appCacheChan->SetInheritApplicationCache(false); 1.263 + if (!appCacheClientID.IsEmpty()) { 1.264 + nsCOMPtr<nsIApplicationCache> appCache; 1.265 + rv = appCacheService->GetApplicationCache(appCacheClientID, 1.266 + getter_AddRefs(appCache)); 1.267 + if (NS_SUCCEEDED(rv)) { 1.268 + appCacheChan->SetApplicationCache(appCache); 1.269 + setChooseApplicationCache = false; 1.270 + } 1.271 + } 1.272 + 1.273 + if (setChooseApplicationCache) { 1.274 + bool inBrowser = false; 1.275 + uint32_t appId = NECKO_NO_APP_ID; 1.276 + if (mLoadContext) { 1.277 + mLoadContext->GetIsInBrowserElement(&inBrowser); 1.278 + mLoadContext->GetAppId(&appId); 1.279 + } 1.280 + 1.281 + bool chooseAppCache = false; 1.282 + nsCOMPtr<nsIScriptSecurityManager> secMan = 1.283 + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); 1.284 + if (secMan) { 1.285 + nsCOMPtr<nsIPrincipal> principal; 1.286 + secMan->GetAppCodebasePrincipal(uri, appId, inBrowser, getter_AddRefs(principal)); 1.287 + 1.288 + // This works because we've already called SetNotificationCallbacks and 1.289 + // done mPBOverride logic by this point. 1.290 + chooseAppCache = NS_ShouldCheckAppCache(principal, NS_UsePrivateBrowsing(mChannel)); 1.291 + } 1.292 + 1.293 + appCacheChan->SetChooseApplicationCache(chooseAppCache); 1.294 + } 1.295 + } 1.296 + 1.297 + rv = mChannel->AsyncOpen(mParentListener, nullptr); 1.298 + if (NS_FAILED(rv)) 1.299 + return SendFailedAsyncOpen(rv); 1.300 + 1.301 + return true; 1.302 +} 1.303 + 1.304 +bool 1.305 +HttpChannelParent::ConnectChannel(const uint32_t& channelId) 1.306 +{ 1.307 + nsresult rv; 1.308 + 1.309 + LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId)); 1.310 + nsCOMPtr<nsIChannel> channel; 1.311 + rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)); 1.312 + mChannel = static_cast<nsHttpChannel*>(channel.get()); 1.313 + LOG((" found channel %p, rv=%08x", mChannel.get(), rv)); 1.314 + 1.315 + if (mPBOverride != kPBOverride_Unset) { 1.316 + // redirected-to channel may not support PB 1.317 + nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel); 1.318 + if (pbChannel) { 1.319 + pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false); 1.320 + } 1.321 + } 1.322 + 1.323 + return true; 1.324 +} 1.325 + 1.326 +bool 1.327 +HttpChannelParent::RecvSetPriority(const uint16_t& priority) 1.328 +{ 1.329 + if (mChannel) { 1.330 + mChannel->SetPriority(priority); 1.331 + } 1.332 + 1.333 + nsCOMPtr<nsISupportsPriority> priorityRedirectChannel = 1.334 + do_QueryInterface(mRedirectChannel); 1.335 + if (priorityRedirectChannel) 1.336 + priorityRedirectChannel->SetPriority(priority); 1.337 + 1.338 + return true; 1.339 +} 1.340 + 1.341 +bool 1.342 +HttpChannelParent::RecvSuspend() 1.343 +{ 1.344 + if (mChannel) { 1.345 + mChannel->Suspend(); 1.346 + } 1.347 + return true; 1.348 +} 1.349 + 1.350 +bool 1.351 +HttpChannelParent::RecvResume() 1.352 +{ 1.353 + if (mChannel) { 1.354 + mChannel->Resume(); 1.355 + } 1.356 + return true; 1.357 +} 1.358 + 1.359 +bool 1.360 +HttpChannelParent::RecvCancel(const nsresult& status) 1.361 +{ 1.362 + // May receive cancel before channel has been constructed! 1.363 + if (mChannel) { 1.364 + mChannel->Cancel(status); 1.365 + } 1.366 + return true; 1.367 +} 1.368 + 1.369 + 1.370 +bool 1.371 +HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset) 1.372 +{ 1.373 + if (mCacheEntry) 1.374 + mCacheEntry->SetMetaDataElement("charset", charset.get()); 1.375 + return true; 1.376 +} 1.377 + 1.378 +bool 1.379 +HttpChannelParent::RecvUpdateAssociatedContentSecurity(const int32_t& broken, 1.380 + const int32_t& no) 1.381 +{ 1.382 + if (mAssociatedContentSecurity) { 1.383 + mAssociatedContentSecurity->SetCountSubRequestsBrokenSecurity(broken); 1.384 + mAssociatedContentSecurity->SetCountSubRequestsNoSecurity(no); 1.385 + } 1.386 + return true; 1.387 +} 1.388 + 1.389 +bool 1.390 +HttpChannelParent::RecvRedirect2Verify(const nsresult& result, 1.391 + const RequestHeaderTuples& changedHeaders, 1.392 + const OptionalURIParams& aAPIRedirectURI) 1.393 +{ 1.394 + if (NS_SUCCEEDED(result)) { 1.395 + nsCOMPtr<nsIHttpChannel> newHttpChannel = 1.396 + do_QueryInterface(mRedirectChannel); 1.397 + 1.398 + if (newHttpChannel) { 1.399 + nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI); 1.400 + 1.401 + if (apiRedirectUri) 1.402 + newHttpChannel->RedirectTo(apiRedirectUri); 1.403 + 1.404 + for (uint32_t i = 0; i < changedHeaders.Length(); i++) { 1.405 + newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader, 1.406 + changedHeaders[i].mValue, 1.407 + changedHeaders[i].mMerge); 1.408 + } 1.409 + } 1.410 + } 1.411 + 1.412 + if (!mRedirectCallback) { 1.413 + // This should according the logic never happen, log the situation. 1.414 + if (mReceivedRedirect2Verify) 1.415 + LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this)); 1.416 + if (mSentRedirect1BeginFailed) 1.417 + LOG(("RecvRedirect2Verify[%p]: Send to child failed", this)); 1.418 + if (mSentRedirect1Begin && NS_FAILED(result)) 1.419 + LOG(("RecvRedirect2Verify[%p]: Redirect failed", this)); 1.420 + if (mSentRedirect1Begin && NS_SUCCEEDED(result)) 1.421 + LOG(("RecvRedirect2Verify[%p]: Redirect succeeded", this)); 1.422 + if (!mRedirectChannel) 1.423 + LOG(("RecvRedirect2Verify[%p]: Missing redirect channel", this)); 1.424 + 1.425 + NS_ERROR("Unexpcted call to HttpChannelParent::RecvRedirect2Verify, " 1.426 + "mRedirectCallback null"); 1.427 + } 1.428 + 1.429 + mReceivedRedirect2Verify = true; 1.430 + 1.431 + if (mRedirectCallback) { 1.432 + mRedirectCallback->OnRedirectVerifyCallback(result); 1.433 + mRedirectCallback = nullptr; 1.434 + } 1.435 + 1.436 + return true; 1.437 +} 1.438 + 1.439 +bool 1.440 +HttpChannelParent::RecvDocumentChannelCleanup() 1.441 +{ 1.442 + // From now on only using mAssociatedContentSecurity. Free everything else. 1.443 + mChannel = 0; // Reclaim some memory sooner. 1.444 + mCacheEntry = 0; // Else we'll block other channels reading same URI 1.445 + return true; 1.446 +} 1.447 + 1.448 +bool 1.449 +HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign() 1.450 +{ 1.451 + if (mOfflineForeignMarker) { 1.452 + mOfflineForeignMarker->MarkAsForeign(); 1.453 + mOfflineForeignMarker = 0; 1.454 + } 1.455 + 1.456 + return true; 1.457 +} 1.458 + 1.459 +bool 1.460 +HttpChannelParent::RecvDivertOnDataAvailable(const nsCString& data, 1.461 + const uint64_t& offset, 1.462 + const uint32_t& count) 1.463 +{ 1.464 + MOZ_ASSERT(mParentListener); 1.465 + if (NS_WARN_IF(!mDivertingFromChild)) { 1.466 + MOZ_ASSERT(mDivertingFromChild, 1.467 + "Cannot RecvDivertOnDataAvailable if diverting is not set!"); 1.468 + FailDiversion(NS_ERROR_UNEXPECTED); 1.469 + return false; 1.470 + } 1.471 + 1.472 + // Drop OnDataAvailables if the parent was canceled already. 1.473 + if (NS_FAILED(mStatus)) { 1.474 + return true; 1.475 + } 1.476 + 1.477 + nsCOMPtr<nsIInputStream> stringStream; 1.478 + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), 1.479 + count, NS_ASSIGNMENT_DEPEND); 1.480 + if (NS_FAILED(rv)) { 1.481 + if (mChannel) { 1.482 + mChannel->Cancel(rv); 1.483 + } 1.484 + mStatus = rv; 1.485 + return true; 1.486 + } 1.487 + 1.488 + rv = mParentListener->OnDataAvailable(mChannel, nullptr, stringStream, 1.489 + offset, count); 1.490 + stringStream->Close(); 1.491 + if (NS_FAILED(rv)) { 1.492 + if (mChannel) { 1.493 + mChannel->Cancel(rv); 1.494 + } 1.495 + mStatus = rv; 1.496 + return true; 1.497 + } 1.498 + return true; 1.499 +} 1.500 + 1.501 +bool 1.502 +HttpChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode) 1.503 +{ 1.504 + MOZ_ASSERT(mParentListener); 1.505 + if (NS_WARN_IF(!mDivertingFromChild)) { 1.506 + MOZ_ASSERT(mDivertingFromChild, 1.507 + "Cannot RecvDivertOnStopRequest if diverting is not set!"); 1.508 + FailDiversion(NS_ERROR_UNEXPECTED); 1.509 + return false; 1.510 + } 1.511 + 1.512 + // Honor the channel's status even if the underlying transaction completed. 1.513 + nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode; 1.514 + 1.515 + // Reset fake pending status in case OnStopRequest has already been called. 1.516 + if (mChannel) { 1.517 + mChannel->ForcePending(false); 1.518 + } 1.519 + 1.520 + mParentListener->OnStopRequest(mChannel, nullptr, status); 1.521 + return true; 1.522 +} 1.523 + 1.524 +bool 1.525 +HttpChannelParent::RecvDivertComplete() 1.526 +{ 1.527 + MOZ_ASSERT(mParentListener); 1.528 + mParentListener = nullptr; 1.529 + if (NS_WARN_IF(!mDivertingFromChild)) { 1.530 + MOZ_ASSERT(mDivertingFromChild, 1.531 + "Cannot RecvDivertComplete if diverting is not set!"); 1.532 + FailDiversion(NS_ERROR_UNEXPECTED); 1.533 + return false; 1.534 + } 1.535 + 1.536 + nsresult rv = ResumeForDiversion(); 1.537 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.538 + FailDiversion(NS_ERROR_UNEXPECTED); 1.539 + return false; 1.540 + } 1.541 + 1.542 + return true; 1.543 +} 1.544 + 1.545 +//----------------------------------------------------------------------------- 1.546 +// HttpChannelParent::nsIRequestObserver 1.547 +//----------------------------------------------------------------------------- 1.548 + 1.549 +NS_IMETHODIMP 1.550 +HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) 1.551 +{ 1.552 + LOG(("HttpChannelParent::OnStartRequest [this=%p]\n", this)); 1.553 + 1.554 + MOZ_RELEASE_ASSERT(!mDivertingFromChild, 1.555 + "Cannot call OnStartRequest if diverting is set!"); 1.556 + 1.557 + nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest); 1.558 + nsHttpResponseHead *responseHead = chan->GetResponseHead(); 1.559 + nsHttpRequestHead *requestHead = chan->GetRequestHead(); 1.560 + bool isFromCache = false; 1.561 + chan->IsFromCache(&isFromCache); 1.562 + uint32_t expirationTime = nsICache::NO_EXPIRATION_TIME; 1.563 + chan->GetCacheTokenExpirationTime(&expirationTime); 1.564 + nsCString cachedCharset; 1.565 + chan->GetCacheTokenCachedCharset(cachedCharset); 1.566 + 1.567 + bool loadedFromApplicationCache; 1.568 + chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache); 1.569 + if (loadedFromApplicationCache) { 1.570 + mOfflineForeignMarker = chan->GetOfflineCacheEntryAsForeignMarker(); 1.571 + nsCOMPtr<nsIApplicationCache> appCache; 1.572 + chan->GetApplicationCache(getter_AddRefs(appCache)); 1.573 + nsCString appCacheGroupId; 1.574 + nsCString appCacheClientId; 1.575 + appCache->GetGroupID(appCacheGroupId); 1.576 + appCache->GetClientID(appCacheClientId); 1.577 + if (mIPCClosed || 1.578 + !SendAssociateApplicationCache(appCacheGroupId, appCacheClientId)) 1.579 + { 1.580 + return NS_ERROR_UNEXPECTED; 1.581 + } 1.582 + } 1.583 + 1.584 + nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(aRequest); 1.585 + if (encodedChannel) 1.586 + encodedChannel->SetApplyConversion(false); 1.587 + 1.588 + // Keep the cache entry for future use in RecvSetCacheTokenCachedCharset(). 1.589 + // It could be already released by nsHttpChannel at that time. 1.590 + nsCOMPtr<nsISupports> cacheEntry; 1.591 + chan->GetCacheToken(getter_AddRefs(cacheEntry)); 1.592 + mCacheEntry = do_QueryInterface(cacheEntry); 1.593 + 1.594 + nsresult channelStatus = NS_OK; 1.595 + chan->GetStatus(&channelStatus); 1.596 + 1.597 + nsCString secInfoSerialization; 1.598 + nsCOMPtr<nsISupports> secInfoSupp; 1.599 + chan->GetSecurityInfo(getter_AddRefs(secInfoSupp)); 1.600 + if (secInfoSupp) { 1.601 + mAssociatedContentSecurity = do_QueryInterface(secInfoSupp); 1.602 + nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfoSupp); 1.603 + if (secInfoSer) 1.604 + NS_SerializeToString(secInfoSer, secInfoSerialization); 1.605 + } 1.606 + 1.607 + uint16_t redirectCount = 0; 1.608 + mChannel->GetRedirectCount(&redirectCount); 1.609 + if (mIPCClosed || 1.610 + !SendOnStartRequest(channelStatus, 1.611 + responseHead ? *responseHead : nsHttpResponseHead(), 1.612 + !!responseHead, 1.613 + requestHead->Headers(), 1.614 + isFromCache, 1.615 + mCacheEntry ? true : false, 1.616 + expirationTime, cachedCharset, secInfoSerialization, 1.617 + mChannel->GetSelfAddr(), mChannel->GetPeerAddr(), 1.618 + redirectCount)) 1.619 + { 1.620 + return NS_ERROR_UNEXPECTED; 1.621 + } 1.622 + return NS_OK; 1.623 +} 1.624 + 1.625 +NS_IMETHODIMP 1.626 +HttpChannelParent::OnStopRequest(nsIRequest *aRequest, 1.627 + nsISupports *aContext, 1.628 + nsresult aStatusCode) 1.629 +{ 1.630 + LOG(("HttpChannelParent::OnStopRequest: [this=%p status=%x]\n", 1.631 + this, aStatusCode)); 1.632 + 1.633 + MOZ_RELEASE_ASSERT(!mDivertingFromChild, 1.634 + "Cannot call OnStopRequest if diverting is set!"); 1.635 + 1.636 + if (mIPCClosed || !SendOnStopRequest(aStatusCode)) 1.637 + return NS_ERROR_UNEXPECTED; 1.638 + return NS_OK; 1.639 +} 1.640 + 1.641 +//----------------------------------------------------------------------------- 1.642 +// HttpChannelParent::nsIStreamListener 1.643 +//----------------------------------------------------------------------------- 1.644 + 1.645 +NS_IMETHODIMP 1.646 +HttpChannelParent::OnDataAvailable(nsIRequest *aRequest, 1.647 + nsISupports *aContext, 1.648 + nsIInputStream *aInputStream, 1.649 + uint64_t aOffset, 1.650 + uint32_t aCount) 1.651 +{ 1.652 + LOG(("HttpChannelParent::OnDataAvailable [this=%p]\n", this)); 1.653 + 1.654 + MOZ_RELEASE_ASSERT(!mDivertingFromChild, 1.655 + "Cannot call OnDataAvailable if diverting is set!"); 1.656 + 1.657 + nsCString data; 1.658 + nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount); 1.659 + if (NS_FAILED(rv)) 1.660 + return rv; 1.661 + 1.662 + nsresult channelStatus = NS_OK; 1.663 + mChannel->GetStatus(&channelStatus); 1.664 + 1.665 + // OnDataAvailable is always preceded by OnStatus/OnProgress calls that set 1.666 + // mStoredStatus/mStoredProgress(Max) to appropriate values, unless 1.667 + // LOAD_BACKGROUND set. In that case, they'll have garbage values, but 1.668 + // child doesn't use them. 1.669 + if (mIPCClosed || !SendOnTransportAndData(channelStatus, mStoredStatus, 1.670 + mStoredProgress, mStoredProgressMax, 1.671 + data, aOffset, aCount)) { 1.672 + return NS_ERROR_UNEXPECTED; 1.673 + } 1.674 + return NS_OK; 1.675 +} 1.676 + 1.677 +//----------------------------------------------------------------------------- 1.678 +// HttpChannelParent::nsIProgressEventSink 1.679 +//----------------------------------------------------------------------------- 1.680 + 1.681 +NS_IMETHODIMP 1.682 +HttpChannelParent::OnProgress(nsIRequest *aRequest, 1.683 + nsISupports *aContext, 1.684 + uint64_t aProgress, 1.685 + uint64_t aProgressMax) 1.686 +{ 1.687 + // OnStatus has always just set mStoredStatus. If it indicates this precedes 1.688 + // OnDataAvailable, store and ODA will send to child. 1.689 + if (mStoredStatus == NS_NET_STATUS_RECEIVING_FROM || 1.690 + mStoredStatus == NS_NET_STATUS_READING) 1.691 + { 1.692 + mStoredProgress = aProgress; 1.693 + mStoredProgressMax = aProgressMax; 1.694 + } else { 1.695 + // Send to child now. The only case I've observed that this handles (i.e. 1.696 + // non-ODA status with progress > 0) is data upload progress notification 1.697 + // (status == NS_NET_STATUS_SENDING_TO) 1.698 + if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax)) 1.699 + return NS_ERROR_UNEXPECTED; 1.700 + } 1.701 + 1.702 + return NS_OK; 1.703 +} 1.704 + 1.705 +NS_IMETHODIMP 1.706 +HttpChannelParent::OnStatus(nsIRequest *aRequest, 1.707 + nsISupports *aContext, 1.708 + nsresult aStatus, 1.709 + const char16_t *aStatusArg) 1.710 +{ 1.711 + // If this precedes OnDataAvailable, store and ODA will send to child. 1.712 + if (aStatus == NS_NET_STATUS_RECEIVING_FROM || 1.713 + aStatus == NS_NET_STATUS_READING) 1.714 + { 1.715 + mStoredStatus = aStatus; 1.716 + return NS_OK; 1.717 + } 1.718 + // Otherwise, send to child now 1.719 + if (mIPCClosed || !SendOnStatus(aStatus)) 1.720 + return NS_ERROR_UNEXPECTED; 1.721 + return NS_OK; 1.722 +} 1.723 + 1.724 +//----------------------------------------------------------------------------- 1.725 +// HttpChannelParent::nsIParentChannel 1.726 +//----------------------------------------------------------------------------- 1.727 + 1.728 +NS_IMETHODIMP 1.729 +HttpChannelParent::SetParentListener(HttpChannelParentListener* aListener) 1.730 +{ 1.731 + MOZ_ASSERT(aListener); 1.732 + MOZ_ASSERT(!mParentListener, "SetParentListener should only be called for " 1.733 + "new HttpChannelParents after a redirect, when " 1.734 + "mParentListener is null."); 1.735 + mParentListener = aListener; 1.736 + return NS_OK; 1.737 +} 1.738 + 1.739 +NS_IMETHODIMP 1.740 +HttpChannelParent::Delete() 1.741 +{ 1.742 + if (!mIPCClosed) 1.743 + unused << SendDeleteSelf(); 1.744 + 1.745 + return NS_OK; 1.746 +} 1.747 + 1.748 +//----------------------------------------------------------------------------- 1.749 +// HttpChannelParent::nsIParentRedirectingChannel 1.750 +//----------------------------------------------------------------------------- 1.751 + 1.752 +NS_IMETHODIMP 1.753 +HttpChannelParent::StartRedirect(uint32_t newChannelId, 1.754 + nsIChannel* newChannel, 1.755 + uint32_t redirectFlags, 1.756 + nsIAsyncVerifyRedirectCallback* callback) 1.757 +{ 1.758 + if (mIPCClosed) 1.759 + return NS_BINDING_ABORTED; 1.760 + 1.761 + nsCOMPtr<nsIURI> newURI; 1.762 + newChannel->GetURI(getter_AddRefs(newURI)); 1.763 + 1.764 + URIParams uriParams; 1.765 + SerializeURI(newURI, uriParams); 1.766 + 1.767 + nsHttpResponseHead *responseHead = mChannel->GetResponseHead(); 1.768 + bool result = SendRedirect1Begin(newChannelId, uriParams, redirectFlags, 1.769 + responseHead ? *responseHead 1.770 + : nsHttpResponseHead()); 1.771 + if (!result) { 1.772 + // Bug 621446 investigation 1.773 + mSentRedirect1BeginFailed = true; 1.774 + return NS_BINDING_ABORTED; 1.775 + } 1.776 + 1.777 + // Bug 621446 investigation 1.778 + mSentRedirect1Begin = true; 1.779 + 1.780 + // Result is handled in RecvRedirect2Verify above 1.781 + 1.782 + mRedirectChannel = newChannel; 1.783 + mRedirectCallback = callback; 1.784 + return NS_OK; 1.785 +} 1.786 + 1.787 +NS_IMETHODIMP 1.788 +HttpChannelParent::CompleteRedirect(bool succeeded) 1.789 +{ 1.790 + if (succeeded && !mIPCClosed) { 1.791 + // TODO: check return value: assume child dead if failed 1.792 + unused << SendRedirect3Complete(); 1.793 + } 1.794 + 1.795 + mRedirectChannel = nullptr; 1.796 + return NS_OK; 1.797 +} 1.798 + 1.799 +//----------------------------------------------------------------------------- 1.800 +// HttpChannelParent::ADivertableParentChannel 1.801 +//----------------------------------------------------------------------------- 1.802 +nsresult 1.803 +HttpChannelParent::SuspendForDiversion() 1.804 +{ 1.805 + MOZ_ASSERT(mChannel); 1.806 + MOZ_ASSERT(mParentListener); 1.807 + if (NS_WARN_IF(mDivertingFromChild)) { 1.808 + MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!"); 1.809 + return NS_ERROR_UNEXPECTED; 1.810 + } 1.811 + 1.812 + // Try suspending the channel. Allow it to fail, since OnStopRequest may have 1.813 + // been called and thus the channel may not be pending. 1.814 + nsresult rv = mChannel->Suspend(); 1.815 + MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE); 1.816 + mSuspendedForDiversion = NS_SUCCEEDED(rv); 1.817 + 1.818 + rv = mParentListener->SuspendForDiversion(); 1.819 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.820 + 1.821 + // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent 1.822 + // to the child. 1.823 + mDivertingFromChild = true; 1.824 + 1.825 + return NS_OK; 1.826 +} 1.827 + 1.828 +/* private, supporting function for ADivertableParentChannel */ 1.829 +nsresult 1.830 +HttpChannelParent::ResumeForDiversion() 1.831 +{ 1.832 + MOZ_ASSERT(mChannel); 1.833 + if (NS_WARN_IF(!mDivertingFromChild)) { 1.834 + MOZ_ASSERT(mDivertingFromChild, 1.835 + "Cannot ResumeForDiversion if not diverting!"); 1.836 + return NS_ERROR_UNEXPECTED; 1.837 + } 1.838 + 1.839 + if (mSuspendedForDiversion) { 1.840 + // The nsHttpChannel will deliver remaining OnData/OnStop for the transfer. 1.841 + nsresult rv = mChannel->Resume(); 1.842 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.843 + FailDiversion(NS_ERROR_UNEXPECTED, true); 1.844 + return rv; 1.845 + } 1.846 + mSuspendedForDiversion = false; 1.847 + } 1.848 + 1.849 + if (NS_WARN_IF(mIPCClosed || !SendDeleteSelf())) { 1.850 + FailDiversion(NS_ERROR_UNEXPECTED); 1.851 + return NS_ERROR_UNEXPECTED; 1.852 + } 1.853 + return NS_OK; 1.854 +} 1.855 + 1.856 +void 1.857 +HttpChannelParent::DivertTo(nsIStreamListener *aListener) 1.858 +{ 1.859 + MOZ_ASSERT(mParentListener); 1.860 + if (NS_WARN_IF(!mDivertingFromChild)) { 1.861 + MOZ_ASSERT(mDivertingFromChild, 1.862 + "Cannot DivertTo new listener if diverting is not set!"); 1.863 + return; 1.864 + } 1.865 + 1.866 + DebugOnly<nsresult> rv = mParentListener->DivertTo(aListener); 1.867 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.868 + 1.869 + if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) { 1.870 + FailDiversion(NS_ERROR_UNEXPECTED); 1.871 + return; 1.872 + } 1.873 + 1.874 + // Call OnStartRequest and SendDivertMessages asynchronously to avoid 1.875 + // reentering client context. 1.876 + NS_DispatchToCurrentThread( 1.877 + NS_NewRunnableMethod(this, &HttpChannelParent::StartDiversion)); 1.878 + return; 1.879 +} 1.880 + 1.881 +void 1.882 +HttpChannelParent::StartDiversion() 1.883 +{ 1.884 + if (NS_WARN_IF(!mDivertingFromChild)) { 1.885 + MOZ_ASSERT(mDivertingFromChild, 1.886 + "Cannot StartDiversion if diverting is not set!"); 1.887 + return; 1.888 + } 1.889 + 1.890 + // Fake pending status in case OnStopRequest has already been called. 1.891 + if (mChannel) { 1.892 + mChannel->ForcePending(true); 1.893 + } 1.894 + 1.895 + // Call OnStartRequest for the "DivertTo" listener. 1.896 + nsresult rv = mParentListener->OnStartRequest(mChannel, nullptr); 1.897 + if (NS_FAILED(rv)) { 1.898 + if (mChannel) { 1.899 + mChannel->Cancel(rv); 1.900 + } 1.901 + mStatus = rv; 1.902 + } 1.903 + mDivertedOnStartRequest = true; 1.904 + 1.905 + // After OnStartRequest has been called, tell HttpChannelChild to divert the 1.906 + // OnDataAvailables and OnStopRequest to this HttpChannelParent. 1.907 + if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) { 1.908 + FailDiversion(NS_ERROR_UNEXPECTED); 1.909 + return; 1.910 + } 1.911 +} 1.912 + 1.913 +class HTTPFailDiversionEvent : public nsRunnable 1.914 +{ 1.915 +public: 1.916 + HTTPFailDiversionEvent(HttpChannelParent *aChannelParent, 1.917 + nsresult aErrorCode, 1.918 + bool aSkipResume) 1.919 + : mChannelParent(aChannelParent) 1.920 + , mErrorCode(aErrorCode) 1.921 + , mSkipResume(aSkipResume) 1.922 + { 1.923 + MOZ_RELEASE_ASSERT(aChannelParent); 1.924 + MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); 1.925 + } 1.926 + NS_IMETHOD Run() 1.927 + { 1.928 + mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume); 1.929 + return NS_OK; 1.930 + } 1.931 +private: 1.932 + nsRefPtr<HttpChannelParent> mChannelParent; 1.933 + nsresult mErrorCode; 1.934 + bool mSkipResume; 1.935 +}; 1.936 + 1.937 +void 1.938 +HttpChannelParent::FailDiversion(nsresult aErrorCode, 1.939 + bool aSkipResume) 1.940 +{ 1.941 + MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); 1.942 + MOZ_RELEASE_ASSERT(mDivertingFromChild); 1.943 + MOZ_RELEASE_ASSERT(mParentListener); 1.944 + MOZ_RELEASE_ASSERT(mChannel); 1.945 + 1.946 + NS_DispatchToCurrentThread( 1.947 + new HTTPFailDiversionEvent(this, aErrorCode, aSkipResume)); 1.948 +} 1.949 + 1.950 +void 1.951 +HttpChannelParent::NotifyDiversionFailed(nsresult aErrorCode, 1.952 + bool aSkipResume) 1.953 +{ 1.954 + MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); 1.955 + MOZ_RELEASE_ASSERT(mDivertingFromChild); 1.956 + MOZ_RELEASE_ASSERT(mParentListener); 1.957 + MOZ_RELEASE_ASSERT(mChannel); 1.958 + 1.959 + mChannel->Cancel(aErrorCode); 1.960 + 1.961 + mChannel->ForcePending(false); 1.962 + 1.963 + bool isPending = false; 1.964 + nsresult rv = mChannel->IsPending(&isPending); 1.965 + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); 1.966 + 1.967 + // Resume only if we suspended earlier. 1.968 + if (mSuspendedForDiversion) { 1.969 + mChannel->Resume(); 1.970 + } 1.971 + // Channel has already sent OnStartRequest to the child, so ensure that we 1.972 + // call it here if it hasn't already been called. 1.973 + if (!mDivertedOnStartRequest) { 1.974 + mChannel->ForcePending(true); 1.975 + mParentListener->OnStartRequest(mChannel, nullptr); 1.976 + mChannel->ForcePending(false); 1.977 + } 1.978 + // If the channel is pending, it will call OnStopRequest itself; otherwise, do 1.979 + // it here. 1.980 + if (!isPending) { 1.981 + mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode); 1.982 + } 1.983 + mParentListener = nullptr; 1.984 + mChannel = nullptr; 1.985 + 1.986 + if (!mIPCClosed) { 1.987 + unused << SendDeleteSelf(); 1.988 + } 1.989 +} 1.990 + 1.991 +}} // mozilla::net