187 lines
6.1 KiB
JavaScript
187 lines
6.1 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
let { setTimeout } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Timer.sys.mjs"
|
|
);
|
|
let { NetUtil } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/NetUtil.sys.mjs"
|
|
);
|
|
|
|
/**
|
|
* Provide search suggestions in the OpenSearch JSON format.
|
|
*/
|
|
|
|
function handleRequest(request, response) {
|
|
// Get the query parameters from the query string.
|
|
let query = parseQueryString(request.queryString);
|
|
|
|
function convertToUtf8(str) {
|
|
return String.fromCharCode(...new TextEncoder().encode(str));
|
|
}
|
|
|
|
function writeSuggestions(q, completions = []) {
|
|
let jsonString = JSON.stringify([q, completions]);
|
|
|
|
// This script must be evaluated as UTF-8 for this to write out the bytes of
|
|
// the string in UTF-8. If it's evaluated as Latin-1, the written bytes
|
|
// will be the result of UTF-8-encoding the result-string *twice*, which
|
|
// will break the "I ❤️" case further down.
|
|
let stringOfUtf8Bytes = convertToUtf8(jsonString);
|
|
|
|
response.write(stringOfUtf8Bytes);
|
|
}
|
|
|
|
/**
|
|
* Sends `data` as suggestions directly. This is useful when testing rich
|
|
* suggestions, which do not conform to the object shape sent by
|
|
* writeSuggestions.
|
|
*
|
|
* @param {Array} data The data to send as suggestions.
|
|
*/
|
|
function writeSuggestionsDirectly(data) {
|
|
let jsonString = JSON.stringify(data);
|
|
let stringOfUtf8Bytes = convertToUtf8(jsonString);
|
|
response.setHeader("Content-Type", "application/json", false);
|
|
response.write(stringOfUtf8Bytes);
|
|
}
|
|
|
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
|
|
let q = request.method == "GET" ? query.q : undefined;
|
|
if (q == "cookie") {
|
|
response.setHeader("Set-Cookie", "cookie=1");
|
|
writeSuggestions(q);
|
|
} else if (q == "no remote" || q == "no results") {
|
|
writeSuggestions(q);
|
|
} else if (q == "Query Mismatch") {
|
|
writeSuggestions("This is an incorrect query string", ["some result"]);
|
|
} else if (q == "Query Case Mismatch") {
|
|
writeSuggestions(q.toUpperCase(), [q]);
|
|
} else if (q == "") {
|
|
writeSuggestions("", ["The server should never be sent an empty query"]);
|
|
} else if (q?.startsWith("mo")) {
|
|
writeSuggestions(q, ["Mozilla", "modern", "mom"]);
|
|
} else if (q?.startsWith("I ❤️")) {
|
|
writeSuggestions(q, ["I ❤️ Mozilla"]);
|
|
} else if (q?.startsWith("stü")) {
|
|
writeSuggestions("st\\u00FC", ["stühle", "stüssy"]);
|
|
} else if (q?.startsWith("tailjunk ")) {
|
|
writeSuggestionsDirectly([
|
|
q,
|
|
[q + " normal", q + " tail 1", q + " tail 2"],
|
|
[],
|
|
{
|
|
"google:irrelevantparameter": [],
|
|
"google:badformat": {
|
|
"google:suggestdetail": [
|
|
{},
|
|
{ mp: "… ", t: "tail 1" },
|
|
{ mp: "… ", t: "tail 2" },
|
|
],
|
|
},
|
|
},
|
|
]);
|
|
} else if (q?.startsWith("tailjunk few ")) {
|
|
writeSuggestionsDirectly([
|
|
q,
|
|
[q + " normal", q + " tail 1", q + " tail 2"],
|
|
[],
|
|
{
|
|
"google:irrelevantparameter": [],
|
|
"google:badformat": {
|
|
"google:suggestdetail": [{ mp: "… ", t: "tail 1" }],
|
|
},
|
|
},
|
|
]);
|
|
} else if (q?.startsWith("tailalt ")) {
|
|
writeSuggestionsDirectly([
|
|
q,
|
|
[q + " normal", q + " tail 1", q + " tail 2"],
|
|
{
|
|
"google:suggestdetail": [
|
|
{},
|
|
{ mp: "… ", t: "tail 1" },
|
|
{ mp: "… ", t: "tail 2" },
|
|
],
|
|
},
|
|
]);
|
|
} else if (q?.startsWith("tail ")) {
|
|
writeSuggestionsDirectly([
|
|
q,
|
|
[q + " normal", q + " tail 1", q + " tail 2"],
|
|
[],
|
|
{
|
|
"google:irrelevantparameter": [],
|
|
"google:suggestdetail": [
|
|
{},
|
|
{ mp: "… ", t: "tail 1" },
|
|
{ mp: "… ", t: "tail 2" },
|
|
],
|
|
},
|
|
]);
|
|
} else if (q?.startsWith("richempty ")) {
|
|
writeSuggestionsDirectly([
|
|
q,
|
|
[q + " normal", q + " tail 1", q + " tail 2"],
|
|
[],
|
|
{
|
|
"google:irrelevantparameter": [],
|
|
"google:suggestdetail": [],
|
|
},
|
|
]);
|
|
} else if (q?.startsWith("letter ")) {
|
|
let letters = [];
|
|
for (
|
|
let charCode = "A".charCodeAt();
|
|
charCode <= "Z".charCodeAt();
|
|
charCode++
|
|
) {
|
|
letters.push("letter " + String.fromCharCode(charCode));
|
|
}
|
|
writeSuggestions(q, letters);
|
|
} else if (q?.startsWith("HTTP ")) {
|
|
response.setStatusLine(request.httpVersion, q.replace("HTTP ", ""), q);
|
|
writeSuggestions(q, [q]);
|
|
} else if (q == "invalidJSON") {
|
|
response.setHeader("Content-Type", "application/json", false);
|
|
response.write('["invalid"]');
|
|
} else if (q == "invalidContentType") {
|
|
response.setHeader("Content-Type", "text/xml", false);
|
|
writeSuggestions(q, ["invalidContentType response"]);
|
|
} else if (q?.startsWith("delay")) {
|
|
// Delay the response by delayMs milliseconds. 200ms is the default, less
|
|
// than the timeout but hopefully enough to abort before completion.
|
|
let match = /^delay([0-9]+)/.exec(q);
|
|
let delayMs = match ? parseInt(match[1]) : 200;
|
|
response.processAsync();
|
|
writeSuggestions(q, [q]);
|
|
setTimeout(() => response.finish(), delayMs);
|
|
} else if (q?.startsWith("slow ")) {
|
|
// Delay the response by 10 seconds so the client timeout is reached.
|
|
response.processAsync();
|
|
writeSuggestions(q, [q]);
|
|
setTimeout(() => response.finish(), 10000);
|
|
} else if (request.method == "POST") {
|
|
// This includes headers, not just the body
|
|
let requestText = NetUtil.readInputStreamToString(
|
|
request.bodyInputStream,
|
|
request.bodyInputStream.available()
|
|
);
|
|
// Only use the last line which contains the encoded params
|
|
let requestLines = requestText.split("\n");
|
|
let postParams = parseQueryString(requestLines[requestLines.length - 1]);
|
|
writeSuggestions(postParams.q, ["Mozilla", "modern", "mom"]);
|
|
} else {
|
|
response.setStatusLine(request.httpVersion, 404, "Not Found");
|
|
}
|
|
}
|
|
|
|
function parseQueryString(queryString) {
|
|
let query = {};
|
|
queryString.split("&").forEach(function (val) {
|
|
let [name, value] = val.split("=");
|
|
query[name] = decodeURIComponent(value).replace(/[+]/g, " ");
|
|
});
|
|
return query;
|
|
}
|