From cce3620d3a47e0afef81dbe63127083ff34dffc0 Mon Sep 17 00:00:00 2001 From: Phillip9587 Date: Wed, 5 Mar 2025 18:06:38 +0100 Subject: [PATCH 1/2] feat: add `text/plain` fallback response Co-authored-by: Douglas Wilson --- HISTORY.md | 5 +++++ index.js | 55 +++++++++++++++++++++++++++++++++++++++++----------- package.json | 1 + test/test.js | 44 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 4bc1850..af93d23 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +unreleased +========== + + * feat: add `text/plain` fallback response + v2.1.0 / 2025-03-05 ================== diff --git a/index.js b/index.js index bf15e48..ee1b70f 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ * @private */ +var accepts = require('accepts') var debug = require('debug')('finalhandler') var encodeUrl = require('encodeurl') var escapeHtml = require('escape-html') @@ -32,21 +33,40 @@ var isFinished = onFinished.isFinished * @private */ -function createHtmlDocument (message) { - var body = escapeHtml(message) +function createHtmlBody (message) { + var msg = escapeHtml(message) .replaceAll('\n', '
') .replaceAll(' ', '  ') - return '\n' + + var html = '\n' + '\n' + '\n' + '\n' + 'Error\n' + '\n' + '\n' + - '
' + body + '
\n' + + '
' + msg + '
\n' + '\n' + '\n' + + var body = Buffer.from(html, 'utf8') + body.type = 'text/html; charset=utf-8' + return body +} + +/** + * Get plain text body string + * + * @param {number} status + * @param {string} message + * @return {Buffer} + * @private + */ + +function createTextBody (message) { + var body = Buffer.from(message + '\n', 'utf8') + body.type = 'text/plain; charset=utf-8' + return body } /** @@ -79,6 +99,7 @@ function finalhandler (req, res, options) { var headers var msg var status + var body // ignore 404 on in-flight response if (!err && res.headersSent) { @@ -123,8 +144,23 @@ function finalhandler (req, res, options) { return } + // negotiate + var accept = accepts(req) + var type = accept.types('html') + + // construct body + switch (type) { + case 'html': + body = createHtmlBody(msg) + break + default: + // default to plain text + body = createTextBody(msg) + break + } + // send response - send(req, res, status, headers, msg) + send(req, res, status, headers, body) } } @@ -241,11 +277,8 @@ function getResponseStatusCode (res) { * @private */ -function send (req, res, status, headers, message) { +function send (req, res, status, headers, body) { function write () { - // response body - var body = createHtmlDocument(message) - // response status res.statusCode = status @@ -268,8 +301,8 @@ function send (req, res, status, headers, message) { res.setHeader('X-Content-Type-Options', 'nosniff') // standard headers - res.setHeader('Content-Type', 'text/html; charset=utf-8') - res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8')) + res.setHeader('Content-Type', body.type) + res.setHeader('Content-Length', body.length) if (req.method === 'HEAD') { res.end() diff --git a/package.json b/package.json index 992b306..2c64ad6 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "license": "MIT", "repository": "pillarjs/finalhandler", "dependencies": { + "accepts": "^2.0.0", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", diff --git a/test/test.js b/test/test.js index 516e088..bfb9ddd 100644 --- a/test/test.js +++ b/test/test.js @@ -297,6 +297,28 @@ var topDescribe = function (type, createServer) { test.write(buf) test.expect(404, done) }) + + describe('when HTML acceptable', function () { + it('should respond with HTML', function (done) { + var server = createServer() + wrapper(request(server) + .get('/foo')) + .set('Accept', 'text/html') + .expect('Content-Type', 'text/html; charset=utf-8') + .expect(404, /= 400', function (done) { var server = createServer(function (req, res) { From 15f5fcaf48ab1bce733f843f843a750e0d34b7ca Mon Sep 17 00:00:00 2001 From: Phillip9587 Date: Wed, 5 Mar 2025 18:32:18 +0100 Subject: [PATCH 2/2] refactor: switch to `negotiator` instead of `accepts` for content negotiation --- index.js | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index ee1b70f..6834b0d 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ * @private */ -var accepts = require('accepts') +var Negotiator = require('negotiator') var debug = require('debug')('finalhandler') var encodeUrl = require('encodeurl') var escapeHtml = require('escape-html') @@ -145,12 +145,12 @@ function finalhandler (req, res, options) { } // negotiate - var accept = accepts(req) - var type = accept.types('html') + var negotiator = new Negotiator(req) + var type = negotiator.mediaType(['text/html']) // construct body switch (type) { - case 'html': + case 'text/html': body = createHtmlBody(msg) break default: diff --git a/package.json b/package.json index 2c64ad6..99c92ed 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "license": "MIT", "repository": "pillarjs/finalhandler", "dependencies": { - "accepts": "^2.0.0", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", + "negotiator": "^1.0.0", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1"