зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1172233
- Allow web packaged apps to be served without a mimetype r=mcmanus
This commit is contained in:
Родитель
1367b663ec
Коммит
13a8848d29
|
@ -651,7 +651,8 @@ PackagedAppService::OpenNewPackageInternal(nsIURI *aURI,
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIStreamListener> mimeConverter;
|
||||
rv = streamconv->AsyncConvertData("multipart/mixed", "*/*", downloader, nullptr,
|
||||
// Passing `this` as context, so nsMultiMixedConv knows this is a packaged app
|
||||
rv = streamconv->AsyncConvertData("multipart/mixed", "*/*", downloader, this,
|
||||
getter_AddRefs(mimeConverter));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsURLHelper.h"
|
||||
#include "nsIStreamConverterService.h"
|
||||
#include "nsIPackagedAppService.h"
|
||||
#include <algorithm>
|
||||
#include "nsHttp.h"
|
||||
|
||||
//
|
||||
// Helper function for determining the length of data bytes up to
|
||||
|
@ -470,6 +472,11 @@ nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
|
|||
// and OnStopRequest() call combinations. We call of series of these for each sub-part
|
||||
// in the raw stream.
|
||||
mFinalListener = aListener;
|
||||
|
||||
nsCOMPtr<nsIPackagedAppService> pas(do_QueryInterface(aCtxt));
|
||||
if (pas) {
|
||||
mPackagedApp = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -732,11 +739,15 @@ nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
|
|||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_FAILED(rv) && !mPackagedApp) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
// try asking the channel directly
|
||||
rv = channel->GetContentType(delimiter);
|
||||
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
|
||||
if (NS_FAILED(rv) && !mPackagedApp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/web-packaging/#streamable-package-format
|
||||
|
@ -745,13 +756,22 @@ nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
|
|||
// the content of the file.
|
||||
if (delimiter.Find("application/package") != kNotFound) {
|
||||
mPackagedApp = true;
|
||||
mHasAppContentType = true;
|
||||
mToken.Truncate();
|
||||
mTokenLen = 0;
|
||||
}
|
||||
|
||||
bndry = strstr(delimiter.BeginWriting(), "boundary");
|
||||
|
||||
if (!bndry && mPackagedApp) {
|
||||
bool requestSucceeded = true;
|
||||
if (httpChannel) {
|
||||
httpChannel->GetRequestSucceeded(&requestSucceeded);
|
||||
}
|
||||
|
||||
// If the package has the appropriate content type, or if it is a successful
|
||||
// packaged app request, without the required content type, there's no need
|
||||
// for a boundary to be included in this header.
|
||||
if (!bndry && (mHasAppContentType || (mPackagedApp && requestSucceeded))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -775,9 +795,9 @@ nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
|
|||
mToken = boundaryString;
|
||||
mTokenLen = boundaryString.Length();
|
||||
|
||||
if (mTokenLen == 0 && !mPackagedApp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (mTokenLen == 0 && !mPackagedApp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -841,6 +861,7 @@ nsMultiMixedConv::nsMultiMixedConv() :
|
|||
mTotalSent = 0;
|
||||
mIsByteRangeRequest = false;
|
||||
mPackagedApp = false;
|
||||
mHasAppContentType = false;
|
||||
}
|
||||
|
||||
nsMultiMixedConv::~nsMultiMixedConv() {
|
||||
|
@ -870,7 +891,9 @@ nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
|
|||
nsresult rv = NS_OK;
|
||||
|
||||
nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
|
||||
if (mContentType.IsEmpty()) {
|
||||
// For packaged apps that don't have a content type we want to just
|
||||
// go ahead and serve them with an empty content type
|
||||
if (mContentType.IsEmpty() && !mPackagedApp) {
|
||||
mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
|
||||
nsCOMPtr<nsIStreamConverterService> serv =
|
||||
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
|
||||
|
@ -1065,6 +1088,17 @@ nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr,
|
|||
// examine header
|
||||
if (headerStr.LowerCaseEqualsLiteral("content-type")) {
|
||||
mContentType = headerVal;
|
||||
|
||||
// If the HTTP channel doesn't have an application/package
|
||||
// content type we still want to serve the resource, but with the
|
||||
// "application/octet-stream" header, so we prevent execution of
|
||||
// unsafe content
|
||||
if (mPackagedApp && !mHasAppContentType) {
|
||||
mContentType = APPLICATION_OCTET_STREAM;
|
||||
mResponseHead->SetHeader(mozilla::net::nsHttp::Content_Type,
|
||||
mContentType);
|
||||
mResponseHead->SetContentType(mContentType);
|
||||
}
|
||||
} else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
|
||||
mContentLength = nsCRT::atoll(headerVal.get());
|
||||
} else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
|
||||
|
|
|
@ -177,6 +177,9 @@ protected:
|
|||
|
||||
uint32_t mCurrentPartID;
|
||||
|
||||
// If true, it means the packaged app had an "application/package" header
|
||||
// Otherwise, we remove "Content-Type" headers from files in the package
|
||||
bool mHasAppContentType;
|
||||
// This is true if the content-type is application/package
|
||||
// Streamable packages don't require the boundary in the header
|
||||
// as it can be ascertained from the package file.
|
||||
|
|
|
@ -24,6 +24,8 @@ Cu.import("resource://testing-common/httpd.js");
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var httpserver = null;
|
||||
var gPAS = Cc["@mozilla.org/network/packaged-app-service;1"]
|
||||
.getService(Ci.nsIPackagedAppService);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "uri", function() {
|
||||
return "http://localhost:" + httpserver.identity.primaryPort;
|
||||
|
@ -69,6 +71,13 @@ function contentHandler_chunked_headers(metadata, response)
|
|||
});
|
||||
}
|
||||
|
||||
function contentHandler_type_missing(metadata, response)
|
||||
{
|
||||
response.setHeader("Content-Type", 'text/plain');
|
||||
var body = testData.getData();
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
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" },
|
||||
|
@ -92,16 +101,21 @@ var testData = {
|
|||
}
|
||||
}
|
||||
|
||||
function multipartListener(test) {
|
||||
function multipartListener(test, badContentType) {
|
||||
this._buffer = "";
|
||||
this.testNum = 0;
|
||||
this.test = test;
|
||||
this.numTests = this.test.content.length;
|
||||
// If set to true, that means the package is missing the application/package
|
||||
// content type. If so, resources will have their content type set to
|
||||
// application/octet-stream
|
||||
this.badContentType = badContentType == undefined ? false : badContentType;
|
||||
}
|
||||
|
||||
multipartListener.prototype.responseHandler = function(request, buffer) {
|
||||
equal(buffer, this.test.content[this.testNum].data);
|
||||
equal(request.QueryInterface(Ci.nsIChannel).contentType, this.test.content[this.testNum].type);
|
||||
equal(request.QueryInterface(Ci.nsIChannel).contentType,
|
||||
this.badContentType ? "application/octet-stream" : this.test.content[this.testNum].type);
|
||||
if (++this.testNum == this.numTests) {
|
||||
run_next_test();
|
||||
}
|
||||
|
@ -117,7 +131,7 @@ multipartListener.prototype.QueryInterface = function(iid) {
|
|||
|
||||
multipartListener.prototype.onStartRequest = function(request, context) {
|
||||
this._buffer = "";
|
||||
this.headerListener = new headerListener(this.test.content[this.testNum].headers);
|
||||
this.headerListener = new headerListener(this.test.content[this.testNum].headers, this.badContentType);
|
||||
let headerProvider = request.QueryInterface(Ci.nsIResponseHeadProvider);
|
||||
if (headerProvider) {
|
||||
headerProvider.visitResponseHeaders(this.headerListener);
|
||||
|
@ -141,8 +155,9 @@ multipartListener.prototype.onStopRequest = function(request, context, status) {
|
|||
}
|
||||
}
|
||||
|
||||
function headerListener(headers) {
|
||||
function headerListener(headers, badContentType) {
|
||||
this.expectedHeaders = headers;
|
||||
this.badContentType = badContentType;
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
|
@ -155,7 +170,8 @@ headerListener.prototype.QueryInterface = function(iid) {
|
|||
|
||||
headerListener.prototype.visitHeader = function(header, value) {
|
||||
ok(this.index <= this.expectedHeaders.length);
|
||||
equal(header + ": " + value, this.expectedHeaders[this.index]);
|
||||
if (!this.badContentType)
|
||||
equal(header + ": " + value, this.expectedHeaders[this.index]);
|
||||
this.index++;
|
||||
}
|
||||
|
||||
|
@ -165,7 +181,7 @@ function test_multipart() {
|
|||
var conv = streamConv.asyncConvertData("multipart/mixed",
|
||||
"*/*",
|
||||
new multipartListener(testData),
|
||||
null);
|
||||
gPAS);
|
||||
|
||||
var chan = make_channel(uri + "/multipart");
|
||||
chan.asyncOpen(conv, null);
|
||||
|
@ -177,7 +193,7 @@ function test_multipart_with_boundary() {
|
|||
var conv = streamConv.asyncConvertData("multipart/mixed",
|
||||
"*/*",
|
||||
new multipartListener(testData),
|
||||
null);
|
||||
gPAS);
|
||||
|
||||
var chan = make_channel(uri + "/multipart2");
|
||||
chan.asyncOpen(conv, null);
|
||||
|
@ -189,18 +205,35 @@ function test_multipart_chunked_headers() {
|
|||
var conv = streamConv.asyncConvertData("multipart/mixed",
|
||||
"*/*",
|
||||
new multipartListener(testData),
|
||||
null);
|
||||
gPAS);
|
||||
|
||||
var chan = make_channel(uri + "/multipart3");
|
||||
chan.asyncOpen(conv, null);
|
||||
}
|
||||
|
||||
function test_multipart_content_type_other() {
|
||||
var streamConv = Cc["@mozilla.org/streamConverters;1"]
|
||||
.getService(Ci.nsIStreamConverterService);
|
||||
|
||||
// mime types other that multipart/mixed and application/package are only
|
||||
// allowed if an nsIPackagedAppService is passed as context
|
||||
var conv = streamConv.asyncConvertData("multipart/mixed",
|
||||
"*/*",
|
||||
new multipartListener(testData, true),
|
||||
gPAS);
|
||||
|
||||
var chan = make_channel(uri + "/multipart4");
|
||||
chan.asyncOpen(conv, null);
|
||||
}
|
||||
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpserver = new HttpServer();
|
||||
httpserver.registerPathHandler("/multipart", contentHandler);
|
||||
httpserver.registerPathHandler("/multipart2", contentHandler_with_boundary);
|
||||
httpserver.registerPathHandler("/multipart3", contentHandler_chunked_headers);
|
||||
httpserver.registerPathHandler("/multipart4", contentHandler_type_missing);
|
||||
httpserver.start(-1);
|
||||
|
||||
run_next_test();
|
||||
|
@ -209,3 +242,4 @@ function run_test()
|
|||
add_test(test_multipart);
|
||||
add_test(test_multipart_with_boundary);
|
||||
add_test(test_multipart_chunked_headers);
|
||||
add_test(test_multipart_content_type_other);
|
||||
|
|
Загрузка…
Ссылка в новой задаче