From 7f95a34e293db9e86939c511fd1f58a4e2478e7f Mon Sep 17 00:00:00 2001 From: jomo Date: Sun, 29 Mar 2020 04:53:56 +0200 Subject: [PATCH] simplify http status codes, update website info --- config.js | 8 ++++--- lib/helpers.js | 4 ++-- lib/response.js | 34 +++++++++++++++++--------- lib/views/index.html.ejs | 52 ++++++++++++++++++++++++++-------------- test/test.js | 6 ++--- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/config.js b/config.js index 0c9bd2c..e524106 100644 --- a/config.js +++ b/config.js @@ -35,7 +35,9 @@ var config = { browser: parseInt(process.env.CACHE_BROWSER) || 3600, // If true, redis is flushed on start. // Use this to avoid issues when you have a persistent redis database but an ephemeral storage - ephemeral: process.env.EPHEMERAL_STORAGE === "true" || false, + ephemeral: process.env.EPHEMERAL_STORAGE === "true", + // Used for information on the front page + cloudflare: process.env.CLOUDFLARE === "true" }, // URL of your redis server redis: process.env.REDIS_URL || 'redis://localhost:6379', @@ -47,9 +49,9 @@ var config = { // ms until connection to Mojang is dropped http_timeout: parseInt(process.env.EXTERNAL_HTTP_TIMEOUT) || 2000, // enables logging.debug & editing index page - debug_enabled: process.env.DEBUG === "true" || false, + debug_enabled: process.env.DEBUG === "true", // set to false if you use an external logger that provides timestamps, - log_time: process.env.LOG_TIME === "true" || true, + log_time: process.env.LOG_TIME === "true", // rate limit per second for outgoing requests to the Mojang session server // requests exceeding this limit are skipped and considered failed sessions_rate_limit: parseInt(process.env.SESSIONS_RATE_LIMIT) || Infinity diff --git a/lib/helpers.js b/lib/helpers.js index fdbe479..65beef1 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -240,13 +240,13 @@ exp.get_image_hash = function(rid, userId, type, callback) { } store_images(rid, userId, cache_details, type, function(store_err, new_hash, slim) { if (store_err) { - // we might have a cached hash although an error occured + // an error occured, but we have a cached hash // (e.g. Mojang servers not reachable, using outdated hash) // when hitting the rate limit, let's pretend the request succeeded and bump the TTL var ratelimited = store_err.code === "RATELIMIT"; cache.update_timestamp(rid, userId, !ratelimited, function(err2) { - callback(err2 || store_err, -1, cache_details && cached_hash, slim); + callback(err2 || store_err, 4, cache_details && cached_hash, slim); }); } else { var status = cache_details && (cached_hash === new_hash) ? 3 : 2; diff --git a/lib/response.js b/lib/response.js index daf08cb..19148ca 100644 --- a/lib/response.js +++ b/lib/response.js @@ -3,12 +3,13 @@ var config = require("../config"); var crc = require("crc").crc32; var human_status = { - "-2": "user error", // e.g. invalid size - "-1": "server error", // e.g. mojang/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 + "-2": "user error", // e.g. invalid size + "-1": "server error", // e.g. mojang/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 + 4: "server error;cached" // tried to check but ran into server error, using cached version }; @@ -83,13 +84,24 @@ module.exports = function(request, response, result) { if (result.status === -2) { response.writeHead(result.code || 422, headers); } else if (result.status === -1) { - // 500 responses shouldn't be cached - headers["Cache-Control"] = "private, max-age=0, no-cache"; - response.writeHead(result.code || 500, headers); + // server errors shouldn't be cached + headers["Cache-Control"] = "no-cache, max-age=0"; + if (result.body && result.hash && !result.hash.startsWith("mhf_")) { + headers["Warning"] = '110 Crafatar "Response is Stale"' + headers["Etag"] = etag; + result.code = result.code || 200; + } + if (result.err && result.err.code === "ENOENT") { + result.code = result.code || 500; + } + response.writeHead(result.code || 502, headers); } else { if (result.body) { - headers.Etag = etag; - response.writeHead(result.status === 2 ? 201 : 200, headers); + if (result.status === 4) { + headers["Warning"] = '111 Crafatar "Revalidation Failed"' + } + headers["Etag"] = etag; + response.writeHead(200, headers); } else { response.writeHead(404, headers); } diff --git a/lib/views/index.html.ejs b/lib/views/index.html.ejs index 51deab0..4439fcb 100644 --- a/lib/views/index.html.ejs +++ b/lib/views/index.html.ejs @@ -76,6 +76,7 @@ +

You can use mcuuid.net to find the UUID of a username.

@@ -210,10 +211,12 @@

About Caching

Crafatar checks for skin updates every <%= config.caching.local / 60 %> minutes.
- Images are cached in your browser for <%= config.caching.browser / 60 %> minutes until a new request to Crafatar is made.
- In addition, CloudFlare caches up to 2 hours on a per-url basis. + Images are also cached in your browser for <%= config.caching.browser / 60 %> minutes unless you clear your browser cache. + <% if (config.caching.cloudflare) { %> +
In addition, Cloudflare may cache images as long as your browser would. + <% } %>

-

When you changed your skin you can try clearing your browser cache to see the change faster.

+

After changing your Minecraft skin, you can try clearing your browser cache to see the change faster.

@@ -224,11 +227,25 @@

HTTP Headers

- Responses come with some custom HTTP headers, useful for debugging.
- Please note that these headers may be cached by CloudFlare. + Crafatar always replies with a 200 OK status code when the requested user's skin/cape was found. This is also used in some rare cases when Mojang servers are having issues and the image couldn't be checked for changes, but Crafatar still had a cached version. 502 Bad Gateway and 500 Server Error are used when no skin/cape was found because of Mojang or Crafatar server issues. +

+

+ Note that requests are usually answered with an image (with Steve/Alex skin), even if an error occured! +

+

+ Responses come with some HTTP headers that are useful for debugging. + <% if (config.caching.cloudflare) { %> +
Please note that these headers may be cached by Cloudflare. + <% } %>

    +
  • + Warning: When using a cached image after an error occured. One of: +
      +
    • 110 Crafatar "Response is Stale"
    • +
    • 111 Crafatar "Revalidation Failed"
    • +
  • X-Storage-Type: Details about how the requested image was stored on the server
      @@ -237,27 +254,19 @@
    • checked: Requested skin details, skin cached. (1 external request)
      This happens either when the user removed their skin or when it didn't change.
    • downloaded: Requested skin details, skin downloaded. (2 external requests)
    • -
    • server error: This can happen, for example, when Mojang's servers are down.
      - If possible, a cached image is served instead.
    • +
    • server error: This can happen, for example, when Mojang's servers are down.
    • +
    • server error;cached: Same as server error, but a cached skin was available.
    • user error: You have done something wrong, such as requesting a malformed uuid.
      Check the response body for details.
  • X-Request-ID: The internal ID assigned to this request.
    - If you think something is wrong with your request, please contact us and provide this ID. + If you think something is wrong with your request, please contact us and provide this ID. +
  • + Response-Time: How long it took Crafatar to answer the request, in ms.
- -
-

Contact

- -
-
@@ -282,7 +291,14 @@ Plumeria (Discord) and many more…
+
+

Contact

+
+ @crafatar on Twitter + Issue tracker +
<% if (config.sponsor.sidebar) { %> +
<%- config.sponsor.sidebar %> <% } %> diff --git a/test/test.js b/test/test.js index 11c60f3..5757ec2 100644 --- a/test/test.js +++ b/test/test.js @@ -258,7 +258,7 @@ describe("Crafatar", function() { 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); + assert.strictEqual(res.statusCode, 200); assert_headers(res); assert(res.headers.etag); assert.strictEqual(res.headers["content-type"], "image/png"); @@ -281,7 +281,7 @@ describe("Crafatar", function() { function req() { request.get(url, function(error, res, body) { assert.ifError(error); - assert.strictEqual(res.statusCode === 201 || res.statusCode === 200, true); + assert.strictEqual(res.statusCode, 200); assert_headers(res); assert(res.headers.etag); assert.strictEqual(res.headers["content-type"], "image/png"); @@ -489,7 +489,7 @@ describe("Crafatar", function() { done(); } else { assert.strictEqual(res.headers["content-type"], "image/png"); - assert.strictEqual(res.statusCode, res.headers["x-storage-type"] === "downloaded" ? 201 : 200); + assert.strictEqual(res.statusCode, 200); assert(res.headers.etag); assert.strictEqual(res.headers.etag, '"' + hash + '"'); assert_cache(location.url, res.headers.etag, function() {