From 61f650fe8a8496c2b650a049b5e19744ceb478af Mon Sep 17 00:00:00 2001 From: Leo Lamprecht Date: Thu, 5 Jul 2018 20:17:23 +0200 Subject: [PATCH 1/3] Add headers to error responses too --- src/index.js | 55 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/index.js b/src/index.js index a245ca3..ccc3a6a 100644 --- a/src/index.js +++ b/src/index.js @@ -192,21 +192,25 @@ const getHeaders = async (customHeaders = [], current, absolutePath, stats) => { } } - const defaultHeaders = { - 'Last-Modified': stats.mtime.toUTCString(), - 'Content-Length': stats.size, - // Default to "inline", which always tries to render in the browser, - // if that's not working, it will save the file. But to be clear: This - // only happens if it cannot find a appropiate value. - 'Content-Disposition': contentDisposition(base, { - type: 'inline' - }) - }; + let defaultHeaders = {}; + + if (stats) { + defaultHeaders = { + 'Last-Modified': stats.mtime.toUTCString(), + 'Content-Length': stats.size, + // Default to "inline", which always tries to render in the browser, + // if that's not working, it will save the file. But to be clear: This + // only happens if it cannot find a appropiate value. + 'Content-Disposition': contentDisposition(base, { + type: 'inline' + }) + }; - const contentType = mime.contentType(base); + const contentType = mime.contentType(base); - if (contentType) { - defaultHeaders['Content-Type'] = contentType; + if (contentType) { + defaultHeaders['Content-Type'] = contentType; + } } return Object.assign(defaultHeaders, related); @@ -422,7 +426,7 @@ const renderDirectory = async (current, acceptsJSON, handlers, methods, config, return {directory: output}; }; -const sendError = async (response, acceptsJSON, current, handlers, config, spec) => { +const sendError = async (absolutePath, response, acceptsJSON, current, handlers, config, spec) => { const {err: original, message, code, statusCode} = spec; /* istanbul ignore next */ @@ -454,7 +458,7 @@ const sendError = async (response, acceptsJSON, current, handlers, config, spec) } catch (err) { if (err.code !== 'ENOENT') { // eslint-disable-next-line no-use-before-define - return internalError(response, acceptsJSON, current, handlers, config, err); + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); } } @@ -468,7 +472,12 @@ const sendError = async (response, acceptsJSON, current, handlers, config, spec) return; } - response.setHeader('Content-Type', 'text/html; charset=utf-8'); + const headers = await getHeaders(config.headers, current, absolutePath, null); + + response.writeHead(statusCode, Object.assign(headers, { + 'Content-Type': 'text/html; charset=utf-8' + })); + response.end(errorTemplate({statusCode, message})); }; @@ -501,7 +510,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { try { relativePath = decodeURIComponent(url.parse(request.url).pathname); } catch (err) { - return sendError(response, acceptsJSON, current, handlers, config, { + return sendError('/', response, acceptsJSON, current, handlers, config, { statusCode: 400, code: 'bad_request', message: 'Bad Request' @@ -513,7 +522,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { // Prevent path traversal vulnerabilities. We could do this // by ourselves, but using the package covers all the edge cases. if (!isPathInside(absolutePath, current)) { - return sendError(response, acceptsJSON, current, handlers, config, { + return sendError(absolutePath, response, acceptsJSON, current, handlers, config, { statusCode: 400, code: 'bad_request', message: 'Bad Request' @@ -551,7 +560,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { stats = await handlers.stat(absolutePath); } catch (err) { if (err.code !== 'ENOENT') { - return internalError(response, acceptsJSON, current, handlers, config, err); + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); } } } @@ -567,7 +576,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { } } catch (err) { if (err.code !== 'ENOENT') { - return internalError(response, acceptsJSON, current, handlers, config, err); + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); } } } @@ -577,7 +586,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { stats = await handlers.stat(absolutePath); } catch (err) { if (err.code !== 'ENOENT') { - return internalError(response, acceptsJSON, current, handlers, config, err); + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); } } } @@ -599,7 +608,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { } } catch (err) { if (err.code !== 'ENOENT') { - return internalError(response, acceptsJSON, current, handlers, config, err); + return internalError(absolutePath, response, acceptsJSON, current, handlers, config, err); } } @@ -621,7 +630,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { } if (!stats) { - return sendError(response, acceptsJSON, current, handlers, config, { + return sendError(absolutePath, response, acceptsJSON, current, handlers, config, { statusCode: 404, code: 'not_found', message: 'The requested path could not be found' From 02c9623894e0830d8a910f0091f629fd8b702fd5 Mon Sep 17 00:00:00 2001 From: Leo Lamprecht Date: Thu, 5 Jul 2018 20:34:12 +0200 Subject: [PATCH 2/3] Added test --- test/integration.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/integration.js b/test/integration.js index 8b8ce61..e3feaba 100644 --- a/test/integration.js +++ b/test/integration.js @@ -972,3 +972,29 @@ test('error for request with malformed URI', async t => { t.is(text, content); }); +test('error responses get custom headers', async t => { + const url = await getUrl({ + 'public': path.join(fixturesTarget, 'single-directory'), + 'headers': [{ + source: '**', + headers: [{ + key: 'who', + value: 'me' + }] + }] + }); + + const response = await fetch(`${url}/non-existing`); + const text = await response.text(); + + t.is(response.status, 404); + t.is(response.headers.get('who'), 'me'); + + const content = errorTemplate({ + statusCode: 404, + message: 'The requested path could not be found' + }); + + t.is(text, content); +}); + From 072df14c829ac610b77c85f93b8317e61eb354c6 Mon Sep 17 00:00:00 2001 From: Leo Lamprecht Date: Thu, 5 Jul 2018 21:48:49 +0200 Subject: [PATCH 3/3] Overwrite it in a better way --- src/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index ccc3a6a..efe2aba 100644 --- a/src/index.js +++ b/src/index.js @@ -473,11 +473,9 @@ const sendError = async (absolutePath, response, acceptsJSON, current, handlers, } const headers = await getHeaders(config.headers, current, absolutePath, null); + headers['Content-Type'] = 'text/html; charset=utf-8'; - response.writeHead(statusCode, Object.assign(headers, { - 'Content-Type': 'text/html; charset=utf-8' - })); - + response.writeHead(statusCode, headers); response.end(errorTemplate({statusCode, message})); };