diff --git a/lib/helpers.js b/lib/helpers.js index f9ba0b6..5afe2fa 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -8,7 +8,7 @@ var path = require("path"); var fs = require("fs"); // 0098cb60-fa8e-427c-b299-793cbd302c9a -var valid_user_id = /^([0-9a-f-A-F-]{32,36}|[a-zA-Z0-9_]{1,16})$/; // uuid|username +var valid_user_id = /^[0-9a-f-A-F-]{32,36}$/; // uuid var hash_pattern = /[0-9a-f]+$/; // gets the hash from the textures.minecraft.net +url+ @@ -22,10 +22,7 @@ function get_hash(url) { // callback: error, skin hash, slim function store_skin(rid, userId, profile, cache_details, callback) { networking.get_skin_info(rid, userId, profile, function(err, url, slim) { - if (!err && userId.length > 16) { - // updating username with model info from uuid details - cache.set_slim(rid, profile.name, slim); - } else { + if (err) { slim = cache_details ? cache_details.slim : undefined; } @@ -161,7 +158,6 @@ function resume(userId, type, err, hash, slim) { // image type should be called back on // callback: error, image hash, slim function store_images(rid, userId, cache_details, type, callback) { - var is_uuid = userId.length > 16; if (requests[type]["!" + userId]) { logging.debug(rid, "adding to request queue"); push_request(userId, type, callback); @@ -169,8 +165,8 @@ function store_images(rid, userId, cache_details, type, callback) { // add request to the queue push_request(userId, type, callback); - networking.get_profile(rid, (is_uuid ? userId : null), function(err, profile) { - if (err || (is_uuid && !profile)) { + networking.get_profile(rid, userId, function(err, profile) { + if (err || !profile) { // error or uuid without profile if (!err && !profile) { // no error, but uuid without profile @@ -212,8 +208,8 @@ function store_images(rid, userId, cache_details, type, callback) { var exp = {}; -// returns true if the +userId+ is a valid userId or username -// the userId may be not exist, however +// returns true if the +userId+ is a valid userId +// the UUID might not exist, however exp.id_valid = function(userId) { return valid_user_id.test(userId); }; diff --git a/lib/networking.js b/lib/networking.js index 84fd4af..2d51f9f 100644 --- a/lib/networking.js +++ b/lib/networking.js @@ -6,29 +6,10 @@ var skins = require("./skins"); require("./object-patch"); var session_url = "https://sessionserver.mojang.com/session/minecraft/profile/"; -var skins_url = "https://skins.minecraft.net/MinecraftSkins/"; -var capes_url = "https://skins.minecraft.net/MinecraftCloaks/"; var textures_url = "http://textures.minecraft.net/texture/"; -var mojang_urls = [skins_url, capes_url]; var exp = {}; -// helper method that calls `get_username_url` or `get_uuid_info` based on the +usedId+ -// +userId+ is used for usernames, while +profile+ is used for UUIDs -// callback: error, url, slim -function get_info(rid, userId, profile, type, callback) { - if (userId.length <= 16) { - // username - exp.get_username_url(rid, userId, type, function(err, url) { - callback(err, url || null, undefined); - }); - } else { - exp.get_uuid_info(profile, type, function(url, slim) { - callback(null, url || null, slim); - }); - } -} - // performs a GET request to the +url+ // +options+ object includes these options: // encoding (string), default is to return a buffer @@ -103,24 +84,6 @@ exp.get_from = function(rid, url, callback) { }); }; -// make a request to skins.miencraft.net -// the skin url is taken from the HTTP redirect -// type reference is above -exp.get_username_url = function(rid, name, type, callback) { - type = Number(type === "CAPE"); - exp.get_from(rid, mojang_urls[type] + name + ".png", function(body, response, err) { - if (!err) { - if (response) { - callback(err, response.statusCode === 404 ? null : response.headers.location); - } else { - callback(err, null); - } - } else { - callback(err, null); - } - }); -}; - // gets the URL for a skin/cape from the profile // +type+ "SKIN" or "CAPE", specifies which to retrieve // callback: url, slim @@ -139,43 +102,39 @@ exp.get_uuid_info = function(profile, type, callback) { slim = Object.get(profile, "textures.SKIN.metadata.model") === "slim"; } - callback(url || null, !!slim); + callback(null, url || null, !!slim); }; // make a request to sessionserver for +uuid+ // callback: error, profile exp.get_profile = function(rid, uuid, callback) { - if (!uuid) { - callback(null, null); - } else { - exp.get_from_options(rid, session_url + uuid, { encoding: "utf8" }, function(body, response, err) { - try { - body = body ? JSON.parse(body) : null; - callback(err || null, body); - } catch(e) { - if (e instanceof SyntaxError) { - logging.warn(rid, "Failed to parse JSON", e); - logging.debug(rid, body); - callback(err || null, null); - } else { - throw e; - } + exp.get_from_options(rid, session_url + uuid, { encoding: "utf8" }, function(body, response, err) { + try { + body = body ? JSON.parse(body) : null; + callback(err || null, body); + } catch(e) { + if (e instanceof SyntaxError) { + logging.warn(rid, "Failed to parse JSON", e); + logging.debug(rid, body); + callback(err || null, null); + } else { + throw e; } - }); - } + } + }); }; // get the skin URL and type for +userId+ // +profile+ is used if +userId+ is a uuid // callback: error, url, slim exp.get_skin_info = function(rid, userId, profile, callback) { - get_info(rid, userId, profile, "SKIN", callback); + exp.get_uuid_info(profile, "SKIN", callback); }; // get the cape URL for +userId+ // +profile+ is used if +userId+ is a uuid exp.get_cape_url = function(rid, userId, profile, callback) { - get_info(rid, userId, profile, "CAPE", callback); + exp.get_uuid_info(profile, "CAPE", callback); }; // download the +tex_hash+ image from the texture server diff --git a/lib/public/javascript/crafatar.js b/lib/public/javascript/crafatar.js index ee828b8..b8d9624 100644 --- a/lib/public/javascript/crafatar.js +++ b/lib/public/javascript/crafatar.js @@ -1,4 +1,4 @@ -var valid_user_id = /^([0-9a-f-A-F-]{32,36}|[a-zA-Z0-9_]{1,16})$/; // uuid|username +var valid_user_id = /^[0-9a-f-A-F-]{32,36}$/; // uuid var xhr = new XMLHttpRequest(); xhr.onload = function() { @@ -9,24 +9,14 @@ xhr.onload = function() { status[key] = elem[key]; }); - var textures = status["textures.minecraft.net"] !== "green"; - var session = status["sessionserver.mojang.com"] !== "green"; - var skins = status["skins.minecraft.net"] !== "green"; - var error = null; + var textures_err = status["textures.minecraft.net"] !== "green"; + var session_err = status["sessionserver.mojang.com"] !== "green"; - if (textures || session && skins) { - error = "all"; - } else if (skins) { - error = "username"; - } else if (session) { - error = "UUID"; - } - - if (error) { + if (textures_err || session_err) { var warn = document.createElement("div"); warn.setAttribute("class", "alert alert-warning"); warn.setAttribute("role", "alert"); - warn.innerHTML = "
Mojang issues
Mojang's servers are having trouble right now, this may affect " + error + " requests at Crafatar. check status"; + warn.innerHTML = "
Mojang issues
Mojang's servers are having trouble right now, this may affect requests at Crafatar. check status"; document.querySelector("#alerts").appendChild(warn); } }; diff --git a/lib/routes/avatars.js b/lib/routes/avatars.js index 6b5d920..9daea9e 100644 --- a/lib/routes/avatars.js +++ b/lib/routes/avatars.js @@ -79,7 +79,7 @@ module.exports = function(req, callback) { } else if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid UserID", + body: "Invalid UUID", }); return; } diff --git a/lib/routes/capes.js b/lib/routes/capes.js index 77f1953..1995c55 100644 --- a/lib/routes/capes.js +++ b/lib/routes/capes.js @@ -20,7 +20,7 @@ module.exports = function(req, callback) { if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid UserID" + body: "Invalid UUID" }); return; } diff --git a/lib/routes/renders.js b/lib/routes/renders.js index a63b134..2f67de7 100644 --- a/lib/routes/renders.js +++ b/lib/routes/renders.js @@ -94,7 +94,7 @@ module.exports = function(req, callback) { } else if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid UserID" + body: "Invalid UUID" }); return; } diff --git a/lib/routes/skins.js b/lib/routes/skins.js index 4922e67..773cab0 100644 --- a/lib/routes/skins.js +++ b/lib/routes/skins.js @@ -78,7 +78,7 @@ module.exports = function(req, callback) { if (!helpers.id_valid(userId)) { callback({ status: -2, - body: "Invalid UserID" + body: "Invalid UUID" }); return; } diff --git a/lib/skins.js b/lib/skins.js index 1d3dbfd..4f5ab28 100644 --- a/lib/skins.js +++ b/lib/skins.js @@ -128,26 +128,17 @@ exp.resize_img = function(inname, size, callback) { // returns "mhf_alex" or "mhf_steve" calculated by the +uuid+ exp.default_skin = function(uuid) { - if (uuid.length <= 16) { - if (uuid.toLowerCase() === "mhf_alex") { - return uuid; - } else { - // we can't get the skin type by username - return "mhf_steve"; - } - } else { - // great thanks to Minecrell for research into Minecraft and Java's UUID hashing! - // https://git.io/xJpV - // MC uses `uuid.hashCode() & 1` for alex - // that can be compacted to counting the LSBs of every 4th byte in the UUID - // an odd sum means alex, an even sum means steve - // XOR-ing all the LSBs gives us 1 for alex and 0 for steve - var lsbs_even = parseInt(uuid[ 7], 16) ^ - parseInt(uuid[15], 16) ^ - parseInt(uuid[23], 16) ^ - parseInt(uuid[31], 16); - return lsbs_even ? "mhf_alex" : "mhf_steve"; - } + // great thanks to Minecrell for research into Minecraft and Java's UUID hashing! + // https://git.io/xJpV + // MC uses `uuid.hashCode() & 1` for alex + // that can be compacted to counting the LSBs of every 4th byte in the UUID + // an odd sum means alex, an even sum means steve + // XOR-ing all the LSBs gives us 1 for alex and 0 for steve + var lsbs_even = parseInt(uuid[ 7], 16) ^ + parseInt(uuid[15], 16) ^ + parseInt(uuid[23], 16) ^ + parseInt(uuid[31], 16); + return lsbs_even ? "mhf_alex" : "mhf_steve"; }; // helper method for opening a skin file from +skinpath+ diff --git a/lib/views/index.html.ejs b/lib/views/index.html.ejs index f3d547d..e02792c 100644 --- a/lib/views/index.html.ejs +++ b/lib/views/index.html.ejs @@ -4,7 +4,7 @@ Crafatar – A blazing fast API for Minecraft faces! - <%# FIXME: Use CDN %> + @@ -62,10 +62,9 @@
@@ -74,7 +73,7 @@
- +
@@ -167,7 +166,6 @@

Meta

- In the examples above, you can generally use usernames instead of uuid. However, apart from the special cases MHF_Steve and MHF_Alex this is discouraged as explained below.
You can append .png or any other file extension to the URL path if you like to, but all images are PNG.

@@ -193,8 +191,8 @@
  • scale: The scale factor for renders. <%= config.renders.min_scale %> - <%= config.renders.max_scale %>
  • overlay: Apply the overlay to the avatar. Presence of this parameter implies true. This option was previously known as helm.
  • - default: The fallback to be used when the requested image cannot be served. You can use a custom URL or any uuid.
    - The option defaults to either MHF_Steve or MHF_Alex, depending on the requested UUID. All usernames default to MHF_Steve. + default: The fallback to be used when the requested image cannot be served. You can use a custom URL, any uuid, or MHF_Steve/MHF_Alex.
    + The option defaults to either MHF_Steve or MHF_Alex, depending on Minecraft's default for the requested UUID.
  • @@ -207,13 +205,9 @@

    About Usernames

    - We strongly advise you to use UUIDs instead of usernames! UUIDs never change while usernames do.
    - Looking up players by username has officially been deprecated by Mojang ever since UUIDs were introduced.
    - Crafatar uses a legacy API which updates very slowly to retrieve skins for usernames.
    - Skins come without any details, including whether a player uses the Alex or Steve skin model.
    - Additionally, Mojang has stated that this legacy interface may be disabled anytime, causing all requests to fail. + By disabling a legacy API in 2018, Mojang has made it practically impossible for Crafatar to support usernames. Please use UUIDs instead!

    -

    Malformed usernames are rejected.

    +

    All usernames are rejected.

    diff --git a/test/bulk.sh b/test/bulk.sh index a975c83..ecd91a9 100755 --- a/test/bulk.sh +++ b/test/bulk.sh @@ -17,7 +17,7 @@ if [ -z "$host" ] || [ ! -z "$@" ]; then fi # insert newline after uuids -ids="$(echo | cat 'uuids.txt' - 'usernames.txt')" +ids="$(cat 'uuids.txt')" # `brew install coreutils` on OS X ids="$(shuf <<< "$ids" 2>/dev/null || gshuf <<< "$ids")" diff --git a/test/test.js b/test/test.js index 8b8ed4f..b42fc60 100644 --- a/test/test.js +++ b/test/test.js @@ -23,11 +23,9 @@ var fs = require("fs"); config.server.http_timeout *= 3; var uuids = fs.readFileSync("test/uuids.txt").toString().split(/\r?\n/); -var names = fs.readFileSync("test/usernames.txt").toString().split(/\r?\n/); -// Get a random UUID + name in order to prevent rate limiting +// Get a random UUIDto prevent rate limiting var uuid = uuids[Math.round(Math.random() * (uuids.length - 1))]; -var name = names[Math.round(Math.random() * (names.length - 1))]; // Let's hope these will never be assigned @@ -62,13 +60,6 @@ function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } -var ids = [ - uuid.toLowerCase(), - name.toLowerCase(), - name.toUpperCase(), - uuid.toUpperCase(), -]; - describe("Crafatar", function() { // we might have to make 2 HTTP requests this.timeout(config.server.http_timeout * 2 + 50); @@ -86,10 +77,6 @@ describe("Crafatar", function() { }); describe("UUID/username", function() { - it("empty username is invalid", function(done) { - assert.strictEqual(helpers.id_valid(""), false); - done(); - }); it("non-hex uuid is invalid", function(done) { assert.strictEqual(helpers.id_valid("g098cb60fa8e427cb299793cbd302c9a"), false); done(); @@ -98,18 +85,6 @@ describe("Crafatar", function() { assert.strictEqual(helpers.id_valid(""), false); done(); }); - it("non-alphanumeric username is invalid", function(done) { - assert.strictEqual(helpers.id_valid("usernäme"), false); - done(); - }); - it("dashed username is invalid", function(done) { - assert.strictEqual(helpers.id_valid("user-name"), false); - done(); - }); - it(">16 length username is invalid", function(done) { - assert.strictEqual(helpers.id_valid("ThisNameIsTooLong"), false); - done(); - }); it("lowercase uuid is valid", function(done) { assert.strictEqual(helpers.id_valid("0098cb60fa8e427cb299793cbd302c9a"), true); done(); @@ -122,12 +97,28 @@ describe("Crafatar", function() { assert.strictEqual(helpers.id_valid("0098cb60-fa8e-427c-b299-793cbd302c9a"), true); done(); }); - it("16 chars, underscored, capital, numbered username is valid", function(done) { - assert.strictEqual(helpers.id_valid("__niceUs3rname__"), true); + it("username is invalid", function(done) { + assert.strictEqual(helpers.id_valid("__niceUs3rname__"), false); done(); }); - it("1 char username is valid", function(done) { - assert.strictEqual(helpers.id_valid("a"), true); + it("username alex is invalid", function(done) { + assert.strictEqual(helpers.id_valid("alex"), false); + done(); + }); + it("username mhf_alex is invalid", function(done) { + assert.strictEqual(helpers.id_valid("mhf_alex"), false); + done(); + }); + it("username steve is invalid", function(done) { + assert.strictEqual(helpers.id_valid("steve"), false); + done(); + }); + it("username mhf_steve is invalid", function(done) { + assert.strictEqual(helpers.id_valid("mhf_steve"), false); + done(); + }); + it(">16 length username is invalid", function(done) { + assert.strictEqual(helpers.id_valid("ThisNameIsTooLong"), false); done(); }); it("should not exist (uuid)", function(done) { @@ -138,29 +129,8 @@ describe("Crafatar", function() { done(); }); }); - it("should not exist (username)", function(done) { - networking.get_username_url(rid(), "Steve", 0, function(err, profile) { - assert.ifError(err); - done(); - }); - }); }); describe("Avatar", function() { - it("uuid's account should exist, but skin should not", function(done) { - // profile "Alex" - hoping it'll never have a skin - networking.get_profile(rid(), "ec561538f3fd461daff5086b22154bce", function(err, profile) { - assert.ifError(err); - assert.notStrictEqual(profile, null); - networking.get_uuid_info(profile, "CAPE", function(url) { - assert.strictEqual(url, null); - done(); - }); - }); - }); - it("Username should default to MHF_Steve", function(done) { - assert.strictEqual(skins.default_skin("TestUser"), "mhf_steve"); - done(); - }); for (var a in alex_ids) { var alexid = alex_ids[a]; (function(alex_id) { @@ -190,15 +160,6 @@ describe("Crafatar", function() { done(); }); }); - it("should time out on username info download", function(done) { - var original_timeout = config.server.http_timeout; - config.server.http_timeout = 1; - networking.get_username_url(rid(), "jomo", 0, function(err, url) { - assert.notStrictEqual(["ETIMEDOUT", "ESOCKETTIMEDOUT"].indexOf(err.code), -1); - config.server.http_timeout = original_timeout; - done(); - }); - }); it("should time out on skin download", function(done) { var original_timeout = config.http_timeout; config.server.http_timeout = 1; @@ -299,7 +260,7 @@ describe("Crafatar", function() { }); it("should return correct HTTP response for URL encoded URLs", function(done) { - var url = "http://localhost:3000/%61%76%61%74%61%72%73/%6a%6f%6d%6f"; // avatars/jomo + var url = "http://localhost:3000/%61%76%61%74%61%72%73/%61%65%37%39%35%61%61%38%36%33%32%37%34%30%38%65%39%32%61%62%32%35%63%38%61%35%39%66%33%62%61%31"; // avatars/ae795aa86327408e92ab25c8a59f3ba1 request.get(url, function(error, res, body) { assert.ifError(error); assert.strictEqual(res.statusCode, 201); @@ -312,95 +273,34 @@ describe("Crafatar", function() { }); it("should not fail on simultaneous requests", function(done) { - // do not change "constructor" ! - // it's a reserved property name, we're testing for that - var sids = ["696a82ce41f44b51aa31b8709b8686f0", "constructor"]; - - for (var j in sids) { - var id = sids[j]; - var url = "http://localhost:3000/avatars/" + id; - // 10 requests at once - var requests = 10; - var finished = 0; - function partDone() { - finished++; - if (requests === finished) { - done(); - } - } - function req() { - request.get(url, function(error, res, body) { - assert.ifError(error); - assert.strictEqual(res.statusCode === 201 || res.statusCode === 200, true); - assert_headers(res); - assert(res.headers.etag); - assert.strictEqual(res.headers["content-type"], "image/png"); - assert(body); - partDone(); - }); - } - // make simultanous requests - for (var k = 0; k < requests; k++) { - req(k); + var url = "http://localhost:3000/avatars/696a82ce41f44b51aa31b8709b8686f0"; + // 10 requests at once + var requests = 10; + var finished = 0; + function partDone() { + finished++; + if (requests === finished) { + done(); } } - }); + function req() { + request.get(url, function(error, res, body) { + assert.ifError(error); + assert.strictEqual(res.statusCode === 201 || res.statusCode === 200, true); + assert_headers(res); + assert(res.headers.etag); + assert.strictEqual(res.headers["content-type"], "image/png"); + assert(body); + partDone(); + }); + } + // make simultanous requests + for (var k = 0; k < requests; k++) { + req(k); + } + }); var server_tests = { - "avatar with existing username": { - url: "http://localhost:3000/avatars/jeb_?size=16", - crc32: [3337292777], - }, - "avatar with non-existent username": { - url: "http://localhost:3000/avatars/0?size=16", - crc32: [2416827277, 1243826040], - }, - "avatar with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/avatars/0?size=16&default=mhf_alex", - crc32: [862751081, 809395677], - }, - "avatar with non-existent username defaulting to username": { - url: "http://localhost:3000/avatars/0?size=16&default=jeb_", - crc32: [0], - redirect: "/avatars/jeb_?size=16", - }, - "avatar with non-existent username defaulting to uuid": { - url: "http://localhost:3000/avatars/0?size=16&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16", - }, - "avatar with non-existent username defaulting to url": { - url: "http://localhost:3000/avatars/0?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, - "overlay avatar with existing username": { - url: "http://localhost:3000/avatars/jeb_?size=16&overlay", - crc32: [1710265722], - }, - "overlay avatar with non-existent username": { - url: "http://localhost:3000/avatars/0?size=16&overlay", - crc32: [2416827277, 1243826040], - }, - "overlay avatar with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/avatars/0?size=16&overlay&default=mhf_alex", - crc32: [862751081, 809395677], - }, - "overlay avatar with non-existent username defaulting to username": { - url: "http://localhost:3000/avatars/0?size=16&overlay&default=jeb_", - crc32: [0], - redirect: "/avatars/jeb_?size=16&overlay=", - }, - "overlay avatar with non-existent username defaulting to uuid": { - url: "http://localhost:3000/avatars/0?size=16&overlay&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay=", - }, - "overlay avatar with non-existent username defaulting to url": { - url: "http://localhost:3000/avatars/0?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, "avatar with existing uuid": { url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16", crc32: [3337292777], @@ -413,11 +313,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex", crc32: [862751081, 809395677], }, - "avatar with non-existent uuid defaulting to username": { - url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_", - crc32: [0], - redirect: "/avatars/jeb_?size=16", - }, "avatar with non-existent uuid defaulting to uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6", crc32: [0], @@ -440,11 +335,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex", crc32: [862751081, 809395677], }, - "overlay avatar with non-existent uuid defaulting to username": { - url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_", - crc32: [0], - redirect: "/avatars/jeb_?size=16", - }, "overlay avatar with non-existent uuid defaulting to uuid": { url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6", crc32: [0], @@ -455,19 +345,6 @@ describe("Crafatar", function() { crc32: [0], redirect: "http://example.com/CaseSensitive", }, - "cape with existing username": { - url: "http://localhost:3000/capes/notch", - crc32: [2556702429], - }, - "cape with non-existent username": { - url: "http://localhost:3000/capes/0", - crc32: [0], - }, - "cape with non-existent username defaulting to url": { - url: "http://localhost:3000/capes/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, "cape with existing uuid": { url: "http://localhost:3000/capes/069a79f444e94726a5befca90e38aaf5", crc32: [2556702429], @@ -481,33 +358,6 @@ describe("Crafatar", function() { crc32: [0], redirect: "http://example.com/CaseSensitive", }, - "skin with existing username": { - url: "http://localhost:3000/skins/jeb_", - crc32: [26500336], - }, - "skin with non-existent username": { - url: "http://localhost:3000/skins/0", - crc32: [981937087], - }, - "skin with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/skins/0?default=mhf_alex", - crc32: [2298915739], - }, - "skin with non-existent username defaulting to username": { - url: "http://localhost:3000/skins/0?size=16&default=jeb_", - crc32: [0], - redirect: "/skins/jeb_?size=16", - }, - "skin with non-existent username defaulting to uuid": { - url: "http://localhost:3000/skins/0?size=16&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16", - }, - "skin with non-existent username defaulting to url": { - url: "http://localhost:3000/skins/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, "skin with existing uuid": { url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6", crc32: [26500336], @@ -520,11 +370,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex", crc32: [2298915739], }, - "skin with non-existent uuid defaulting to username": { - url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=jeb_", - crc32: [0], - redirect: "/skins/jeb_?size=16", - }, "skin with non-existent uuid defaulting to uuid": { url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6", crc32: [0], @@ -535,60 +380,6 @@ describe("Crafatar", function() { crc32: [0], redirect: "http://example.com/CaseSensitive", }, - "head render with existing username": { - url: "http://localhost:3000/renders/head/jeb_?scale=2", - crc32: [3487896679, 3001090792], - }, - "head render with non-existent username": { - url: "http://localhost:3000/renders/head/0?scale=2", - crc32: [3257141069, 214248305], - }, - "head render with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/renders/head/0?scale=2&default=mhf_alex", - crc32: [263450586, 3116770561], - }, - "head render with non-existent username defaulting to username": { - url: "http://localhost:3000/avatars/0?scale=2&default=jeb_", - crc32: [0], - redirect: "/avatars/jeb_?scale=2", - }, - "head render with non-existent username defaulting to uuid": { - url: "http://localhost:3000/avatars/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?scale=2", - }, - "head render with non-existent username defaulting to url": { - url: "http://localhost:3000/renders/head/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, - "overlay head render with existing username": { - url: "http://localhost:3000/renders/head/jeb_?scale=2&overlay", - crc32: [762377383, 1726474987], - }, - "overlay head render with non-existent username": { - url: "http://localhost:3000/renders/head/0?scale=2&overlay", - crc32: [3257141069, 214248305], - }, - "overlay head render with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=mhf_alex", - crc32: [263450586, 3116770561], - }, - "overlay head render with non-existent username defaulting to username": { - url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=jeb_", - crc32: [0], - redirect: "/renders/head/jeb_?scale=2&overlay=", - }, - "overlay head render with non-existent username defaulting to uuid": { - url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=", - }, - "overlay head render with non-existent username defaulting to url": { - url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, "head render with existing uuid": { url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2", crc32: [3487896679, 3001090792], @@ -601,11 +392,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=mhf_alex", crc32: [263450586, 3116770561], }, - "head render with non-existent uuid defaulting to username": { - url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=jeb_", - crc32: [0], - redirect: "/renders/head/jeb_?scale=2", - }, "head render with non-existent uuid defaulting to uuid": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6", crc32: [0], @@ -628,11 +414,6 @@ describe("Crafatar", function() { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex", crc32: [263450586, 3116770561], }, - "overlay head with non-existent uuid defaulting to username": { - url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=jeb_", - crc32: [0], - redirect: "/renders/head/jeb_?scale=2&overlay=", - }, "overlay head with non-existent uuid defaulting to uuid": { url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6", crc32: [0], @@ -643,60 +424,6 @@ describe("Crafatar", function() { crc32: [0], redirect: "http://example.com/CaseSensitive", }, - "body render with existing username": { - url: "http://localhost:3000/renders/body/jeb_?scale=2", - crc32: [3127075871, 2595192206], - }, - "body render with non-existent username": { - url: "http://localhost:3000/renders/body/0?scale=2", - crc32: [1046655221, 1620063267], - }, - "body render with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/renders/body/0?scale=2&default=mhf_alex", - crc32: [549240598, 3952648540], - }, - "body render with non-existent username defaulting to username": { - url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_", - crc32: [0], - redirect: "/renders/body/jeb_?scale=2", - }, - "body render with non-existent username defaulting to uuid": { - url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", - }, - "body render with non-existent username defaulting to url": { - url: "http://localhost:3000/renders/body/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, - "overlay body render with existing username": { - url: "http://localhost:3000/renders/body/jeb_?scale=2&overlay", - crc32: [699892097, 2732138694], - }, - "overlay body render with non-existent username": { - url: "http://localhost:3000/renders/body/0?scale=2&overlay", - crc32: [1046655221, 1620063267], - }, - "overlay body render with non-existent username defaulting to mhf_alex": { - url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=mhf_alex", - crc32: [549240598, 3952648540], - }, - "overlay body render with non-existent username defaulting to username": { - url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=jeb_", - crc32: [0], - redirect: "/renders/body/jeb_?scale=2&overlay=", - }, - "overlay body render with non-existent username defaulting to uuid": { - url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6", - crc32: [0], - redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=", - }, - "overlay body render with non-existent username defaulting to url": { - url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", - crc32: [0], - redirect: "http://example.com/CaseSensitive", - }, "body render with existing uuid": { url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", crc32: [3127075871, 2595192206], @@ -709,13 +436,8 @@ describe("Crafatar", function() { url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex", crc32: [549240598, 3952648540], }, - "body render with non-existent uuid defaulting to username": { - url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_", - crc32: [0], - redirect: "/renders/body/jeb_?scale=2", - }, "body render with non-existent uuid defaulting to uuid": { - url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6", + url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6", crc32: [0], redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", }, @@ -784,28 +506,12 @@ describe("Crafatar", function() { }(loc)); } - it("should update the username skin type on uuid request", function(done) { - /*eslint-disable handle-callback-err */ - request.get("http://localhost:3000/renders/body/mhf_alex", function(error, res, body) { - cache.get_details("mhf_alex", function(err, details) { - assert.strictEqual(details.slim, false); - request.get("http://localhost:3000/renders/body/6ab4317889fd490597f60f67d9d76fd9", function(uerror, ures, ubody) { - cache.get_details("mhf_alex", function(cerr, cdetails) { - /*eslint-enable handle-callback-err */ - assert.strictEqual(cdetails.slim, true); - done(); - }); - }); - }); - }); - }); - it("should return 304 on server error", function(done) { var original_debug = config.server.debug_enabled; var original_timeout = config.server.http_timeout; config.server.debug_enabled = false; config.server.http_timeout = 1; - request.get({url: "http://localhost:3000/avatars/whatever", headers: {"If-None-Match": '"some-etag"'}}, function(error, res, body) { + request.get({url: "http://localhost:3000/avatars/0000000000000000000000000000000f", headers: {"If-None-Match": '"some-etag"'}}, function(error, res, body) { assert.ifError(error); assert.ifError(body); assert.strictEqual(res.statusCode, 304); @@ -817,7 +523,7 @@ describe("Crafatar", function() { it("should return a 422 (invalid size)", function(done) { var size = config.avatars.max_size + 1; - request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) { + request.get("http://localhost:3000/avatars/2d5aa9cdaeb049189930461fc9b91cc5?size=" + size, function(error, res, body) { assert.ifError(error); assert.strictEqual(res.statusCode, 422); done(); @@ -826,7 +532,7 @@ describe("Crafatar", function() { it("should return a 422 (invalid scale)", function(done) { var scale = config.renders.max_scale + 1; - request.get("http://localhost:3000/renders/head/Jake_0?scale=" + scale, function(error, res, body) { + request.get("http://localhost:3000/renders/head/2d5aa9cdaeb049189930461fc9b91cc5?scale=" + scale, function(error, res, body) { assert.ifError(error); assert.strictEqual(res.statusCode, 422); done(); @@ -841,12 +547,12 @@ describe("Crafatar", function() { }); }); - // testing all paths for Invalid UserID + // testing all paths for Invalid UUID var locations = ["avatars", "skins", "capes", "renders/body", "renders/head"]; for (var l in locations) { loc = locations[l]; (function(location) { - it("should return a 422 (invalid id " + location + ")", function(done) { + it("should return a 422 (invalid uuid " + location + ")", function(done) { request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) { assert.ifError(error); assert.strictEqual(res.statusCode, 422); @@ -873,14 +579,14 @@ describe("Crafatar", function() { // we have to make sure that we test both a 32x64 and 64x64 skin describe("Networking: Render", function() { - it("should not fail (username, 32x64 skin)", function(done) { - helpers.get_render(rid(), "md_5", 6, true, true, function(err, hash, img) { + it("should not fail (uuid, 32x64 skin)", function(done) { + helpers.get_render(rid(), "af74a02d19cb445bb07f6866a861f783", 6, true, true, function(err, hash, img) { assert.strictEqual(err, null); done(); }); }); - it("should not fail (username, 64x64 skin)", function(done) { - helpers.get_render(rid(), "Jake_0", 6, true, true, function(err, hash, img) { + it("should not fail (uuid, 64x64 skin)", function(done) { + helpers.get_render(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", 6, true, true, function(err, hash, img) { assert.strictEqual(err, null); done(); }); @@ -931,116 +637,74 @@ describe("Crafatar", function() { }); - // DRY with uuid and username tests - for (var i in ids) { - var iid = ids[i]; - var iid_type = iid.length > 16 ? "uuid" : "name"; - // needs an anonymous function because id and id_type aren't constant - (function(n, id, id_type) { - // Mojang's UUID rate limiting is case-insensitive - // so we don't run UUID tests twice - if(n < 3) { - describe("Networking: Avatar", function() { - before(function() { - cache.get_redis().flushall(); - console.log("\n\nRunning tests with " + id_type + " '" + id + "'\n\n"); - }); - it("should be downloaded", function(done) { - helpers.get_avatar(rid(), id, false, 160, function(err, status, image) { - assert.ifError(err); - assert.strictEqual(status, 2); - done(); - }); - }); - it("should be cached", function(done) { - helpers.get_avatar(rid(), id, false, 160, function(err, status, image) { - assert.ifError(err); - assert.strictEqual(status === 0 || status === 1, true); - done(); - }); - }); - if (id.length > 16) { - // can't run 'checked' test due to Mojang's rate limits :( - } else { - it("should be checked", function(done) { - var original_cache_time = config.caching.local; - config.caching.local = 0; - helpers.get_avatar(rid(), id, false, 160, function(err, status, image) { - assert.ifError(err); - assert.strictEqual(status, 3); - config.caching.local = original_cache_time; - done(); - }); - }); - } - }); - describe("Networking: Skin", function() { - it("should not fail (uuid)", function(done) { - helpers.get_skin(rid(), id, function(err, hash, status, img) { - assert.strictEqual(err, null); - done(); - }); - }); - }); - - describe("Networking: Render", function() { - it("should not fail (full body)", function(done) { - helpers.get_render(rid(), id, 6, true, true, function(err, hash, img) { - assert.ifError(err); - done(); - }); - }); - it("should not fail (only head)", function(done) { - helpers.get_render(rid(), id, 6, true, false, function(err, hash, img) { - assert.ifError(err); - done(); - }); - }); - }); - - describe("Networking: Cape", function() { - it("should not fail (possible cape)", function(done) { - helpers.get_cape(rid(), id, function(err, hash, status, img) { - assert.ifError(err); - done(); - }); - }); - }); - } - - - describe("Errors", function() { - before(function() { - if(n >= 3) { - // Notice for tests skipped above - console.log("\n\nSkipping tests with " + id_type + " '" + id + "' due to case-insensitive rate-limiting\n\n"); - } - cache.get_redis().flushall(); - }); - - if (id_type === "uuid") { - // just making sure ... - it("uuid SHOULD be rate limited", function(done) { - networking.get_profile(rid(), id, function() { - networking.get_profile(rid(), id, function(err, profile) { - assert.strictEqual(err.toString(), "HTTP: 429"); - assert.strictEqual(profile, null); - done(); - }); - }); - }); - } else { - it("username should NOT be rate limited (username)", function(done) { - helpers.get_avatar(rid(), id, false, 160, function() { - helpers.get_avatar(rid(), id, false, 160, function(err, status, image) { - assert.strictEqual(err, null); - done(); - }); - }); - }); - } + describe("Networking: Avatar", function() { + before(function() { + cache.get_redis().flushall(); + }); + it("should be downloaded", function(done) { + helpers.get_avatar(rid(), uuid, false, 160, function(err, status, image) { + assert.ifError(err); + assert.strictEqual(status, 2); + done(); }); - }(i, iid, iid_type)); - } + }); + it("should be cached", function(done) { + helpers.get_avatar(rid(), uuid, false, 160, function(err, status, image) { + assert.ifError(err); + assert.strictEqual(status === 0 || status === 1, true); + done(); + }); + }); + }); + + describe("Networking: Skin", function() { + it("should not fail (uuid)", function(done) { + helpers.get_skin(rid(), uuid, function(err, hash, status, img) { + assert.strictEqual(err, null); + done(); + }); + }); + }); + + describe("Networking: Render", function() { + it("should not fail (full body)", function(done) { + helpers.get_render(rid(), uuid, 6, true, true, function(err, hash, img) { + assert.ifError(err); + done(); + }); + }); + it("should not fail (only head)", function(done) { + helpers.get_render(rid(), uuid, 6, true, false, function(err, hash, img) { + assert.ifError(err); + done(); + }); + }); + }); + + describe("Networking: Cape", function() { + it("should not fail (possible cape)", function(done) { + helpers.get_cape(rid(), uuid, function(err, hash, status, img) { + assert.ifError(err); + done(); + }); + }); + }); + + + describe("Errors", function() { + before(function() { + cache.get_redis().flushall(); + }); + + it("uuid SHOULD be rate limited", function(done) { + networking.get_profile(rid(), uuid, function() { + networking.get_profile(rid(), uuid, function(err, profile) { + assert.strictEqual(err.toString(), "HTTP: 429"); + assert.strictEqual(profile, null); + done(); + }); + }); + }); + }); }); \ No newline at end of file