Skip to content

Commit e3e0a68

Browse files
nkzawarauchg
authored andcommitted
Fix handling http methods (vercel#748)
* support HEAD method * respond with 501 if method is not GET or HEAD
1 parent 1dc52db commit e3e0a68

File tree

3 files changed

+64
-53
lines changed

3 files changed

+64
-53
lines changed

server/index.js

+57-42
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { resolve, join } from 'path'
22
import { parse } from 'url'
33
import fs from 'mz/fs'
4-
import http from 'http'
4+
import http, { STATUS_CODES } from 'http'
55
import {
66
renderToHTML,
77
renderErrorToHTML,
@@ -38,7 +38,7 @@ export default class Server {
3838
.catch((err) => {
3939
if (!this.quiet) console.error(err)
4040
res.statusCode = 500
41-
res.end('error')
41+
res.end(STATUS_CODES[500])
4242
})
4343
}
4444
}
@@ -67,43 +67,52 @@ export default class Server {
6767
}
6868

6969
defineRoutes () {
70-
this.router.get('/_next-prefetcher.js', async (req, res, params) => {
71-
const p = join(__dirname, '../client/next-prefetcher-bundle.js')
72-
await serveStatic(req, res, p)
73-
})
74-
75-
this.router.get('/_next/:buildId/main.js', async (req, res, params) => {
76-
this.handleBuildId(params.buildId, res)
77-
const p = join(this.dir, '.next/main.js')
78-
await serveStaticWithGzip(req, res, p)
79-
})
80-
81-
this.router.get('/_next/:buildId/commons.js', async (req, res, params) => {
82-
this.handleBuildId(params.buildId, res)
83-
const p = join(this.dir, '.next/commons.js')
84-
await serveStaticWithGzip(req, res, p)
85-
})
86-
87-
this.router.get('/_next/:buildId/pages/:path*', async (req, res, params) => {
88-
this.handleBuildId(params.buildId, res)
89-
const paths = params.path || ['index']
90-
const pathname = `/${paths.join('/')}`
91-
await this.renderJSON(req, res, pathname)
92-
})
93-
94-
this.router.get('/_next/:path+', async (req, res, params) => {
95-
const p = join(__dirname, '..', 'client', ...(params.path || []))
96-
await serveStatic(req, res, p)
97-
})
98-
this.router.get('/static/:path+', async (req, res, params) => {
99-
const p = join(this.dir, 'static', ...(params.path || []))
100-
await serveStatic(req, res, p)
101-
})
70+
const routes = {
71+
'/_next-prefetcher.js': async (req, res, params) => {
72+
const p = join(__dirname, '../client/next-prefetcher-bundle.js')
73+
await this.serveStatic(req, res, p)
74+
},
75+
76+
'/_next/:buildId/main.js': async (req, res, params) => {
77+
this.handleBuildId(params.buildId, res)
78+
const p = join(this.dir, '.next/main.js')
79+
await this.serveStaticWithGzip(req, res, p)
80+
},
81+
82+
'/_next/:buildId/commons.js': async (req, res, params) => {
83+
this.handleBuildId(params.buildId, res)
84+
const p = join(this.dir, '.next/commons.js')
85+
await this.serveStaticWithGzip(req, res, p)
86+
},
87+
88+
'/_next/:buildId/pages/:path*': async (req, res, params) => {
89+
this.handleBuildId(params.buildId, res)
90+
const paths = params.path || ['index']
91+
const pathname = `/${paths.join('/')}`
92+
await this.renderJSON(req, res, pathname)
93+
},
94+
95+
'/_next/:path+': async (req, res, params) => {
96+
const p = join(__dirname, '..', 'client', ...(params.path || []))
97+
await this.serveStatic(req, res, p)
98+
},
99+
100+
'/static/:path+': async (req, res, params) => {
101+
const p = join(this.dir, 'static', ...(params.path || []))
102+
await this.serveStatic(req, res, p)
103+
},
104+
105+
'/:path*': async (req, res) => {
106+
const { pathname, query } = parse(req.url, true)
107+
await this.render(req, res, pathname, query)
108+
}
109+
}
102110

103-
this.router.get('/:path*', async (req, res) => {
104-
const { pathname, query } = parse(req.url, true)
105-
await this.render(req, res, pathname, query)
106-
})
111+
for (const method of ['GET', 'HEAD']) {
112+
for (const p of Object.keys(routes)) {
113+
this.router.add(method, p, routes[p])
114+
}
115+
}
107116
}
108117

109118
async start (port) {
@@ -125,8 +134,14 @@ export default class Server {
125134
const fn = this.router.match(req, res)
126135
if (fn) {
127136
await fn()
128-
} else {
137+
return
138+
}
139+
140+
if (req.method === 'GET' || req.method === 'HEAD') {
129141
await this.render404(req, res)
142+
} else {
143+
res.statusCode = 501
144+
res.end(STATUS_CODES[501])
130145
}
131146
}
132147

@@ -135,7 +150,7 @@ export default class Server {
135150
res.setHeader('X-Powered-By', `Next.js ${pkg.version}`)
136151
}
137152
const html = await this.renderToHTML(req, res, pathname, query)
138-
sendHTML(res, html)
153+
sendHTML(res, html, req.method)
139154
}
140155

141156
async renderToHTML (req, res, pathname, query) {
@@ -163,7 +178,7 @@ export default class Server {
163178

164179
async renderError (err, req, res, pathname, query) {
165180
const html = await this.renderErrorToHTML(err, req, res, pathname, query)
166-
sendHTML(res, html)
181+
sendHTML(res, html, req.method)
167182
}
168183

169184
async renderErrorToHTML (err, req, res, pathname, query) {
@@ -191,7 +206,7 @@ export default class Server {
191206
async render404 (req, res) {
192207
const { pathname, query } = parse(req.url, true)
193208
res.statusCode = 404
194-
this.renderErrorToHTML(null, req, res, pathname, query)
209+
this.renderError(null, req, res, pathname, query)
195210
}
196211

197212
async renderJSON (req, res, page) {

server/render.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import App from '../lib/app'
1414

1515
export async function render (req, res, pathname, query, opts) {
1616
const html = await renderToHTML(req, res, pathname, opts)
17-
sendHTML(res, html)
17+
sendHTML(res, html, req.method)
1818
}
1919

2020
export function renderToHTML (req, res, pathname, query, opts) {
@@ -23,7 +23,7 @@ export function renderToHTML (req, res, pathname, query, opts) {
2323

2424
export async function renderError (err, req, res, pathname, query, opts) {
2525
const html = await renderErrorToHTML(err, req, res, query, opts)
26-
sendHTML(res, html)
26+
sendHTML(res, html, req.method)
2727
}
2828

2929
export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) {
@@ -111,24 +111,24 @@ export async function renderErrorJSON (err, req, res, { dir = process.cwd(), dev
111111
sendJSON(res, {
112112
component,
113113
err: err && dev ? errorToJSON(err) : null
114-
})
114+
}, req.method)
115115
}
116116

117-
export function sendHTML (res, html) {
117+
export function sendHTML (res, html, method) {
118118
if (res.finished) return
119119

120120
res.setHeader('Content-Type', 'text/html')
121121
res.setHeader('Content-Length', Buffer.byteLength(html))
122-
res.end(html)
122+
res.end(method === 'HEAD' ? null : html)
123123
}
124124

125-
export function sendJSON (res, obj) {
125+
export function sendJSON (res, obj, method) {
126126
if (res.finished) return
127127

128128
const json = JSON.stringify(obj)
129129
res.setHeader('Content-Type', 'application/json')
130130
res.setHeader('Content-Length', Buffer.byteLength(json))
131-
res.end(json)
131+
res.end(method === 'HEAD' ? null : json)
132132
}
133133

134134
function errorToJSON (err) {

server/router.js

-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ export default class Router {
88
this.routes = new Map()
99
}
1010

11-
get (path, fn) {
12-
this.add('GET', path, fn)
13-
}
14-
1511
add (method, path, fn) {
1612
const routes = this.routes.get(method) || new Set()
1713
routes.add({ match: route(path), fn })

0 commit comments

Comments
 (0)