Bug 1036275 - Change nsHttpChannel to call into PackagedAppService for urls containing !// r=honzab

This commit is contained in:
Valentin Gosu 2015-06-03 01:46:02 +03:00
parent bed283ae55
commit 2584adb0bb
7 changed files with 192 additions and 0 deletions

View File

@ -1382,6 +1382,12 @@ pref("network.http.tcp_keepalive.long_lived_idle_time", 600);
pref("network.http.enforce-framing.http1", false); // should be named "strict"
pref("network.http.enforce-framing.soft", true);
// Whether nsHttpChannel should use the PackagedAppService to load
// resources from a package when directed to a URL
// such as http://domain.com/package.pak!//resource.html
// See http://www.w3.org/TR/web-packaging/#streamable-package-format
pref("network.http.enable-packaged-apps", false);
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)

View File

@ -79,6 +79,7 @@
#include "nsIX509Cert.h"
#include "ScopedNSSTypes.h"
#include "nsNullPrincipal.h"
#include "nsIPackagedAppService.h"
namespace mozilla { namespace net {
@ -249,6 +250,7 @@ nsHttpChannel::nsHttpChannel()
, mConcurentCacheAccess(0)
, mIsPartialRequest(0)
, mHasAutoRedirectVetoNotifier(0)
, mIsPackagedAppResource(0)
, mPushedStream(nullptr)
, mLocalBlocklist(false)
, mWarningReporter(nullptr)
@ -3339,6 +3341,13 @@ nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry *entry,
if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
}
if (mIsPackagedAppResource) {
// We need to return FILE_NOT_FOUND in case an error occurs
// or we will take the user to the <you're offline> screen.
return NS_ERROR_FILE_NOT_FOUND;
}
return NS_ERROR_DOCUMENT_NOT_CACHED;
}
@ -4770,6 +4779,13 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
nsresult rv;
MOZ_ASSERT(NS_IsMainThread());
if (gHttpHandler->PackagedAppsEnabled()) {
nsAutoCString path;
mURI->GetPath(path);
mIsPackagedAppResource = path.Find(PACKAGED_APP_TOKEN) != kNotFound;
}
rv = NS_CheckPortSafety(mURI);
if (NS_FAILED(rv)) {
ReleaseListeners();
@ -4982,6 +4998,22 @@ nsHttpChannel::BeginConnect()
if (!mTimingEnabled)
mAsyncOpenTime = TimeStamp();
if (mIsPackagedAppResource) {
// If this is a packaged app resource, the content will be fetched
// by the packaged app service into the cache, and the cache entry will
// be passed to OnCacheEntryAvailable.
mLoadFlags |= LOAD_ONLY_FROM_CACHE;
mLoadFlags |= LOAD_FROM_CACHE;
mLoadFlags &= ~VALIDATE_ALWAYS;
nsCOMPtr<nsIPackagedAppService> pas =
do_GetService("@mozilla.org/network/packaged-app-service;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return pas->RequestURI(mURI, GetLoadContextInfo(this), this);
}
// when proxying only use the pipeline bit if ProxyPipelining() allows it.
if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;

View File

@ -475,6 +475,11 @@ private:
uint32_t mIsPartialRequest : 1;
// true iff there is AutoRedirectVetoNotifier on the stack
uint32_t mHasAutoRedirectVetoNotifier : 1;
// Whether fetching the content is meant to be handled by the
// packaged app service, which behaves like a caching layer.
// Upon successfully fetching the package, the resource will be placed in
// the cache, and served by calling OnCacheEntryAvailable.
uint32_t mIsPackagedAppResource : 1;
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;

View File

@ -284,6 +284,12 @@ nsHttpHandler::Init()
PrefsChanged(prefBranch, nullptr);
}
rv = Preferences::AddBoolVarCache(&mPackagedAppsEnabled,
"network.http.enable-packaged-apps", false);
if (NS_FAILED(rv)) {
mPackagedAppsEnabled = false;
}
nsHttpChannelAuthProvider::InitializePrefs();
mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);

View File

