use regex + static method to centralize hashedfavicon URL behavior

This commit is contained in:
Nick Krecklow 2020-05-08 16:00:36 -05:00
parent 15814cf86b
commit 04d94a9461
No known key found for this signature in database
GPG Key ID: 5F149FDE156FFA94
2 changed files with 28 additions and 23 deletions

@ -1,4 +1,5 @@
const http = require('http') const http = require('http')
const format = require('util').format
const WebSocket = require('ws') const WebSocket = require('ws')
const finalHttpHandler = require('finalhandler') const finalHttpHandler = require('finalhandler')
@ -6,14 +7,18 @@ const serveStatic = require('serve-static')
const logger = require('./logger') const logger = require('./logger')
const HASHED_FAVICON_URL = '/hashedfavicon_' const HASHED_FAVICON_URL_REGEX = /hashedfavicon_([a-z0-9]{32}).png/g
const HASHED_FAVICON_EXTENSION = '.png'
function getRemoteAddr (req) { function getRemoteAddr (req) {
return req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress return req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress
} }
class Server { class Server {
static getHashedFaviconUrl (hash) {
// Format must be compatible with HASHED_FAVICON_URL_REGEX
return format('/hashedfavicon_%s.png', hash)
}
constructor (app) { constructor (app) {
this._app = app this._app = app
@ -28,24 +33,26 @@ class Server {
this._http = http.createServer((req, res) => { this._http = http.createServer((req, res) => {
logger.log('info', '%s requested: %s', getRemoteAddr(req), req.url) logger.log('info', '%s requested: %s', getRemoteAddr(req), req.url)
if (req.url.startsWith(HASHED_FAVICON_URL) && req.url.endsWith(HASHED_FAVICON_EXTENSION)) { // Test the URL against a regex for hashed favicon URLs
this.handleFaviconRequest(req, res) // Require only 1 match ([0]) and test its first captured group ([1])
} else { // 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 // Attempt to handle req using distServeStatic, otherwise fail over to faviconServeStatic
// If faviconServeStatic fails, pass to finalHttpHandler to terminate // If faviconServeStatic fails, pass to finalHttpHandler to terminate
distServeStatic(req, res, () => { distServeStatic(req, res, () => {
faviconsServeStatic(req, res, finalHttpHandler(req, res)) faviconsServeStatic(req, res, finalHttpHandler(req, res))
}) })
}
}) })
} }
handleFaviconRequest = (req, res) => { handleFaviconRequest = (res, faviconHash) => {
let hash = req.url.substring(HASHED_FAVICON_URL.length)
hash = hash.substring(0, hash.length - HASHED_FAVICON_EXTENSION.length)
for (const serverRegistration of this._app.serverRegistrations) { 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') const buf = Buffer.from(serverRegistration.lastFavicon.split(',')[1], 'base64')
res.writeHead(200, { res.writeHead(200, {
@ -54,13 +61,11 @@ class Server {
'Cache-Control': 'max-age=604800' // Cache hashed favicon for 7 days 'Cache-Control': 'max-age=604800' // Cache hashed favicon for 7 days
}).end(buf) }).end(buf)
return return true
} }
} }
// Terminate request, no match return false
res.writeHead(404)
res.end()
} }
createWebSocketServer () { createWebSocketServer () {

@ -1,13 +1,11 @@
const crypto = require('crypto') const crypto = require('crypto')
const TimeTracker = require('./time') const TimeTracker = require('./time')
const Server = require('./server')
const config = require('../config') const config = require('../config')
const minecraftVersions = require('../minecraft_versions') const minecraftVersions = require('../minecraft_versions')
const HASHED_FAVICON_URL = '/hashedfavicon_'
const HASHED_FAVICON_EXTENSION = '.png'
class ServerRegistration { class ServerRegistration {
serverId serverId
lastFavicon lastFavicon
@ -233,16 +231,18 @@ class ServerRegistration {
if (!this._faviconHasher) { if (!this._faviconHasher) {
this._faviconHasher = crypto.createHash('md5') this._faviconHasher = crypto.createHash('md5')
} }
this.faviconHash = this._faviconHasher.update(favicon).digest('hex').toString() this.faviconHash = this._faviconHasher.update(favicon).digest('hex').toString()
return true return true
} }
return false return false
} }
getFaviconUrl () { getFaviconUrl () {
if (this.faviconHash) { if (this.faviconHash) {
return HASHED_FAVICON_URL + this.faviconHash + HASHED_FAVICON_EXTENSION return Server.getHashedFaviconUrl(this.faviconHash)
} else if (this.data.favicon) { } else if (this.data.favicon) {
return this.data.favicon return this.data.favicon
} }