From fce58722c8dedaaab0376c1b8c022e5263bac7a3 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 12 Apr 2015 19:53:34 +0200 Subject: [PATCH 01/47] start work on #45 --- lib/routes/avatars.js | 83 +++++++++++++------------------------------ lib/server.js | 12 +++++-- 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index de47ed3..21b5ec2 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -12,81 +12,48 @@ var human_status = { "-1": "error" }; +function handle_default(http_status, img_status, userId, size, def, callback) { + if (def && def !== "steve" && def !== "alex") { + callback(http_status, img_status, def); + } else { + def = def || skins.default_skin(userId); + skins.resize_img("public/images/" + def + ".png", size, function(err, image) { + callback(http_status, img_status, image); + }); + } +} + // GET avatar request -module.exports = function(req, res) { - var start = new Date(); +module.exports = function(req, callback) { var userId = (req.url.path_list[2] || "").split(".")[0]; var size = parseInt(req.url.query.size) || config.default_size; var def = req.url.query.default; var helm = req.url.query.hasOwnProperty("helm"); var etag = null; - var rid = req.id; - - function sendimage(rid, http_status, img_status, image) { - logging.log(rid, "status:", http_status); - res.writeHead(http_status, { - "Content-Type": "image/png", - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": human_status[img_status], - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Etag": '"' + etag + '"' - }); - res.end(http_status === 304 ? null : image); - } - - function handle_default(rid, http_status, img_status, userId) { - if (def && def !== "steve" && def !== "alex") { - logging.log(rid, "status: 301"); - res.writeHead(301, { - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": human_status[img_status], - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Location": def - }); - res.end(); - } else { - def = def || skins.default_skin(userId); - skins.resize_img("public/images/" + def + ".png", size, function(err, image) { - sendimage(rid, http_status, img_status, image); - }); - } - } // Prevent app from crashing/freezing if (size < config.min_size || size > config.max_size) { // "Unprocessable Entity", valid request, but semantically erroneous: // https://tools.ietf.org/html/rfc4918#page-78 - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start - }); - res.end("Invalid Size"); + callback(422, 0, "Invalid Size"); return; } else if (!helpers.id_valid(userId)) { - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start - }); - res.end("Invalid ID"); + callback(422, 0, "Invalid ID"); return; } // strip dashes userId = userId.replace(/-/g, ""); - logging.debug(rid, "userid:", userId); + logging.debug(req.id, "userid:", userId); try { - helpers.get_avatar(rid, userId, helm, size, function(err, status, image, hash) { - logging.log(rid, "storage type:", human_status[status]); + helpers.get_avatar(req.id, userId, helm, size, function(err, status, image, hash) { + logging.log(req.id, "storage type:", human_status[status]); if (err) { - logging.error(rid, err); + logging.error(req.id, err); if (err.code === "ENOENT") { // no such file - cache.remove_hash(rid, userId); + cache.remove_hash(req.id, userId); } } etag = image && hash && hash.substr(0, 32) || "none"; @@ -96,15 +63,15 @@ module.exports = function(req, res) { if (err) { http_status = 503; } - logging.debug(rid, "etag:", req.headers["if-none-match"]); - logging.debug(rid, "matches:", matches); - sendimage(rid, matches ? 304 : http_status, status, image); + logging.debug(req.id, "etag:", req.headers["if-none-match"]); + logging.debug(req.id, "matches:", matches); + callback(matches ? 304 : http_status, status, image); } else { - handle_default(rid, matches ? 304 : 200, status, userId); + handle_default(matches ? 304 : 200, status, userId, size, def, callback); } }); } catch(e) { - logging.error(rid, "error:", e.stack); - handle_default(rid, 500, -1, userId); + logging.error(req.id, "error:", e.stack); + handle_default(500, -1, userId, size, def, callback); } }; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index b00f7f7..d414b25 100644 --- a/lib/server.js +++ b/lib/server.js @@ -21,7 +21,10 @@ function asset_request(req, res) { var filename = path.join(__dirname, "public", req.url.path_list.join("/")); fs.exists(filename, function(exists) { if (exists) { - res.writeHead(200, { "Content-type": mime.lookup(filename) }); + res.writeHead(200, { + "Content-type": mime.lookup(filename), + "Cache-Control": "max-age=7200, public", // cache for 2 hours + }); fs.createReadStream(filename).pipe(res); } else { res.writeHead(404, { @@ -48,6 +51,8 @@ function requestHandler(req, res) { // generate 12 character random string request.id = Math.random().toString(36).substring(2, 14); + res.start = new Date(); + var local_path = request.url.path_list[1]; logging.log(request.id, request.method, request.url.href); if (request.method === "GET" || request.method === "HEAD") { @@ -57,7 +62,10 @@ function requestHandler(req, res) { routes.index(request, res); break; case "avatars": - routes.avatars(request, res); + routes.avatars(request, function(http_status, img_status, body) { + res.writeHead(http_status, {}); + res.end(body); + }); break; case "skins": routes.skins(request, res); From 3cbf73b0d751e32ef190afb6fba1dbc3ef071c7f Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 00:41:11 +0200 Subject: [PATCH 02/47] create new response module & use it for avatars --- lib/response.js | 86 +++++++++++++++++++++++++++++++++++++++++++ lib/routes/avatars.js | 57 ++++++++++++++-------------- lib/routes/renders.js | 5 ++- lib/server.js | 57 ++++++++++++++++------------ lib/views/index.jade | 5 ++- 5 files changed, 157 insertions(+), 53 deletions(-) create mode 100644 lib/response.js diff --git a/lib/response.js b/lib/response.js new file mode 100644 index 0000000..8344642 --- /dev/null +++ b/lib/response.js @@ -0,0 +1,86 @@ +var logging = require("./logging"); +var config = require("./config"); +var crc = require("crc").crc32; + +var human_status = { + "-2": "user error", + "-1": "server error", + 0: "none", + 1: "cached", + 2: "downloaded", + 3: "checked", +}; + + +// handles HTTP responses +// +request+ a http.IncomingMessage +// +response+ a http.ServerResponse +// +result+ an object with: +// * status: see human_status, required +// * redirect: redirect URL +// * body: file or message, required unless redirect is present or status is < 0 +// * type: a valid Content-Type, required if body is present +// * hash: image hash, required when body is an image +// * err: a possible Error +module.exports = function(request, response, result) { + + response.on("close", function() { + logging.warn(request.id, "Connection closed"); + }); + + response.on("finish", function() { + logging.log(request.id, response.statusCode, "(" + human_status[result.status] + ")"); + }); + + response.on("error", function(err) { + logging.error(request.id, err); + }); + + // These headers are the same for every response + var headers = { + "Content-Type": result.type || "text/plain", + "Cache-Control": "max-age=" + config.browser_cache_time + ", public", + "Response-Time": Date.now() - request.start, + "X-Storage-Type": human_status[result.status], + "X-Request-ID": request.id, + "Access-Control-Allow-Origin": "*" + }; + + if (result.err) { + logging.error(result.err); + } + + if (result.body) { + // use Mojang's image hash if available + // use crc32 as a hash function otherwise + var etag = result.body && result.hash && result.hash.substr(0, 10) || crc(result.body); + headers.Etag = "\"" + etag + "\""; + + // handle etag caching + var incoming_etag = request.headers["if-none-match"]; + if (incoming_etag && incoming_etag === headers.Etag) { + logging.debug("Etag matches"); + response.writeHead(304, headers); + response.end(); + return; + } + } + + if (result.redirect) { + headers.Location = result.redirect; + response.writeHead(307, headers); + response.end(); + return; + } + + if (result.status === -2) { + response.writeHead(422, headers); + response.end(result.body); + } else if (result.status === -1) { + response.writeHead(500, headers); + response.end(result.body); + } else { + response.writeHead(200, headers); + response.end(result.body); + } +}; \ No newline at end of file diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index 21b5ec2..36bf6dd 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -3,22 +3,23 @@ var helpers = require("../helpers"); var config = require("../config"); var skins = require("../skins"); var cache = require("../cache"); +var path = require("path"); -var human_status = { - 0: "none", - 1: "cached", - 2: "downloaded", - 3: "checked", - "-1": "error" -}; - -function handle_default(http_status, img_status, userId, size, def, callback) { +function handle_default(img_status, userId, size, def, callback) { if (def && def !== "steve" && def !== "alex") { - callback(http_status, img_status, def); + callback({ + status: img_status, + redirect: def + }); } else { def = def || skins.default_skin(userId); - skins.resize_img("public/images/" + def + ".png", size, function(err, image) { - callback(http_status, img_status, image); + skins.resize_img(path.join(__dirname, "..", "public", "images", def + ".png"), size, function(err, image) { + callback({ + status: img_status, + body: image, + type: "image/png", + err: err + }); }); } } @@ -29,16 +30,21 @@ module.exports = function(req, callback) { var size = parseInt(req.url.query.size) || config.default_size; var def = req.url.query.default; var helm = req.url.query.hasOwnProperty("helm"); - var etag = null; // Prevent app from crashing/freezing if (size < config.min_size || size > config.max_size) { // "Unprocessable Entity", valid request, but semantically erroneous: // https://tools.ietf.org/html/rfc4918#page-78 - callback(422, 0, "Invalid Size"); + callback({ + status: -2, + body: "Invalid Size" + }); return; } else if (!helpers.id_valid(userId)) { - callback(422, 0, "Invalid ID"); + callback({ + status: -2, + body: "Invalid userid" + }); return; } @@ -48,7 +54,6 @@ module.exports = function(req, callback) { try { helpers.get_avatar(req.id, userId, helm, size, function(err, status, image, hash) { - logging.log(req.id, "storage type:", human_status[status]); if (err) { logging.error(req.id, err); if (err.code === "ENOENT") { @@ -56,22 +61,20 @@ module.exports = function(req, callback) { cache.remove_hash(req.id, userId); } } - etag = image && hash && hash.substr(0, 32) || "none"; - var matches = req.headers["if-none-match"] === '"' + etag + '"'; if (image) { - var http_status = 200; - if (err) { - http_status = 503; - } - logging.debug(req.id, "etag:", req.headers["if-none-match"]); - logging.debug(req.id, "matches:", matches); - callback(matches ? 304 : http_status, status, image); + callback({ + status: status, + body: image, + type: "image/png", + err: err, + hash: hash + }); } else { - handle_default(matches ? 304 : 200, status, userId, size, def, callback); + handle_default(status, userId, size, def, callback); } }); } catch(e) { logging.error(req.id, "error:", e.stack); - handle_default(500, -1, userId, size, def, callback); + handle_default(-1, userId, size, def, callback); } }; \ No newline at end of file diff --git a/lib/routes/renders.js b/lib/routes/renders.js index 7c2853d..8d0b6ba 100644 --- a/lib/routes/renders.js +++ b/lib/routes/renders.js @@ -1,9 +1,10 @@ var logging = require("../logging"); var helpers = require("../helpers"); +var renders = require("../renders"); var config = require("../config"); var cache = require("../cache"); var skins = require("../skins"); -var renders = require("../renders"); +var path = require("path"); var fs = require("fs"); var human_status = { @@ -71,7 +72,7 @@ module.exports = function(req, res) { res.end(); } else { def = def || skins.default_skin(userId); - fs.readFile("public/images/" + def + "_skin.png", function (err, buf) { + fs.readFile(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function (err, buf) { if (err) { // errored while loading the default image, continuing with null image logging.error(rid, "error loading default render image:", err); diff --git a/lib/server.js b/lib/server.js index d414b25..732b785 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,6 +1,7 @@ #!/usr/bin/env node var logging = require("./logging"); var querystring = require("querystring"); +var response = require("./response"); var config = require("./config"); var http = require("http"); var mime = require("mime"); @@ -35,53 +36,63 @@ function asset_request(req, res) { }); } -function requestHandler(req, res) { - var request = req; - request.url = url.parse(req.url, true); - request.url.query = request.url.query || {}; +// generates a 12 character random string +function request_id() { + return Math.random().toString(36).substring(2, 14); +} +// splits a URL path into an Array +// the path is resolved and decoded +function path_list(pathname) { // remove trailing and double slashes + other junk - var path_list = request.url.pathname.split("/"); - for (var i = 0; i < path_list.length; i++) { + + // FIXME: also accepts relative paths? + + pathname = path.resolve(pathname); + var list = pathname.split("/"); + for (var i = 0; i < list.length; i++) { // URL decode - path_list[i] = querystring.unescape(path_list[i]); + list[i] = querystring.unescape(list[i]); } - request.url.path_list = path_list; + return list; +} - // generate 12 character random string - request.id = Math.random().toString(36).substring(2, 14); +function requestHandler(req, res) { + req.url = url.parse(req.url, true); + req.url.query = req.url.query || {}; + req.url.path_list = path_list(req.url.pathname); - res.start = new Date(); + req.id = request_id(); + req.start = Date.now(); - var local_path = request.url.path_list[1]; - logging.log(request.id, request.method, request.url.href); - if (request.method === "GET" || request.method === "HEAD") { + var local_path = req.url.path_list[1]; + logging.log(req.id, req.method, req.url.href); + if (req.method === "GET" || req.method === "HEAD") { try { switch (local_path) { case "": - routes.index(request, res); + routes.index(req, res); break; case "avatars": - routes.avatars(request, function(http_status, img_status, body) { - res.writeHead(http_status, {}); - res.end(body); + routes.avatars(req, function(result) { + response(req, res, result); }); break; case "skins": - routes.skins(request, res); + routes.skins(req, res); break; case "renders": - routes.renders(request, res); + routes.renders(req, res); break; case "capes": - routes.capes(request, res); + routes.capes(req, res); break; default: - asset_request(request, res); + asset_request(req, res); } } catch(e) { var error = JSON.stringify(req.headers) + "\n" + e.stack; - logging.error(request.id + "Error:", error); + logging.error(req.id + "Error:", error); res.writeHead(500, { "Content-Type": "text/plain" }); diff --git a/lib/views/index.jade b/lib/views/index.jade index 65db2aa..4e2359a 100644 --- a/lib/views/index.jade +++ b/lib/views/index.jade @@ -287,8 +287,11 @@ block content | This happens either when the user removed their skin or when it didn't change. li downloaded: 2 external requests. Skin changed or unknown, downloaded. li - | error: This can happen, for example, when Mojang's servers are down.
+ | server error: This can happen, for example, when Mojang's servers are down.
| If possible, an outdated image is served instead. + li + | user error: You have done something wrong, such as requesting a malformed userid.
+ | Check the response body for details. section a(id="meta-x-request-id" class="anchor") a(href="#meta-x-request-id") From 71a13c3bab2b3bb75b4a1ba904a26df737217bb8 Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 00:46:03 +0200 Subject: [PATCH 03/47] add missing src dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7473c2a..f6d8512 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "mime": "1.3.4", "node-df": "0.1.1", "redis": "0.12.1", - "request": "^2.51.0" + "request": "^2.51.0", + "crc": "3.2.1" }, "devDependencies": { "coveralls": "^2.11.2", From 0b687d8f8edde933edbe241b53e68710e613b171 Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 00:52:08 +0200 Subject: [PATCH 04/47] correct comment about result.type --- lib/response.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/response.js b/lib/response.js index 8344642..4cb3f13 100644 --- a/lib/response.js +++ b/lib/response.js @@ -19,7 +19,7 @@ var human_status = { // * status: see human_status, required // * redirect: redirect URL // * body: file or message, required unless redirect is present or status is < 0 -// * type: a valid Content-Type, required if body is present +// * type: a valid Content-Type for the body, defaults to "text/plain" // * hash: image hash, required when body is an image // * err: a possible Error module.exports = function(request, response, result) { From d025a3004d650b7b7ba3baddff9a9ffbb4c43e9f Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 01:10:39 +0200 Subject: [PATCH 05/47] remove always-empty first entry in req.url.path_list rhyme pro :sunglasses: --- lib/routes/avatars.js | 2 +- lib/routes/renders.js | 4 ++-- lib/routes/skins.js | 2 +- lib/server.js | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index 36bf6dd..b13b6ed 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -26,7 +26,7 @@ function handle_default(img_status, userId, size, def, callback) { // GET avatar request module.exports = function(req, callback) { - var userId = (req.url.path_list[2] || "").split(".")[0]; + var userId = (req.url.path_list[1] || "").split(".")[0]; var size = parseInt(req.url.query.size) || config.default_size; var def = req.url.query.default; var helm = req.url.query.hasOwnProperty("helm"); diff --git a/lib/routes/renders.js b/lib/routes/renders.js index 8d0b6ba..2b7ab26 100644 --- a/lib/routes/renders.js +++ b/lib/routes/renders.js @@ -22,7 +22,7 @@ var human_status = { // GET render request module.exports = function(req, res) { var start = new Date(); - var raw_type = (req.url.path_list[2] || ""); + var raw_type = (req.url.path_list[1] || ""); var rid = req.id; // validate type @@ -36,7 +36,7 @@ module.exports = function(req, res) { } var body = raw_type === "body"; - var userId = (req.url.path_list[3] || "").split(".")[0]; + var userId = (req.url.path_list[2] || "").split(".")[0]; var def = req.url.query.default; var scale = parseInt(req.url.query.scale) || config.default_scale; var helm = req.url.query.hasOwnProperty("helm"); diff --git a/lib/routes/skins.js b/lib/routes/skins.js index 9cb576c..6fd2dec 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -8,7 +8,7 @@ var lwip = require("lwip"); // GET skin request module.exports = function(req, res) { var start = new Date(); - var userId = (req.url.path_list[2] || "").split(".")[0]; + var userId = (req.url.path_list[1] || "").split(".")[0]; var def = req.url.query.default; var etag = null; var rid = req.id; diff --git a/lib/server.js b/lib/server.js index 732b785..3594f5e 100644 --- a/lib/server.js +++ b/lib/server.js @@ -46,14 +46,14 @@ function request_id() { function path_list(pathname) { // remove trailing and double slashes + other junk - // FIXME: also accepts relative paths? - pathname = path.resolve(pathname); var list = pathname.split("/"); + list.shift(); for (var i = 0; i < list.length; i++) { // URL decode list[i] = querystring.unescape(list[i]); } + logging.debug("path:", list); return list; } @@ -65,8 +65,9 @@ function requestHandler(req, res) { req.id = request_id(); req.start = Date.now(); - var local_path = req.url.path_list[1]; + var local_path = req.url.path_list[0]; logging.log(req.id, req.method, req.url.href); + logging.debug(req.id, req.url.path_list); if (req.method === "GET" || req.method === "HEAD") { try { switch (local_path) { From 8dc55442b174a94dbe2090f445cc696739f2d910 Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 01:12:16 +0200 Subject: [PATCH 06/47] remove debug log --- lib/server.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index 3594f5e..605b338 100644 --- a/lib/server.js +++ b/lib/server.js @@ -53,7 +53,6 @@ function path_list(pathname) { // URL decode list[i] = querystring.unescape(list[i]); } - logging.debug("path:", list); return list; } @@ -67,7 +66,6 @@ function requestHandler(req, res) { var local_path = req.url.path_list[0]; logging.log(req.id, req.method, req.url.href); - logging.debug(req.id, req.url.path_list); if (req.method === "GET" || req.method === "HEAD") { try { switch (local_path) { From 0b0882e63d1add6e1bec4e0672cfd8e6445185cc Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 22:19:57 +0200 Subject: [PATCH 07/47] use new response module for index --- lib/response.js | 7 +++++-- lib/routes/index.js | 11 +++++------ lib/server.js | 4 +++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/response.js b/lib/response.js index 4cb3f13..0d780ad 100644 --- a/lib/response.js +++ b/lib/response.js @@ -16,7 +16,7 @@ var human_status = { // +request+ a http.IncomingMessage // +response+ a http.ServerResponse // +result+ an object with: -// * status: see human_status, required +// * status: see human_status, required for images // * redirect: redirect URL // * body: file or message, required unless redirect is present or status is < 0 // * type: a valid Content-Type for the body, defaults to "text/plain" @@ -41,11 +41,14 @@ module.exports = function(request, response, result) { "Content-Type": result.type || "text/plain", "Cache-Control": "max-age=" + config.browser_cache_time + ", public", "Response-Time": Date.now() - request.start, - "X-Storage-Type": human_status[result.status], "X-Request-ID": request.id, "Access-Control-Allow-Origin": "*" }; + if (result.status) { + headers["X-Storage-Type"] = human_status[result.status]; + } + if (result.err) { logging.error(result.err); } diff --git a/lib/routes/index.js b/lib/routes/index.js index 3a2498a..7ffbcb2 100644 --- a/lib/routes/index.js +++ b/lib/routes/index.js @@ -3,17 +3,16 @@ var path = require("path"); var jade = require("jade"); // compile jade -var index = jade.compileFile(path.join(__dirname, "../views/index.jade")); +var index = jade.compileFile(path.join(__dirname, "..", "views", "index.jade")); -module.exports = function(req, res) { +module.exports = function(req, callback) { var html = index({ title: "Crafatar", domain: "https://" + req.headers.host, config: config }); - res.writeHead(200, { - "Content-Length": Buffer.byteLength(html, "UTF-8"), - "Content-Type": "text/html; charset=utf-8" + callback({ + body: html, + type: "text/html; charset=utf-8" }); - res.end(html); }; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 605b338..66a4d12 100644 --- a/lib/server.js +++ b/lib/server.js @@ -70,7 +70,9 @@ function requestHandler(req, res) { try { switch (local_path) { case "": - routes.index(req, res); + routes.index(req, function(result) { + response(req, res, result); + }); break; case "avatars": routes.avatars(req, function(result) { From a947b02c87d64ddba4e979218859ccfaeb1cb4cb Mon Sep 17 00:00:00 2001 From: jomo Date: Mon, 20 Apr 2015 23:18:27 +0200 Subject: [PATCH 08/47] use new response module for skins --- lib/response.js | 14 +++--- lib/routes/avatars.js | 13 +++--- lib/routes/skins.js | 100 +++++++++++++++++------------------------- lib/server.js | 4 +- 4 files changed, 58 insertions(+), 73 deletions(-) diff --git a/lib/response.js b/lib/response.js index 0d780ad..08c156c 100644 --- a/lib/response.js +++ b/lib/response.js @@ -16,7 +16,7 @@ var human_status = { // +request+ a http.IncomingMessage // +response+ a http.ServerResponse // +result+ an object with: -// * status: see human_status, required for images +// * status: see human_status, required for images without err // * redirect: redirect URL // * body: file or message, required unless redirect is present or status is < 0 // * type: a valid Content-Type for the body, defaults to "text/plain" @@ -38,19 +38,21 @@ module.exports = function(request, response, result) { // These headers are the same for every response var headers = { - "Content-Type": result.type || "text/plain", + "Content-Type": (result.body && result.type) || "text/plain", "Cache-Control": "max-age=" + config.browser_cache_time + ", public", "Response-Time": Date.now() - request.start, "X-Request-ID": request.id, "Access-Control-Allow-Origin": "*" }; - if (result.status) { - headers["X-Storage-Type"] = human_status[result.status]; - } - if (result.err) { logging.error(result.err); + logging.error(result.err.stack); + result.status = -1; + } + + if (result.status !== undefined) { + headers["X-Storage-Type"] = human_status[result.status]; } if (result.body) { diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index b13b6ed..7b4f23d 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -5,20 +5,21 @@ var skins = require("../skins"); var cache = require("../cache"); var path = require("path"); -function handle_default(img_status, userId, size, def, callback) { +function handle_default(img_status, userId, size, def, err, callback) { if (def && def !== "steve" && def !== "alex") { callback({ status: img_status, - redirect: def + redirect: def, + err: err }); } else { def = def || skins.default_skin(userId); - skins.resize_img(path.join(__dirname, "..", "public", "images", def + ".png"), size, function(err, image) { + skins.resize_img(path.join(__dirname, "..", "public", "images", def + ".png"), size, function(resize_err, image) { callback({ status: img_status, body: image, type: "image/png", - err: err + err: resize_err || err }); }); } @@ -70,11 +71,11 @@ module.exports = function(req, callback) { hash: hash }); } else { - handle_default(status, userId, size, def, callback); + handle_default(status, userId, size, def, err, callback); } }); } catch(e) { logging.error(req.id, "error:", e.stack); - handle_default(-1, userId, size, def, callback); + handle_default(-1, userId, size, def, e, callback); } }; \ No newline at end of file diff --git a/lib/routes/skins.js b/lib/routes/skins.js index 6fd2dec..cd1ac9d 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -1,62 +1,49 @@ var logging = require("../logging"); var helpers = require("../helpers"); -var config = require("../config"); var skins = require("../skins"); var path = require("path"); var lwip = require("lwip"); +function handle_default(img_status, userId, def, err, callback) { + if (def && def !== "steve" && def !== "alex") { + callback({ + status: img_status, + redirect: def, + err: err + }); + } else { + def = def || skins.default_skin(userId); + lwip.open(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function(lwip_err, image) { + if (image) { + image.toBuffer("png", function(buf_err, buffer) { + callback({ + status: img_status, + body: buffer, + type: "image/png", + err: buf_err || lwip_err || err + }); + }); + } else { + callback({ + status: -1, + err: lwip_err || err + }); + } + }); + } +} + // GET skin request -module.exports = function(req, res) { - var start = new Date(); +module.exports = function(req, callback) { var userId = (req.url.path_list[1] || "").split(".")[0]; var def = req.url.query.default; - var etag = null; var rid = req.id; - function sendimage(rid, http_status, image) { - logging.log(rid, "status:", http_status); - res.writeHead(http_status, { - "Content-Type": "image/png", - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": "downloaded", - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Etag": '"' + etag + '"' - }); - res.end(http_status === 304 ? null : image); - } - - function handle_default(rid, http_status, userId) { - if (def && def !== "steve" && def !== "alex") { - logging.log(rid, "status: 301"); - res.writeHead(301, { - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": "downloaded", - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Location": def - }); - res.end(); - } else { - def = def || skins.default_skin(userId); - lwip.open(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function(err, image) { - // FIXME: err is not handled - image.toBuffer("png", function(buf_err, buffer) { - // FIXME: buf_err is not handled - sendimage(rid, http_status, buffer); - }); - }); - } - } - if (!helpers.id_valid(userId)) { - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start + callback({ + status: -2, + body: "Invalid userid" }); - res.end("Invalid ID"); return; } @@ -66,25 +53,18 @@ module.exports = function(req, res) { try { helpers.get_skin(rid, userId, function(err, hash, image) { - if (err) { - logging.error(rid, err); - } - etag = hash && hash.substr(0, 32) || "none"; - var matches = req.headers["if-none-match"] === '"' + etag + '"'; if (image) { - var http_status = 200; - if (err) { - http_status = 503; - } - logging.debug(rid, "etag:", req.headers["if-none-match"]); - logging.debug(rid, "matches:", matches); - sendimage(rid, matches ? 304 : http_status, image); + callback({ + body: image, + type: "image/png", + err: err + }); } else { - handle_default(rid, 200, userId); + handle_default(2, userId, def, err, callback); } }); } catch(e) { logging.error(rid, "error:", e.stack); - handle_default(rid, 500, userId); + handle_default(-1, userId, def, e, callback); } }; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index 66a4d12..9ac0097 100644 --- a/lib/server.js +++ b/lib/server.js @@ -80,7 +80,9 @@ function requestHandler(req, res) { }); break; case "skins": - routes.skins(req, res); + routes.skins(req, function(result) { + response(req, res, result); + }); break; case "renders": routes.renders(req, res); From 8971e3c02b19d98c5e3eba67bb4524a5f50d02f6 Mon Sep 17 00:00:00 2001 From: jomo Date: Tue, 21 Apr 2015 00:57:14 +0200 Subject: [PATCH 09/47] use new response module for renders + bug fixes --- lib/helpers.js | 39 +++++++------ lib/response.js | 14 ++--- lib/routes/avatars.js | 1 + lib/routes/renders.js | 133 +++++++++++++++--------------------------- lib/routes/skins.js | 5 +- lib/server.js | 4 +- test/test.js | 2 +- 7 files changed, 85 insertions(+), 113 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index de65da9..e55a872 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -286,23 +286,26 @@ exp.get_avatar = function(rid, userId, helm, size, callback) { }; // handles requests for +userId+ skins -// callback: error, skin hash, image buffer +// callback: error, skin hash, status, image buffer exp.get_skin = function(rid, userId, callback) { exp.get_image_hash(rid, userId, "skin", function(err, status, skin_hash) { - // FIXME: err is not used / handled - var skinpath = path.join(__dirname, "..", config.skins_dir, skin_hash + ".png"); - fs.exists(skinpath, function(exists) { - if (exists) { - logging.log(rid, "skin already exists, not downloading"); - skins.open_skin(rid, skinpath, function(skin_err, img) { - callback(skin_err, skin_hash, img); - }); - } else { - networking.save_texture(rid, skin_hash, skinpath, function(net_err, response, img) { - callback(net_err, skin_hash, img); - }); - } - }); + if (skin_hash) { + var skinpath = path.join(__dirname, "..", config.skins_dir, skin_hash + ".png"); + fs.exists(skinpath, function(exists) { + if (exists) { + logging.log(rid, "skin already exists, not downloading"); + skins.open_skin(rid, skinpath, function(skin_err, img) { + callback(skin_err || err, skin_hash, status, img); + }); + } else { + networking.save_texture(rid, skin_hash, skinpath, function(net_err, response, img) { + callback(net_err || err, skin_hash, status, img); + }); + } + }); + } else { + callback(err, null, status, null); + } }); }; @@ -317,9 +320,9 @@ function get_type(helm, body) { // handles creations of 3D renders // callback: error, skin hash, image buffer exp.get_render = function(rid, userId, scale, helm, body, callback) { - exp.get_skin(rid, userId, function(err, skin_hash, img) { + exp.get_skin(rid, userId, function(err, skin_hash, status, img) { if (!skin_hash) { - callback(err, -1, skin_hash, null); + callback(err, status, skin_hash, null); return; } var renderpath = path.join(__dirname, "..", config.renders_dir, [skin_hash, scale, get_type(helm, body)].join("-") + ".png"); @@ -344,7 +347,7 @@ exp.get_render = function(rid, userId, scale, helm, body, callback) { if (fs_err) { logging.error(rid, fs_err.stack); } - callback(null, 2, skin_hash, img); + callback(null, 2, skin_hash, drawn_img); }); } }); diff --git a/lib/response.js b/lib/response.js index 08c156c..7172c46 100644 --- a/lib/response.js +++ b/lib/response.js @@ -3,12 +3,12 @@ var config = require("./config"); var crc = require("crc").crc32; var human_status = { - "-2": "user error", - "-1": "server error", - 0: "none", - 1: "cached", - 2: "downloaded", - 3: "checked", + "-2": "user error", // e.g. invalid size + "-1": "server error", // e.g. network issues + 0: "none", // cached as null (user has no skin) + 1: "cached", // found on disk + 2: "downloaded", // profile downloaded, skin downloaded from mojang servers + 3: "checked", // profile re-downloaded (was too old), has no skin or skin cached }; @@ -64,7 +64,7 @@ module.exports = function(request, response, result) { // handle etag caching var incoming_etag = request.headers["if-none-match"]; if (incoming_etag && incoming_etag === headers.Etag) { - logging.debug("Etag matches"); + logging.debug(request.id, "Etag matches"); response.writeHead(304, headers); response.end(); return; diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index 7b4f23d..259542d 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -19,6 +19,7 @@ function handle_default(img_status, userId, size, def, err, callback) { status: img_status, body: image, type: "image/png", + hash: def, err: resize_err || err }); }); diff --git a/lib/routes/renders.js b/lib/routes/renders.js index 2b7ab26..3e9fcc2 100644 --- a/lib/routes/renders.js +++ b/lib/routes/renders.js @@ -7,100 +7,66 @@ var skins = require("../skins"); var path = require("path"); var fs = require("fs"); -var human_status = { - 0: "none", - 1: "cached", - 2: "downloaded", - 3: "checked", - "-1": "error" -}; - // valid types: head, body // helmet is query param // TODO: The Type logic should be two separate GET functions once response methods are extracted +// default alex/steve images can be rendered, but +// custom images will not be +function handle_default(rid, scale, helm, body, img_status, userId, size, def, err, callback) { + if (def && def !== "steve" && def !== "alex") { + callback({ + status: img_status, + redirect: def, + err: err + }); + } else { + def = def || skins.default_skin(userId); + fs.readFile(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function (fs_err, buf) { + // we render the default skins, but not custom images + renders.draw_model(rid, buf, scale, helm, body, function(render_err, def_img) { + callback({ + status: img_status, + body: def_img, + type: "image/png", + hash: def, + err: render_err || fs_err || err + }); + }); + }); + } +} + // GET render request -module.exports = function(req, res) { - var start = new Date(); +module.exports = function(req, callback) { var raw_type = (req.url.path_list[1] || ""); var rid = req.id; - - // validate type - if (raw_type !== "body" && raw_type !== "head") { - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start - }); - res.end("Invalid Render Type"); - return; - } - var body = raw_type === "body"; var userId = (req.url.path_list[2] || "").split(".")[0]; var def = req.url.query.default; var scale = parseInt(req.url.query.scale) || config.default_scale; var helm = req.url.query.hasOwnProperty("helm"); - var etag = null; - function sendimage(rid, http_status, img_status, image) { - logging.log(rid, "status:", http_status); - res.writeHead(http_status, { - "Content-Type": "image/png", - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": human_status[img_status], - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Etag": '"' + etag + '"' + // validate type + if (raw_type !== "body" && raw_type !== "head") { + callback({ + status: -2, + body: "Invalid Render Type" }); - res.end(http_status === 304 ? null : image); - } - - // default alex/steve images can be rendered, but - // custom images will not be - function handle_default(rid, http_status, img_status, userId) { - if (def && def !== "steve" && def !== "alex") { - logging.log(rid, "status: 301"); - res.writeHead(301, { - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": human_status[img_status], - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Location": def - }); - res.end(); - } else { - def = def || skins.default_skin(userId); - fs.readFile(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function (err, buf) { - if (err) { - // errored while loading the default image, continuing with null image - logging.error(rid, "error loading default render image:", err); - } - // we render the default skins, but not custom images - renders.draw_model(rid, buf, scale, helm, body, function(render_err, def_img) { - if (render_err) { - logging.error(rid, "error while rendering default image:", render_err); - } - sendimage(rid, http_status, img_status, def_img); - }); - }); - } + return; } if (scale < config.min_scale || scale > config.max_scale) { - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start + callback({ + status: -2, + body: "422 Invalid Scale" }); - res.end("422 Invalid Scale"); return; } else if (!helpers.id_valid(userId)) { - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start + callback({ + status: -2, + body: "422 Invalid ID" }); - res.end("422 Invalid ID"); return; } @@ -110,7 +76,6 @@ module.exports = function(req, res) { try { helpers.get_render(rid, userId, scale, helm, body, function(err, status, hash, image) { - logging.log(rid, "storage type:", human_status[status]); if (err) { logging.error(rid, err); if (err.code === "ENOENT") { @@ -118,23 +83,21 @@ module.exports = function(req, res) { cache.remove_hash(rid, userId); } } - etag = hash && hash.substr(0, 32) || "none"; - var matches = req.headers["if-none-match"] === '"' + etag + '"'; if (image) { - var http_status = 200; - if (err) { - http_status = 503; - } - logging.debug(rid, "etag:", req.headers["if-none-match"]); - logging.debug(rid, "matches:", matches); - sendimage(rid, matches ? 304 : http_status, status, image); + callback({ + status: status, + body: image, + type: "image/png", + hash: hash, + err: err + }); } else { logging.log(rid, "image not found, using default."); - handle_default(rid, matches ? 304 : 200, status, userId); + handle_default(rid, scale, helm, body, status, userId, scale, def, err, callback); } }); } catch(e) { logging.error(rid, "error:", e.stack); - handle_default(rid, 500, -1, userId); + handle_default(rid, scale, helm, body, -1, userId, scale, def, e, callback); } }; \ No newline at end of file diff --git a/lib/routes/skins.js b/lib/routes/skins.js index cd1ac9d..6666ecd 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -20,6 +20,7 @@ function handle_default(img_status, userId, def, err, callback) { status: img_status, body: buffer, type: "image/png", + hash: def, err: buf_err || lwip_err || err }); }); @@ -52,11 +53,13 @@ module.exports = function(req, callback) { logging.debug(rid, "userid:", userId); try { - helpers.get_skin(rid, userId, function(err, hash, image) { + helpers.get_skin(rid, userId, function(err, hash, status, image) { if (image) { callback({ + status: status, body: image, type: "image/png", + hash: hash, err: err }); } else { diff --git a/lib/server.js b/lib/server.js index 9ac0097..aa70842 100644 --- a/lib/server.js +++ b/lib/server.js @@ -85,7 +85,9 @@ function requestHandler(req, res) { }); break; case "renders": - routes.renders(req, res); + routes.renders(req, function(result) { + response(req, res, result); + }); break; case "capes": routes.capes(req, res); diff --git a/test/test.js b/test/test.js index 34f7afc..c287619 100644 --- a/test/test.js +++ b/test/test.js @@ -432,7 +432,7 @@ describe("Crafatar", function() { describe("Networking: Skin", function() { it("should not fail (uuid)", function(done) { - helpers.get_skin(rid, id, function(err, hash, img) { + helpers.get_skin(rid, id, function(err, hash, status, img) { assert.strictEqual(err, null); done(); }); From 13169be7746e1b770f66eb699e38d911de87b07a Mon Sep 17 00:00:00 2001 From: jomo Date: Tue, 21 Apr 2015 23:27:10 +0200 Subject: [PATCH 10/47] use new response module for capes + more - made sure that get_cape returns a status - response.js returns 404 if body is empty - 'X-Storage-Type: undefined' is no longer returned when status is `null` --- lib/helpers.js | 17 ++++------ lib/response.js | 7 +++-- lib/routes/capes.js | 77 +++++++++------------------------------------ lib/server.js | 4 ++- 4 files changed, 28 insertions(+), 77 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index e55a872..985ca39 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -210,12 +210,7 @@ exp.id_valid = function(userId) { // decides whether to get a +type+ image for +userId+ from disk or to download it // callback: error, status, hash -// the status gives information about how the image was received -// -1: "error" -// 0: "none" - cached as null -// 1: "cached" - found on disk -// 2: "downloaded" - profile downloaded, skin downloaded from mojang servers -// 3: "checked" - profile re-downloaded (was too old), but it has either not changed or has no skin +// for status, see response.js exp.get_image_hash = function(rid, userId, type, callback) { cache.get_details(userId, function(err, cache_details) { var cached_hash = null; @@ -357,11 +352,11 @@ exp.get_render = function(rid, userId, scale, helm, body, callback) { }; // handles requests for +userId+ capes -// callback: error, cape hash, image buffer +// callback: error, cape hash, status, image buffer exp.get_cape = function(rid, userId, callback) { exp.get_image_hash(rid, userId, "cape", function(err, status, cape_hash) { if (!cape_hash) { - callback(err, null, null); + callback(err, null, null, null); return; } var capepath = path.join(__dirname, "..", config.capes_dir, cape_hash + ".png"); @@ -369,14 +364,14 @@ exp.get_cape = function(rid, userId, callback) { if (exists) { logging.log(rid, "cape already exists, not downloading"); skins.open_skin(rid, capepath, function(skin_err, img) { - callback(skin_err, cape_hash, img); + callback(skin_err || err, cape_hash, status, img); }); } else { networking.save_texture(rid, cape_hash, capepath, function(net_err, response, img) { if (response && response.statusCode === 404) { - callback(net_err, cape_hash, null); + callback(net_err, cape_hash, status, null); } else { - callback(net_err, cape_hash, img); + callback(net_err, cape_hash, status, img); } }); } diff --git a/lib/response.js b/lib/response.js index 7172c46..0db3dcd 100644 --- a/lib/response.js +++ b/lib/response.js @@ -29,7 +29,7 @@ module.exports = function(request, response, result) { }); response.on("finish", function() { - logging.log(request.id, response.statusCode, "(" + human_status[result.status] + ")"); + logging.log(request.id, response.statusCode, "(" + (human_status[result.status] || "-") + ")"); }); response.on("error", function(err) { @@ -51,7 +51,8 @@ module.exports = function(request, response, result) { result.status = -1; } - if (result.status !== undefined) { + if (result.status !== undefined && result.status !== null) { + logging.debug("status: " + result.status); headers["X-Storage-Type"] = human_status[result.status]; } @@ -85,7 +86,7 @@ module.exports = function(request, response, result) { response.writeHead(500, headers); response.end(result.body); } else { - response.writeHead(200, headers); + response.writeHead(result.body ? 200 : 404, headers); response.end(result.body); } }; \ No newline at end of file diff --git a/lib/routes/capes.js b/lib/routes/capes.js index e72fd0e..415e272 100644 --- a/lib/routes/capes.js +++ b/lib/routes/capes.js @@ -1,52 +1,25 @@ var logging = require("../logging"); var helpers = require("../helpers"); -var config = require("../config"); var cache = require("../cache"); -var human_status = { - 0: "none", - 1: "cached", - 2: "downloaded", - 3: "checked", - "-1": "error" -}; - // GET cape request -module.exports = function(req, res) { - var start = new Date(); +module.exports = function(req, callback) { var userId = (req.url.pathname.split("/")[2] || "").split(".")[0]; - var etag = null; var rid = req.id; - function sendimage(rid, http_status, img_status, image) { - res.writeHead(http_status, { - "Content-Type": "image/png", - "Cache-Control": "max-age=" + config.browser_cache_time + ", public", - "Response-Time": new Date() - start, - "X-Storage-Type": human_status[img_status], - "X-Request-ID": rid, - "Access-Control-Allow-Origin": "*", - "Etag": '"' + etag + '"' - }); - res.end(http_status === 304 ? null : image); - } - if (!helpers.id_valid(userId)) { - res.writeHead(422, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start + callback({ + status: -2, + body: "Invalid userid" }); - res.end("Invalid ID"); return; } // strip dashes userId = userId.replace(/-/g, ""); - logging.debug(rid, "userid:", userId); try { - helpers.get_cape(rid, userId, function(err, status, image, hash) { - logging.log(rid, "storage type:", human_status[status]); + helpers.get_cape(rid, userId, function(err, hash, status, image) { if (err) { logging.error(rid, err); if (err.code === "ENOENT") { @@ -54,38 +27,18 @@ module.exports = function(req, res) { cache.remove_hash(rid, userId); } } - etag = hash && hash.substr(0, 32) || "none"; - var matches = req.headers["if-none-match"] === '"' + etag + '"'; - if (image) { - var http_status = 200; - if (err) { - http_status = 503; - } - logging.debug(rid, "etag:", req.headers["if-none-match"]); - logging.debug(rid, "matches:", matches); - logging.log(rid, "status:", http_status); - sendimage(rid, matches ? 304 : http_status, status, image); - } else if (matches) { - res.writeHead(304, { - "Etag": '"' + etag + '"', - "Response-Time": new Date() - start - }); - res.end(); - } else { - res.writeHead(404, { - "Content-Type": "text/plain", - "Etag": '"' + etag + '"', - "Response-Time": new Date() - start - }); - res.end("404 not found"); - } + callback({ + status: status, + body: image, + type: image ? "image/png" : undefined, + hash: hash, + err: err + }); }); } catch(e) { - logging.error(rid, "error:" + e.stack); - res.writeHead(500, { - "Content-Type": "text/plain", - "Response-Time": new Date() - start + callback({ + status: -1, + err: e }); - res.end("500 server error"); } }; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index aa70842..37e7f20 100644 --- a/lib/server.js +++ b/lib/server.js @@ -90,7 +90,9 @@ function requestHandler(req, res) { }); break; case "capes": - routes.capes(req, res); + routes.capes(req, function(result) { + response(req, res, result); + }); break; default: asset_request(req, res); From 244f90c4c78b27469cf089d1f823bb22dcd2fbb6 Mon Sep 17 00:00:00 2001 From: jomo Date: Tue, 21 Apr 2015 23:34:14 +0200 Subject: [PATCH 11/47] fix bug with skin caching that cached capes as non-existent when using cache.save_hash, an `undefined` skin or cape hash means "do not touch" while `null` means "overwrite as non-existent" this was sent to redis after calling /capes/Notch for the first time: "hmset" "notch" "c" "3f688e0e699b3d9fe448b5bb50a3a288f9c589762b3dae8308842122dcb81" "t" "1429651244222" "hmset" "notch" "s" "a116e69a845e227f7ca1fdde8c357c8c821ebd4ba619382ea4a1f87d4ae94" "c" "" "t" "1429651244235" as you can see, the first request stores the c(ape) but does not touch the s(kin), whereas the second request sets the s(kin) and replaces the c(ape) value --- lib/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers.js b/lib/helpers.js index 985ca39..cb1052a 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -177,7 +177,7 @@ function store_images(rid, userId, cache_details, type, callback) { // an error occured, not caching. we can try in 60 seconds callback_for(userId, "skin", store_err, null); } else { - cache.save_hash(rid, userId, skin_hash, null, function(cache_err) { + cache.save_hash(rid, userId, skin_hash, undefined, function(cache_err) { callback_for(userId, "skin", (store_err || cache_err), skin_hash); }); } From 2e66e5c7947f864e5b078e2b16bd1653a2c7c839 Mon Sep 17 00:00:00 2001 From: jomo Date: Tue, 21 Apr 2015 23:46:07 +0200 Subject: [PATCH 12/47] add missing status for empty capes --- lib/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers.js b/lib/helpers.js index cb1052a..2c8286a 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -356,7 +356,7 @@ exp.get_render = function(rid, userId, scale, helm, body, callback) { exp.get_cape = function(rid, userId, callback) { exp.get_image_hash(rid, userId, "cape", function(err, status, cape_hash) { if (!cape_hash) { - callback(err, null, null, null); + callback(err, null, status, null); return; } var capepath = path.join(__dirname, "..", config.capes_dir, cape_hash + ".png"); From 69f0ee23be6eaf0fa860df45b92ae6108214d84b Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 22 Apr 2015 00:06:10 +0200 Subject: [PATCH 13/47] use new response module for assets unfortunately we can't use stream pipes because we need to generate a hash of the content for the Etag. I think proper caching (i.e. Etag) is very important --- lib/server.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/server.js b/lib/server.js index 37e7f20..2f90489 100644 --- a/lib/server.js +++ b/lib/server.js @@ -18,20 +18,20 @@ var routes = { capes: require("./routes/capes") }; -function asset_request(req, res) { +// serves assets from lib/public +function asset_request(req, callback) { var filename = path.join(__dirname, "public", req.url.path_list.join("/")); fs.exists(filename, function(exists) { if (exists) { - res.writeHead(200, { - "Content-type": mime.lookup(filename), - "Cache-Control": "max-age=7200, public", // cache for 2 hours + fs.readFile(filename, function(err, data) { + callback({ + body: data, + type: mime.lookup(filename), + err: err + }); }); - fs.createReadStream(filename).pipe(res); } else { - res.writeHead(404, { - "Content-type": "text/plain" - }); - res.end("Not Found"); + callback({}); } }); } @@ -95,7 +95,9 @@ function requestHandler(req, res) { }); break; default: - asset_request(req, res); + asset_request(req, function(result) { + response(req, res, result); + }); } } catch(e) { var error = JSON.stringify(req.headers) + "\n" + e.stack; From cf1119e2cb6c89e20b3311e860ca216e47fdb5f5 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 22 Apr 2015 00:14:18 +0200 Subject: [PATCH 14/47] update preload images on index --- lib/views/index.jade | 84 +++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/lib/views/index.jade b/lib/views/index.jade index 4e2359a..79a7832 100644 --- a/lib/views/index.jade +++ b/lib/views/index.jade @@ -343,52 +343,54 @@ block content // preload hover images - img.preload(src="/avatars/jeb_", alt="preloaded image") - img.preload(src="/avatars/853c80ef3c3749fdaa49938b674adae6", alt="preloaded image") + img.preload(src="/avatars/020242a17b9441799eff511eea1221da?size=64", alt="preloaded image") + img.preload(src="/avatars/020242a17b9441799eff511eea1221da?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/069a79f444e94726a5befca90e38aaf5?size=64", alt="preloaded image") img.preload(src="/avatars/0?default=alex", alt="preloaded image") img.preload(src="/avatars/0?default=https%3A%2F%2Fi.imgur.com%2FocJVWAc.png", alt="preloaded image") - img.preload(src="/skins/0?default=alex", alt="preloaded image") - img.preload(src="/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=8", alt="preloaded image") - img.preload(src="/avatars/ae795aa86327408e92ab25c8a59f3ba1?size=64", alt="preloaded image") - img.preload(src="/avatars/2d5aa9cdaeb049189930461fc9b91cc5?size=64", alt="preloaded image") img.preload(src="/avatars/0ea8eca3dbf647cc9d1ac64551ca975c?size=64", alt="preloaded image") - img.preload(src="/avatars/af74a02d19cb445bb07f6866a861f783?size=64", alt="preloaded image") - img.preload(src="/avatars/853c80ef3c3749fdaa49938b674adae6?size=64", alt="preloaded image") - img.preload(src="/avatars/069a79f444e94726a5befca90e38aaf5?size=64", alt="preloaded image") - img.preload(src="/avatars/61699b2ed3274a019f1e0ea8c3f06bc6?size=64", alt="preloaded image") - img.preload(src="/avatars/7d043c7389524696bfba571c05b6aec0?size=64", alt="preloaded image") - img.preload(src="/avatars/e6b5c088068044df9e1b9bf11792291b?size=64", alt="preloaded image") - img.preload(src="/avatars/1c1bd09a6a0f4928a7914102a35d2670?size=64", alt="preloaded image") - img.preload(src="/avatars/b05881186e75410db2db4d3066b223f7?size=64", alt="preloaded image") - img.preload(src="/avatars/jeb_?size=128", alt="preloaded image") - img.preload(src="/avatars/696a82ce41f44b51aa31b8709b8686f0?size=64", alt="preloaded image") - img.preload(src="/avatars/b9583ca43e64488a9c8c4ab27e482255?size=64", alt="preloaded image") - img.preload(src="/avatars/c9b54008fd8047428b238787b5f2401c?size=64", alt="preloaded image") - img.preload(src="/avatars/d8f9a4340f2d415f9acfcd70341c75ec?size=64", alt="preloaded image") - img.preload(src="/avatars/7125ba8b1c864508b92bb5c042ccfe2b?size=64", alt="preloaded image") - img.preload(src="/avatars/4566e69fc90748ee8d71d7ba5aa00d20?size=64", alt="preloaded image") - img.preload(src="/avatars/020242a17b9441799eff511eea1221da?size=64", alt="preloaded image") - img.preload(src="/avatars/9769ecf6331448f3ace67ae06cec64a3?size=64", alt="preloaded image") - img.preload(src="/avatars/f8cdb6839e9043eea81939f85d9c5d69?size=64", alt="preloaded image") - img.preload(src="/avatars/jeb_?helm", alt="preloaded image") - img.preload(src="/avatars/ae795aa86327408e92ab25c8a59f3ba1?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/2d5aa9cdaeb049189930461fc9b91cc5?size=64&helm", alt="preloaded image") img.preload(src="/avatars/0ea8eca3dbf647cc9d1ac64551ca975c?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/af74a02d19cb445bb07f6866a861f783?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/853c80ef3c3749fdaa49938b674adae6?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/61699b2ed3274a019f1e0ea8c3f06bc6?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/7d043c7389524696bfba571c05b6aec0?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/e6b5c088068044df9e1b9bf11792291b?size=64&helm", alt="preloaded image") - img.preload(src="/renders/body/jeb_?helm&scale=4", alt="preloaded image") + img.preload(src="/avatars/1c1bd09a6a0f4928a7914102a35d2670?size=64", alt="preloaded image") img.preload(src="/avatars/1c1bd09a6a0f4928a7914102a35d2670?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/b05881186e75410db2db4d3066b223f7?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/696a82ce41f44b51aa31b8709b8686f0?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/b9583ca43e64488a9c8c4ab27e482255?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/c9b54008fd8047428b238787b5f2401c?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/d8f9a4340f2d415f9acfcd70341c75ec?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/7125ba8b1c864508b92bb5c042ccfe2b?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/2d5aa9cdaeb049189930461fc9b91cc5?size=64", alt="preloaded image") + img.preload(src="/avatars/2d5aa9cdaeb049189930461fc9b91cc5?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/4566e69fc90748ee8d71d7ba5aa00d20?size=64", alt="preloaded image") img.preload(src="/avatars/4566e69fc90748ee8d71d7ba5aa00d20?size=64&helm", alt="preloaded image") - img.preload(src="/avatars/020242a17b9441799eff511eea1221da?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/61699b2ed3274a019f1e0ea8c3f06bc6?size=64", alt="preloaded image") + img.preload(src="/avatars/61699b2ed3274a019f1e0ea8c3f06bc6?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/696a82ce41f44b51aa31b8709b8686f0?size=64", alt="preloaded image") + img.preload(src="/avatars/696a82ce41f44b51aa31b8709b8686f0?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/7125ba8b1c864508b92bb5c042ccfe2b?size=64", alt="preloaded image") + img.preload(src="/avatars/7125ba8b1c864508b92bb5c042ccfe2b?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/7d043c7389524696bfba571c05b6aec0?size=64", alt="preloaded image") + img.preload(src="/avatars/7d043c7389524696bfba571c05b6aec0?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/853c80ef3c3749fdaa49938b674adae6", alt="preloaded image") + img.preload(src="/avatars/853c80ef3c3749fdaa49938b674adae6?size=64", alt="preloaded image") + img.preload(src="/avatars/853c80ef3c3749fdaa49938b674adae6?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/9769ecf6331448f3ace67ae06cec64a3?size=64", alt="preloaded image") img.preload(src="/avatars/9769ecf6331448f3ace67ae06cec64a3?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/ae795aa86327408e92ab25c8a59f3ba1?size=64", alt="preloaded image") + img.preload(src="/avatars/ae795aa86327408e92ab25c8a59f3ba1?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/af74a02d19cb445bb07f6866a861f783?size=64", alt="preloaded image") + img.preload(src="/avatars/af74a02d19cb445bb07f6866a861f783?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/b05881186e75410db2db4d3066b223f7?size=64", alt="preloaded image") + img.preload(src="/avatars/b05881186e75410db2db4d3066b223f7?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/b9583ca43e64488a9c8c4ab27e482255?size=64", alt="preloaded image") + img.preload(src="/avatars/b9583ca43e64488a9c8c4ab27e482255?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/c9b54008fd8047428b238787b5f2401c?size=64", alt="preloaded image") + img.preload(src="/avatars/c9b54008fd8047428b238787b5f2401c?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/d8f9a4340f2d415f9acfcd70341c75ec?size=64", alt="preloaded image") + img.preload(src="/avatars/d8f9a4340f2d415f9acfcd70341c75ec?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/e6b5c088068044df9e1b9bf11792291b?size=64", alt="preloaded image") + img.preload(src="/avatars/e6b5c088068044df9e1b9bf11792291b?size=64&helm", alt="preloaded image") + img.preload(src="/avatars/f8cdb6839e9043eea81939f85d9c5d69?size=64", alt="preloaded image") img.preload(src="/avatars/f8cdb6839e9043eea81939f85d9c5d69?size=64&helm", alt="preloaded image") - img.preload(src="/skins/jeb_", alt="preloaded image") + img.preload(src="/avatars/jeb_", alt="preloaded image") + img.preload(src="/avatars/jeb_?helm", alt="preloaded image") + img.preload(src="/avatars/jeb_?size=128", alt="preloaded image") + img.preload(src="/capes/Dinnerbone", alt="preloaded image") + img.preload(src="/capes/md_5", alt="preloaded image") + img.preload(src="/renders/body/jeb_?helm&scale=4", alt="preloaded image") + img.preload(src="/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=8", alt="preloaded image") + img.preload(src="/skins/0?default=alex", alt="preloaded image") + img.preload(src="/skins/jeb_", alt="preloaded image") \ No newline at end of file From 7ca43e3cd9c131233490cc47e58d7c272e3c48c6 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 22 Apr 2015 00:23:48 +0200 Subject: [PATCH 15/47] add description for X-Storage-Type, remove debug log --- lib/response.js | 1 - lib/views/index.jade | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/response.js b/lib/response.js index 0db3dcd..b23391f 100644 --- a/lib/response.js +++ b/lib/response.js @@ -52,7 +52,6 @@ module.exports = function(request, response, result) { } if (result.status !== undefined && result.status !== null) { - logging.debug("status: " + result.status); headers["X-Storage-Type"] = human_status[result.status]; } diff --git a/lib/views/index.jade b/lib/views/index.jade index 79a7832..3cb8624 100644 --- a/lib/views/index.jade +++ b/lib/views/index.jade @@ -279,6 +279,7 @@ block content a(id="meta-x-storage-type" class="anchor") a(href="#meta-x-storage-type") h4 X-Storage-Type + p Details about how the requested image was stored on the server ul li none: No external requests. Cached: User has no skin. li cached: No external requests. Skin cached and stored locally. From 3cdcccde574d6c3af886961268cdf79440562710 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 22 Apr 2015 00:26:10 +0200 Subject: [PATCH 16/47] improve logging --- lib/cache.js | 6 +++--- lib/routes/avatars.js | 1 - lib/routes/renders.js | 1 - lib/routes/skins.js | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/cache.js b/lib/cache.js index 53b90e0..f681f4c 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -98,7 +98,7 @@ exp.info = function(callback) { // these 60 seconds match the duration of Mojang's rate limit ban // callback: error exp.update_timestamp = function(rid, userId, hash, temp, callback) { - logging.log(rid, "cache: updating timestamp"); + logging.debug(rid, "updating cache timestamp"); var sub = temp ? (config.local_cache_time - 60) : 0; var time = Date.now() - sub; // store userId in lower case if not null @@ -114,7 +114,7 @@ exp.update_timestamp = function(rid, userId, hash, temp, callback) { // this feature can be used to write both cape and skin at separate times // +callback+ contans error exp.save_hash = function(rid, userId, skin_hash, cape_hash, callback) { - logging.log(rid, "cache: saving skin:" + skin_hash + " cape:" + cape_hash); + logging.debug(rid, "caching skin:" + skin_hash + " cape:" + cape_hash); var time = Date.now(); // store shorter null byte instead of "null" skin_hash = (skin_hash === null ? "" : skin_hash); @@ -138,7 +138,7 @@ exp.save_hash = function(rid, userId, skin_hash, cape_hash, callback) { // removes the hash for +userId+ from the cache exp.remove_hash = function(rid, userId) { - logging.log(rid, "cache: deleting hash"); + logging.log(rid, "deleting hash from cache"); redis.del(userId.toLowerCase(), "h", "t"); }; diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index 259542d..afa627b 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -52,7 +52,6 @@ module.exports = function(req, callback) { // strip dashes userId = userId.replace(/-/g, ""); - logging.debug(req.id, "userid:", userId); try { helpers.get_avatar(req.id, userId, helm, size, function(err, status, image, hash) { diff --git a/lib/routes/renders.js b/lib/routes/renders.js index 3e9fcc2..7ab77cb 100644 --- a/lib/routes/renders.js +++ b/lib/routes/renders.js @@ -72,7 +72,6 @@ module.exports = function(req, callback) { // strip dashes userId = userId.replace(/-/g, ""); - logging.debug(rid, "userId:", userId); try { helpers.get_render(rid, userId, scale, helm, body, function(err, status, hash, image) { diff --git a/lib/routes/skins.js b/lib/routes/skins.js index 6666ecd..ffeeede 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -50,7 +50,6 @@ module.exports = function(req, callback) { // strip dashes userId = userId.replace(/-/g, ""); - logging.debug(rid, "userid:", userId); try { helpers.get_skin(rid, userId, function(err, hash, status, image) { From c8dad9dfbbf0fdf22c2e7b2a30eb1adecba9c3aa Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 22 Apr 2015 00:31:37 +0200 Subject: [PATCH 17/47] invalidate cache when skin file is gone --- lib/routes/skins.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/routes/skins.js b/lib/routes/skins.js index ffeeede..252b05c 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -53,6 +53,13 @@ module.exports = function(req, callback) { try { helpers.get_skin(rid, userId, function(err, hash, status, image) { + if (err) { + logging.error(req.id, err); + if (err.code === "ENOENT") { + // no such file + cache.remove_hash(req.id, userId); + } + } if (image) { callback({ status: status, From 34adbd32d7442002902b05b0126606575dd271c7 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 22 Apr 2015 00:35:39 +0200 Subject: [PATCH 18/47] be more clear in docs --- lib/views/index.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/views/index.jade b/lib/views/index.jade index 3cb8624..7fe22ac 100644 --- a/lib/views/index.jade +++ b/lib/views/index.jade @@ -286,10 +286,10 @@ block content li | checked: 1 external request. Skin cached, checked for updates, no skin downloaded.
| This happens either when the user removed their skin or when it didn't change. - li downloaded: 2 external requests. Skin changed or unknown, downloaded. + li downloaded: 2 external requests. First request or skin changed, skin downloaded. li | server error: This can happen, for example, when Mojang's servers are down.
- | If possible, an outdated image is served instead. + | If possible, a cached image is served instead. li | user error: You have done something wrong, such as requesting a malformed userid.
| Check the response body for details. From 1464ec81f4bdb1dceb22ef8da8f9c65c69747628 Mon Sep 17 00:00:00 2001 From: jomo Date: Thu, 23 Apr 2015 23:42:20 +0200 Subject: [PATCH 19/47] update calls to changed function helpers.get_cape() --- test/test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test.js b/test/test.js index c287619..757245b 100644 --- a/test/test.js +++ b/test/test.js @@ -350,7 +350,7 @@ describe("Crafatar", function() { describe("Networking: Cape", function() { it("should not fail (guaranteed cape)", function(done) { - helpers.get_cape(rid, "Dinnerbone", function(err, hash, img) { + helpers.get_cape(rid, "Dinnerbone", function(err, hash, status, img) { assert.strictEqual(err, null); done(); }); @@ -359,13 +359,13 @@ describe("Crafatar", function() { before(function() { cache.get_redis().flushall(); }); - helpers.get_cape(rid, "Dinnerbone", function(err, hash, img) { + helpers.get_cape(rid, "Dinnerbone", function(err, hash, status, img) { assert.strictEqual(err, null); done(); }); }); it("should not be found", function(done) { - helpers.get_cape(rid, "Jake_0", function(err, hash, img) { + helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) { assert.strictEqual(img, null); done(); }); @@ -374,7 +374,7 @@ describe("Crafatar", function() { describe("Networking: Skin", function() { it("should not fail", function(done) { - helpers.get_cape(rid, "Jake_0", function(err, hash, img) { + helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) { assert.strictEqual(err, null); done(); }); @@ -383,7 +383,7 @@ describe("Crafatar", function() { before(function() { cache.get_redis().flushall(); }); - helpers.get_cape(rid, "Jake_0", function(err, hash, img) { + helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) { assert.strictEqual(err, null); done(); }); @@ -456,7 +456,7 @@ describe("Crafatar", function() { describe("Networking: Cape", function() { it("should not fail (possible cape)", function(done) { - helpers.get_cape(rid, id, function(err, hash, img) { + helpers.get_cape(rid, id, function(err, hash, status, img) { assert.strictEqual(err, null); done(); }); From 22ea5a7e271f2c8f8d6d0050961228de40cb3dc4 Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 14:43:56 +0200 Subject: [PATCH 20/47] use iojs 1.8.x --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6d8512..65404b4 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "test-travis": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, "engines": { - "iojs": "1.6.x" + "iojs": "1.8.x" }, "dependencies": { "canvas": "crafatar/node-canvas", From 79ab296f1ff18085e0b9e95af4567cc4801263ef Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 15:49:47 +0200 Subject: [PATCH 21/47] get rid of path.resolve This doesn't handle '..' and other things that path.resolve does, but if you're passing weird things to a URL, your fault. --- lib/server.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/server.js b/lib/server.js index 2f90489..a990a18 100644 --- a/lib/server.js +++ b/lib/server.js @@ -44,9 +44,8 @@ function request_id() { // splits a URL path into an Array // the path is resolved and decoded function path_list(pathname) { - // remove trailing and double slashes + other junk - - pathname = path.resolve(pathname); + // remove double and trailing slashes + pathname = pathname.replace(/\/\/+/g, "/").replace(/\/$/, ""); var list = pathname.split("/"); list.shift(); for (var i = 0; i < list.length; i++) { From efd9c0ccee80f8a3d06d9f56de3eaabcd731eac9 Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 15:55:59 +0200 Subject: [PATCH 22/47] don't remove trailing slash for root path --- lib/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index a990a18..3b808b2 100644 --- a/lib/server.js +++ b/lib/server.js @@ -45,7 +45,7 @@ function request_id() { // the path is resolved and decoded function path_list(pathname) { // remove double and trailing slashes - pathname = pathname.replace(/\/\/+/g, "/").replace(/\/$/, ""); + pathname = pathname.replace(/\/\/+/g, "/").replace(/(.)\/$/, "$1"); var list = pathname.split("/"); list.shift(); for (var i = 0; i < list.length; i++) { From 02092aec08f181b616465c106ec37f0bb3026f04 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 25 Apr 2015 09:09:22 -0500 Subject: [PATCH 23/47] Add test for username defaulting to steve --- test/test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test.js b/test/test.js index 757245b..40c68f6 100644 --- a/test/test.js +++ b/test/test.js @@ -140,6 +140,10 @@ describe("Crafatar", function() { }); }); }); + it("Username should default to Steve", function(done) { + assert.strictEqual(skins.default_skin("TestUser"), "steve"); + done(); + }); for (var a in alex_ids) { var alex_id = alex_ids[a]; (function(alex_id) { From 8ad35aeea2fbe4f528b38e40829373b24d9e5ed6 Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 21:22:16 +0200 Subject: [PATCH 24/47] rewrite http tests, check http headers, body, and caching also disabled *all* logs in tests unless VERBOSE_TEST=true --- test/test.js | 348 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 250 insertions(+), 98 deletions(-) diff --git a/test/test.js b/test/test.js index 40c68f6..b5cdb47 100644 --- a/test/test.js +++ b/test/test.js @@ -1,21 +1,22 @@ -var assert = require("assert"); -var fs = require("fs"); var networking = require("../lib/networking"); var helpers = require("../lib/helpers"); var logging = require("../lib/logging"); -var config = require("../lib/config"); -var skins = require("../lib/skins"); -var cache = require("../lib/cache"); -var server = require("../lib/server"); var cleaner = require("../lib/cleaner"); var request = require("request"); +var config = require("../lib/config"); +var server = require("../lib/server"); +var assert = require("assert"); +var skins = require("../lib/skins"); +var cache = require("../lib/cache"); +var crc = require("crc").crc32; +var fs = require("fs"); // we don't want tests to fail because of slow internet config.http_timeout *= 3; // no spam if (process.env.VERBOSE_TEST !== "true") { - logging.log = function() {}; + logging.log = logging.debug = logging.warn = logging.error = function() {}; } var uuids = fs.readFileSync("test/uuids.txt").toString().split(/\r?\n/); @@ -215,118 +216,269 @@ describe("Crafatar", function() { }); describe("Server", function() { + + // throws Exception when default headers are not in res.headers + function assert_headers(res) { + assert(res.headers["content-type"]); + assert(res.headers["response-time"]); + assert(res.headers["x-request-id"]); + assert.equal(res.headers["access-control-allow-origin"], "*"); + assert.equal(res.headers["cache-control"], "max-age=" + config.browser_cache_time + ", public"); + } + + // throws Exception when +url+ is requested with +etag+ + // and it does not return 304 without a body + function assert_cache(url, etag, callback) { + request.get(url, { + headers: { + "If-None-Match": etag + } + }, function(error, res, body) { + assert.ifError(error); + assert.ifError(body); + assert.equal(res.statusCode, 304); + assert(res.headers["etag"]); + assert_headers(res); + callback(); + }); + } + before(function(done) { server.boot(function() { done(); }); }); - // Test the home page - it("should return a 200 (home page)", function(done) { - request.get("http://localhost:3000", function(error, res, body) { - assert.equal(200, res.statusCode); - done(); - }); - }); - - it("should return a 200 (asset request)", function(done) { - request.get("http://localhost:3000/stylesheets/style.css", function(error, res, body) { - assert.equal(200, res.statusCode); - done(); - }); - }); - - // invalid method, we only allow GET and HEAD requests - it("should return a 405 (invalid method)", function(done) { + it("should return 405 Method Not Allowed for POST", function(done) { request.post("http://localhost:3000", function(error, res, body) { - assert.equal(405, res.statusCode); + assert.ifError(error); + assert.strictEqual(res.statusCode, 405); done(); }); }); - it("should return a 422 (invalid size)", function(done) { - var size = config.max_size + 1; - request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) { - assert.equal(422, res.statusCode); - done(); + it("should return correct HTTP response for home page", function(done) { + var url = "http://localhost:3000"; + request.get(url, function(error, res, body) { + assert.ifError(error); + assert.strictEqual(res.statusCode, 200); + assert_headers(res); + assert(res.headers["etag"]); + assert.strictEqual(res.headers["content-type"], "text/html; charset=utf-8"); + assert.strictEqual(res.headers["etag"], "\"" + crc(body) + "\""); + assert(body); + + assert_cache(url, res.headers["etag"], function() { + done(); + }); }); }); - it("should return a 422 (invalid scale)", function(done) { - var scale = config.max_scale + 1; - request.get("http://localhost:3000/renders/head/Jake_0?scale=" + scale, function(error, res, body) { - assert.equal(422, res.statusCode); - done(); + it("should return correct HTTP response for assets", function(done) { + var url = "http://localhost:3000/stylesheets/style.css"; + request.get(url, function(error, res, body) { + assert.ifError(error); + assert.strictEqual(res.statusCode, 200); + assert_headers(res); + assert(res.headers["etag"]); + assert.strictEqual(res.headers["content-type"], "text/css"); + assert.strictEqual(res.headers["etag"], "\"" + crc(body) + "\""); + assert(body); + + assert_cache(url, res.headers["etag"], function() { + done(); + }); }); }); - // no default images for capes, should 404 - it("should return a 404 (no cape)", function(done) { - request.get("http://localhost:3000/capes/Jake_0", function(error, res, body) { - assert.equal(404, res.statusCode); - done(); - }); - }); + var locations = { + "avatar with existing username": { + url: "http://localhost:3000/avatars/jeb_?size=16", + etag: '"a846b82963"', + crc32: 3898010414 + }, + "avatar with not existing username": { + url: "http://localhost:3000/avatars/0?size=16", + etag: '"steve"', + crc32: 2172290550 + }, + "helm avatar with existing username": { + url: "http://localhost:3000/avatars/jeb_?size=16&helm", + etag: '"a846b82963"', + crc32: 2775605405 + }, + "helm avatar with not existing username": { + url: "http://localhost:3000/avatars/0?size=16&helm", + etag: '"steve"', + crc32: 2172290550 + }, + "avatar with existing uuid": { + url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16", + etag: '"a846b82963"', + crc32: 3898010414 + }, + "avatar with not existing uuid": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16", + etag: '"steve"', + crc32: 2172290550 + }, + "helm avatar with existing uuid": { + url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&helm", + etag: '"a846b82963"', + crc32: 2775605405 + }, + "helm avatar with not existing uuid": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm", + etag: '"steve"', + crc32: 2172290550 + }, + "cape with existing username": { + url: "http://localhost:3000/capes/jeb_", + etag: '"3f688e0e69"', + crc32: 3151059445 + }, + "cape with not existing username": { + url: "http://localhost:3000/capes/0", + etag: undefined, + crc32: 0 + }, + "cape with existing uuid": { + url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6", + etag: '"3f688e0e69"', + crc32: 3151059445 + }, + "cape with not existing uuid": { + url: "http://localhost:3000/capes/00000000000000000000000000000000", + etag: undefined, + crc32: 0 + }, + "skin with existing username": { + url: "http://localhost:3000/skins/jeb_", + etag: '"a846b82963"', + crc32: 2804046136 + }, + "skin with not existing username": { + url: "http://localhost:3000/skins/0", + etag: '"steve"', + crc32: 3033070999 + }, + "skin with existing uuid": { + url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6", + etag: '"a846b82963"', + crc32: 2804046136 + }, + "skin with not existing uuid": { + url: "http://localhost:3000/skins/00000000000000000000000000000000", + etag: '"steve"', + crc32: 3033070999 + }, + "head render with existing username": { + url: "http://localhost:3000/renders/head/jeb_?scale=2", + etag: '"a846b82963"', + crc32: 4068337011 + }, + "head render with not existing username": { + url: "http://localhost:3000/renders/head/0?scale=2", + etag: '"steve"', + crc32: 3356822684 + }, + "helm head render with existing username": { + url: "http://localhost:3000/renders/head/jeb_?scale=2&helm", + etag: '"a846b82963"', + crc32: 4150827424 + }, + "helm head render with not existing username": { + url: "http://localhost:3000/renders/head/0?scale=2&helm", + etag: '"steve"', + crc32: 349655416 + }, + "head render with existing uuid": { + url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2", + etag: '"a846b82963"', + crc32: 4068337011 + }, + "head render with not existing uuid": { + url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2", + etag: '"steve"', + crc32: 3356822684 + }, + "helm head render with existing uuid": { + url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", + etag: '"a846b82963"', + crc32: 4150827424 + }, + "helm head render with not existing uuid": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm", + etag: '"steve"', + crc32: 1255390784 + }, + "body render with existing username": { + url: "http://localhost:3000/renders/body/jeb_?scale=2", + etag: '"a846b82963"', + crc32: 2118588728 + }, + "body render with not existing username": { + url: "http://localhost:3000/renders/body/0?scale=2", + etag: '"steve"', + crc32: 216470159 + }, + "helm body render with existing username": { + url: "http://localhost:3000/renders/body/jeb_?scale=2&helm", + etag: '"a846b82963"', + crc32: 2280652919 + }, + "helm body render with not existing username": { + url: "http://localhost:3000/renders/body/0?scale=2&helm", + etag: '"steve"', + crc32: 1991273336 + }, + "body render with existing uuid": { + url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", + etag: '"a846b82963"', + crc32: 2118588728 + }, + "body render with not existing uuid": { + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2", + etag: '"steve"', + crc32: 216470159 + }, + "helm body render with existing uuid": { + url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", + etag: '"a846b82963"', + crc32: 2280652919 + }, + "helm body render with not existing uuid": { + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm", + etag: '"steve"', + crc32: 1991273336 + }, + }; - it("should return a 422 (invalid render type)", function(done) { - request.get("http://localhost:3000/renders/side/Jake_0", function(error, res, body) { - assert.equal(422, res.statusCode); - done(); - }); - }); - - // testing all paths for valid inputs - var locations = ["avatars", "skins", "renders/head"]; - for (var l in locations) { - var location = locations[l]; + for (var description in locations) { + var location = locations[description]; (function(location) { - it("should return a 200 (valid input " + location + ")", function(done) { - request.get("http://localhost:3000/" + location + "/Jake_0", function(error, res, body) { - assert.equal(200, res.statusCode); - done(); + it("should return correct HTTP response for " + description, function(done) { + request.get(location.url, function(error, res, body) { + assert.ifError(error); + assert_headers(res); + assert(res.headers["x-storage-type"]); + assert.strictEqual(res.headers["etag"], location.etag); + assert.strictEqual(crc(body), location.crc32); + if (location.url.substr(21, 7) === "/capes/" && !body) { + assert.strictEqual(res.statusCode, 404); + assert.strictEqual(res.headers["content-type"], "text/plain"); + done(); + } else { + assert(res.headers["etag"]); + assert.strictEqual(res.headers["content-type"], "image/png"); + assert.strictEqual(res.statusCode, 200); + assert_cache(location.url, res.headers["etag"], function() { + done(); + }); + } }); }); - it("should return a 422 (invalid id " + location + ")", function(done) { - request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) { - assert.equal(422, res.statusCode); - done(); - }); - }); - })(location); - } - - // testing all paths for invalid id formats - locations = ["avatars", "capes", "skins", "renders/head"]; - for (l in locations) { - var location = locations[l]; - (function(location) { - it("should return a 422 (invalid id " + location + ")", function(done) { - request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) { - assert.equal(422, res.statusCode); - done(); - }); - }); - })(location); - } - - //testing all paths for default images - locations = ["avatars", "skins", "renders/head"]; - for (l in locations) { - var location = locations[l]; - (function(location) { - it("should return a 200 (default steve image " + location + ")", function(done) { - request.get("http://localhost:3000/" + location + "/invalidjsvns?default=steve", function(error, res, body) { - assert.equal(200, res.statusCode); - done(); - }); - }); - it("should return a 200 (default external image " + location + ")", function(done) { - request.get("http://localhost:3000/" + location + "/invalidjsvns?default=https%3A%2F%2Fi.imgur.com%2FocJVWAc.png", function(error, res, body) { - assert.equal(200, res.statusCode); - done(); - }); - }); - })(location); + }(location)); } after(function(done) { From d981b34d0bc7d82bbb86a53e2ef2edf4cd3ed395 Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 21:25:43 +0200 Subject: [PATCH 25/47] compact code for cape tests --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index b5cdb47..9298f5e 100644 --- a/test/test.js +++ b/test/test.js @@ -464,7 +464,7 @@ describe("Crafatar", function() { assert(res.headers["x-storage-type"]); assert.strictEqual(res.headers["etag"], location.etag); assert.strictEqual(crc(body), location.crc32); - if (location.url.substr(21, 7) === "/capes/" && !body) { + if (location.etag === undefined) { assert.strictEqual(res.statusCode, 404); assert.strictEqual(res.headers["content-type"], "text/plain"); done(); From 23080370b2428e7e74b81c5cf73cb712415c48de Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 22:50:31 +0200 Subject: [PATCH 26/47] add test for redirection --- test/test.js | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 3 deletions(-) diff --git a/test/test.js b/test/test.js index 9298f5e..4a5bd7f 100644 --- a/test/test.js +++ b/test/test.js @@ -220,7 +220,7 @@ describe("Crafatar", function() { // throws Exception when default headers are not in res.headers function assert_headers(res) { assert(res.headers["content-type"]); - assert(res.headers["response-time"]); + assert("" + res.headers["response-time"]); assert(res.headers["x-request-id"]); assert.equal(res.headers["access-control-allow-origin"], "*"); assert.equal(res.headers["cache-control"], "max-age=" + config.browser_cache_time + ", public"); @@ -302,6 +302,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 2172290550 }, + "avatar with not existing username defaulting to alex": { + url: "http://localhost:3000/avatars/0?size=16&default=alex", + etag: '"alex"', + crc32: 2968787440 + }, + "avatar with not existing username defaulting to url": { + url: "http://localhost:3000/avatars/0?size=16&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "helm avatar with existing username": { url: "http://localhost:3000/avatars/jeb_?size=16&helm", etag: '"a846b82963"', @@ -312,6 +323,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 2172290550 }, + "helm avatar with not existing username defaulting to alex": { + url: "http://localhost:3000/avatars/0?size=16&helm&default=alex", + etag: '"alex"', + crc32: 2968787440 + }, + "helm avatar with not existing username defaulting to url": { + url: "http://localhost:3000/avatars/0?size=16&helm&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "avatar with existing uuid": { url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16", etag: '"a846b82963"', @@ -322,6 +344,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 2172290550 }, + "avatar with not existing uuid defaulting to alex": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=alex", + etag: '"alex"', + crc32: 2968787440 + }, + "avatar with not existing uuid defaulting to url": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "helm avatar with existing uuid": { url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&helm", etag: '"a846b82963"', @@ -332,6 +365,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 2172290550 }, + "helm avatar with not existing uuid defaulting to alex": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=alex", + etag: '"alex"', + crc32: 2968787440 + }, + "helm avatar with not existing uuid defaulting to url": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "cape with existing username": { url: "http://localhost:3000/capes/jeb_", etag: '"3f688e0e69"', @@ -342,6 +386,17 @@ describe("Crafatar", function() { etag: undefined, crc32: 0 }, + "cape with not existing username defaulting to alex": { + url: "http://localhost:3000/capes/0?default=alex", + etag: undefined, + crc32: 0 + }, + "cape with not existing username defaulting to url": { + url: "http://localhost:3000/capes/0?default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "cape with existing uuid": { url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6", etag: '"3f688e0e69"', @@ -352,6 +407,17 @@ describe("Crafatar", function() { etag: undefined, crc32: 0 }, + "cape with not existing uuid defaulting to alex": { + url: "http://localhost:3000/capes/00000000000000000000000000000000?default=alex", + etag: undefined, + crc32: 0 + }, + "cape with not existing uuid defaulting to url": { + url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "skin with existing username": { url: "http://localhost:3000/skins/jeb_", etag: '"a846b82963"', @@ -362,6 +428,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 3033070999 }, + "skin with not existing username defaulting to alex": { + url: "http://localhost:3000/skins/0?default=alex", + etag: '"alex"', + crc32: 777408988 + }, + "skin with not existing username defaulting to url": { + url: "http://localhost:3000/skins/0?default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "skin with existing uuid": { url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6", etag: '"a846b82963"', @@ -372,6 +449,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 3033070999 }, + "skin with not existing uuid defaulting to alex": { + url: "http://localhost:3000/skins/00000000000000000000000000000000?default=alex", + etag: '"alex"', + crc32: 777408988 + }, + "skin with not existing uuid defaulting to url": { + url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "head render with existing username": { url: "http://localhost:3000/renders/head/jeb_?scale=2", etag: '"a846b82963"', @@ -382,6 +470,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 3356822684 }, + "head render with not existing username defaulting to alex": { + url: "http://localhost:3000/renders/head/0?scale=2&default=alex", + etag: '"alex"', + crc32: 1133025464 + }, + "head render with not existing username defaulting to url": { + url: "http://localhost:3000/renders/head/0?scale=2&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "helm head render with existing username": { url: "http://localhost:3000/renders/head/jeb_?scale=2&helm", etag: '"a846b82963"', @@ -392,6 +491,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 349655416 }, + "helm head render with not existing username defaulting to alex": { + url: "http://localhost:3000/renders/head/0?scale=2&helm&default=alex", + etag: '"alex"', + crc32: 4031806631 + }, + "helm head render with not existing username defaulting to url": { + url: "http://localhost:3000/renders/head/0?scale=2&helm&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2", etag: '"a846b82963"', @@ -402,6 +512,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 3356822684 }, + "head render with not existing uuid defaulting to alex": { + url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=alex", + etag: '"alex"', + crc32: 1133025464 + }, + "head render with not existing uuid defaulting to url": { + url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "helm head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", etag: '"a846b82963"', @@ -412,6 +533,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 1255390784 }, + "helm head render with not existing uuid defaulting to alex": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=alex", + etag: '"alex"', + crc32: 2283463177 + }, + "helm head render with not existing uuid defaulting to url": { + url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "body render with existing username": { url: "http://localhost:3000/renders/body/jeb_?scale=2", etag: '"a846b82963"', @@ -422,6 +554,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 216470159 }, + "body render with not existing username defaulting to alex": { + url: "http://localhost:3000/renders/body/0?scale=2&default=alex", + etag: '"alex"', + crc32: 1873964761 + }, + "body render with not existing username defaulting to url": { + url: "http://localhost:3000/renders/body/0?scale=2&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "helm body render with existing username": { url: "http://localhost:3000/renders/body/jeb_?scale=2&helm", etag: '"a846b82963"', @@ -432,6 +575,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 1991273336 }, + "helm body render with not existing username defaulting to alex": { + url: "http://localhost:3000/renders/body/0?scale=2&helm&default=alex", + etag: '"alex"', + crc32: 1201672813 + }, + "helm body render with not existing username defaulting to url": { + url: "http://localhost:3000/renders/body/0?scale=2&helm&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", etag: '"a846b82963"', @@ -442,6 +596,17 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 216470159 }, + "body render with not existing uuid defaulting to alex": { + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=alex", + etag: '"alex"', + crc32: 1873964761 + }, + "body render with not existing uuid defaulting to url": { + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, "helm body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", etag: '"a846b82963"', @@ -452,20 +617,32 @@ describe("Crafatar", function() { etag: '"steve"', crc32: 1991273336 }, + "helm body render with not existing uuid defaulting to alex": { + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=alex", + etag: '"alex"', + crc32: 1201672813 + }, + "helm body render with not existing uuid defaulting to url": { + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=http://example.com", + etag: undefined, + crc32: 0, + redirect: "http://example.com" + }, }; for (var description in locations) { var location = locations[description]; (function(location) { it("should return correct HTTP response for " + description, function(done) { - request.get(location.url, function(error, res, body) { + request.get(location.url, {followRedirect: false}, function(error, res, body) { assert.ifError(error); assert_headers(res); assert(res.headers["x-storage-type"]); assert.strictEqual(res.headers["etag"], location.etag); assert.strictEqual(crc(body), location.crc32); + assert.strictEqual(res.headers["location"], location.redirect); if (location.etag === undefined) { - assert.strictEqual(res.statusCode, 404); + assert.strictEqual(res.statusCode, location.redirect ? 307 : 404); assert.strictEqual(res.headers["content-type"], "text/plain"); done(); } else { From 5c53694f9dc378917df969f5c8abcb79ea50d23a Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 22:51:22 +0200 Subject: [PATCH 27/47] no need to defined undefined --- test/test.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test/test.js b/test/test.js index 4a5bd7f..c61251e 100644 --- a/test/test.js +++ b/test/test.js @@ -309,7 +309,6 @@ describe("Crafatar", function() { }, "avatar with not existing username defaulting to url": { url: "http://localhost:3000/avatars/0?size=16&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -330,7 +329,6 @@ describe("Crafatar", function() { }, "helm avatar with not existing username defaulting to url": { url: "http://localhost:3000/avatars/0?size=16&helm&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -351,7 +349,6 @@ describe("Crafatar", function() { }, "avatar with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -372,7 +369,6 @@ describe("Crafatar", function() { }, "helm avatar with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -383,17 +379,14 @@ describe("Crafatar", function() { }, "cape with not existing username": { url: "http://localhost:3000/capes/0", - etag: undefined, crc32: 0 }, "cape with not existing username defaulting to alex": { url: "http://localhost:3000/capes/0?default=alex", - etag: undefined, crc32: 0 }, "cape with not existing username defaulting to url": { url: "http://localhost:3000/capes/0?default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -404,17 +397,14 @@ describe("Crafatar", function() { }, "cape with not existing uuid": { url: "http://localhost:3000/capes/00000000000000000000000000000000", - etag: undefined, crc32: 0 }, "cape with not existing uuid defaulting to alex": { url: "http://localhost:3000/capes/00000000000000000000000000000000?default=alex", - etag: undefined, crc32: 0 }, "cape with not existing uuid defaulting to url": { url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -435,7 +425,6 @@ describe("Crafatar", function() { }, "skin with not existing username defaulting to url": { url: "http://localhost:3000/skins/0?default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -456,7 +445,6 @@ describe("Crafatar", function() { }, "skin with not existing uuid defaulting to url": { url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -477,7 +465,6 @@ describe("Crafatar", function() { }, "head render with not existing username defaulting to url": { url: "http://localhost:3000/renders/head/0?scale=2&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -498,7 +485,6 @@ describe("Crafatar", function() { }, "helm head render with not existing username defaulting to url": { url: "http://localhost:3000/renders/head/0?scale=2&helm&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -519,7 +505,6 @@ describe("Crafatar", function() { }, "head render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -540,7 +525,6 @@ describe("Crafatar", function() { }, "helm head render with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -561,7 +545,6 @@ describe("Crafatar", function() { }, "body render with not existing username defaulting to url": { url: "http://localhost:3000/renders/body/0?scale=2&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -582,7 +565,6 @@ describe("Crafatar", function() { }, "helm body render with not existing username defaulting to url": { url: "http://localhost:3000/renders/body/0?scale=2&helm&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -603,7 +585,6 @@ describe("Crafatar", function() { }, "body render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, @@ -624,7 +605,6 @@ describe("Crafatar", function() { }, "helm body render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=http://example.com", - etag: undefined, crc32: 0, redirect: "http://example.com" }, From 4286b3577505efb2590660a3103c232fef1fbc62 Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 22:59:33 +0200 Subject: [PATCH 28/47] support redirection for capes --- lib/routes/capes.js | 2 ++ test/test.js | 8 -------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/routes/capes.js b/lib/routes/capes.js index 415e272..b43ae9a 100644 --- a/lib/routes/capes.js +++ b/lib/routes/capes.js @@ -5,6 +5,7 @@ var cache = require("../cache"); // GET cape request module.exports = function(req, callback) { var userId = (req.url.pathname.split("/")[2] || "").split(".")[0]; + var def = req.url.query.default; var rid = req.id; if (!helpers.id_valid(userId)) { @@ -31,6 +32,7 @@ module.exports = function(req, callback) { status: status, body: image, type: image ? "image/png" : undefined, + redirect: image ? undefined : def, hash: hash, err: err }); diff --git a/test/test.js b/test/test.js index c61251e..9619261 100644 --- a/test/test.js +++ b/test/test.js @@ -381,10 +381,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/capes/0", crc32: 0 }, - "cape with not existing username defaulting to alex": { - url: "http://localhost:3000/capes/0?default=alex", - crc32: 0 - }, "cape with not existing username defaulting to url": { url: "http://localhost:3000/capes/0?default=http://example.com", crc32: 0, @@ -399,10 +395,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/capes/00000000000000000000000000000000", crc32: 0 }, - "cape with not existing uuid defaulting to alex": { - url: "http://localhost:3000/capes/00000000000000000000000000000000?default=alex", - crc32: 0 - }, "cape with not existing uuid defaulting to url": { url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http://example.com", crc32: 0, From aeffc8fc512d3edb1589c53a416748f13c9fe640 Mon Sep 17 00:00:00 2001 From: jomo Date: Sat, 25 Apr 2015 23:01:46 +0200 Subject: [PATCH 29/47] update travis' iojs version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c865f43..2c347e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - "iojs-v1.6.0" + - "iojs-v1.8.0" before_install: - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: From b2d31c5b7e34282e96c7fc774aebc81c728fd7ba Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 26 Apr 2015 02:10:47 +0200 Subject: [PATCH 30/47] travis dependency :poop: --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c347e9..e6694bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ language: node_js node_js: - - "iojs-v1.8.0" + - "iojs-v1.8" before_install: - - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ + - cat /etc/apt/sources.list + - sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ vivid main restricted" + - cat /etc/apt/sources.list + - sudo apt-get update + - sudo apt-get install -y --force-yes libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: - npm run-script test-travis notifications: From 14769f6f362e66d1223275ef0140a4215fb332f1 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 26 Apr 2015 02:21:06 +0200 Subject: [PATCH 31/47] :poop: --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e6694bb..07fd0aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ before_install: - sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ vivid main restricted" - cat /etc/apt/sources.list - sudo apt-get update + - sudo rm /etc/dpkg/dpkg.cfg.d/multiarch - sudo apt-get install -y --force-yes libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: - npm run-script test-travis From 89a80eeaad074392ed7f44cfbb2639d2b8f22751 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 26 Apr 2015 02:31:17 +0200 Subject: [PATCH 32/47] add expat dependency --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 07fd0aa..e65b881 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_install: - cat /etc/apt/sources.list - sudo apt-get update - sudo rm /etc/dpkg/dpkg.cfg.d/multiarch - - sudo apt-get install -y --force-yes libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ + - sudo apt-get install -y --force-yes expat libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: - npm run-script test-travis notifications: From 2d1876b6b7514830009991a6bccdeae0e38e8711 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 26 Apr 2015 02:37:37 +0200 Subject: [PATCH 33/47] add libexpat1-dev dependency --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e65b881..b027b05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_install: - cat /etc/apt/sources.list - sudo apt-get update - sudo rm /etc/dpkg/dpkg.cfg.d/multiarch - - sudo apt-get install -y --force-yes expat libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ + - sudo apt-get install -y --force-yes libexpat1-dev libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: - npm run-script test-travis notifications: From 6e41423b7a7e13dfb9f977350fe69d9dcfebe71a Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 3 May 2015 22:46:03 +0200 Subject: [PATCH 34/47] set encoding: null in test HTTP requests from the 'request' docs: encoding: Encoding to be used on setEncoding of response data. If null, the body is returned as a Buffer. Anything else (including the default value of undefined) will be passed as the encoding parameter to toString() (meaning this is effectively utf8 by default). --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 9619261..3ad18cf 100644 --- a/test/test.js +++ b/test/test.js @@ -606,7 +606,7 @@ describe("Crafatar", function() { var location = locations[description]; (function(location) { it("should return correct HTTP response for " + description, function(done) { - request.get(location.url, {followRedirect: false}, function(error, res, body) { + request.get(location.url, {followRedirect: false, encoding: null}, function(error, res, body) { assert.ifError(error); assert_headers(res); assert(res.headers["x-storage-type"]); From b039e97fa7d7642fdaa5862109b3350e5d11aec9 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 3 May 2015 23:06:21 +0200 Subject: [PATCH 35/47] fix test crc32 values --- test/test.js | 88 ++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/test/test.js b/test/test.js index 3ad18cf..aeccdee 100644 --- a/test/test.js +++ b/test/test.js @@ -295,17 +295,17 @@ describe("Crafatar", function() { "avatar with existing username": { url: "http://localhost:3000/avatars/jeb_?size=16", etag: '"a846b82963"', - crc32: 3898010414 + crc32: 1623808067 }, "avatar with not existing username": { url: "http://localhost:3000/avatars/0?size=16", etag: '"steve"', - crc32: 2172290550 + crc32: 1243826040 }, "avatar with not existing username defaulting to alex": { url: "http://localhost:3000/avatars/0?size=16&default=alex", etag: '"alex"', - crc32: 2968787440 + crc32: 809395677 }, "avatar with not existing username defaulting to url": { url: "http://localhost:3000/avatars/0?size=16&default=http://example.com", @@ -315,17 +315,17 @@ describe("Crafatar", function() { "helm avatar with existing username": { url: "http://localhost:3000/avatars/jeb_?size=16&helm", etag: '"a846b82963"', - crc32: 2775605405 + crc32: 646871998 }, "helm avatar with not existing username": { url: "http://localhost:3000/avatars/0?size=16&helm", etag: '"steve"', - crc32: 2172290550 + crc32: 1243826040 }, "helm avatar with not existing username defaulting to alex": { url: "http://localhost:3000/avatars/0?size=16&helm&default=alex", etag: '"alex"', - crc32: 2968787440 + crc32: 809395677 }, "helm avatar with not existing username defaulting to url": { url: "http://localhost:3000/avatars/0?size=16&helm&default=http://example.com", @@ -335,17 +335,17 @@ describe("Crafatar", function() { "avatar with existing uuid": { url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16", etag: '"a846b82963"', - crc32: 3898010414 + crc32: 1623808067 }, "avatar with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16", etag: '"steve"', - crc32: 2172290550 + crc32: 1243826040 }, "avatar with not existing uuid defaulting to alex": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=alex", etag: '"alex"', - crc32: 2968787440 + crc32: 809395677 }, "avatar with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http://example.com", @@ -355,17 +355,17 @@ describe("Crafatar", function() { "helm avatar with existing uuid": { url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&helm", etag: '"a846b82963"', - crc32: 2775605405 + crc32: 646871998 }, "helm avatar with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm", etag: '"steve"', - crc32: 2172290550 + crc32: 1243826040 }, "helm avatar with not existing uuid defaulting to alex": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=alex", etag: '"alex"', - crc32: 2968787440 + crc32: 809395677 }, "helm avatar with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=http://example.com", @@ -375,7 +375,7 @@ describe("Crafatar", function() { "cape with existing username": { url: "http://localhost:3000/capes/jeb_", etag: '"3f688e0e69"', - crc32: 3151059445 + crc32: 1901140141 }, "cape with not existing username": { url: "http://localhost:3000/capes/0", @@ -389,7 +389,7 @@ describe("Crafatar", function() { "cape with existing uuid": { url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6", etag: '"3f688e0e69"', - crc32: 3151059445 + crc32: 1901140141 }, "cape with not existing uuid": { url: "http://localhost:3000/capes/00000000000000000000000000000000", @@ -403,17 +403,17 @@ describe("Crafatar", function() { "skin with existing username": { url: "http://localhost:3000/skins/jeb_", etag: '"a846b82963"', - crc32: 2804046136 + crc32: 110922424 }, "skin with not existing username": { url: "http://localhost:3000/skins/0", etag: '"steve"', - crc32: 3033070999 + crc32: 981937087 }, "skin with not existing username defaulting to alex": { url: "http://localhost:3000/skins/0?default=alex", etag: '"alex"', - crc32: 777408988 + crc32: 2298915739 }, "skin with not existing username defaulting to url": { url: "http://localhost:3000/skins/0?default=http://example.com", @@ -423,17 +423,17 @@ describe("Crafatar", function() { "skin with existing uuid": { url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6", etag: '"a846b82963"', - crc32: 2804046136 + crc32: 110922424 }, "skin with not existing uuid": { url: "http://localhost:3000/skins/00000000000000000000000000000000", etag: '"steve"', - crc32: 3033070999 + crc32: 981937087 }, "skin with not existing uuid defaulting to alex": { url: "http://localhost:3000/skins/00000000000000000000000000000000?default=alex", etag: '"alex"', - crc32: 777408988 + crc32: 2298915739 }, "skin with not existing uuid defaulting to url": { url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http://example.com", @@ -443,17 +443,17 @@ describe("Crafatar", function() { "head render with existing username": { url: "http://localhost:3000/renders/head/jeb_?scale=2", etag: '"a846b82963"', - crc32: 4068337011 + crc32: 353633671 }, "head render with not existing username": { url: "http://localhost:3000/renders/head/0?scale=2", etag: '"steve"', - crc32: 3356822684 + crc32: 883439147 }, "head render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/head/0?scale=2&default=alex", etag: '"alex"', - crc32: 1133025464 + crc32: 1240086237 }, "head render with not existing username defaulting to url": { url: "http://localhost:3000/renders/head/0?scale=2&default=http://example.com", @@ -463,17 +463,17 @@ describe("Crafatar", function() { "helm head render with existing username": { url: "http://localhost:3000/renders/head/jeb_?scale=2&helm", etag: '"a846b82963"', - crc32: 4150827424 + crc32: 3456497067 }, "helm head render with not existing username": { url: "http://localhost:3000/renders/head/0?scale=2&helm", etag: '"steve"', - crc32: 349655416 + crc32: 1858563554 }, "helm head render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/head/0?scale=2&helm&default=alex", etag: '"alex"', - crc32: 4031806631 + crc32: 2963161105 }, "helm head render with not existing username defaulting to url": { url: "http://localhost:3000/renders/head/0?scale=2&helm&default=http://example.com", @@ -483,17 +483,17 @@ describe("Crafatar", function() { "head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2", etag: '"a846b82963"', - crc32: 4068337011 + crc32: 353633671 }, "head render with not existing uuid": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2", etag: '"steve"', - crc32: 3356822684 + crc32: 883439147 }, "head render with not existing uuid defaulting to alex": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=alex", etag: '"alex"', - crc32: 1133025464 + crc32: 1240086237 }, "head render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http://example.com", @@ -503,17 +503,17 @@ describe("Crafatar", function() { "helm head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", etag: '"a846b82963"', - crc32: 4150827424 + crc32: 3456497067 }, "helm head render with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm", etag: '"steve"', - crc32: 1255390784 + crc32: 24686928 }, "helm head render with not existing uuid defaulting to alex": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=alex", etag: '"alex"', - crc32: 2283463177 + crc32: 2086405365 }, "helm head render with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=http://example.com", @@ -523,17 +523,17 @@ describe("Crafatar", function() { "body render with existing username": { url: "http://localhost:3000/renders/body/jeb_?scale=2", etag: '"a846b82963"', - crc32: 2118588728 + crc32: 1291941229 }, "body render with not existing username": { url: "http://localhost:3000/renders/body/0?scale=2", etag: '"steve"', - crc32: 216470159 + crc32: 2652947188 }, "body render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/body/0?scale=2&default=alex", etag: '"alex"', - crc32: 1873964761 + crc32: 407932087 }, "body render with not existing username defaulting to url": { url: "http://localhost:3000/renders/body/0?scale=2&default=http://example.com", @@ -543,17 +543,17 @@ describe("Crafatar", function() { "helm body render with existing username": { url: "http://localhost:3000/renders/body/jeb_?scale=2&helm", etag: '"a846b82963"', - crc32: 2280652919 + crc32: 3556188297 }, "helm body render with not existing username": { url: "http://localhost:3000/renders/body/0?scale=2&helm", etag: '"steve"', - crc32: 1991273336 + crc32: 272191039 }, "helm body render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/body/0?scale=2&helm&default=alex", etag: '"alex"', - crc32: 1201672813 + crc32: 737759773 }, "helm body render with not existing username defaulting to url": { url: "http://localhost:3000/renders/body/0?scale=2&helm&default=http://example.com", @@ -563,17 +563,17 @@ describe("Crafatar", function() { "body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", etag: '"a846b82963"', - crc32: 2118588728 + crc32: 1291941229 }, "body render with not existing uuid": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2", etag: '"steve"', - crc32: 216470159 + crc32: 2652947188 }, "body render with not existing uuid defaulting to alex": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=alex", etag: '"alex"', - crc32: 1873964761 + crc32: 407932087 }, "body render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http://example.com", @@ -583,17 +583,17 @@ describe("Crafatar", function() { "helm body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", etag: '"a846b82963"', - crc32: 2280652919 + crc32: 3556188297 }, "helm body render with not existing uuid": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm", etag: '"steve"', - crc32: 1991273336 + crc32: 272191039 }, "helm body render with not existing uuid defaulting to alex": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=alex", etag: '"alex"', - crc32: 1201672813 + crc32: 737759773 }, "helm body render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=http://example.com", From d8303eb1a671d20aae13907491173c9309670efb Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 3 May 2015 23:09:32 +0200 Subject: [PATCH 36/47] revert b2d31c5..2d1876b this wasn't a cairo issue, no need to slow down travis less recent version of cairo works just as well --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b027b05..e6694bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,7 @@ before_install: - sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ vivid main restricted" - cat /etc/apt/sources.list - sudo apt-get update - - sudo rm /etc/dpkg/dpkg.cfg.d/multiarch - - sudo apt-get install -y --force-yes libexpat1-dev libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ + - sudo apt-get install -y --force-yes libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: - npm run-script test-travis notifications: From f48a9e66cf7bbb975c2dbaf451607ac99c0b184d Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 3 May 2015 23:15:52 +0200 Subject: [PATCH 37/47] Revert "travis dependency :poop:" This reverts commit b2d31c5b7e34282e96c7fc774aebc81c728fd7ba. --- .travis.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index e6694bb..2c347e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,8 @@ language: node_js node_js: - - "iojs-v1.8" + - "iojs-v1.8.0" before_install: - - cat /etc/apt/sources.list - - sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ vivid main restricted" - - cat /etc/apt/sources.list - - sudo apt-get update - - sudo apt-get install -y --force-yes libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ + - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: - npm run-script test-travis notifications: From deeb72770e1dca5efd7649c0e7816e84cfcb0df8 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 3 May 2015 23:19:29 +0200 Subject: [PATCH 38/47] fix iojs version in .travis.yml (again) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2c347e9..5aeddc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - "iojs-v1.8.0" + - "iojs-v1.8" before_install: - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: From 67e635dbbbf973c0dcd3a513bdbf7ab754ff3391 Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 3 May 2015 23:33:23 +0200 Subject: [PATCH 39/47] remove unnecessary quotes in .travis.yml I just want travis to run these tests again --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5aeddc6..ef98bb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - "iojs-v1.8" + - iojs-v1.8 before_install: - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: @@ -8,7 +8,7 @@ script: notifications: irc: channels: - - "irc.esper.net#crafatar" + - irc.esper.net#crafatar skip_join: true services: - redis-server From 9f04fbc136b6c5b1ed79e2b20908de0953a1a132 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 21:06:09 +0200 Subject: [PATCH 40/47] allow multiple checksums for tests, fixes #119 As discussed in #119, the rendered image can be slightly different in different environments --- test/test.js | 57 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/test/test.js b/test/test.js index aeccdee..253b16e 100644 --- a/test/test.js +++ b/test/test.js @@ -443,17 +443,17 @@ describe("Crafatar", function() { "head render with existing username": { url: "http://localhost:3000/renders/head/jeb_?scale=2", etag: '"a846b82963"', - crc32: 353633671 + crc32: [353633671, 370672768] }, "head render with not existing username": { url: "http://localhost:3000/renders/head/0?scale=2", etag: '"steve"', - crc32: 883439147 + crc32: [883439147, 433083528] }, "head render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/head/0?scale=2&default=alex", etag: '"alex"', - crc32: 1240086237 + crc32: [1240086237, 1108800327] }, "head render with not existing username defaulting to url": { url: "http://localhost:3000/renders/head/0?scale=2&default=http://example.com", @@ -463,17 +463,17 @@ describe("Crafatar", function() { "helm head render with existing username": { url: "http://localhost:3000/renders/head/jeb_?scale=2&helm", etag: '"a846b82963"', - crc32: 3456497067 + crc32: [3456497067, 3490318764] }, "helm head render with not existing username": { url: "http://localhost:3000/renders/head/0?scale=2&helm", etag: '"steve"', - crc32: 1858563554 + crc32: [1858563554, 2647471936] }, "helm head render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/head/0?scale=2&helm&default=alex", etag: '"alex"', - crc32: 2963161105 + crc32: [2963161105, 1769904825] }, "helm head render with not existing username defaulting to url": { url: "http://localhost:3000/renders/head/0?scale=2&helm&default=http://example.com", @@ -483,17 +483,17 @@ describe("Crafatar", function() { "head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2", etag: '"a846b82963"', - crc32: 353633671 + crc32: [353633671, 370672768] }, "head render with not existing uuid": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2", etag: '"steve"', - crc32: 883439147 + crc32: [883439147, 433083528] }, "head render with not existing uuid defaulting to alex": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=alex", etag: '"alex"', - crc32: 1240086237 + crc32: [1240086237, 1108800327] }, "head render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http://example.com", @@ -503,7 +503,7 @@ describe("Crafatar", function() { "helm head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", etag: '"a846b82963"', - crc32: 3456497067 + crc32: [3456497067, 3490318764] }, "helm head render with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm", @@ -523,17 +523,17 @@ describe("Crafatar", function() { "body render with existing username": { url: "http://localhost:3000/renders/body/jeb_?scale=2", etag: '"a846b82963"', - crc32: 1291941229 + crc32: [1291941229, 2628108474] }, "body render with not existing username": { url: "http://localhost:3000/renders/body/0?scale=2", etag: '"steve"', - crc32: 2652947188 + crc32: [2652947188, 2115706574] }, "body render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/body/0?scale=2&default=alex", etag: '"alex"', - crc32: 407932087 + crc32: [407932087, 2516216042] }, "body render with not existing username defaulting to url": { url: "http://localhost:3000/renders/body/0?scale=2&default=http://example.com", @@ -543,17 +543,17 @@ describe("Crafatar", function() { "helm body render with existing username": { url: "http://localhost:3000/renders/body/jeb_?scale=2&helm", etag: '"a846b82963"', - crc32: 3556188297 + crc32: [3556188297, 4269754007] }, "helm body render with not existing username": { url: "http://localhost:3000/renders/body/0?scale=2&helm", etag: '"steve"', - crc32: 272191039 + crc32: [272191039, 542896675] }, "helm body render with not existing username defaulting to alex": { url: "http://localhost:3000/renders/body/0?scale=2&helm&default=alex", etag: '"alex"', - crc32: 737759773 + crc32: [737759773, 66512449] }, "helm body render with not existing username defaulting to url": { url: "http://localhost:3000/renders/body/0?scale=2&helm&default=http://example.com", @@ -563,17 +563,17 @@ describe("Crafatar", function() { "body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", etag: '"a846b82963"', - crc32: 1291941229 + crc32: [1291941229, 2628108474] }, "body render with not existing uuid": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2", etag: '"steve"', - crc32: 2652947188 + crc32: [2652947188, 2115706574] }, "body render with not existing uuid defaulting to alex": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=alex", etag: '"alex"', - crc32: 407932087 + crc32: [407932087, 2516216042] }, "body render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http://example.com", @@ -583,17 +583,17 @@ describe("Crafatar", function() { "helm body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&helm", etag: '"a846b82963"', - crc32: 3556188297 + crc32: [3556188297, 4269754007] }, "helm body render with not existing uuid": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm", etag: '"steve"', - crc32: 272191039 + crc32: [272191039, 542896675] }, "helm body render with not existing uuid defaulting to alex": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=alex", etag: '"alex"', - crc32: 737759773 + crc32: [737759773, 66512449] }, "helm body render with not existing uuid defaulting to url": { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=http://example.com", @@ -611,7 +611,18 @@ describe("Crafatar", function() { assert_headers(res); assert(res.headers["x-storage-type"]); assert.strictEqual(res.headers["etag"], location.etag); - assert.strictEqual(crc(body), location.crc32); + var matches = false; + if (location.crc32 instanceof Array) { + for (var i = 0; i < location.crc32.length; i++) { + if (location.crc32[i] === crc(body)) { + matches = true; + break; + } + } + } else { + matches = (location.crc32 === crc(body)); + } + assert.ok(matches); assert.strictEqual(res.headers["location"], location.redirect); if (location.etag === undefined) { assert.strictEqual(res.statusCode, location.redirect ? 307 : 404); From ed25d30ff0cf57aeed9cdd5eb06c901b0366d63f Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 21:07:19 +0200 Subject: [PATCH 41/47] remove unnecessary log --- lib/response.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/response.js b/lib/response.js index b23391f..64251da 100644 --- a/lib/response.js +++ b/lib/response.js @@ -64,7 +64,6 @@ module.exports = function(request, response, result) { // handle etag caching var incoming_etag = request.headers["if-none-match"]; if (incoming_etag && incoming_etag === headers.Etag) { - logging.debug(request.id, "Etag matches"); response.writeHead(304, headers); response.end(); return; From 46d10fdc81644fc8dbe59098b8e7368cd8ef8408 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 21:07:43 +0200 Subject: [PATCH 42/47] remove unnecessary double check --- lib/response.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/response.js b/lib/response.js index 64251da..e438f31 100644 --- a/lib/response.js +++ b/lib/response.js @@ -58,7 +58,7 @@ module.exports = function(request, response, result) { if (result.body) { // use Mojang's image hash if available // use crc32 as a hash function otherwise - var etag = result.body && result.hash && result.hash.substr(0, 10) || crc(result.body); + var etag = result.hash && result.hash.substr(0, 10) || crc(result.body); headers.Etag = "\"" + etag + "\""; // handle etag caching From 15afa940f0a323dc95f2164adf4d593cc11f7df9 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 21:50:57 +0200 Subject: [PATCH 43/47] update lwip for iojs 2.0.0 --- package.json | 2 +- test/test.js | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 65404b4..560fc44 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "canvas": "crafatar/node-canvas", "forever": "0.14.1", "jade": "~1.9.1", - "lwip": "0.0.6", + "lwip": "crafatar/lwip", "mime": "1.3.4", "node-df": "0.1.1", "redis": "0.12.1", diff --git a/test/test.js b/test/test.js index 253b16e..d567f83 100644 --- a/test/test.js +++ b/test/test.js @@ -300,12 +300,12 @@ describe("Crafatar", function() { "avatar with not existing username": { url: "http://localhost:3000/avatars/0?size=16", etag: '"steve"', - crc32: 1243826040 + crc32: 2416827277 }, "avatar with not existing username defaulting to alex": { url: "http://localhost:3000/avatars/0?size=16&default=alex", etag: '"alex"', - crc32: 809395677 + crc32: 862751081 }, "avatar with not existing username defaulting to url": { url: "http://localhost:3000/avatars/0?size=16&default=http://example.com", @@ -320,12 +320,12 @@ describe("Crafatar", function() { "helm avatar with not existing username": { url: "http://localhost:3000/avatars/0?size=16&helm", etag: '"steve"', - crc32: 1243826040 + crc32: 2416827277 }, "helm avatar with not existing username defaulting to alex": { url: "http://localhost:3000/avatars/0?size=16&helm&default=alex", etag: '"alex"', - crc32: 809395677 + crc32: 862751081 }, "helm avatar with not existing username defaulting to url": { url: "http://localhost:3000/avatars/0?size=16&helm&default=http://example.com", @@ -340,12 +340,12 @@ describe("Crafatar", function() { "avatar with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16", etag: '"steve"', - crc32: 1243826040 + crc32: 2416827277 }, "avatar with not existing uuid defaulting to alex": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=alex", etag: '"alex"', - crc32: 809395677 + crc32: 862751081 }, "avatar with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http://example.com", @@ -360,12 +360,12 @@ describe("Crafatar", function() { "helm avatar with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm", etag: '"steve"', - crc32: 1243826040 + crc32: 2416827277 }, "helm avatar with not existing uuid defaulting to alex": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=alex", etag: '"alex"', - crc32: 809395677 + crc32: 862751081 }, "helm avatar with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=http://example.com", @@ -375,7 +375,7 @@ describe("Crafatar", function() { "cape with existing username": { url: "http://localhost:3000/capes/jeb_", etag: '"3f688e0e69"', - crc32: 1901140141 + crc32: 989800403 }, "cape with not existing username": { url: "http://localhost:3000/capes/0", @@ -389,7 +389,7 @@ describe("Crafatar", function() { "cape with existing uuid": { url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6", etag: '"3f688e0e69"', - crc32: 1901140141 + crc32: 989800403 }, "cape with not existing uuid": { url: "http://localhost:3000/capes/00000000000000000000000000000000", @@ -508,12 +508,12 @@ describe("Crafatar", function() { "helm head render with not existing uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm", etag: '"steve"', - crc32: 24686928 + crc32: 1327292273 }, "helm head render with not existing uuid defaulting to alex": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=alex", etag: '"alex"', - crc32: 2086405365 + crc32: 2233777481 }, "helm head render with not existing uuid defaulting to url": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=http://example.com", From d1e174405a40aae1e8baab8d80dc7f285bfb7205 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 21:59:44 +0200 Subject: [PATCH 44/47] fix wrong URL in test --- test/test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test.js b/test/test.js index d567f83..0dad70b 100644 --- a/test/test.js +++ b/test/test.js @@ -506,17 +506,17 @@ describe("Crafatar", function() { crc32: [3456497067, 3490318764] }, "helm head render with not existing uuid": { - url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm", + url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm", etag: '"steve"', - crc32: 1327292273 + crc32: [1858563554, 2647471936] }, "helm head render with not existing uuid defaulting to alex": { - url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=alex", + url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm&default=alex", etag: '"alex"', - crc32: 2233777481 + crc32: [2963161105, 1769904825] }, "helm head render with not existing uuid defaulting to url": { - url: "http://localhost:3000/avatars/00000000000000000000000000000000?scale=2&helm&default=http://example.com", + url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm&default=http://example.com", crc32: 0, redirect: "http://example.com" }, From d27eb0049fe7a0ce78abffa5c105879b309aec42 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 22:00:54 +0200 Subject: [PATCH 45/47] update to iojs 2.0 --- .travis.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef98bb9..ae7c388 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - iojs-v1.8 + - iojs-v2.0 before_install: - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ script: diff --git a/package.json b/package.json index 560fc44..a79180e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "test-travis": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, "engines": { - "iojs": "1.8.x" + "iojs": "2.0.x" }, "dependencies": { "canvas": "crafatar/node-canvas", From 00f6c28cfcc9a4d0fafdb255fcef5096a147510f Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 22:11:19 +0200 Subject: [PATCH 46/47] add tests for 422 / invalid xy / user error --- test/test.js | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/test/test.js b/test/test.js index 0dad70b..3846cd6 100644 --- a/test/test.js +++ b/test/test.js @@ -291,7 +291,7 @@ describe("Crafatar", function() { }); }); - var locations = { + var server_tests = { "avatar with existing username": { url: "http://localhost:3000/avatars/jeb_?size=16", etag: '"a846b82963"', @@ -602,8 +602,8 @@ describe("Crafatar", function() { }, }; - for (var description in locations) { - var location = locations[description]; + for (var description in server_tests) { + var location = server_tests[description]; (function(location) { it("should return correct HTTP response for " + description, function(done) { request.get(location.url, {followRedirect: false, encoding: null}, function(error, res, body) { @@ -641,6 +641,43 @@ describe("Crafatar", function() { }(location)); } + it("should return a 422 (invalid size)", function(done) { + var size = config.max_size + 1; + request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) { + assert.strictEqual(res.statusCode, 422); + done(); + }); + }); + + it("should return a 422 (invalid scale)", function(done) { + var scale = config.max_scale + 1; + request.get("http://localhost:3000/renders/head/Jake_0?scale=" + scale, function(error, res, body) { + assert.strictEqual(res.statusCode, 422); + done(); + }); + }); + + it("should return a 422 (invalid render type)", function(done) { + request.get("http://localhost:3000/renders/invalid/Jake_0", function(error, res, body) { + assert.strictEqual(res.statusCode, 422); + done(); + }); + }); + + // testing all paths for invalid userid + var locations = ["avatars", "skins", "capes", "renders/body", "renders/head"]; + for (var l in locations) { + var location = locations[l]; + (function(location) { + it("should return a 422 (invalid id " + location + ")", function(done) { + request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) { + assert.strictEqual(res.statusCode, 422); + done(); + }); + }); + })(location); + } + after(function(done) { server.close(function() { done(); From 6273e3bcc8b0e86c2aa68476c903087051a8c846 Mon Sep 17 00:00:00 2001 From: jomo Date: Wed, 6 May 2015 22:13:28 +0200 Subject: [PATCH 47/47] adjust 422 messages --- lib/routes/avatars.js | 2 +- lib/routes/capes.js | 2 +- lib/routes/renders.js | 4 ++-- lib/routes/skins.js | 2 +- test/test.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index afa627b..4e46991 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -45,7 +45,7 @@ module.exports = function(req, callback) { } else if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid userid" + body: "Invalid UserID" }); return; } diff --git a/lib/routes/capes.js b/lib/routes/capes.js index b43ae9a..f97fd4c 100644 --- a/lib/routes/capes.js +++ b/lib/routes/capes.js @@ -11,7 +11,7 @@ module.exports = function(req, callback) { if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid userid" + body: "Invalid UserID" }); return; } diff --git a/lib/routes/renders.js b/lib/routes/renders.js index 7ab77cb..d1932db 100644 --- a/lib/routes/renders.js +++ b/lib/routes/renders.js @@ -59,13 +59,13 @@ module.exports = function(req, callback) { if (scale < config.min_scale || scale > config.max_scale) { callback({ status: -2, - body: "422 Invalid Scale" + body: "Invalid Scale" }); return; } else if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "422 Invalid ID" + body: "Invalid UserID" }); return; } diff --git a/lib/routes/skins.js b/lib/routes/skins.js index 252b05c..6e917ef 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -43,7 +43,7 @@ module.exports = function(req, callback) { if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid userid" + body: "Invalid UserID" }); return; } diff --git a/test/test.js b/test/test.js index 3846cd6..358cce0 100644 --- a/test/test.js +++ b/test/test.js @@ -664,7 +664,7 @@ describe("Crafatar", function() { }); }); - // testing all paths for invalid userid + // testing all paths for Invalid UserID var locations = ["avatars", "skins", "capes", "renders/body", "renders/head"]; for (var l in locations) { var location = locations[l];