@ -79,6 +79,7 @@ public:
uint8_t ReferrerTrimmingPolicy() { return mReferrerTrimmingPolicy; }
uint8_t ReferrerXOriginPolicy() { return mReferrerXOriginPolicy; }
bool SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
bool PackagedAppsEnabled() { return mPackagedAppsEnabled; }
uint8_t RedirectionLimit() { return mRedirectionLimit; }
PRIntervalTime IdleTimeout() { return mIdleTimeout; }
PRIntervalTime SpdyTimeout() { return mSpdyTimeout; }
@ -408,6 +409,9 @@ private:
PRIntervalTime mPipelineReadTimeout;
nsCOMPtr<nsITimer> mPipelineTestTimer;
// Whether a URL containing !// should be interpreted as a packaged app channel
bool mPackagedAppsEnabled = false;
uint8_t mRedirectionLimit;
// we'll warn the user if we load an URL containing a userpass field

View File

@ -0,0 +1,138 @@
//
// This tests checks that a regular http channel can be used to load a
// packaged app resource. It sets the network.http.enable-packaged-apps pref
// to enable this feature, then creates a channel and loads the resource.
// reset_pref resets the pref at the end of the test.
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "uri", function() {
return "http://localhost:" + httpserver.identity.primaryPort;
});
const nsIBinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream"
);
function make_channel(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel2(url,
"",
null,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
}
function Listener(callback) {
this._callback = callback;
}
Listener.prototype = {
gotStartRequest: false,
available: -1,
gotStopRequest: false,
QueryInterface: function(iid) {
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIRequestObserver))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
onDataAvailable: function(request, ctx, stream, offset, count) {
try {
this.available = stream.available();
do_check_eq(this.available, count);
// Need to consume stream to avoid assertion
var str = new nsIBinaryInputStream(stream).readBytes(count);
equal(str, "<html>\r\n <head>\r\n <script src=\"/scripts/app.js\"></script>\r\n ...\r\n </head>\r\n ...\r\n</html>\r\n", "check proper content");
}
catch (ex) {
do_throw(ex);
}
},
onStartRequest: function(request, ctx) {
this.gotStartRequest = true;
},
onStopRequest: function(request, ctx, status) {
this.gotStopRequest = true;
do_check_eq(status, 0);
if (this._callback) {
this._callback.call(null, this);
}
}
};
// The package content
// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
var testData = {
content: [
{ headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n <head>\r\n <script src=\"/scripts/app.js\"></script>\r\n ...\r\n </head>\r\n ...\r\n</html>\r\n", type: "text/html" },
{ headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
{ headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
],
token : "gc0pJq0M:08jU534c0p",
getData: function() {
var str = "";
for (var i in this.content) {
str += "--" + this.token + "\r\n";
for (var j in this.content[i].headers) {
str += this.content[i].headers[j] + "\r\n";
}
str += "\r\n";
str += this.content[i].data + "\r\n";
}
str += "--" + this.token + "--";
return str;
}
}
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", 'application/package');
var body = testData.getData();
response.bodyOutputStream.write(body, body.length);
}
var httpserver = null;
var originalPref = false;
function run_test()
{
// setup test
httpserver = new HttpServer();
httpserver.registerPathHandler("/package", contentHandler);
httpserver.start(-1);
// Enable the feature and save the original pref value
originalPref = Services.prefs.getBoolPref("network.http.enable-packaged-apps");
Services.prefs.setBoolPref("network.http.enable-packaged-apps", true);
do_register_cleanup(reset_pref);
add_test(test_channel);
// run tests
run_next_test();
}
function test_channel() {
var channel = make_channel(uri+"/package!//index.html");
channel.asyncOpen(new Listener(function(l) {
// XXX: no content length available for this resource
//do_check_true(channel.contentLength > 0);
do_check_true(l.gotStartRequest);
do_check_true(l.gotStopRequest);
run_next_test();
}), null);
}
function reset_pref() {
// Set the pref to its original value
Services.prefs.setBoolPref("network.http.enable-packaged-apps", originalPref);
}

View File

@ -21,6 +21,7 @@ support-files =
test_link.url
../../dns/effective_tld_names.dat
[test_packaged_app_channel.js]
[test_nsIBufferedOutputStream_writeFrom_block.js]
[test_cache2-00-service-get.js]
[test_cache2-01-basic.js]