Skip to content

Commit

Permalink
Add support for rewriting to external resources
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Jan 10, 2020
1 parent 243c038 commit 2144afa
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 19 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@babel/preset-react": "7.7.0",
"@fullhuman/postcss-purgecss": "1.3.0",
"@mdx-js/loader": "0.18.0",
"@types/http-proxy": "1.17.3",
"@types/jest": "24.0.13",
"@types/string-hash": "1.1.1",
"@typescript-eslint/eslint-plugin": "2.6.1",
Expand All @@ -53,7 +54,9 @@
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.3",
"babel-jest": "24.9.0",
"browserslist": "^4.8.3",
"browserstack-local": "1.4.0",
"caniuse-lite": "^1.0.30001019",
"cheerio": "0.22.0",
"clone": "2.1.2",
"coveralls": "3.0.3",
Expand Down Expand Up @@ -100,9 +103,7 @@
"tree-kill": "1.2.1",
"typescript": "3.7.3",
"wait-port": "0.2.2",
"webpack-bundle-analyzer": "3.3.2",
"browserslist": "^4.8.3",
"caniuse-lite": "^1.0.30001019"
"webpack-bundle-analyzer": "3.3.2"
},
"resolutions": {
"browserslist": "^4.8.3",
Expand Down
9 changes: 7 additions & 2 deletions packages/next/lib/check-custom-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@ export default function checkCustomRoutes(
invalidParts.push('`destination` is missing')
} else if (typeof _route.destination !== 'string') {
invalidParts.push('`destination` is not a string')
} else if (type === 'rewrite' && !_route.destination.startsWith('/')) {
invalidParts.push('`destination` does not start with /')
} else if (
type === 'rewrite' &&
!_route.destination.match(/^(\/|https:\/\/|http:\/\/)/)
) {
invalidParts.push(
'`destination` does not start with `/`, `http://`, or `https://`'
)
}
}

Expand Down
43 changes: 31 additions & 12 deletions packages/next/next-server/server/next-server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import compression from 'compression'
import fs from 'fs'
import Proxy from 'http-proxy'
import { IncomingMessage, ServerResponse } from 'http'
import { join, resolve, sep } from 'path'
import { compile as compilePathToRegex } from 'path-to-regexp'
Expand Down Expand Up @@ -449,7 +450,7 @@ export default class Server {
type: route.type,
statusCode: (route as Redirect).statusCode,
name: `${route.type} ${route.source} route`,
fn: async (_req, res, params, _parsedUrl) => {
fn: async (req, res, params, _parsedUrl) => {
const parsedDestination = parseUrl(route.destination, true)
const destQuery = parsedDestination.query
let destinationCompiler = compilePathToRegex(
Expand Down Expand Up @@ -483,25 +484,43 @@ export default class Server {
throw err
}

const parsedNewUrl = parseUrl(newUrl)
const updatedDestination = formatUrl({
...parsedDestination,
pathname: parsedNewUrl.pathname,
hash: parsedNewUrl.hash,
search: undefined,
})

if (route.type === 'redirect') {
const parsedNewUrl = parseUrl(newUrl)
res.setHeader(
'Location',
formatUrl({
...parsedDestination,
pathname: parsedNewUrl.pathname,
hash: parsedNewUrl.hash,
search: undefined,
})
)
res.setHeader('Location', updatedDestination)
res.statusCode =
(route as Redirect).statusCode || DEFAULT_REDIRECT_STATUS
res.end()
return {
finished: true,
}
} else {
;(_req as any)._nextDidRewrite = true
// external rewrite, proxy it
if (parsedDestination.protocol) {
const proxy = new Proxy({
target: updatedDestination,
changeOrigin: true,
ignorePath: true,
})
proxy.web(req, res)

proxy.on('error', err => {
console.error(
`Error occurred proxying ${updatedDestination}`,
err
)
})
return {
finished: true,
}
}
;(req as any)._nextDidRewrite = true
}

return {
Expand Down
1 change: 1 addition & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"fork-ts-checker-webpack-plugin": "3.1.1",
"fresh": "0.5.2",
"gzip-size": "5.1.1",
"http-proxy": "1.18.0",
"ignore-loader": "0.1.2",
"is-docker": "2.0.0",
"is-wsl": "2.1.1",
Expand Down
4 changes: 4 additions & 0 deletions test/integration/custom-routes/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ module.exports = {
source: '/hidden/_next/:path*',
destination: '/_next/:path*',
},
{
source: '/proxy-me',
destination: 'https://zeit.co',
},
]
},
async redirects() {
Expand Down
12 changes: 12 additions & 0 deletions test/integration/custom-routes/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ const runTests = (isDev = false) => {
expect(res.headers.get('x-second-header')).toBe('second')
})

it('should support proxying to external site', async () => {
const res = await fetchViaHTTP(appPort, '/proxy-me')
expect(res.status).toBe(200)
expect(await res.text()).toContain('ZEIT, Inc')
})

if (!isDev) {
it('should output routes-manifest successfully', async () => {
const manifest = await fs.readJSON(
Expand Down Expand Up @@ -454,6 +460,12 @@ const runTests = (isDev = false) => {
regexKeys: ['path'],
source: '/hidden/_next/:path*',
},
{
destination: 'https://zeit.co',
regex: normalizeRegEx('^\\/proxy-me$'),
regexKeys: [],
source: '/proxy-me',
},
],
dynamicRoutes: [
{
Expand Down
2 changes: 1 addition & 1 deletion test/integration/invalid-custom-routes/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const invalidRewriteAssertions = (stderr = '') => {
)

expect(stderr).toContain(
`\`destination\` does not start with / for route {"source":"/hello","destination":"another"}`
`\`destination\` does not start with \`/\`, \`http://\`, or \`https://\` for route {"source":"/hello","destination":"another"}`
)

expect(stderr).toContain(
Expand Down
35 changes: 34 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,13 @@
"@types/tough-cookie" "*"
form-data "^2.5.0"

"@types/http-proxy@1.17.3":
version "1.17.3"
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.3.tgz#348e1b808ff9585423cb909e9992d89ccdbf4c14"
integrity sha512-wIPqXANye5BbORbuh74exbwNzj+UWCwWyeEFJzUQ7Fq3W2NSAy+7x7nX1fgbEypr2/TdKqpeuxLnXWgzN533/Q==
dependencies:
"@types/node" "*"

"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
Expand Down Expand Up @@ -5337,7 +5344,7 @@ debug@3.1.0:
dependencies:
ms "2.0.0"

debug@^3.1.0, debug@^3.2.6:
debug@^3.0.0, debug@^3.1.0, debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
Expand Down Expand Up @@ -6050,6 +6057,11 @@ eventemitter3@^3.1.0:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==

eventemitter3@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==

events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
Expand Down Expand Up @@ -6640,6 +6652,13 @@ fn-annotate@^1.1.3:
resolved "https://registry.yarnpkg.com/fn-annotate/-/fn-annotate-1.2.0.tgz#28da000117dea61842fe61f353f41cf4c93a7a7e"
integrity sha1-KNoAARfephhC/mHzU/Qc9Mk6en4=

follow-redirects@^1.0.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f"
integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==
dependencies:
debug "^3.0.0"

for-in@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
Expand Down Expand Up @@ -7516,6 +7535,15 @@ http-proxy-agent@^2.1.0:
agent-base "4"
debug "3.1.0"

http-proxy@1.18.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
requires-port "^1.0.0"

http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
Expand Down Expand Up @@ -13003,6 +13031,11 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==

requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=

resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
Expand Down

0 comments on commit 2144afa

Please sign in to comment.