Bug 1157283 - Recreate IPC redirected HTTP channels as necessary after intercepting the request in the child. r=mayhemer

This commit is contained in:
Josh Matthews 2015-05-28 10:47:35 -04:00
parent 23551b0aaa
commit 961d0686c7
11 changed files with 131 additions and 13 deletions

View File

@ -21,7 +21,7 @@ function fetchXHR(name, onload, onerror, headers) {
x.send();
}
fetchXHR('synthesized.txt', function(xhr) {
fetchXHR('bare-synthesized.txt', function(xhr) {
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response");
finish();
@ -46,16 +46,33 @@ fetchXHR('synthesized-headers.txt', function(xhr) {
finish();
});
fetch('synthesized-redirect-real-file.txt', function(xhr) {
fetchXHR('synthesized-redirect-real-file.txt', function(xhr) {
dump("Got status AARRGH " + xhr.status + " " + xhr.responseText + "\n");
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "This is a real file.\n", "Redirect to real file should complete.");
finish();
});
fetch('synthesized-redirect-synthesized.txt', function(xhr) {
fetchXHR('synthesized-redirect-twice-real-file.txt', function(xhr) {
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response");
my_ok(xhr.responseText == "This is a real file.\n", "Redirect to real file (twice) should complete.");
finish();
});
fetchXHR('synthesized-redirect-synthesized.txt', function(xhr) {
my_ok(xhr.status == 200, "synth+redirect+synth load should be successful");
my_ok(xhr.responseText == "synthesized response body", "load should have redirected+synthesized response");
finish();
});
fetchXHR('synthesized-redirect-twice-synthesized.txt', function(xhr) {
my_ok(xhr.status == 200, "synth+redirect+synth (twice) load should be successful");
my_ok(xhr.responseText == "synthesized response body", "load should have redirected+synthesized (twice) response");
finish();
});
fetchXHR('fetch/redirect.sjs', function(xhr) {
my_ok(xhr.status == 404, "redirected load should be uninterrupted");
finish();
});

View File

@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
response.setHeader("Location", "synthesized-redirect-twice-real-file.txt");
}

View File

@ -1,7 +1,7 @@
var seenIndex = false;
onfetch = function(ev) {
if (ev.request.url.includes("synthesized.txt")) {
if (ev.request.url.includes("bare-synthesized.txt")) {
ev.respondWith(Promise.resolve(
new Response("synthesized response body", {})
));
@ -33,12 +33,24 @@ onfetch = function(ev) {
));
}
else if (ev.request.url.includes("synthesized-redirect-synthesized.txt")) {
else if (ev.request.url.includes("synthesized-redirect-twice-real-file.txt")) {
ev.respondWith(Promise.resolve(
Response.redirect("synthesized.txt")
Response.redirect("synthesized-redirect-real-file.txt")
));
}
else if (ev.request.url.includes("synthesized-redirect-synthesized.txt")) {
ev.respondWith(Promise.resolve(
Response.redirect("bare-synthesized.txt")
));
}
else if (ev.request.url.includes("synthesized-redirect-twice-synthesized.txt")) {
ev.respondWith(Promise.resolve(
Response.redirect("synthesized-redirect-synthesized.txt")
));
}
else if (ev.request.url.includes("ignored.txt")) {
}

View File

@ -27,6 +27,7 @@ support-files =
fetch/fetch_worker_script.js
fetch/fetch_tests.js
fetch/deliver-gzip.sjs
fetch/redirect.sjs
fetch/real-file.txt
fetch/context/index.html
fetch/context/register.html

View File

@ -73,6 +73,7 @@ struct HttpChannelOpenArgs
struct HttpChannelConnectArgs
{
uint32_t channelId;
bool shouldIntercept;
};
union HttpChannelCreationArgs

View File

@ -2254,6 +2254,14 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
}
}
// Preserve any skip-serviceworker-flag if possible.
if (mForceNoIntercept) {
nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(newChannel);
if (httpChan) {
httpChan->ForceNoIntercept();
}
}
// Propagate our loadinfo if needed.
newChannel->SetLoadInfo(mLoadInfo);

View File

@ -177,6 +177,8 @@ HttpChannelChild::HttpChannelChild()
, mDivertingToParent(false)
, mFlushedForDiversion(false)
, mSuspendSent(false)
, mSynthesizedResponse(false)
, mShouldParentIntercept(false)
{
LOG(("Creating HttpChannelChild @%x\n", this));
@ -1111,6 +1113,14 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId,
return;
}
nsCOMPtr<nsIHttpChannelChild> httpChannelChild = do_QueryInterface(newChannel);
if (mSynthesizedResponse && 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.
httpChannelChild->ForceIntercepted();
}
mRedirectChannelChild = do_QueryInterface(newChannel);
if (mRedirectChannelChild) {
mRedirectChannelChild->ConnectParent(newChannelId);
@ -1261,7 +1271,7 @@ HttpChannelChild::ConnectParent(uint32_t id)
// until OnStopRequest, or we do a redirect, or we hit an IPDL error.
AddIPDLReference();
HttpChannelConnectArgs connectArgs(id);
HttpChannelConnectArgs connectArgs(id, mShouldParentIntercept);
PBrowserOrId browser = static_cast<ContentChild*>(gNeckoChild->Manager())
->GetBrowserOrId(tabChild);
if (!gNeckoChild->
@ -1283,6 +1293,15 @@ HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
if (mShouldParentIntercept) {
// This is a redirected channel, and the corresponding parent channel has started
// AsyncOpen but was intercepted and suspended. We must tear it down and start
// fresh - we will intercept the child channel this time, before creating a new
// parent channel unnecessarily.
PHttpChannelChild::Send__delete__(this);
return AsyncOpen(listener, aContext);
}
/*
* No need to check for cancel: we don't get here if nsHttpChannel canceled
* before AsyncOpen(); if it's canceled after that, OnStart/Stop will just
@ -2149,6 +2168,10 @@ HttpChannelChild::ResetInterception()
}
mInterceptListener = nullptr;
// The chance to intercept any further requests associated with this channel
// (such as redirects) has passed.
ForceNoIntercept();
// Continue with the original cross-process request
nsresult rv = ContinueAsyncOpen();
NS_ENSURE_SUCCESS_VOID(rv);
@ -2163,6 +2186,7 @@ HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr<nsHttpResponseHead>&
SetApplyConversion(false);
mResponseHead = aResponseHead;
mSynthesizedResponse = true;
uint16_t status = mResponseHead->Status();
if (status != 200 && status != 404) {
@ -2206,4 +2230,11 @@ HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr<nsHttpResponseHead>&
}
}
NS_IMETHODIMP
HttpChannelChild::ForceIntercepted()
{
mShouldParentIntercept = true;
return NS_OK;
}
}} // mozilla::net

View File

@ -195,6 +195,14 @@ private:
// diverting callbacks to parent.
bool mSuspendSent;
// Set if a response was synthesized, indicating that any forthcoming redirects
// should be intercepted.
bool mSynthesizedResponse;
// Set if the corresponding parent channel should force an interception to occur
// before the network transaction is initiated.
bool mShouldParentIntercept;
// true after successful AsyncOpen until OnStopRequest completes.
bool RemoteChannelExists() { return mIPCOpen && !mKeptAlive; }

View File

@ -57,6 +57,8 @@ HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
, mDivertingFromChild(false)
, mDivertedOnStartRequest(false)
, mSuspendedForDiversion(false)
, mShouldIntercept(false)
, mShouldSuspendIntercept(false)
, mNestedFrameId(0)
{
LOG(("Creating HttpChannelParent [this=%p]\n", this));
@ -92,6 +94,13 @@ HttpChannelParent::ActorDestroy(ActorDestroyReason why)
// 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;
// If this is an intercepted channel, we need to make sure that any resources are
// cleaned up to avoid leaks.
if (mInterceptedChannel) {
mInterceptedChannel->Cancel();
mInterceptedChannel = nullptr;
}
}
bool
@ -118,7 +127,7 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
case HttpChannelCreationArgs::THttpChannelConnectArgs:
{
const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
return ConnectChannel(cArgs.channelId());
return ConnectChannel(cArgs.channelId(), cArgs.shouldIntercept());
}
default:
NS_NOTREACHED("unknown open type");
@ -143,7 +152,7 @@ NS_IMPL_ISUPPORTS(HttpChannelParent,
NS_IMETHODIMP
HttpChannelParent::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate, bool* aShouldIntercept)
{
*aShouldIntercept = !!mSynthesizedResponseHead;
*aShouldIntercept = mShouldIntercept;
return NS_OK;
}
@ -188,6 +197,11 @@ public:
NS_IMETHODIMP
HttpChannelParent::ChannelIntercepted(nsIInterceptedChannel* aChannel)
{
if (mShouldSuspendIntercept) {
mInterceptedChannel = aChannel;
return NS_OK;
}
aChannel->SynthesizeStatus(mSynthesizedResponseHead->Status(),
mSynthesizedResponseHead->StatusText());
nsCOMPtr<nsIHttpHeaderVisitor> visitor = new HeaderVisitor(aChannel);
@ -388,6 +402,9 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) {
mSynthesizedResponseHead = new nsHttpResponseHead(aSynthesizedResponseHead.get_nsHttpResponseHead());
mShouldIntercept = true;
} else {
mChannel->ForceNoIntercept();
}
nsCOMPtr<nsISupportsPRUint32> cacheKey =
@ -467,7 +484,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
}
bool
HttpChannelParent::ConnectChannel(const uint32_t& channelId)
HttpChannelParent::ConnectChannel(const uint32_t& channelId, const bool& shouldIntercept)
{
nsresult rv;
@ -478,6 +495,13 @@ HttpChannelParent::ConnectChannel(const uint32_t& channelId)
mChannel = static_cast<nsHttpChannel*>(channel.get());
LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
mShouldIntercept = shouldIntercept;
if (mShouldIntercept) {
// When an interception occurs, this channel should suspend all further activity.
// It will be torn down and recreated if necessary.
mShouldSuspendIntercept = true;
}
if (mPBOverride != kPBOverride_Unset) {
// redirected-to channel may not support PB
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);

View File

@ -88,7 +88,7 @@ public:
protected:
// used to connect redirected-to channel in parent with just created
// ChildChannel. Used during redirects.
bool ConnectChannel(const uint32_t& channelId);
bool ConnectChannel(const uint32_t& channelId, const bool& shouldIntercept);
bool DoAsyncOpen(const URIParams& uri,
const OptionalURIParams& originalUri,
@ -204,7 +204,15 @@ private:
bool mSuspendedForDiversion;
// Set if this channel should be intercepted before it sets up the HTTP transaction.
bool mShouldIntercept : 1;
// Set if this channel should suspend on interception.
bool mShouldSuspendIntercept : 1;
dom::TabId mNestedFrameId;
// Handle to the channel wrapper if this channel has been intercepted.
nsCOMPtr<nsIInterceptedChannel> mInterceptedChannel;
};
} // namespace net

View File

@ -7,11 +7,15 @@
[ptr] native RequestHeaderTuples(mozilla::net::RequestHeaderTuples);
[uuid(306ACF4D-C6DF-4EF6-BDA9-5CB92E83EDD9)]
[uuid(3842c5e9-b5b1-400c-8eb7-936a3316ff21)]
interface nsIHttpChannelChild : nsISupports
{
void addCookiesToRequest();
// Mark this channel as requiring an interception; this will propagate
// to the corresponding parent channel when a redirect occurs.
void forceIntercepted();
// Headers that the channel client has set via SetRequestHeader.
readonly attribute RequestHeaderTuples clientSetRequestHeaders;
};