Skip to content

Commit

Permalink
feat!: bring dev server headers to parity with production (#5490)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: The `x-powered-by` header in Netlify Dev responses was replaced with a `server` header
  • Loading branch information
eduardoboucas authored Feb 21, 2023
1 parent 171a14a commit edad634
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 10 deletions.
14 changes: 14 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
"through2-map": "^3.0.0",
"to-readable-stream": "^2.1.0",
"toml": "^3.0.0",
"ulid": "^2.3.0",
"unixify": "^1.0.0",
"update-notifier": "^6.0.0",
"uuid": "^9.0.0",
Expand Down
1 change: 0 additions & 1 deletion src/lib/edge-functions/headers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const headers = {
Functions: 'x-nf-edge-functions',
Geo: 'x-nf-geo',
Passthrough: 'x-nf-passthrough',
RequestID: 'X-NF-Request-ID',
IP: 'x-nf-client-connection-ip',
Site: 'X-NF-Site-Info',
DebugLogging: 'x-nf-debug-logging',
Expand Down
2 changes: 0 additions & 2 deletions src/lib/edge-functions/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { relative } from 'path'
import { cwd, env } from 'process'

import getAvailablePort from 'get-port'
import { v4 as generateUUID } from 'uuid'

import { NETLIFYDEVERR, NETLIFYDEVWARN, chalk, error as printError, log } from '../../utils/command-helpers.mjs'
import { getGeoLocation } from '../geo-location.mjs'
Expand Down Expand Up @@ -133,7 +132,6 @@ export const initializeProxy = async ({
[headers.Functions]: functionNames.join(','),
[headers.ForwardedHost]: `localhost:${passthroughPort}`,
[headers.Passthrough]: 'passthrough',
[headers.RequestID]: generateUUID(),
[headers.IP]: LOCAL_HOST,
}

Expand Down
2 changes: 2 additions & 0 deletions src/utils/headers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ const handleHeadersErrors = function (errors) {
const getErrorMessage = function ({ message }) {
return message
}

export const NFRequestID = 'x-nf-request-id'
16 changes: 15 additions & 1 deletion src/utils/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import renderErrorTemplate from '../lib/render-error-template.mjs'

import { NETLIFYDEVLOG, NETLIFYDEVWARN, log, chalk } from './command-helpers.mjs'
import createStreamPromise from './create-stream-promise.mjs'
import { headersForPath, parseHeaders } from './headers.mjs'
import { headersForPath, parseHeaders, NFRequestID } from './headers.mjs'
import { generateRequestID } from './request-id.mjs'
import { createRewriter, onChanges } from './rules-proxy.mjs'
import { signRedirect } from './sign-redirect.mjs'

Expand Down Expand Up @@ -369,6 +370,11 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,
res.end(message)
})
proxy.on('proxyReq', (proxyReq, req) => {
const requestID = generateRequestID()

proxyReq.setHeader(NFRequestID, requestID)
req.headers[NFRequestID] = requestID

if (isEdgeFunctionsRequest(req)) {
handleProxyRequest(req, proxyReq)
}
Expand All @@ -383,6 +389,14 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,
}
})
proxy.on('proxyRes', (proxyRes, req, res) => {
res.setHeader('server', 'Netlify')

const requestID = req.headers[NFRequestID]

if (requestID) {
res.setHeader(NFRequestID, requestID)
}

if (proxyRes.statusCode === 404 || proxyRes.statusCode === 403) {
if (req.alternativePaths && req.alternativePaths.length !== 0) {
req.url = req.alternativePaths.shift()
Expand Down
4 changes: 4 additions & 0 deletions src/utils/request-id.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @ts-check
import { ulid } from 'ulid'

export const generateRequestID = () => ulid()
3 changes: 2 additions & 1 deletion src/utils/static-server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const startStaticServer = async ({ settings }) => {
})

server.addHook('onRequest', (req, reply, done) => {
reply.header('X-Powered-by', 'netlify-dev')
reply.header('age', '0')
reply.header('cache-control', 'public, max-age=0, must-revalidate')
const validMethods = ['GET', 'HEAD']
if (!validMethods.includes(req.method)) {
reply.code(405).send('Method Not Allowed')
Expand Down
5 changes: 3 additions & 2 deletions tests/integration/100.command.dev.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ test('Serves an Edge Function that terminates a response', async (t) => {
},
])
.withEdgeFunction({
handler: () => new Response('Hello world'),
handler: (req) => new Response(req.headers.get('x-nf-request-id')),
name: 'hello',
})

Expand All @@ -208,7 +208,8 @@ test('Serves an Edge Function that terminates a response', async (t) => {
const response = await got(`${server.url}/edge-function`)

t.is(response.statusCode, 200)
t.is(response.body, 'Hello world')
t.is(response.body.length, 26)
t.is(response.body, response.headers['x-nf-request-id'])
})
})
})
Expand Down
12 changes: 9 additions & 3 deletions tests/integration/130.eleventy.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ test('force rewrite', async (t) => {
test('functions rewrite echo without body', async (t) => {
const { host, port, url } = t.context.server
const response = await got(`${url}/api/echo?ding=dong`).json()
const { 'x-nf-request-id': requestID, ...headers } = response.headers

t.is(response.body, undefined)
t.deepEqual(response.headers, {
t.deepEqual(headers, {
accept: 'application/json',
'accept-encoding': 'gzip, deflate, br',
'client-ip': clientIP,
Expand All @@ -67,6 +68,7 @@ test('functions rewrite echo without body', async (t) => {
'user-agent': 'got (https://github.com/sindresorhus/got)',
'x-forwarded-for': originalIP,
})
t.is(requestID.length, 26)
t.is(response.httpMethod, 'GET')
t.is(response.isBase64Encoded, true)
t.is(response.path, '/api/echo')
Expand All @@ -83,9 +85,10 @@ test('functions rewrite echo with body', async (t) => {
body: 'some=thing',
})
.json()
const { 'x-nf-request-id': requestID, ...headers } = response.headers

t.is(response.body, 'some=thing')
t.deepEqual(response.headers, {
t.deepEqual(headers, {
accept: 'application/json',
'accept-encoding': 'gzip, deflate, br',
'client-ip': clientIP,
Expand All @@ -96,6 +99,7 @@ test('functions rewrite echo with body', async (t) => {
'user-agent': 'got (https://github.com/sindresorhus/got)',
'x-forwarded-for': originalIP,
})
t.is(requestID.length, 26)
t.is(response.httpMethod, 'POST')
t.is(response.isBase64Encoded, false)
t.is(response.path, '/api/echo')
Expand All @@ -105,8 +109,9 @@ test('functions rewrite echo with body', async (t) => {
test('functions echo with multiple query params', async (t) => {
const { host, port, url } = t.context.server
const response = await got(`${url}/.netlify/functions/echo?category=a&category=b`).json()
const { 'x-nf-request-id': requestID, ...headers } = response.headers

t.deepEqual(response.headers, {
t.deepEqual(headers, {
accept: 'application/json',
'accept-encoding': 'gzip, deflate, br',
'client-ip': clientIP,
Expand All @@ -115,6 +120,7 @@ test('functions echo with multiple query params', async (t) => {
'user-agent': 'got (https://github.com/sindresorhus/got)',
'x-forwarded-for': originalIP,
})
t.is(requestID.length, 26)
t.is(response.httpMethod, 'GET')
t.is(response.isBase64Encoded, true)
t.is(response.path, '/.netlify/functions/echo')
Expand Down

1 comment on commit edad634

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

  • Package size: 263 MB

Please sign in to comment.