/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et tw=80 : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HttpLog.h should generally be included first #include"ErrorList.h" #include"HttpLog.h"
// Ensure gHttpHandler is initialized: we need the atom table up and running.
nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
void HttpChannelParent::ActorDestroy(ActorDestroyReason why) { // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest // yet, but child process has crashed. We must not try to send any more msgs // to child, or IPDL will kill chrome process, too.
mIPCClosed = true;
CleanupBackgroundChannel();
}
// TryInvokeAsyncOpen is called more than we expected. // Assert in nightly build but ignore it in release channel.
MOZ_DIAGNOSTIC_ASSERT(mAsyncOpenBarrier > 0); if (NS_WARN_IF(!mAsyncOpenBarrier)) { return;
}
if (--mAsyncOpenBarrier > 0 && NS_SUCCEEDED(aRv)) { // Need to wait for more events. return;
}
if (mBgParent) {
RefPtr<HttpBackgroundChannelParent> bgParent = std::move(mBgParent);
bgParent->OnChannelClosed(); return;
}
// The nsHttpChannel may have a reference to this parent, release it // to avoid circular references.
RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel); if (httpChannelImpl) {
httpChannelImpl->SetWarningReporter(nullptr);
}
if (!mPromise.IsEmpty()) {
mRequest.DisconnectIfExists();
mPromise.Reject(NS_ERROR_FAILURE, __func__);
if (!mChannel) { return;
}
// This HttpChannelParent might still have a reference from // BackgroundChannelRegistrar.
nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
BackgroundChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
registrar->DeleteChannel(mChannel->ChannelId());
// If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure // is still on going. we need to abort AsyncOpen with failure to destroy // PHttpChannel actor. if (mAsyncOpenBarrier) {
TryInvokeAsyncOpen(NS_ERROR_FAILURE);
}
}
}
NS_IMETHODIMP
HttpChannelParent::GetInterface(const nsIID& aIID, void** result) { // A system XHR can be created without reference to a window, hence mTabParent // may be null. In that case we want to let the window watcher pick a prompt // directly. if (!mBrowserParent && (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
aIID.Equals(NS_GET_IID(nsIAuthPrompt2)))) {
nsresult rv;
nsCOMPtr<nsIWindowWatcher> wwatch;
wwatch = mozilla::components::WindowWatcher::Service(&rv);
NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
// Only support nsILoadContext if child channel's callbacks did too if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
nsCOMPtr<nsILoadContext> copy = mLoadContext;
copy.forget(result); return NS_OK;
}
// Set attributes needed to create a FetchEvent from this channel.
httpChannel->SetRequestMode(aRequestMode);
httpChannel->SetRedirectMode(aRedirectMode);
// Set the channelId allocated in child to the parent instance
httpChannel->SetChannelId(aChannelId);
httpChannel->SetTopLevelContentWindowId(aContentWindowId);
httpChannel->SetBrowserId(aBrowserId);
RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(httpChannel); if (httpChannelImpl) {
httpChannelImpl->SetWarningReporter(this);
} if (mPBOverride != kPBOverride_Unset) {
httpChannel->SetPrivate(mPBOverride == kPBOverride_Private);
}
if (doResumeAt) httpChannel->ResumeAt(startPos, entityID);
if (aOriginalURI) {
httpChannel->SetOriginalURI(aOriginalURI);
}
if (aDocURI) {
httpChannel->SetDocumentURI(aDocURI);
}
if (aReferrerInfo) { // Referrer header is computed in child no need to recompute here
rv =
httpChannel->SetReferrerInfoInternal(aReferrerInfo, false, false, true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// Store the strong reference of channel and parent listener object until // all the initialization procedure is complete without failure, to remove // cycle reference in fail case and to avoid memory leakage.
mChannel = std::move(httpChannel);
mParentListener = std::move(parentListener);
mChannel->SetNotificationCallbacks(mParentListener);
MOZ_ASSERT(!mBgParent);
MOZ_ASSERT(mPromise.IsEmpty()); // Wait for HttpBackgrounChannel to continue the async open procedure.
++mAsyncOpenBarrier;
RefPtr<HttpChannelParent> self = this;
WaitForBgParent(mChannel->ChannelId())
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self]() {
self->mRequest.Complete();
self->TryInvokeAsyncOpen(NS_OK);
},
[self](nsresult aStatus) {
self->mRequest.Complete();
self->TryInvokeAsyncOpen(aStatus);
})
->Track(mRequest); returntrue;
}
LOG(
("HttpChannelParent::ConnectChannel: Looking for a registered channel " "[this=%p, id=%" PRIu32 "]\n", this, registrarId));
nsCOMPtr<nsIChannel> channel;
rv = NS_LinkRedirectChannels(registrarId, this, getter_AddRefs(channel)); if (NS_FAILED(rv)) {
NS_WARNING("Could not find the http channel to connect its IPC parent"); // This makes the channel delete itself safely. It's the only thing // we can do now, since this parent channel cannot be used and there is // no other way to tell the child side there were something wrong. Delete(); returntrue;
}
LOG((" found channel %p, rv=%08" PRIx32, channel.get(), static_cast<uint32_t>(rv)));
mChannel = do_QueryObject(channel); if (!mChannel) {
LOG((" but it's not HttpBaseChannel")); Delete(); returntrue;
}
LOG((" and it is HttpBaseChannel %p", mChannel.get()));
RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel); if (httpChannelImpl) {
httpChannelImpl->SetWarningReporter(this);
}
if (mPBOverride != kPBOverride_Unset) { // redirected-to channel may not support PB
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel); if (pbChannel) {
pbChannel->SetPrivate(mPBOverride == kPBOverride_Private);
}
}
// Once we receive |Cancel|, child will stop sending RecvBytesRead. Force // the channel resumed if needed. if (mSuspendedForFlowControl) {
LOG((" resume the channel due to e10s backpressure relief by cancel"));
Unused << mChannel->Resume();
mSuspendedForFlowControl = false;
}
} elseif (!mIPCClosed) { // Make sure that the child correctly delivers all stream listener // notifications.
Unused << SendFailedAsyncOpen(status);
}
// We won't need flow control anymore. Toggle the flag to avoid |Suspend| // since OnDataAvailable could be off-main-thread.
mCacheNeedFlowControlInitialized = true;
mNeedFlowControl = false;
// If the channel is cancelled before the redirect is completed // RecvRedirect2Verify will not be called, so we must clear the callback. if (mRedirectCallback) {
mRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_UNEXPECTED);
mRedirectCallback = nullptr;
}
// Result from the child. If something fails here, we might overwrite a // success with a further failure.
nsresult result = aResult;
// Local results.
nsresult rv;
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIHttpChannel> newHttpChannel =
do_QueryInterface(mRedirectChannel);
if (newHttpChannel) { if (aAPIRedirectURI) {
rv = newHttpChannel->RedirectTo(aAPIRedirectURI);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
for (uint32_t i = 0; i < changedHeaders.Length(); i++) { if (changedHeaders[i].mEmpty) {
rv = newHttpChannel->SetEmptyRequestHeader(changedHeaders[i].mHeader);
} else {
rv = newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
changedHeaders[i].mValue,
changedHeaders[i].mMerge);
}
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// A successfully redirected channel must have the LOAD_REPLACE flag.
MOZ_ASSERT(loadFlags & nsIChannel::LOAD_REPLACE); if (loadFlags & nsIChannel::LOAD_REPLACE) {
newHttpChannel->SetLoadFlags(loadFlags);
}
if (aReferrerInfo) {
RefPtr<HttpBaseChannel> baseChannel = do_QueryObject(newHttpChannel);
MOZ_ASSERT(baseChannel); if (baseChannel) { // Referrer header is computed in child no need to recompute here
rv = baseChannel->SetReferrerInfoInternal(aReferrerInfo, false, false, true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
if (aTargetLoadInfoForwarder.isSome()) {
nsCOMPtr<nsILoadInfo> newLoadInfo = newHttpChannel->LoadInfo();
rv = MergeChildLoadInfoForwarder(aTargetLoadInfoForwarder.ref(),
newLoadInfo); if (NS_FAILED(rv) && NS_SUCCEEDED(result)) {
result = rv;
}
}
}
}
// If the redirect is vetoed, reason is set on the source (current) channel's // load info, so we must carry iver the change. // The channel may have already been cleaned up, so there is nothing we can // do. if (MOZ_UNLIKELY(aSourceRequestBlockingReason !=
nsILoadInfo::BLOCKING_REASON_NONE) &&
mChannel) {
nsCOMPtr<nsILoadInfo> sourceLoadInfo = mChannel->LoadInfo();
sourceLoadInfo->SetRequestBlockingReason(aSourceRequestBlockingReason);
}
// Continue the verification procedure if child has veto the redirection. if (NS_FAILED(result)) {
ContinueRedirect2Verify(result); return IPC_OK();
}
// Wait for background channel ready on target channel
nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(redirectReg);
nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
do_QueryInterface(redirectParentChannel); if (!redirectedParent) { // Continue verification procedure if redirecting to non-Http protocol
ContinueRedirect2Verify(result); return IPC_OK();
}
// Ask redirected channel if verification can proceed. // ContinueRedirect2Verify will be invoked when redirected channel is ready.
redirectedParent->ContinueVerification(this);
aChannel->GetEncodedBodySize(&size);
args.encodedBodySize() = size; // decodedBodySize can be computed in the child process so it doesn't need // to be passed down.
if (!chan) {
LOG((" aRequest is not HttpBaseChannel"));
NS_ERROR( "Expecting only HttpBaseChannel as aRequest in " "HttpChannelParent::OnStartRequest"); return NS_ERROR_UNEXPECTED;
}
mAfterOnStartRequestBegun = true;
// Todo: re-enable when bug 1589749 is fixed. /*MOZ_ASSERT(mChannel == chan, "HttpChannelParent getting OnStartRequest from a different "
"HttpBaseChannel instance");*/
HttpChannelOnStartRequestArgs args;
// Send down any permissions/cookies which are relevant to this URL if we are // performing a document load. We can't do that if mIPCClosed is set. if (!mIPCClosed) {
PContentParent* pcp = Manager()->Manager();
MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
DebugOnly<nsresult> rv = static_cast<ContentParent*>(pcp)->AboutToLoadHttpDocumentForChild(
chan, &args.shouldWaitForOnStartRequestSent());
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// If RCWN is enabled and cache wins, we can't use the ODA from socket // process. if (args.isRacing()) {
mDataSentToChildProcess =
httpChannelImpl->DataSentToChildProcess() && !args.isFromCache();
}
args.dataFromSocketProcess() = mDataSentToChildProcess;
}
// Propagate whether or not conversion should occur from the parent-side // channel to the child-side channel. Then disable the parent-side // conversion so that it only occurs in the child.
Unused << chan->GetApplyConversion(&args.applyConversion());
chan->SetApplyConversion(false);
// If we've already applied the conversion (as can happen if we installed // a multipart converted), then don't apply it again on the child. if (chan->HasAppliedConversion()) {
args.applyConversion() = false;
}
chan->GetStatus(&args.channelStatus());
// Keep the cache entry for future use when opening alternative streams. // It could be already released by nsHttpChannel at that time.
nsCOMPtr<nsISupports> cacheEntry;
if (httpChannelImpl) {
httpChannelImpl->GetCacheToken(getter_AddRefs(cacheEntry));
mCacheEntry = do_QueryInterface(cacheEntry);
args.cacheEntryAvailable() = static_cast<bool>(mCacheEntry);
if (responseHead &&
(responseHead->HasHeader(nsHttp::Set_Cookie) || multiPartID)) {
cleanedUpResponseHead = *responseHead;
cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie); if (multiPartID) {
nsCOMPtr<nsIChannel> multiPartChannel = do_QueryInterface(aRequest); // For the multipart channel, use the parsed subtype instead. Note that // `chan` is the underlying base channel of the multipart channel in this // case, which is different from `multiPartChannel`.
MOZ_ASSERT(multiPartChannel);
nsAutoCString contentType;
multiPartChannel->GetContentType(contentType);
cleanedUpResponseHead.SetContentType(contentType);
}
responseHead = &cleanedUpResponseHead;
}
if (!responseHead) {
responseHead = &cleanedUpResponseHead;
}
if (chan->ChannelBlockedByOpaqueResponse() &&
chan->CachedOpaqueResponseBlockingPref()) {
responseHead->ClearHeaders();
}
nsHttpRequestHead* requestHead = chan->GetRequestHead(); // !!! We need to lock headers and please don't forget to unlock them !!!
requestHead->Enter();
// Need to wait for the cookies/permissions to content process, which is sent // via PContent in AboutToLoadHttpFtpDocumentForChild. For multipart channel, // send only one time since the cookies/permissions are the same. if (NS_SUCCEEDED(rv) && args.shouldWaitForOnStartRequestSent() &&
multiPartID.valueOr(0) == 0) {
LOG(("HttpChannelParent::SendOnStartRequestSent\n"));
Unused << SendOnStartRequestSent();
}
// Either IPC channel is closed or background channel // is ready to send OnStopRequest.
MOZ_ASSERT(mIPCClosed || mBgParent);
if (mDataSentToChildProcess) { if (mIPCClosed || !mBgParent ||
!mBgParent->OnConsoleReport(consoleReports)) { return NS_ERROR_UNEXPECTED;
} return NS_OK;
}
// If we're handling a multi-part stream, then send this directly // over PHttpChannel to make synchronization easier. if (mIPCClosed || !mBgParent ||
!mBgParent->OnStopRequest(
aStatusCode, GetTimingAttributes(mChannel),
responseTrailer ? *responseTrailer : nsHttpHeaderArray(),
consoleReports, onStopRequestStart)) { return NS_ERROR_UNEXPECTED;
}
if (NeedFlowControl()) { // We're going to run out of sending window size if (mSendWindowSize > 0 && mSendWindowSize <= count) {
MOZ_ASSERT(!mSuspendedForFlowControl);
LOG((" suspend the channel due to e10s backpressure"));
Unused << mChannel->Suspend();
mSuspendedForFlowControl = true;
mHasSuspendedByBackPressure = true;
} elseif (!mResumedTimestamp.IsNull()) { // Calculate the delay when the first packet arrived after resume
Telemetry::AccumulateTimeDelta(
Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_DELAY_TIME_MS,
mResumedTimestamp);
mResumedTimestamp = TimeStamp();
}
mSendWindowSize -= count;
}
return NS_OK;
}
bool HttpChannelParent::NeedFlowControl() { if (mCacheNeedFlowControlInitialized) { return mNeedFlowControl;
}
// By design, we won't trigger the flow control if // a. pref-out // b. the resource is from cache or partial cache // c. the resource is small // d. data will be sent from socket process to child process directly // Note that we served the cached resource first for partical cache, which is // ignored here since we only take the first ODA into consideration. if (gHttpHandler->SendWindowSize() == 0 || !httpChannelImpl ||
httpChannelImpl->IsReadingFromCache() ||
NS_FAILED(httpChannelImpl->GetContentLength(&contentLength)) ||
contentLength < gHttpHandler->SendWindowSize() ||
mDataSentToChildProcess) {
mNeedFlowControl = false;
}
mCacheNeedFlowControlInitialized = true; return mNeedFlowControl;
}
// If IPC channel is closed, there is nothing we can do. Just return NS_OK. if (mIPCClosed) { return NS_OK;
}
// If it indicates this precedes OnDataAvailable, child can derive the value // in ODA. if (mIgnoreProgress) {
mIgnoreProgress = false; return NS_OK;
}
// If IPC channel is open, background channel should be ready to send // OnProgress.
MOZ_ASSERT(mBgParent);
// Send OnProgress events to the child for data upload progress notifications // (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has // LOAD_BACKGROUND set. if (!mBgParent || !mBgParent->OnProgress(aProgress, aProgressMax)) { return NS_ERROR_UNEXPECTED;
}
// If IPC channel is closed, there is nothing we can do. Just return NS_OK. if (mIPCClosed) { return NS_OK;
}
// If this precedes OnDataAvailable, transportStatus will be derived in ODA. if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
aStatus == NS_NET_STATUS_READING) { // The transport status and progress generated by ODA will be coalesced // into one IPC message. Therefore, we can ignore the next OnProgress event // since it is generated by ODA as well.
mIgnoreProgress = true; return NS_OK;
}
// If IPC channel is open, background channel should be ready to send // OnStatus.
MOZ_ASSERT(mIPCClosed || mBgParent);
// Otherwise, send to child now if (!mBgParent || !mBgParent->OnStatus(aStatus)) { return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
HttpChannelParent::SetParentListener(ParentChannelListener* aListener) {
LOG(("HttpChannelParent::SetParentListener [this=%p aListener=%p]\n", this,
aListener));
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mParentListener, "SetParentListener should only be called for " "new HttpChannelParents after a redirect, when " "mParentListener is null.");
mParentListener = aListener; return NS_OK;
}
// Register the new channel and obtain id for it
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
LOG(("Registered %p channel under id=%" PRIx64, newChannel,
mRedirectChannelId));
if (mIPCClosed) { return NS_BINDING_ABORTED;
}
// If this is an internal redirect for service worker interception or // internal redirect due to auth retries, then hide it from the child // process. The original e10s interception code was not designed with this // in mind and its not necessary to replace the HttpChannelChild/Parent // objects in this case. if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
nsCOMPtr<nsIInterceptedChannel> oldIntercepted =
do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
nsCOMPtr<nsIInterceptedChannel> newIntercepted =
do_QueryInterface(newChannel);
// 1. We only want to hide the special internal redirects from // nsHttpChannel to InterceptedHttpChannel. // 2. We want to allow through internal redirects // initiated from the InterceptedHttpChannel even if they are to another // InterceptedHttpChannel, except the interception reset, since // corresponding HttpChannelChild/Parent objects can be reused for reset // case. // 3. If this is an internal redirect due to auth retry then we will // hide it from the child process
if ((!oldIntercepted && newIntercepted) ||
(oldIntercepted && !newIntercepted && oldIntercepted->IsReset()) ||
(IsRedirectDueToAuthRetry(redirectFlags))) { // We need to move across the reserved and initial client information // to the new channel. Normally this would be handled by the child // ClientChannelHelper, but that is not notified of this redirect since // we're not propagating it back to the child process.
nsCOMPtr<nsILoadInfo> oldLoadInfo = mChannel->LoadInfo();
Maybe<ClientInfo> reservedClientInfo(
oldLoadInfo->GetReservedClientInfo()); if (reservedClientInfo.isSome()) {
newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
}
Maybe<ClientInfo> initialClientInfo(oldLoadInfo->GetInitialClientInfo()); if (initialClientInfo.isSome()) {
newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
}
// If this is ServiceWorker fallback redirect, info HttpChannelChild to // detach StreamFilters. Otherwise StreamFilters will be attached twice // on the same HttpChannelChild when opening the new nsHttpChannel. if (oldIntercepted) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.34Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.