From e8c416076ca0ee2f735919aa9851326243e2e62f Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 22 Oct 2015 09:23:48 -0400 Subject: [PATCH] Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly --- netwerk/protocol/http/HttpChannelChild.cpp | 163 ++++++++++++++----- netwerk/protocol/http/HttpChannelChild.h | 25 ++- netwerk/protocol/http/InterceptedChannel.cpp | 26 ++- netwerk/protocol/http/InterceptedChannel.h | 5 +- 4 files changed, 172 insertions(+), 47 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 75d5939aeb4..5b035046889 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -184,6 +184,8 @@ HttpChannelChild::HttpChannelChild() , mFlushedForDiversion(false) , mSuspendSent(false) , mSynthesizedResponse(false) + , mShouldInterceptSubsequentRedirect(false) + , mRedirectingForSubsequentSynthesizedResponse(false) , mShouldParentIntercept(false) { LOG(("Creating HttpChannelChild @%x\n", this)); @@ -1157,25 +1159,17 @@ HttpChannelChild::RecvRedirect1Begin(const uint32_t& newChannelId, return true; } -void -HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, - const URIParams& newUri, - const uint32_t& redirectFlags, - const nsHttpResponseHead& responseHead, - const nsACString& securityInfoSerialization) +nsresult +HttpChannelChild::SetupRedirect(nsIURI* uri, + const nsHttpResponseHead* responseHead, + nsIChannel** outChannel) { - LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this)); + LOG(("HttpChannelChild::SetupRedirect [this=%p]\n", this)); nsresult rv; nsCOMPtr ioService; rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); - if (NS_FAILED(rv)) { - // Veto redirect. nsHttpChannel decides to cancel or continue. - OnRedirectVerifyCallback(rv); - return; - } - - nsCOMPtr uri = DeserializeURI(newUri); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr newChannel; rv = NS_NewChannelInternal(getter_AddRefs(newChannel), @@ -1185,33 +1179,19 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, nullptr, // aCallbacks nsIRequest::LOAD_NORMAL, ioService); - - if (NS_FAILED(rv)) { - // Veto redirect. nsHttpChannel decides to cancel or continue. - OnRedirectVerifyCallback(rv); - return; - } + NS_ENSURE_SUCCESS(rv, rv); // We won't get OnStartRequest, set cookies here. - mResponseHead = new nsHttpResponseHead(responseHead); - - if (!securityInfoSerialization.IsEmpty()) { - NS_DeserializeObject(securityInfoSerialization, - getter_AddRefs(mSecurityInfo)); - } + mResponseHead = new nsHttpResponseHead(*responseHead); bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(mResponseHead->Status(), mRequestHead.ParsedMethod()); rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET); - if (NS_FAILED(rv)) { - // Veto redirect. nsHttpChannel decides to cancel or continue. - OnRedirectVerifyCallback(rv); - return; - } + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr httpChannelChild = do_QueryInterface(newChannel); - if (mSynthesizedResponse && httpChannelChild) { + if (mShouldInterceptSubsequentRedirect && httpChannelChild) { // In the case where there was a synthesized response that caused a redirection, // we must force the new channel to intercept the request in the parent before a // network transaction is initiated. @@ -1219,15 +1199,64 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, } mRedirectChannelChild = do_QueryInterface(newChannel); - if (mRedirectChannelChild) { - mRedirectChannelChild->ConnectParent(newChannelId); + newChannel.forget(outChannel); + + return NS_OK; +} + +void +HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, + const URIParams& newUri, + const uint32_t& redirectFlags, + const nsHttpResponseHead& responseHead, + const nsACString& securityInfoSerialization) +{ + LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this)); + + nsCOMPtr uri = DeserializeURI(newUri); + + if (!securityInfoSerialization.IsEmpty()) { + NS_DeserializeObject(securityInfoSerialization, + getter_AddRefs(mSecurityInfo)); + } + + nsCOMPtr newChannel; + nsresult rv = SetupRedirect(uri, + &responseHead, + getter_AddRefs(newChannel)); + + if (NS_SUCCEEDED(rv)) { + if (mRedirectChannelChild) { + mRedirectChannelChild->ConnectParent(newChannelId); + rv = gHttpHandler->AsyncOnChannelRedirect(this, + newChannel, + redirectFlags); + } else { + LOG((" redirecting to a protocol that doesn't implement" + " nsIChildChannel")); + rv = NS_ERROR_FAILURE; + } + } + + if (NS_FAILED(rv)) + OnRedirectVerifyCallback(rv); +} + +void +HttpChannelChild::BeginNonIPCRedirect(nsIURI* responseURI, + const nsHttpResponseHead* responseHead) +{ + LOG(("HttpChannelChild::BeginNonIPCRedirect [this=%p]\n", this)); + + nsCOMPtr newChannel; + nsresult rv = SetupRedirect(responseURI, + responseHead, + getter_AddRefs(newChannel)); + + if (NS_SUCCEEDED(rv)) { rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, - redirectFlags); - } else { - LOG((" redirecting to a protocol that doesn't implement" - " nsIChildChannel")); - rv = NS_ERROR_FAILURE; + nsIChannelEventSink::REDIRECT_INTERNAL); } if (NS_FAILED(rv)) @@ -1435,6 +1464,34 @@ HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, // HttpChannelChild::nsIAsyncVerifyRedirectCallback //----------------------------------------------------------------------------- +class OverrideRunnable : public nsRunnable { + RefPtr mChannel; + RefPtr mNewChannel; + RefPtr mListener; + nsCOMPtr mInput; + nsAutoPtr mHead; + +public: + OverrideRunnable(HttpChannelChild* aChannel, + HttpChannelChild* aNewChannel, + InterceptStreamListener* aListener, + nsIInputStream* aInput, + nsAutoPtr& aHead) + : mChannel(aChannel) + , mNewChannel(aNewChannel) + , mListener(aListener) + , mInput(aInput) + , mHead(aHead) + { + } + + NS_IMETHOD Run() { + mChannel->Redirect3Complete(); + mNewChannel->OverrideWithSynthesizedResponse(mHead, mInput, mListener); + return NS_OK; + } +}; + NS_IMETHODIMP HttpChannelChild::OnRedirectVerifyCallback(nsresult result) { @@ -1448,6 +1505,21 @@ HttpChannelChild::OnRedirectVerifyCallback(nsresult result) newHttpChannel->SetOriginalURI(mOriginalURI); } + if (mRedirectingForSubsequentSynthesizedResponse) { + nsCOMPtr httpChannelChild = do_QueryInterface(mRedirectChannelChild); + MOZ_ASSERT(httpChannelChild); + RefPtr redirectedChannel = + static_cast(httpChannelChild.get()); + + RefPtr streamListener = + new InterceptStreamListener(redirectedChannel, mListenerContext); + + NS_DispatchToMainThread(new OverrideRunnable(this, redirectedChannel, + streamListener, mSynthesizedInput, + mResponseHead)); + return NS_OK; + } + RequestHeaderTuples emptyHeaders; RequestHeaderTuples* headerTuples = &emptyHeaders; nsLoadFlags loadFlags = 0; @@ -2385,8 +2457,10 @@ HttpChannelChild::GetResponseSynthesized(bool* aSynthesized) void HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr& aResponseHead, nsIInputStream* aSynthesizedInput, - nsIStreamListener* aStreamListener) + InterceptStreamListener* aStreamListener) { + mInterceptListener = aStreamListener; + // Intercepted responses should already be decoded. If its a redirect, // however, we want to respect the encoding of the final result instead. if (!WillRedirect(aResponseHead)) { @@ -2397,6 +2471,7 @@ HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr& mSynthesizedResponse = true; if (WillRedirect(mResponseHead)) { + mShouldInterceptSubsequentRedirect = true; // Continue with the original cross-process request nsresult rv = ContinueAsyncOpen(); NS_ENSURE_SUCCESS_VOID(rv); @@ -2450,6 +2525,14 @@ HttpChannelChild::ForceIntercepted(uint64_t aInterceptionID) return NS_ERROR_NOT_IMPLEMENTED; } +void +HttpChannelChild::ForceIntercepted(nsIInputStream* aSynthesizedInput) +{ + mSynthesizedInput = aSynthesizedInput; + mSynthesizedResponse = true; + mRedirectingForSubsequentSynthesizedResponse = true; +} + bool HttpChannelChild::RecvIssueDeprecationWarning(const uint32_t& warning, const bool& asError) diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 88752828873..21809e18623 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -37,6 +37,7 @@ namespace net { class InterceptedChannelContent; class InterceptStreamListener; +class OverrideRunnable; class HttpChannelChild final : public PHttpChannelChild , public HttpBaseChannel @@ -175,12 +176,16 @@ private: // asynchronously read from the pump. void OverrideWithSynthesizedResponse(nsAutoPtr& aResponseHead, nsIInputStream* aSynthesizedInput, - nsIStreamListener* aStreamListener); + InterceptStreamListener* aStreamListener); + + void ForceIntercepted(nsIInputStream* aSynthesizedInput); RequestHeaderTuples mClientSetRequestHeaders; nsCOMPtr mRedirectChannelChild; RefPtr mInterceptListener; RefPtr mSynthesizedResponsePump; + nsAutoPtr mSynthesizedResponseHead; + nsCOMPtr mSynthesizedInput; int64_t mSynthesizedStreamLength; bool mIsFromCache; @@ -215,6 +220,13 @@ private: // should be intercepted. bool mSynthesizedResponse; + // Set if a synthesized response should cause us to explictly allows intercepting + // an expected forthcoming redirect. + bool mShouldInterceptSubsequentRedirect; + // Set if a redirection is being initiated to facilitate providing a synthesized + // response to a channel using a different principal than the current one. + bool mRedirectingForSubsequentSynthesizedResponse; + // Set if the corresponding parent channel should force an interception to occur // before the network transaction is initiated. bool mShouldParentIntercept; @@ -260,6 +272,16 @@ private: void Redirect3Complete(); void DeleteSelf(); + // Create a a new channel to be used in a redirection, based on the provided + // response headers. + nsresult SetupRedirect(nsIURI* uri, + const nsHttpResponseHead* responseHead, + nsIChannel** outChannel); + + // Perform a redirection without communicating with the parent process at all. + void BeginNonIPCRedirect(nsIURI* responseURI, + const nsHttpResponseHead* responseHead); + friend class AssociateApplicationCacheEvent; friend class StartRequestEvent; friend class StopRequestEvent; @@ -275,6 +297,7 @@ private: friend class HttpAsyncAborter; friend class InterceptStreamListener; friend class InterceptedChannelContent; + friend class OverrideRunnable; }; //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/InterceptedChannel.cpp b/netwerk/protocol/http/InterceptedChannel.cpp index 4c541f49da2..7937cf7b489 100644 --- a/netwerk/protocol/http/InterceptedChannel.cpp +++ b/netwerk/protocol/http/InterceptedChannel.cpp @@ -284,7 +284,7 @@ InterceptedChannelChrome::GetInternalContentPolicyType(nsContentPolicyType* aPol InterceptedChannelContent::InterceptedChannelContent(HttpChannelChild* aChannel, nsINetworkInterceptController* aController, - nsIStreamListener* aListener) + InterceptStreamListener* aListener) : InterceptedChannelBase(aController) , mChannel(aChannel) , mStreamListener(aListener) @@ -353,9 +353,27 @@ InterceptedChannelContent::FinishSynthesizedResponse(const nsACString& aFinalURL EnsureSynthesizedResponse(); - mChannel->OverrideWithSynthesizedResponse(mSynthesizedResponseHead.ref(), - mSynthesizedInput, - mStreamListener); + nsCOMPtr originalURI; + mChannel->GetURI(getter_AddRefs(originalURI)); + + nsCOMPtr responseURI; + if (!aFinalURLSpec.IsEmpty()) { + nsresult rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec); + NS_ENSURE_SUCCESS(rv, rv); + } else { + responseURI = originalURI; + } + + bool equal = false; + originalURI->Equals(responseURI, &equal); + if (!equal) { + mChannel->ForceIntercepted(mSynthesizedInput); + mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr()); + } else { + mChannel->OverrideWithSynthesizedResponse(mSynthesizedResponseHead.ref(), + mSynthesizedInput, + mStreamListener); + } mResponseBody = nullptr; mChannel = nullptr; diff --git a/netwerk/protocol/http/InterceptedChannel.h b/netwerk/protocol/http/InterceptedChannel.h index bb60c605044..3e134d04a79 100644 --- a/netwerk/protocol/http/InterceptedChannel.h +++ b/netwerk/protocol/http/InterceptedChannel.h @@ -21,6 +21,7 @@ namespace net { class nsHttpChannel; class HttpChannelChild; class nsHttpResponseHead; +class InterceptStreamListener; // An object representing a channel that has been intercepted. This avoids complicating // the actual channel implementation with the details of synthesizing responses. @@ -93,11 +94,11 @@ class InterceptedChannelContent : public InterceptedChannelBase // Listener for the synthesized response to fix up the notifications before they reach // the actual channel. - nsCOMPtr mStreamListener; + RefPtr mStreamListener; public: InterceptedChannelContent(HttpChannelChild* aChannel, nsINetworkInterceptController* aController, - nsIStreamListener* aListener); + InterceptStreamListener* aListener); NS_IMETHOD ResetInterception() override; NS_IMETHOD FinishSynthesizedResponse(const nsACString& aFinalURLSpec) override;