/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* Server side http server script for application update tests.
*/
const { classes: Cc, interfaces: Ci } = Components;
const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data/";
function getTestDataFile(aFilename) {
let file = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties).get("CurWorkD", Ci.nsILocalFile);
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() {
let scriptFile = getTestDataFile("sharedUpdateXML.js");
let io = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService2);
let scriptSpec = io.newFileURI(scriptFile).spec;
let scriptloader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
scriptloader.loadSubScript(scriptSpec, this);
}
loadHelperScript();
const URL_HOST = "http://example.com";
const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs";
const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;
const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR;
const SLOW_MAR_DOWNLOAD_INTERVAL = 100;
var gTimer;
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);
var continueFile = getTestDataFile("continue");
var contents = readFileBytes(getTestDataFile(FILE_SIMPLE_MAR));
gTimer = Cc["@mozilla.org/timer;1"].
createInstance(Ci.nsITimer);
gTimer.initWithCallback(function(aTimer) {
if (continueFile.exists()) {
gTimer.cancel();
aResponse.write(contents);
aResponse.finish();
}
}, SLOW_MAR_DOWNLOAD_INTERVAL, Ci.nsITimer.TYPE_REPEATING_SLACK);
return;
}
if (params.uiURL) {
let remoteType = "";
if (!params.remoteNoTypeAttr && params.uiURL == "BILLBOARD") {
remoteType = " " + params.uiURL.toLowerCase() + "=\"1\"";
}
aResponse.write("
" + params.uiURL +
"
this is a test mar that will not affect your " +
"build.");
return;
}
if (params.xmlMalformed) {
aResponse.write("xml error");
return;
}
if (params.noUpdates) {
aResponse.write(getRemoteUpdatesXMLString(""));
return;
}
if (params.unsupported) {
aResponse.write(getRemoteUpdatesXMLString(" \n"));
return;
}
let size;
let patches = "";
if (!params.partialPatchOnly) {
size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : "");
patches += getRemotePatchString("complete", SERVICE_URL, "SHA512",
SHA512_HASH_SIMPLE_MAR, size);
}
if (!params.completePatchOnly) {
size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : "");
patches += getRemotePatchString("partial", SERVICE_URL, "SHA512",
SHA512_HASH_SIMPLE_MAR, size);
}
let type = params.type ? params.type : "major";
let name = params.name ? params.name : "App Update Test";
let appVersion = params.appVersion ? params.appVersion : "999999.9";
let displayVersion = params.displayVersion ? params.displayVersion
: "version " + appVersion;
let buildID = params.buildID ? params.buildID : "01234567890123";
// XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
// let detailsURL = params.showDetails ? URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS" : null;
let detailsURL = URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS";
let showPrompt = params.showPrompt ? "true" : null;
let showNever = params.showNever ? "true" : null;
let promptWaitTime = params.promptWaitTime ? params.promptWaitTime : null;
let updates = getRemoteUpdateString(patches, type, "App Update Test",
displayVersion, appVersion, buildID,
detailsURL, showPrompt, showNever,
promptWaitTime);
aResponse.write(getRemoteUpdatesXMLString(updates));
}
/**
* 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 "Bad parameter in queryString! '" + paramArray[i] + "'";
}
params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
}
return params;
}
/**
* Reads the binary contents of a file and returns it as a string.
*
* @param aFile
* The file to read from.
* @return The contents of the file as a string.
*/
function readFileBytes(aFile) {
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fis.init(aFile, -1, -1, false);
let bis = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(fis);
let data = [];
let count = fis.available();
while (count > 0) {
let bytes = bis.readByteArray(Math.min(65535, count));
data.push(String.fromCharCode.apply(null, bytes));
count -= bytes.length;
if (bytes.length == 0) {
throw "Nothing read from input stream!";
}
}
data.join('');
fis.close();
return data.toString();
}