Bug 1444539: Disconnect StreamFilters on redirect. r=mixedpuppy

MozReview-Commit-ID: AuCjXTlsFSC

--HG--
extra : rebase_source : 40b0836c3efd739020dc59f1ed3f63020e187b47
This commit is contained in:
Kris Maglione 2018-03-09 14:38:41 -08:00
Родитель f8ae1c0643
Коммит 73efc480a8
7 изменённых файлов: 175 добавлений и 4 удалений

Просмотреть файл

@ -0,0 +1,131 @@
"use strict";
XPCOMUtils.defineLazyServiceGetter(this, "proxyService",
"@mozilla.org/network/protocol-proxy-service;1",
"nsIProtocolProxyService");
const server = createHttpServer();
const gHost = "localhost";
const gPort = server.identity.primaryPort;
const HOSTS = new Set([
"example.com",
"example.org",
"example.net",
]);
for (let host of HOSTS) {
server.identity.add("http", host, 80);
}
const proxyFilter = {
proxyInfo: proxyService.newProxyInfo("http", gHost, gPort, 0, 4096, null),
applyFilter(service, channel, defaultProxyInfo, callback) {
if (HOSTS.has(channel.URI.host)) {
callback.onProxyFilterResult(this.proxyInfo);
} else {
callback.onProxyFilterResult(defaultProxyInfo);
}
},
};
proxyService.registerChannelFilter(proxyFilter, 0);
registerCleanupFunction(() => {
proxyService.unregisterChannelFilter(proxyFilter);
});
server.registerPathHandler("/redirect", (request, response) => {
let params = new URLSearchParams(request.queryString);
response.setStatusLine(request.httpVersion, 302, "Moved Temporarily");
response.setHeader("Location", params.get("redirect_uri"));
response.setHeader("Access-Control-Allow-Origin", "*");
});
server.registerPathHandler("/dummy", (request, response) => {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Access-Control-Allow-Origin", "*");
response.write("ok");
});
Cu.importGlobalProperties(["fetch"]);
add_task(async function() {
const {fetch} = Cu.Sandbox("http://example.com/", {wantGlobalProperties: ["fetch"]});
let extension = ExtensionTestUtils.loadExtension({
background() {
let pending = [];
browser.webRequest.onBeforeRequest.addListener(
data => {
let filter = browser.webRequest.filterResponseData(data.requestId);
let url = new URL(data.url);
if (url.searchParams.get("redirect_uri")) {
pending.push(
new Promise(resolve => { filter.onerror = resolve; }).then(() => {
browser.test.assertEq("Channel redirected", filter.error,
"Got correct error for redirected filter");
}));
}
filter.onstart = () => {
filter.write(new TextEncoder().encode(data.url));
};
filter.ondata = event => {
let str = new TextDecoder().decode(event.data);
browser.test.assertEq("ok", str, `Got unfiltered data for ${data.url}`);
};
filter.onstop = () => {
filter.close();
};
}, {
urls: ["<all_urls>"],
},
["blocking"]);
browser.test.onMessage.addListener(async msg => {
if (msg === "done") {
await Promise.all(pending);
browser.test.notifyPass("stream-filter");
}
});
},
manifest: {
permissions: [
"webRequest",
"webRequestBlocking",
"http://example.com/",
"http://example.org/",
],
},
});
await extension.startup();
let results = [
["http://example.com/dummy", "http://example.com/dummy"],
["http://example.org/dummy", "http://example.org/dummy"],
["http://example.net/dummy", "ok"],
["http://example.com/redirect?redirect_uri=http://example.com/dummy", "http://example.com/dummy"],
["http://example.com/redirect?redirect_uri=http://example.org/dummy", "http://example.org/dummy"],
["http://example.com/redirect?redirect_uri=http://example.net/dummy", "ok"],
["http://example.net/redirect?redirect_uri=http://example.com/dummy", "http://example.com/dummy"],
].map(async ([url, expectedResponse]) => {
let resp = await fetch(url);
let text = await resp.text();
equal(text, expectedResponse, `Expected response for ${url}`);
});
await Promise.all(results);
extension.sendMessage("done");
await extension.awaitFinish("stream-filter");
await extension.unload();
ChromeUtils.import("resource://gre/modules/Timer.jsm");
await new Promise(resolve => setTimeout(resolve, 100));
});

Просмотреть файл

