Skip to content

Commit

Permalink
Merge branch 'canary' into fix-bypassing-image-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
shuding authored Mar 24, 2021
2 parents 8a1df50 + 75c721c commit a7c7fd7
Show file tree
Hide file tree
Showing 17 changed files with 1,188 additions and 18 deletions.
94 changes: 94 additions & 0 deletions docs/api-reference/next.config.js/headers.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,100 @@ module.exports = {
}
```

## Header, Cookie, and Query Matching

To only apply a header when either header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the header to be applied.

`has` items have the following fields:

- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`.
- `key`: `String` - the key from the selected type to match against.
- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`.

```js
module.exports = {
async headers() {
return [
// if the header `x-add-header` is present,
// the `x-another-header` header will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-add-header',
},
],
headers: [
{
key: 'x-another-header',
value: 'hello',
},
],
},
// if the source, query, and cookie are matched,
// the `x-authorized` header will be applied
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
headers: [
{
key: 'x-authorized',
value: ':authorized',
},
],
},
// if the header `x-authorized` is present and
// contains a matching value, the `x-another-header` will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
headers: [
{
key: 'x-another-header',
value: ':authorized',
},
],
},
// if the host is `example.com`,
// this header will be applied
{
source: '/:path*',
has: [
{
type: 'host',
value: 'example.com',
},
],
headers: [
{
key: 'x-another-header',
value: ':authorized',
},
],
},
]
},
}
```

### Headers with basePath support

When leveraging [`basePath` support](/docs/api-reference/next.config.js/basepath.md) with headers each `source` is automatically prefixed with the `basePath` unless you add `basePath: false` to the header:
Expand Down
77 changes: 77 additions & 0 deletions docs/api-reference/next.config.js/redirects.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,83 @@ module.exports = {
}
```

## Header, Cookie, and Query Matching

To only match a redirect when header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the redirect to be applied.

`has` items have the following fields:

- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`.
- `key`: `String` - the key from the selected type to match against.
- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`.

```js
module.exports = {
async redirects() {
return [
// if the header `x-redirect-me` is present,
// this redirect will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-redirect-me',
},
],
permanent: false,
destination: '/another-page',
},
// if the source, query, and cookie are matched,
// this redirect will be applied
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
permanent: false,
destination: '/:path*/:page',
},
// if the header `x-authorized` is present and
// contains a matching value, this redirect will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
permanent: false,
destination: '/home?authorized=:authorized',
},
// if the host is `example.com`,
// this redirect will be applied
{
source: '/:path*',
has: [
{
type: 'host',
value: 'example.com',
},
],
destination: '/another-page',
},
]
},
}
```

### Redirects with basePath support

When leveraging [`basePath` support](/docs/api-reference/next.config.js/basepath.md) with redirects each `source` and `destination` is automatically prefixed with the `basePath` unless you add `basePath: false` to the redirect:
Expand Down
74 changes: 74 additions & 0 deletions docs/api-reference/next.config.js/rewrites.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,80 @@ module.exports = {
}
```

## Header, Cookie, and Query Matching

To only match a rewrite when header, cookie, or query values also match the `has` field can be used. Both the `source` and all `has` items must match for the rewrite to be applied.

`has` items have the following fields:

- `type`: `String` - must be either `header`, `cookie`, `host`, or `query`.
- `key`: `String` - the key from the selected type to match against.
- `value`: `String` or `undefined` - the value to check for, if undefined any value will match. A regex like string can be used to capture a specific part of the value, e.g. if the value `first-(?<paramName>.*)` is used for `first-second` then `second` will be usable in the destination with `:paramName`.

```js
module.exports = {
async rewrites() {
return [
// if the header `x-rewrite-me` is present,
// this rewrite will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-rewrite-me',
},
],
destination: '/another-page',
},
// if the source, query, and cookie are matched,
// this rewrite will be applied
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
destination: '/:path*/:page',
},
// if the header `x-authorized` is present and
// contains a matching value, this rewrite will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
destination: '/home?authorized=:authorized',
},
// if the host is `example.com`,
// this rewrite will be applied
{
source: '/:path*',
has: [
{
type: 'host',
value: 'example.com',
},
],
destination: '/another-page',
},
]
},
}
```

## Rewriting to an external URL

<details>
Expand Down
3 changes: 3 additions & 0 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,9 @@ export default async function build(
rewritesCount: rewrites.length,
headersCount: headers.length,
redirectsCount: redirects.length - 1, // reduce one for trailing slash
headersWithHasCount: headers.filter((r: any) => !!r.has).length,
rewritesWithHasCount: rewrites.filter((r: any) => !!r.has).length,
redirectsWithHasCount: redirects.filter((r: any) => !!r.has).length,
})
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function getApiHandler(ctx: ServerlessHandlerCtx) {
// We need to trust the dynamic route params from the proxy
// to ensure we are using the correct values
const trustQuery = req.headers[vercelHeader]
const parsedUrl = handleRewrites(parseUrl(req.url!, true))
const parsedUrl = handleRewrites(req, parseUrl(req.url!, true))

if (parsedUrl.query.nextInternalLocale) {
delete parsedUrl.query.nextInternalLocale
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {
}
const origQuery = Object.assign({}, parsedUrl.query)

parsedUrl = handleRewrites(parsedUrl)
parsedUrl = handleRewrites(req, parsedUrl)
handleBasePath(req, parsedUrl)

// remove ?amp=1 from request URL if rendering for export
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { normalizeLocalePath } from '../../../../next-server/lib/i18n/normalize-
import pathMatch from '../../../../next-server/lib/router/utils/path-match'
import { getRouteRegex } from '../../../../next-server/lib/router/utils/route-regex'
import { getRouteMatcher } from '../../../../next-server/lib/router/utils/route-matcher'
import prepareDestination from '../../../../next-server/lib/router/utils/prepare-destination'
import prepareDestination, {
matchHas,
} from '../../../../next-server/lib/router/utils/prepare-destination'
import { __ApiPreviewProps } from '../../../../next-server/server/api-utils'
import { BuildManifest } from '../../../../next-server/server/get-page-files'
import {
Expand Down Expand Up @@ -85,10 +87,20 @@ export function getUtils({
defaultRouteMatches = dynamicRouteMatcher(page) as ParsedUrlQuery
}

function handleRewrites(parsedUrl: UrlWithParsedQuery) {
function handleRewrites(req: IncomingMessage, parsedUrl: UrlWithParsedQuery) {
for (const rewrite of rewrites) {
const matcher = getCustomRouteMatcher(rewrite.source)
const params = matcher(parsedUrl.pathname)
let params = matcher(parsedUrl.pathname)

if (rewrite.has && params) {
const hasParams = matchHas(req, rewrite.has, parsedUrl.query)

if (hasParams) {
Object.assign(params, hasParams)
} else {
params = false
}
}

if (params) {
const { parsedDestination } = prepareDestination(
Expand Down
Loading

0 comments on commit a7c7fd7

Please sign in to comment.