Skip to content

Commit

Permalink
fix: detect requests without body correctly (#5290)
Browse files Browse the repository at this point in the history
* fix: detect requests without body correctly

if a request goes through an edge function the content-length header
will not be set and instead the transfer-enconding is set to chunked.

* chore: use header for better readability

* chore: add test

* chore: change to isNan

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
danez and kodiakhq[bot] authored Dec 6, 2022
1 parent fd7db45 commit 57ee933
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 5 deletions.
21 changes: 16 additions & 5 deletions src/lib/functions/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ const buildClientContext = function (headers) {
}
}

const hasBody = (req) =>
// copied from is-type package
// eslint-disable-next-line unicorn/prefer-number-properties
(req.header('transfer-encoding') !== undefined || !isNaN(req.header('content-length'))) &&
// we expect a string or a buffer, because we use the two bodyParsers(text, raw) from express
// eslint-disable-next-line n/prefer-global/buffer
(typeof req.body === 'string' || Buffer.isBuffer(req.body))

export const createHandler = function (options) {
const { config, functionsRegistry } = options

Expand All @@ -62,18 +70,21 @@ export const createHandler = function (options) {
return
}

const isBase64Encoded = shouldBase64Encode(request.headers['content-type'])
const body = request.get('content-length') ? request.body.toString(isBase64Encoded ? 'base64' : 'utf8') : undefined
const isBase64Encoded = shouldBase64Encode(request.header('content-type'))
let body
if (hasBody(request)) {
body = request.body.toString(isBase64Encoded ? 'base64' : 'utf8')
}

let remoteAddress = request.get('x-forwarded-for') || request.connection.remoteAddress || ''
let remoteAddress = request.header('x-forwarded-for') || request.connection.remoteAddress || ''
remoteAddress = remoteAddress
.split(remoteAddress.includes('.') ? ':' : ',')
.pop()
.trim()

let requestPath = request.path
if (request.get('x-netlify-original-pathname')) {
requestPath = request.get('x-netlify-original-pathname')
if (request.header('x-netlify-original-pathname')) {
requestPath = request.header('x-netlify-original-pathname')
delete request.headers['x-netlify-original-pathname']
}
const queryParams = Object.entries(request.query).reduce(
Expand Down
51 changes: 51 additions & 0 deletions tests/integration/400.command.dev.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,57 @@ test('should pass body to functions event for POST requests when redirecting', a
})
})

test('should pass body to functions event for POST requests with passthrough edge function', async (t) => {
await withSiteBuilder('site-with-post-echo-function', async (builder) => {
builder
.withNetlifyToml({
config: {
functions: { directory: 'functions' },
redirects: [{ from: '/api/*', to: '/.netlify/functions/:splat', status: 200 }],
edge_functions: [
{
function: 'passthrough',
path: '/*',
},
],
},
})
.withEdgeFunction({
name: 'passthrough',
handler: async (_, context) => context.next(),
})
.withFunction({
path: 'echo.js',
handler: async (event) => ({
statusCode: 200,
body: JSON.stringify(event),
}),
})

await builder.buildAsync()

await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got
.post(`${server.url}/api/echo?ding=dong`, {
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
body: 'some=thing',
})
.json()

t.is(response.body, 'some=thing')
t.is(response.headers.host, `${server.host}:${server.port}`)
t.is(response.headers['content-type'], 'application/x-www-form-urlencoded')
t.is(response.headers['transfer-encoding'], 'chunked')
t.is(response.httpMethod, 'POST')
t.is(response.isBase64Encoded, false)
t.is(response.path, '/api/echo')
t.deepEqual(response.queryStringParameters, { ding: 'dong' })
})
})
})

test('should return an empty body for a function with no body when redirecting', async (t) => {
await withSiteBuilder('site-with-no-body-function', async (builder) => {
builder
Expand Down

1 comment on commit 57ee933

@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: 247 MB

Please sign in to comment.