From e3e0a68b9a8b2276faad51fa73965f479727069c Mon Sep 17 00:00:00 2001 From: Naoyuki Kanezawa Date: Fri, 13 Jan 2017 00:38:43 +0900 Subject: [PATCH] Fix handling http methods (#748) * support HEAD method * respond with 501 if method is not GET or HEAD --- server/index.js | 91 ++++++++++++++++++++++++++++-------------------- server/render.js | 14 ++++---- server/router.js | 4 --- 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/server/index.js b/server/index.js index 1db0a7b2..494e4422 100644 --- a/server/index.js +++ b/server/index.js @@ -1,7 +1,7 @@ import { resolve, join } from 'path' import { parse } from 'url' import fs from 'mz/fs' -import http from 'http' +import http, { STATUS_CODES } from 'http' import { renderToHTML, renderErrorToHTML, @@ -38,7 +38,7 @@ export default class Server { .catch((err) => { if (!this.quiet) console.error(err) res.statusCode = 500 - res.end('error') + res.end(STATUS_CODES[500]) }) } } @@ -67,43 +67,52 @@ export default class Server { } defineRoutes () { - this.router.get('/_next-prefetcher.js', async (req, res, params) => { - const p = join(__dirname, '../client/next-prefetcher-bundle.js') - await serveStatic(req, res, p) - }) + const routes = { + '/_next-prefetcher.js': async (req, res, params) => { + const p = join(__dirname, '../client/next-prefetcher-bundle.js') + await this.serveStatic(req, res, p) + }, - this.router.get('/_next/:buildId/main.js', async (req, res, params) => { - this.handleBuildId(params.buildId, res) - const p = join(this.dir, '.next/main.js') - await serveStaticWithGzip(req, res, p) - }) + '/_next/:buildId/main.js': async (req, res, params) => { + this.handleBuildId(params.buildId, res) + const p = join(this.dir, '.next/main.js') + await this.serveStaticWithGzip(req, res, p) + }, - this.router.get('/_next/:buildId/commons.js', async (req, res, params) => { - this.handleBuildId(params.buildId, res) - const p = join(this.dir, '.next/commons.js') - await serveStaticWithGzip(req, res, p) - }) + '/_next/:buildId/commons.js': async (req, res, params) => { + this.handleBuildId(params.buildId, res) + const p = join(this.dir, '.next/commons.js') + await this.serveStaticWithGzip(req, res, p) + }, - this.router.get('/_next/:buildId/pages/:path*', async (req, res, params) => { - this.handleBuildId(params.buildId, res) - const paths = params.path || ['index'] - const pathname = `/${paths.join('/')}` - await this.renderJSON(req, res, pathname) - }) + '/_next/:buildId/pages/:path*': async (req, res, params) => { + this.handleBuildId(params.buildId, res) + const paths = params.path || ['index'] + const pathname = `/${paths.join('/')}` + await this.renderJSON(req, res, pathname) + }, - this.router.get('/_next/:path+', async (req, res, params) => { - const p = join(__dirname, '..', 'client', ...(params.path || [])) - await serveStatic(req, res, p) - }) - this.router.get('/static/:path+', async (req, res, params) => { - const p = join(this.dir, 'static', ...(params.path || [])) - await serveStatic(req, res, p) - }) + '/_next/:path+': async (req, res, params) => { + const p = join(__dirname, '..', 'client', ...(params.path || [])) + await this.serveStatic(req, res, p) + }, - this.router.get('/:path*', async (req, res) => { - const { pathname, query } = parse(req.url, true) - await this.render(req, res, pathname, query) - }) + '/static/:path+': async (req, res, params) => { + const p = join(this.dir, 'static', ...(params.path || [])) + await this.serveStatic(req, res, p) + }, + + '/:path*': async (req, res) => { + const { pathname, query } = parse(req.url, true) + await this.render(req, res, pathname, query) + } + } + + for (const method of ['GET', 'HEAD']) { + for (const p of Object.keys(routes)) { + this.router.add(method, p, routes[p]) + } + } } async start (port) { @@ -125,8 +134,14 @@ export default class Server { const fn = this.router.match(req, res) if (fn) { await fn() - } else { + return + } + + if (req.method === 'GET' || req.method === 'HEAD') { await this.render404(req, res) + } else { + res.statusCode = 501 + res.end(STATUS_CODES[501]) } } @@ -135,7 +150,7 @@ export default class Server { res.setHeader('X-Powered-By', `Next.js ${pkg.version}`) } const html = await this.renderToHTML(req, res, pathname, query) - sendHTML(res, html) + sendHTML(res, html, req.method) } async renderToHTML (req, res, pathname, query) { @@ -163,7 +178,7 @@ export default class Server { async renderError (err, req, res, pathname, query) { const html = await this.renderErrorToHTML(err, req, res, pathname, query) - sendHTML(res, html) + sendHTML(res, html, req.method) } async renderErrorToHTML (err, req, res, pathname, query) { @@ -191,7 +206,7 @@ export default class Server { async render404 (req, res) { const { pathname, query } = parse(req.url, true) res.statusCode = 404 - this.renderErrorToHTML(null, req, res, pathname, query) + this.renderError(null, req, res, pathname, query) } async renderJSON (req, res, page) { diff --git a/server/render.js b/server/render.js index 7c59c5b3..a9ea7648 100644 --- a/server/render.js +++ b/server/render.js @@ -14,7 +14,7 @@ import App from '../lib/app' export async function render (req, res, pathname, query, opts) { const html = await renderToHTML(req, res, pathname, opts) - sendHTML(res, html) + sendHTML(res, html, req.method) } export function renderToHTML (req, res, pathname, query, opts) { @@ -23,7 +23,7 @@ export function renderToHTML (req, res, pathname, query, opts) { export async function renderError (err, req, res, pathname, query, opts) { const html = await renderErrorToHTML(err, req, res, query, opts) - sendHTML(res, html) + sendHTML(res, html, req.method) } export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) { @@ -111,24 +111,24 @@ export async function renderErrorJSON (err, req, res, { dir = process.cwd(), dev sendJSON(res, { component, err: err && dev ? errorToJSON(err) : null - }) + }, req.method) } -export function sendHTML (res, html) { +export function sendHTML (res, html, method) { if (res.finished) return res.setHeader('Content-Type', 'text/html') res.setHeader('Content-Length', Buffer.byteLength(html)) - res.end(html) + res.end(method === 'HEAD' ? null : html) } -export function sendJSON (res, obj) { +export function sendJSON (res, obj, method) { if (res.finished) return const json = JSON.stringify(obj) res.setHeader('Content-Type', 'application/json') res.setHeader('Content-Length', Buffer.byteLength(json)) - res.end(json) + res.end(method === 'HEAD' ? null : json) } function errorToJSON (err) { diff --git a/server/router.js b/server/router.js index 849c03ed..f15b9e4d 100644 --- a/server/router.js +++ b/server/router.js @@ -8,10 +8,6 @@ export default class Router { this.routes = new Map() } - get (path, fn) { - this.add('GET', path, fn) - } - add (method, path, fn) { const routes = this.routes.get(method) || new Set() routes.add({ match: route(path), fn })