Skip to content

Commit

Permalink
[middleware] Support any method when fetching a Request instance (#…
Browse files Browse the repository at this point in the history
…37540)

There was a bug that ignored `Request` options when one was given to the `fetch` function:

```ts
const request = new Request("https://example.vercel.sh", { method: "POST" });
await fetch(request);
```

The code above was expected to make a `POST` request, but instead it
made a `GET` request.

This commit fixes it and adds some tests to verify that fetching with a
`Request` object works as expected, and therefore resolves #37123.

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`
  • Loading branch information
Schniz committed Jun 8, 2022
1 parent 01ecff3 commit 88d0440
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
7 changes: 7 additions & 0 deletions packages/next/lib/pick.ts
@@ -0,0 +1,7 @@
export function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const newObj = {} as Pick<T, K>
for (const key of keys) {
newObj[key] = obj[key]
}
return newObj
}
14 changes: 14 additions & 0 deletions packages/next/server/web/sandbox/context.ts
Expand Up @@ -4,6 +4,7 @@ import { EDGE_UNSUPPORTED_NODE_APIS } from '../../../shared/lib/constants'
import { EdgeRuntime } from 'next/dist/compiled/edge-runtime'
import { readFileSync, promises as fs } from 'fs'
import { validateURL } from '../utils'
import { pick } from '../../../lib/pick'

const WEBPACK_HASH_REGEX =
/__webpack_require__\.h = function\(\) \{ return "[0-9a-f]+"; \}/g
Expand Down Expand Up @@ -134,6 +135,19 @@ async function createModuleContext(options: ModuleContextOptions) {

if (typeof input === 'object' && 'url' in input) {
return __fetch(input.url, {
...pick(input, [
'method',
'body',
'cache',
'credentials',
'integrity',
'keepalive',
'mode',
'redirect',
'referrer',
'referrerPolicy',
'signal',
]),
...init,
headers: {
...Object.fromEntries(input.headers),
Expand Down
91 changes: 91 additions & 0 deletions test/e2e/middleware-fetches-with-any-http-method/index.test.ts
@@ -0,0 +1,91 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { fetchViaHTTP } from 'next-test-utils'

describe('Middleware fetches with any HTTP method', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
'pages/api/ping.js': `
export default (req, res) => {
res.send(JSON.stringify({
method: req.method,
headers: {...req.headers},
}))
}
`,
'middleware.js': `
import { NextResponse } from 'next/server';
const HTTP_ECHO_URL = 'https://http-echo-kou029w.vercel.app/';
export default async (req) => {
const kind = req.nextUrl.searchParams.get('kind')
const handler = handlers[kind] ?? handlers['normal-fetch'];
const response = await handler({url: HTTP_ECHO_URL, method: req.method});
const json = await response.text()
const res = NextResponse.next();
res.headers.set('x-resolved', json ?? '{}');
return res
}
const handlers = {
'new-request': ({url, method}) =>
fetch(new Request(url, { method, headers: { 'x-kind': 'new-request' } })),
'normal-fetch': ({url, method}) =>
fetch(url, { method, headers: { 'x-kind': 'normal-fetch' } })
}
`,
},
dependencies: {},
})
})
afterAll(() => next.destroy())

it('passes the method on a direct fetch request', async () => {
const response = await fetchViaHTTP(
next.url,
'/api/ping',
{},
{ method: 'POST' }
)
const json = await response.json()
expect(json).toMatchObject({
method: 'POST',
})

const headerJson = JSON.parse(response.headers.get('x-resolved'))
expect(headerJson).toMatchObject({
method: 'POST',
headers: {
'x-kind': 'normal-fetch',
},
})
})

it('passes the method when providing a Request object', async () => {
const response = await fetchViaHTTP(
next.url,
'/api/ping',
{ kind: 'new-request' },
{ method: 'POST' }
)
const json = await response.json()
expect(json).toMatchObject({
method: 'POST',
})

const headerJson = JSON.parse(response.headers.get('x-resolved'))
expect(headerJson).toMatchObject({
method: 'POST',
headers: {
'x-kind': 'new-request',
},
})
})
})

0 comments on commit 88d0440

Please sign in to comment.