diff options
Diffstat (limited to 'browser/components/translation/test/yandex.sjs')
-rw-r--r-- | browser/components/translation/test/yandex.sjs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/browser/components/translation/test/yandex.sjs b/browser/components/translation/test/yandex.sjs new file mode 100644 index 000000000..ed515beab --- /dev/null +++ b/browser/components/translation/test/yandex.sjs @@ -0,0 +1,199 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, Constructor: CC} = Components; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); + +function handleRequest(req, res) { + try { + reallyHandleRequest(req, res); + } catch (ex) { + res.setStatusLine("1.0", 200, "AlmostOK"); + let msg = "Error handling request: " + ex + "\n" + ex.stack; + log(msg); + res.write(msg); + } +} + +function log(msg) { + dump("YANDEX-SERVER-MOCK: " + msg + "\n"); +} + +const statusCodes = { + 400: "Bad Request", + 401: "Invalid API key", + 402: "This API key has been blocked", + 403: "Daily limit for requests reached", + 404: "Daily limit for chars reached", + 413: "The text size exceeds the maximum", + 422: "The text could not be translated", + 500: "Internal Server Error", + 501: "The specified translation direction is not supported", + 503: "Service Unavailable" +}; + +function HTTPError(code = 500, message) { + this.code = code; + this.name = statusCodes[code] || "HTTPError"; + this.message = message || this.name; +} +HTTPError.prototype = new Error(); +HTTPError.prototype.constructor = HTTPError; + +function sendError(res, err) { + if (!(err instanceof HTTPError)) { + err = new HTTPError(typeof err == "number" ? err : 500, + err.message || typeof err == "string" ? err : ""); + } + res.setStatusLine("1.1", err.code, err.name); + res.write(err.message); +} + +// Based on the code borrowed from: +// http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript +function parseQuery(query) { + let match, + params = {}, + pl = /\+/g, + search = /([^&=]+)=?([^&]*)/g, + decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }; + + while (match = search.exec(query)) { + let k = decode(match[1]), + v = decode(match[2]); + if (k in params) { + if(params[k] instanceof Array) + params[k].push(v); + else + params[k] = [params[k], v]; + } else { + params[k] = v; + } + } + + return params; +} + +function sha1(str) { + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + // `result` is an out parameter, `result.value` will contain the array length. + let result = {}; + // `data` is an array of bytes. + let data = converter.convertToByteArray(str, result); + let ch = Cc["@mozilla.org/security/hash;1"] + .createInstance(Ci.nsICryptoHash); + ch.init(ch.SHA1); + ch.update(data, data.length); + let hash = ch.finish(false); + + // Return the two-digit hexadecimal code for a byte. + function toHexString(charCode) { + return ("0" + charCode.toString(16)).slice(-2); + } + + // Convert the binary hash data to a hex string. + return Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join(""); +} + +function getRequestBody(req) { + let avail; + let bytes = []; + let body = new BinaryInputStream(req.bodyInputStream); + + while ((avail = body.available()) > 0) + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + + return String.fromCharCode.apply(null, bytes); +} + +function getInputStream(path) { + let file = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsILocalFile); + for (let part of path.split("/")) + file.append(part); + let fileStream = Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Ci.nsIFileInputStream); + fileStream.init(file, 1, 0, false); + return fileStream; +} + +/** + * Yandex Requests have to be signed with an API Key. This mock server + * supports the following keys: + * + * yandexValidKey Always passes the authentication, + * yandexInvalidKey Never passes authentication and fails with 401 code, + * yandexBlockedKey Never passes authentication and fails with 402 code, + * yandexOutOfRequestsKey Never passes authentication and fails with 403 code, + * yandexOutOfCharsKey Never passes authentication and fails with 404 code. + * + * If any other key is used the server reponds with 401 error code. + */ +function checkAuth(params) { + if(!("key" in params)) + throw new HTTPError(400); + + let key = params.key; + if(key === "yandexValidKey") + return true; + + let invalidKeys = { + "yandexInvalidKey" : 401, + "yandexBlockedKey" : 402, + "yandexOutOfRequestsKey" : 403, + "yandexOutOfCharsKey" : 404, + }; + + if(key in invalidKeys) + throw new HTTPError(invalidKeys[key]); + + throw new HTTPError(401); +} + +function reallyHandleRequest(req, res) { + + try { + + // Preparing the query parameters. + let params = {}; + if(req.method == 'POST') { + params = parseQuery(getRequestBody(req)); + } + + // Extracting the API key and attempting to authenticate the request. + log(JSON.stringify(params)); + + checkAuth(params); + methodHandlers['translate'](res, params); + + } catch (ex) { + sendError(res, ex, ex.code); + } + +} + +const methodHandlers = { + translate: function(res, params) { + res.setStatusLine("1.1", 200, "OK"); + res.setHeader("Content-Type", "application/json"); + + let hash = sha1(JSON.stringify(params)).substr(0, 10); + log("SHA1 hash of content: " + hash); + + let fixture = "browser/browser/components/translation/test/fixtures/result-yandex-" + hash + ".json"; + log("PATH: " + fixture); + + let inputStream = getInputStream(fixture); + res.bodyOutputStream.writeFrom(inputStream, inputStream.available()); + inputStream.close(); + } + +}; |