@ -81,6 +81,7 @@ skip-if = os == "android" # checking for telemetry needs to be updated: 1384923
[test_ext_trustworthy_origin.js]
[test_ext_topSites.js]
skip-if = os == "android"
[test_ext_webRequest_filterResponseData.js]
[test_native_manifests.js]
subprocess = true
skip-if = os == "android"

Просмотреть файл

@ -18,11 +18,13 @@ parent:
async Resume();
async Close();
async Disconnect();
async Destroy();
child:
async Resumed();
async Suspended();
async Closed();
async Error(nsCString error);
async FlushData();

Просмотреть файл

@ -304,6 +304,18 @@ StreamFilterChild::RecvInitialized(bool aSuccess)
}
}
IPCResult
StreamFilterChild::RecvError(const nsCString& aError)
{
mState = State::Error;
if (mStreamFilter) {
mStreamFilter->FireErrorEvent(NS_ConvertUTF8toUTF16(aError));
mStreamFilter = nullptr;
}
SendDestroy();
return IPC_OK();
}
IPCResult
StreamFilterChild::RecvClosed() {
MOZ_DIAGNOSTIC_ASSERT(mState == State::Closing);

Просмотреть файл

@ -99,6 +99,7 @@ protected:
virtual IPCResult RecvStartRequest() override;
virtual IPCResult RecvData(Data&& data) override;
virtual IPCResult RecvStopRequest(const nsresult& aStatus) override;
virtual IPCResult RecvError(const nsCString& aError) override;
virtual IPCResult RecvClosed() override;
virtual IPCResult RecvSuspended() override;

Просмотреть файл

@ -225,6 +225,7 @@ StreamFilterParent::Broken()
RunOnActorThread(FUNC, [=] {
if (self->IPCActive()) {
self->mDisconnected = true;
self->mState = State::Disconnected;
}
});
@ -267,6 +268,15 @@ StreamFilterParent::Destroy()
NS_DISPATCH_NORMAL);
}
IPCResult
StreamFilterParent::RecvDestroy()
{
AssertIsActorThread();
Destroy();
return IPC_OK();
}
IPCResult
StreamFilterParent::RecvSuspend()
{
@ -311,7 +321,6 @@ StreamFilterParent::RecvResume()
}
return IPC_OK();
}
IPCResult
StreamFilterParent::RecvDisconnect()
{
@ -346,6 +355,7 @@ StreamFilterParent::RecvFlushedData()
RunOnActorThread(FUNC, [=] {
self->mState = State::Disconnected;
self->mDisconnected = true;
});
});
return IPC_OK();
@ -406,7 +416,19 @@ StreamFilterParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
mContext = aContext;
if (mState != State::Disconnected) {
if (aRequest != mChannel) {
mDisconnected = true;
RefPtr<StreamFilterParent> self(this);
RunOnActorThread(FUNC, [=] {
if (self->IPCActive()) {
self->mState = State::Disconnected;
CheckResult(self->SendError(NS_LITERAL_CSTRING("Channel redirected")));
}
});
}
if (!mDisconnected) {
RefPtr<StreamFilterParent> self(this);
RunOnActorThread(FUNC, [=] {
if (self->IPCActive()) {
@ -439,7 +461,7 @@ StreamFilterParent::OnStopRequest(nsIRequest* aRequest,
AssertIsMainThread();
mReceivedStop = true;
if (mState == State::Disconnected) {
if (mDisconnected) {
return EmitStopRequest(aStatusCode);
}
@ -485,7 +507,7 @@ StreamFilterParent::OnDataAvailable(nsIRequest* aRequest,
{
AssertIsIOThread();
if (mState == State::Disconnected) {
if (mDisconnected) {
// If we're offloading data in a thread pool, it's possible that we'll
// have buffered some additional data while waiting for the buffer to
// flush. So, if there's any buffered data left, flush that before we

Просмотреть файл

@ -93,6 +93,7 @@ protected:
virtual IPCResult RecvResume() override;
virtual IPCResult RecvClose() override;
virtual IPCResult RecvDisconnect() override;
virtual IPCResult RecvDestroy() override;
virtual void DeallocPStreamFilterParent() override;
@ -175,6 +176,7 @@ private:
bool mReceivedStop;
bool mSentStop;
bool mDisconnected = false;
nsCOMPtr<nsISupports> mContext;
uint64_t mOffset;