Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly

This commit is contained in:
Josh Matthews 2015-10-22 09:23:48 -04:00
parent 3239c406c6
commit e8c416076c
4 changed files with 172 additions and 47 deletions

View File

@ -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<nsIIOService> ioService;
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
if (NS_FAILED(rv)) {
// Veto redirect. nsHttpChannel decides to cancel or continue.
OnRedirectVerifyCallback(rv);
return;
}
nsCOMPtr<nsIURI> uri = DeserializeURI(newUri);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> 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<nsIHttpChannelChild> 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,6 +1199,33 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId,
}
mRedirectChannelChild = do_QueryInterface(newChannel);
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<nsIURI> uri = DeserializeURI(newUri);
if (!securityInfoSerialization.IsEmpty()) {
NS_DeserializeObject(securityInfoSerialization,
getter_AddRefs(mSecurityInfo));
}
nsCOMPtr<nsIChannel> newChannel;
nsresult rv = SetupRedirect(uri,
&responseHead,
getter_AddRefs(newChannel));
if (NS_SUCCEEDED(rv)) {
if (mRedirectChannelChild) {
mRedirectChannelChild->ConnectParent(newChannelId);
rv = gHttpHandler->AsyncOnChannelRedirect(this,
@ -1229,6 +1236,28 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId,
" 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<nsIChannel> newChannel;
nsresult rv = SetupRedirect(responseURI,
responseHead,
getter_AddRefs(newChannel));
if (NS_SUCCEEDED(rv)) {
rv = gHttpHandler->AsyncOnChannelRedirect(this,
newChannel,
nsIChannelEventSink::REDIRECT_INTERNAL);
}
if (NS_FAILED(rv))
OnRedirectVerifyCallback(rv);
@ -1435,6 +1464,34 @@ HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
// HttpChannelChild::nsIAsyncVerifyRedirectCallback
//-----------------------------------------------------------------------------
class OverrideRunnable : public nsRunnable {
RefPtr<HttpChannelChild> mChannel;
RefPtr<HttpChannelChild> mNewChannel;
RefPtr<InterceptStreamListener> mListener;
nsCOMPtr<nsIInputStream> mInput;
nsAutoPtr<nsHttpResponseHead> mHead;
public:
OverrideRunnable(HttpChannelChild* aChannel,
HttpChannelChild* aNewChannel,
InterceptStreamListener* aListener,
nsIInputStream* aInput,
nsAutoPtr<nsHttpResponseHead>& 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<nsIHttpChannelChild> httpChannelChild = do_QueryInterface(mRedirectChannelChild);
MOZ_ASSERT(httpChannelChild);
RefPtr<HttpChannelChild> redirectedChannel =
static_cast<HttpChannelChild*>(httpChannelChild.get());
RefPtr<InterceptStreamListener> 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<nsHttpResponseHead>& 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<nsHttpResponseHead>&
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)

View File

@ -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<nsHttpResponseHead>& aResponseHead,
nsIInputStream* aSynthesizedInput,
nsIStreamListener* aStreamListener);
InterceptStreamListener* aStreamListener);
void ForceIntercepted(nsIInputStream* aSynthesizedInput);
RequestHeaderTuples mClientSetRequestHeaders;
nsCOMPtr<nsIChildChannel> mRedirectChannelChild;
RefPtr<InterceptStreamListener> mInterceptListener;
RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
nsAutoPtr<nsHttpResponseHead> mSynthesizedResponseHead;
nsCOMPtr<nsIInputStream> 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<HttpChannelChild>;
friend class InterceptStreamListener;
friend class InterceptedChannelContent;
friend class OverrideRunnable;
};
//-----------------------------------------------------------------------------

View File

@ -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();
nsCOMPtr<nsIURI> originalURI;
mChannel->GetURI(getter_AddRefs(originalURI));
nsCOMPtr<nsIURI> 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;

View File

@ -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<nsIStreamListener> mStreamListener;
RefPtr<InterceptStreamListener> mStreamListener;
public:
InterceptedChannelContent(HttpChannelChild* aChannel,
nsINetworkInterceptController* aController,
nsIStreamListener* aListener);
InterceptStreamListener* aListener);
NS_IMETHOD ResetInterception() override;
NS_IMETHOD FinishSynthesizedResponse(const nsACString& aFinalURLSpec) override;