263 lines
7.9 KiB
JavaScript
263 lines
7.9 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Server side http server script for application update tests.
|
|
*/
|
|
|
|
// Definitions from test and other files used by the tests
|
|
/* global getState */
|
|
|
|
function getTestDataFile(aFilename) {
|
|
let file = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
|
|
let pathParts = REL_PATH_DATA.split("/");
|
|
for (let i = 0; i < pathParts.length; ++i) {
|
|
file.append(pathParts[i]);
|
|
}
|
|
if (aFilename) {
|
|
file.append(aFilename);
|
|
}
|
|
return file;
|
|
}
|
|
|
|
function loadHelperScript(aScriptFile) {
|
|
let scriptSpec = Services.io.newFileURI(aScriptFile).spec;
|
|
Services.scriptloader.loadSubScript(scriptSpec, this);
|
|
}
|
|
|
|
var scriptFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
|
scriptFile.initWithPath(getState("__LOCATION__"));
|
|
scriptFile = scriptFile.parent;
|
|
/* import-globals-from ../browser/testConstants.js */
|
|
scriptFile.append("testConstants.js");
|
|
loadHelperScript(scriptFile);
|
|
|
|
/* import-globals-from sharedUpdateXML.js */
|
|
scriptFile = getTestDataFile("sharedUpdateXML.js");
|
|
loadHelperScript(scriptFile);
|
|
|
|
const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR;
|
|
const BAD_SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + "not_here.mar";
|
|
|
|
// A value of 10 caused the tests to intermittently fail on Mac OS X so be
|
|
// careful when changing this value.
|
|
const SLOW_RESPONSE_INTERVAL = 100;
|
|
const MAX_SLOW_RESPONSE_RETRIES = 200;
|
|
var gSlowDownloadTimer;
|
|
var gSlowCheckTimer;
|
|
|
|
function handleRequest(aRequest, aResponse) {
|
|
let params = {};
|
|
if (aRequest.queryString) {
|
|
params = parseQueryString(aRequest.queryString);
|
|
}
|
|
|
|
let statusCode = params.statusCode ? parseInt(params.statusCode) : 200;
|
|
let statusReason = params.statusReason ? params.statusReason : "OK";
|
|
aResponse.setStatusLine(aRequest.httpVersion, statusCode, statusReason);
|
|
aResponse.setHeader("Cache-Control", "no-cache", false);
|
|
|
|
// When a mar download is started by the update service it can finish
|
|
// downloading before the ui has loaded. By specifying a serviceURL for the
|
|
// update patch that points to this file and has a slowDownloadMar param the
|
|
// mar will be downloaded asynchronously which will allow the ui to load
|
|
// before the download completes.
|
|
if (params.slowDownloadMar) {
|
|
aResponse.processAsync();
|
|
aResponse.setHeader("Content-Type", "binary/octet-stream");
|
|
aResponse.setHeader("Content-Length", SIZE_SIMPLE_MAR);
|
|
|
|
// BITS will first make a HEAD request followed by a GET request.
|
|
if (aRequest.method == "HEAD") {
|
|
aResponse.finish();
|
|
return;
|
|
}
|
|
|
|
let marBytes = readFileBytes(getTestDataFile(FILE_SIMPLE_MAR));
|
|
if (params.firstByteEarly) {
|
|
// Sending the first byte early causes the request's `onStartRequest`
|
|
// to be fired before the continue file is written.
|
|
const firstByte = marBytes[0];
|
|
marBytes = marBytes.substring(1);
|
|
aResponse.write(firstByte);
|
|
}
|
|
|
|
let retries = 0;
|
|
gSlowDownloadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
gSlowDownloadTimer.initWithCallback(
|
|
function (_aTimer) {
|
|
let continueFile = getTestDataFile(CONTINUE_DOWNLOAD);
|
|
retries++;
|
|
if (continueFile.exists() || retries == MAX_SLOW_RESPONSE_RETRIES) {
|
|
try {
|
|
// If the continue file is in use try again the next time the timer
|
|
// fires unless the retries has reached the value defined by
|
|
// MAX_SLOW_RESPONSE_RETRIES in which case let the test remove the
|
|
// continue file.
|
|
if (retries < MAX_SLOW_RESPONSE_RETRIES) {
|
|
continueFile.remove(false);
|
|
}
|
|
gSlowDownloadTimer.cancel();
|
|
aResponse.write(marBytes);
|
|
aResponse.finish();
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
SLOW_RESPONSE_INTERVAL,
|
|
Ci.nsITimer.TYPE_REPEATING_SLACK
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (params.uiURL) {
|
|
aResponse.write(
|
|
'<html><head><meta http-equiv="content-type" content=' +
|
|
'"text/html; charset=utf-8"></head><body>' +
|
|
params.uiURL +
|
|
"<br><br>this is a test mar that will not " +
|
|
"affect your build.</body></html>"
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (params.xmlMalformed) {
|
|
respond(aResponse, params, "xml error");
|
|
return;
|
|
}
|
|
|
|
if (params.noUpdates) {
|
|
respond(aResponse, params, getRemoteUpdatesXMLString(""));
|
|
return;
|
|
}
|
|
|
|
if (params.unsupported) {
|
|
let detailsURL = params.detailsURL ? params.detailsURL : URL_HOST;
|
|
let unsupportedXML = getRemoteUpdatesXMLString(
|
|
' <update type="major" ' +
|
|
'unsupported="true" ' +
|
|
'detailsURL="' +
|
|
detailsURL +
|
|
'"></update>\n'
|
|
);
|
|
respond(aResponse, params, unsupportedXML);
|
|
return;
|
|
}
|
|
|
|
let size;
|
|
let patches = "";
|
|
let url = "";
|
|
if (params.useSlowDownloadMar) {
|
|
url = URL_HTTP_UPDATE_SJS + "?slowDownloadMar=1";
|
|
if (params.useFirstByteEarly) {
|
|
url += "&firstByteEarly=1";
|
|
}
|
|
} else {
|
|
url = params.badURL ? BAD_SERVICE_URL : SERVICE_URL;
|
|
}
|
|
if (!params.partialPatchOnly) {
|
|
size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : "");
|
|
let patchProps = { type: "complete", url, size };
|
|
patches += getRemotePatchString(patchProps);
|
|
}
|
|
|
|
if (!params.completePatchOnly) {
|
|
size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : "");
|
|
let patchProps = { type: "partial", url, size };
|
|
patches += getRemotePatchString(patchProps);
|
|
}
|
|
|
|
let updateProps = {};
|
|
if (params.type) {
|
|
updateProps.type = params.type;
|
|
}
|
|
|
|
if (params.name) {
|
|
updateProps.name = params.name;
|
|
}
|
|
|
|
if (params.appVersion) {
|
|
updateProps.appVersion = params.appVersion;
|
|
}
|
|
|
|
if (params.displayVersion) {
|
|
updateProps.displayVersion = params.displayVersion;
|
|
}
|
|
|
|
if (params.buildID) {
|
|
updateProps.buildID = params.buildID;
|
|
}
|
|
|
|
if (params.promptWaitTime) {
|
|
updateProps.promptWaitTime = params.promptWaitTime;
|
|
}
|
|
|
|
if (params.disableBITS) {
|
|
updateProps.disableBITS = params.disableBITS;
|
|
}
|
|
|
|
let updates = getRemoteUpdateString(updateProps, patches);
|
|
let xml = getRemoteUpdatesXMLString(updates);
|
|
respond(aResponse, params, xml);
|
|
}
|
|
|
|
function respond(aResponse, aParams, aResponseString) {
|
|
if (aParams.slowUpdateCheck) {
|
|
let retries = 0;
|
|
aResponse.processAsync();
|
|
gSlowCheckTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
gSlowCheckTimer.initWithCallback(
|
|
function (_aTimer) {
|
|
retries++;
|
|
let continueFile = getTestDataFile(CONTINUE_CHECK);
|
|
if (continueFile.exists() || retries == MAX_SLOW_RESPONSE_RETRIES) {
|
|
try {
|
|
// If the continue file is in use try again the next time the timer
|
|
// fires unless the retries has reached the value defined by
|
|
// MAX_SLOW_RESPONSE_RETRIES in which case let the test remove the
|
|
// continue file.
|
|
if (retries < MAX_SLOW_RESPONSE_RETRIES) {
|
|
continueFile.remove(false);
|
|
}
|
|
gSlowCheckTimer.cancel();
|
|
aResponse.write(aResponseString);
|
|
aResponse.finish();
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
SLOW_RESPONSE_INTERVAL,
|
|
Ci.nsITimer.TYPE_REPEATING_SLACK
|
|
);
|
|
} else {
|
|
aResponse.write(aResponseString);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function to create a JS object representing the url parameters from
|
|
* the request's queryString.
|
|
*
|
|
* @param aQueryString
|
|
* The request's query string.
|
|
* @return A JS object representing the url parameters from the request's
|
|
* queryString.
|
|
*/
|
|
function parseQueryString(aQueryString) {
|
|
let paramArray = aQueryString.split("&");
|
|
let regex = /^([^=]+)=(.*)$/;
|
|
let params = {};
|
|
for (let i = 0, sz = paramArray.length; i < sz; i++) {
|
|
let match = regex.exec(paramArray[i]);
|
|
if (!match) {
|
|
throw Components.Exception(
|
|
"Bad parameter in queryString! '" + paramArray[i] + "'",
|
|
Cr.NS_ERROR_ILLEGAL_VALUE
|
|
);
|
|
}
|
|
params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
|
|
}
|
|
|
|
return params;
|
|
}
|