From 04d94a9461f71b24ecd6e9b290ffeb3828ebfed5 Mon Sep 17 00:00:00 2001 From: Nick Krecklow Date: Fri, 8 May 2020 16:00:36 -0500 Subject: [PATCH] use regex + static method to centralize hashedfavicon URL behavior --- lib/server.js | 43 ++++++++++++++++++++++++------------------- lib/servers.js | 8 ++++---- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/lib/server.js b/lib/server.js index 063b484..1dfbbad 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,4 +1,5 @@ const http = require('http') +const format = require('util').format const WebSocket = require('ws') const finalHttpHandler = require('finalhandler') @@ -6,14 +7,18 @@ const serveStatic = require('serve-static') const logger = require('./logger') -const HASHED_FAVICON_URL = '/hashedfavicon_' -const HASHED_FAVICON_EXTENSION = '.png' +const HASHED_FAVICON_URL_REGEX = /hashedfavicon_([a-z0-9]{32}).png/g function getRemoteAddr (req) { return req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress } class Server { + static getHashedFaviconUrl (hash) { + // Format must be compatible with HASHED_FAVICON_URL_REGEX + return format('/hashedfavicon_%s.png', hash) + } + constructor (app) { this._app = app @@ -28,24 +33,26 @@ class Server { this._http = http.createServer((req, res) => { logger.log('info', '%s requested: %s', getRemoteAddr(req), req.url) - if (req.url.startsWith(HASHED_FAVICON_URL) && req.url.endsWith(HASHED_FAVICON_EXTENSION)) { - this.handleFaviconRequest(req, res) - } else { - // Attempt to handle req using distServeStatic, otherwise fail over to faviconServeStatic - // If faviconServeStatic fails, pass to finalHttpHandler to terminate - distServeStatic(req, res, () => { - faviconsServeStatic(req, res, finalHttpHandler(req, res)) - }) + // Test the URL against a regex for hashed favicon URLs + // Require only 1 match ([0]) and test its first captured group ([1]) + // Any invalid value or hit miss will pass into static handlers below + const faviconHash = [...req.url.matchAll(HASHED_FAVICON_URL_REGEX)] + + if (faviconHash.length === 1 && this.handleFaviconRequest(res, faviconHash[0][1])) { + return } + + // Attempt to handle req using distServeStatic, otherwise fail over to faviconServeStatic + // If faviconServeStatic fails, pass to finalHttpHandler to terminate + distServeStatic(req, res, () => { + faviconsServeStatic(req, res, finalHttpHandler(req, res)) + }) }) } - handleFaviconRequest = (req, res) => { - let hash = req.url.substring(HASHED_FAVICON_URL.length) - hash = hash.substring(0, hash.length - HASHED_FAVICON_EXTENSION.length) - + handleFaviconRequest = (res, faviconHash) => { for (const serverRegistration of this._app.serverRegistrations) { - if (serverRegistration.faviconHash && serverRegistration.faviconHash === hash) { + if (serverRegistration.faviconHash && serverRegistration.faviconHash === faviconHash) { const buf = Buffer.from(serverRegistration.lastFavicon.split(',')[1], 'base64') res.writeHead(200, { @@ -54,13 +61,11 @@ class Server { 'Cache-Control': 'max-age=604800' // Cache hashed favicon for 7 days }).end(buf) - return + return true } } - // Terminate request, no match - res.writeHead(404) - res.end() + return false } createWebSocketServer () { diff --git a/lib/servers.js b/lib/servers.js index 11b8d3d..064903b 100644 --- a/lib/servers.js +++ b/lib/servers.js @@ -1,13 +1,11 @@ const crypto = require('crypto') const TimeTracker = require('./time') +const Server = require('./server') const config = require('../config') const minecraftVersions = require('../minecraft_versions') -const HASHED_FAVICON_URL = '/hashedfavicon_' -const HASHED_FAVICON_EXTENSION = '.png' - class ServerRegistration { serverId lastFavicon @@ -233,16 +231,18 @@ class ServerRegistration { if (!this._faviconHasher) { this._faviconHasher = crypto.createHash('md5') } + this.faviconHash = this._faviconHasher.update(favicon).digest('hex').toString() return true } + return false } getFaviconUrl () { if (this.faviconHash) { - return HASHED_FAVICON_URL + this.faviconHash + HASHED_FAVICON_EXTENSION + return Server.getHashedFaviconUrl(this.faviconHash) } else if (this.data.favicon) { return this.data.favicon }