mirror of
https://github.com/azures04/crafatar.git
synced 2026-03-21 23:41:18 +01:00
create new response module & use it for avatars
This commit is contained in:
parent
fce58722c8
commit
3cbf73b0d7
86
lib/response.js
Normal file
86
lib/response.js
Normal file
@ -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);
|
||||
}
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
@ -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);
|
||||
|
||||
@ -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 || {};
|
||||
|
||||
// remove trailing and double slashes + other junk
|
||||
var path_list = request.url.pathname.split("/");
|
||||
for (var i = 0; i < path_list.length; i++) {
|
||||
// URL decode
|
||||
path_list[i] = querystring.unescape(path_list[i]);
|
||||
// generates a 12 character random string
|
||||
function request_id() {
|
||||
return Math.random().toString(36).substring(2, 14);
|
||||
}
|
||||
request.url.path_list = path_list;
|
||||
|
||||
// generate 12 character random string
|
||||
request.id = 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
|
||||
|
||||
res.start = new Date();
|
||||
// FIXME: also accepts relative paths?
|
||||
|
||||
var local_path = request.url.path_list[1];
|
||||
logging.log(request.id, request.method, request.url.href);
|
||||
if (request.method === "GET" || request.method === "HEAD") {
|
||||
pathname = path.resolve(pathname);
|
||||
var list = pathname.split("/");
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
// URL decode
|
||||
list[i] = querystring.unescape(list[i]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
req.id = request_id();
|
||||
req.start = Date.now();
|
||||
|
||||
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"
|
||||
});
|
||||
|
||||
@ -287,8 +287,11 @@ block content
|
||||
| This happens either when the user removed their skin or when it didn't change.
|
||||
li <b>downloaded</b>: 2 external requests. Skin changed or unknown, downloaded.
|
||||
li
|
||||
| <b>error</b>: This can happen, for example, when Mojang's servers are down.<br>
|
||||
| <b>server error</b>: This can happen, for example, when Mojang's servers are down.<br>
|
||||
| If possible, an outdated image is served instead.
|
||||
li
|
||||
| <b>user error</b>: You have done something wrong, such as requesting a malformed userid.<br>
|
||||
| Check the response body for details.
|
||||
section
|
||||
a(id="meta-x-request-id" class="anchor")
|
||||
a(href="#meta-x-request-id")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user