UNPKG

next

Version:

The React Framework

930 lines (929 loc) • 43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _exportNames = {}; exports.default = void 0; require("./node-polyfill-fetch"); require("./node-polyfill-web-streams"); var _fs = _interopRequireDefault(require("fs")); var _path = require("path"); var _http = require("http"); var _utils = require("../shared/lib/utils"); var _requestMeta = require("./request-meta"); var _constants = require("../shared/lib/constants"); var _recursiveReaddirSync = require("./lib/recursive-readdir-sync"); var _url = require("url"); var _compression = _interopRequireDefault(require("next/dist/compiled/compression")); var _httpProxy = _interopRequireDefault(require("next/dist/compiled/http-proxy")); var _router = require("./router"); var _sandbox = require("./web/sandbox"); var _node = require("./base-http/node"); var _sendPayload = require("./send-payload"); var _serveStatic = require("./serve-static"); var _node1 = require("./api-utils/node"); var _render = require("./render"); var _parseUrl = require("../shared/lib/router/utils/parse-url"); var Log = _interopRequireWildcard(require("../build/output/log")); var _baseServer = _interopRequireWildcard(require("./base-server")); Object.keys(_baseServer).forEach(function(key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _baseServer[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function() { return _baseServer[key]; } }); }); var _require = require("./require"); var _normalizePagePath = require("./normalize-page-path"); var _loadComponents = require("./load-components"); var _isError = _interopRequireWildcard(require("../lib/is-error")); var _utils1 = require("./web/utils"); var _relativizeUrl = require("../shared/lib/router/utils/relativize-url"); var _parseNextUrl = require("../shared/lib/router/utils/parse-next-url"); var _prepareDestination = require("../shared/lib/router/utils/prepare-destination"); var _normalizeLocalePath = require("../shared/lib/i18n/normalize-locale-path"); var _utils2 = require("../shared/lib/router/utils"); var _constants1 = require("../lib/constants"); var _env = require("@next/env"); var _serverRouteUtils = require("./server-route-utils"); var _querystring = require("../shared/lib/router/utils/querystring"); var _responseCache = _interopRequireDefault(require("../server/response-cache")); var _normalizeTrailingSlash = require("../client/normalize-trailing-slash"); var _bodyStreams = require("./body-streams"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for(var key in obj){ if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } class NextNodeServer extends _baseServer.default { constructor(options){ // Initialize super class super(options); this.compression = this.nextConfig.compress && this.nextConfig.target === 'server' ? (0, _compression).default() : undefined; this._validFilesystemPathSet = null; this.middlewareBetaWarning = (0, _utils).execOnce(()=>{ Log.warn(`using beta Middleware (not covered by semver) - https://nextjs.org/docs/messages/beta-middleware`); }); /** * This sets environment variable to be used at the time of SSR by head.tsx. * Using this from process.env allows targeting both serverless and SSR by calling * `process.env.__NEXT_OPTIMIZE_CSS`. */ if (this.renderOpts.optimizeFonts) { process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true); } if (this.renderOpts.optimizeCss) { process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true); } if (this.renderOpts.nextScriptWorkers) { process.env.__NEXT_SCRIPT_WORKERS = JSON.stringify(true); } if (!this.minimalMode) { const { ImageOptimizerCache } = require('./image-optimizer'); this.imageResponseCache = new _responseCache.default(new ImageOptimizerCache({ distDir: this.distDir, nextConfig: this.nextConfig }), this.minimalMode); } if (!this.renderOpts.dev) { // pre-warm _document and _app as these will be // needed for most requests (0, _loadComponents).loadComponents(this.distDir, '/_document', this._isLikeServerless).catch(()=>{}); (0, _loadComponents).loadComponents(this.distDir, '/_app', this._isLikeServerless).catch(()=>{}); } } loadEnvConfig({ dev }) { (0, _env).loadEnvConfig(this.dir, dev, Log); } getPublicDir() { return (0, _path).join(this.dir, _constants.CLIENT_PUBLIC_FILES_PATH); } getHasStaticDir() { return _fs.default.existsSync((0, _path).join(this.dir, 'static')); } getPagesManifest() { const serverBuildDir = (0, _path).join(this.distDir, this._isLikeServerless ? _constants.SERVERLESS_DIRECTORY : _constants.SERVER_DIRECTORY); const pagesManifestPath = (0, _path).join(serverBuildDir, _constants.PAGES_MANIFEST); return require(pagesManifestPath); } getBuildId() { const buildIdFile = (0, _path).join(this.distDir, _constants.BUILD_ID_FILE); try { return _fs.default.readFileSync(buildIdFile, 'utf8').trim(); } catch (err) { if (!_fs.default.existsSync(buildIdFile)) { throw new Error(`Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id`); } throw err; } } generateImageRoutes() { return [ { match: (0, _router).route('/_next/image'), type: 'route', name: '_next/image catchall', fn: async (req, res, _params, parsedUrl)=>{ if (this.minimalMode) { res.statusCode = 400; res.body('Bad Request').send(); return { finished: true }; } const { getHash , ImageOptimizerCache , sendResponse , ImageError } = require('./image-optimizer'); if (!this.imageResponseCache) { throw new Error('invariant image optimizer cache was not initialized'); } const imagesConfig = this.nextConfig.images; if (imagesConfig.loader !== 'default') { await this.render404(req, res); return { finished: true }; } const paramsResult = ImageOptimizerCache.validateParams(req.originalRequest, parsedUrl.query, this.nextConfig, !!this.renderOpts.dev); if ('errorMessage' in paramsResult) { res.statusCode = 400; res.body(paramsResult.errorMessage).send(); return { finished: true }; } const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult); try { var ref; const cacheEntry = await this.imageResponseCache.get(cacheKey, async ()=>{ const { buffer , contentType , maxAge } = await this.imageOptimizer(req, res, paramsResult); const etag = getHash([ buffer ]); return { value: { kind: 'IMAGE', buffer, etag, extension: (0, _serveStatic).getExtension(contentType) }, revalidate: maxAge }; }, {}); if ((cacheEntry === null || cacheEntry === void 0 ? void 0 : (ref = cacheEntry.value) === null || ref === void 0 ? void 0 : ref.kind) !== 'IMAGE') { throw new Error('invariant did not get entry from image response cache'); } sendResponse(req.originalRequest, res.originalResponse, paramsResult.href, cacheEntry.value.extension, cacheEntry.value.buffer, paramsResult.isStatic, cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT', imagesConfig.contentSecurityPolicy); } catch (err) { if (err instanceof ImageError) { res.statusCode = err.statusCode; res.body(err.message).send(); return { finished: true }; } throw err; } return { finished: true }; } }, ]; } generateStaticRoutes() { return this.hasStaticDir ? [ { // It's very important to keep this route's param optional. // (but it should support as many params as needed, separated by '/') // Otherwise this will lead to a pretty simple DOS attack. // See more: https://github.com/vercel/next.js/issues/2617 match: (0, _router).route('/static/:path*'), name: 'static catchall', fn: async (req, res, params, parsedUrl)=>{ const p = (0, _path).join(this.dir, 'static', ...params.path); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } }, ] : []; } setImmutableAssetCacheControl(res) { res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); } generateFsStaticRoutes() { return [ { match: (0, _router).route('/_next/static/:path*'), type: 'route', name: '_next/static catchall', fn: async (req, res, params, parsedUrl)=>{ // make sure to 404 for /_next/static itself if (!params.path) { await this.render404(req, res, parsedUrl); return { finished: true }; } if (params.path[0] === _constants.CLIENT_STATIC_FILES_RUNTIME || params.path[0] === 'chunks' || params.path[0] === 'css' || params.path[0] === 'image' || params.path[0] === 'media' || params.path[0] === this.buildId || params.path[0] === 'pages' || params.path[1] === 'pages') { this.setImmutableAssetCacheControl(res); } const p = (0, _path).join(this.distDir, _constants.CLIENT_STATIC_FILES_PATH, ...params.path || []); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } }, ]; } generatePublicRoutes() { if (!_fs.default.existsSync(this.publicDir)) return []; const publicFiles = new Set((0, _recursiveReaddirSync).recursiveReadDirSync(this.publicDir).map((p)=>encodeURI(p.replace(/\\/g, '/')) )); return [ { match: (0, _router).route('/:path*'), name: 'public folder catchall', fn: async (req, res, params, parsedUrl)=>{ const pathParts = params.path || []; const { basePath } = this.nextConfig; // if basePath is defined require it be present if (basePath) { const basePathParts = basePath.split('/'); // remove first empty value basePathParts.shift(); if (!basePathParts.every((part, idx)=>{ return part === pathParts[idx]; })) { return { finished: false }; } pathParts.splice(0, basePathParts.length); } let path = `/${pathParts.join('/')}`; if (!publicFiles.has(path)) { // In `next-dev-server.ts`, we ensure encoded paths match // decoded paths on the filesystem. So we need do the // opposite here: make sure decoded paths match encoded. path = encodeURI(path); } if (publicFiles.has(path)) { await this.serveStatic(req, res, (0, _path).join(this.publicDir, ...pathParts), parsedUrl); return { finished: true }; } return { finished: false }; } }, ]; } getFilesystemPaths() { if (this._validFilesystemPathSet) { return this._validFilesystemPathSet; } const pathUserFilesStatic = (0, _path).join(this.dir, 'static'); let userFilesStatic = []; if (this.hasStaticDir && _fs.default.existsSync(pathUserFilesStatic)) { userFilesStatic = (0, _recursiveReaddirSync).recursiveReadDirSync(pathUserFilesStatic).map((f)=>(0, _path).join('.', 'static', f) ); } let userFilesPublic = []; if (this.publicDir && _fs.default.existsSync(this.publicDir)) { userFilesPublic = (0, _recursiveReaddirSync).recursiveReadDirSync(this.publicDir).map((f)=>(0, _path).join('.', 'public', f) ); } let nextFilesStatic = []; nextFilesStatic = !this.minimalMode && _fs.default.existsSync((0, _path).join(this.distDir, 'static')) ? (0, _recursiveReaddirSync).recursiveReadDirSync((0, _path).join(this.distDir, 'static')).map((f)=>(0, _path).join('.', (0, _path).relative(this.dir, this.distDir), 'static', f) ) : []; return this._validFilesystemPathSet = new Set([ ...nextFilesStatic, ...userFilesPublic, ...userFilesStatic, ]); } sendRenderResult(req, res, options) { return (0, _sendPayload).sendRenderResult({ req: req.originalRequest, res: res.originalResponse, ...options }); } sendStatic(req, res, path) { return (0, _serveStatic).serveStatic(req.originalRequest, res.originalResponse, path); } handleCompression(req, res) { if (this.compression) { this.compression(req.originalRequest, res.originalResponse, ()=>{}); } } async proxyRequest(req, res, parsedUrl) { const { query } = parsedUrl; delete parsedUrl.query; parsedUrl.search = (0, _baseServer).stringifyQuery(req, query); const target = (0, _url).format(parsedUrl); const proxy = new _httpProxy.default({ target, changeOrigin: true, ignorePath: true, xfwd: true, proxyTimeout: 30000 }); await new Promise((proxyResolve, proxyReject)=>{ let finished = false; proxy.on('proxyReq', (proxyReq)=>{ proxyReq.on('close', ()=>{ if (!finished) { finished = true; proxyResolve(true); } }); }); proxy.on('error', (err)=>{ if (!finished) { finished = true; proxyReject(err); } }); proxy.web(req.originalRequest, res.originalResponse); }); return { finished: true }; } async runApi(req, res, query, params, page, builtPagePath) { const pageModule = await require(builtPagePath); query = { ...query, ...params }; delete query.__nextLocale; delete query.__nextDefaultLocale; if (!this.renderOpts.dev && this._isLikeServerless) { if (typeof pageModule.default === 'function') { (0, _baseServer).prepareServerlessUrl(req, query); await pageModule.default(req, res); return true; } } await (0, _node1).apiResolver(req.originalRequest, res.originalResponse, query, pageModule, { ...this.renderOpts.previewProps, revalidate: (newReq, newRes)=>this.getRequestHandler()(new _node.NodeNextRequest(newReq), new _node.NodeNextResponse(newRes)) , // internal config so is not typed trustHostHeader: this.nextConfig.experimental.trustHostHeader }, this.minimalMode, this.renderOpts.dev, page); return true; } async renderHTML(req, res, pathname, query, renderOpts) { // Due to the way we pass data by mutating `renderOpts`, we can't extend the // object here but only updating its `serverComponentManifest` field. // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 renderOpts.serverComponentManifest = this.serverComponentManifest; return (0, _render).renderToHTML(req.originalRequest, res.originalResponse, pathname, query, renderOpts); } streamResponseChunk(res, chunk) { res.originalResponse.write(chunk); // When both compression and streaming are enabled, we need to explicitly // flush the response to avoid it being buffered by gzip. if (this.compression && 'flush' in res.originalResponse) { res.originalResponse.flush(); } } async imageOptimizer(req, res, paramsResult) { const { imageOptimizer } = require('./image-optimizer'); return imageOptimizer(req.originalRequest, res.originalResponse, paramsResult, this.nextConfig, (newReq, newRes, newParsedUrl)=>this.getRequestHandler()(new _node.NodeNextRequest(newReq), new _node.NodeNextResponse(newRes), newParsedUrl) ); } getPagePath(pathname, locales) { return (0, _require).getPagePath(pathname, this.distDir, this._isLikeServerless, this.renderOpts.dev, locales); } async findPageComponents(pathname, query = {}, params = null) { let paths = [ // try serving a static AMP version first query.amp ? (0, _normalizePagePath).normalizePagePath(pathname) + '.amp' : null, pathname, ].filter(Boolean); if (query.__nextLocale) { paths = [ ...paths.map((path)=>`/${query.__nextLocale}${path === '/' ? '' : path}` ), ...paths, ]; } for (const pagePath of paths){ try { const components = await (0, _loadComponents).loadComponents(this.distDir, pagePath, !this.renderOpts.dev && this._isLikeServerless, this.renderOpts.serverComponents); if (query.__nextLocale && typeof components.Component === 'string' && !(pagePath === null || pagePath === void 0 ? void 0 : pagePath.startsWith(`/${query.__nextLocale}`))) { continue; } return { components, query: { ...components.getStaticProps ? { amp: query.amp, _nextDataReq: query._nextDataReq, __nextLocale: query.__nextLocale, __nextDefaultLocale: query.__nextDefaultLocale, __flight__: query.__flight__ } : query, ...params || {} } }; } catch (err) { if ((0, _isError).default(err) && err.code !== 'ENOENT') throw err; } } return null; } getFontManifest() { return (0, _require).requireFontManifest(this.distDir, this._isLikeServerless); } getServerComponentManifest() { if (!this.nextConfig.experimental.serverComponents) return undefined; return require((0, _path).join(this.distDir, 'server', _constants.MIDDLEWARE_FLIGHT_MANIFEST + '.json')); } getCacheFilesystem() { return { readFile: (f)=>_fs.default.promises.readFile(f, 'utf8') , readFileSync: (f)=>_fs.default.readFileSync(f, 'utf8') , writeFile: (f, d)=>_fs.default.promises.writeFile(f, d, 'utf8') , mkdir: (dir)=>_fs.default.promises.mkdir(dir, { recursive: true }) , stat: (f)=>_fs.default.promises.stat(f) }; } normalizeReq(req) { return req instanceof _http.IncomingMessage ? new _node.NodeNextRequest(req) : req; } normalizeRes(res) { return res instanceof _http.ServerResponse ? new _node.NodeNextResponse(res) : res; } getRequestHandler() { const handler = super.getRequestHandler(); return async (req, res, parsedUrl)=>{ return handler(this.normalizeReq(req), this.normalizeRes(res), parsedUrl); }; } async render(req, res, pathname, query, parsedUrl, internal = false) { return super.render(this.normalizeReq(req), this.normalizeRes(res), pathname, query, parsedUrl, internal); } async renderToHTML(req, res, pathname, query) { return super.renderToHTML(this.normalizeReq(req), this.normalizeRes(res), pathname, query); } async renderError(err, req, res, pathname, query, setHeaders) { return super.renderError(err, this.normalizeReq(req), this.normalizeRes(res), pathname, query, setHeaders); } async renderErrorToHTML(err, req, res, pathname, query) { return super.renderErrorToHTML(err, this.normalizeReq(req), this.normalizeRes(res), pathname, query); } async render404(req, res, parsedUrl, setHeaders) { return super.render404(this.normalizeReq(req), this.normalizeRes(res), parsedUrl, setHeaders); } async hasMiddleware(pathname, _isSSR) { try { return (0, _require).getMiddlewareInfo({ dev: this.renderOpts.dev, distDir: this.distDir, page: pathname, serverless: this._isLikeServerless }).paths.length > 0; } catch (_) {} return false; } async serveStatic(req, res, path, parsedUrl) { if (!this.isServeableUrl(path)) { return this.render404(req, res, parsedUrl); } if (!(req.method === 'GET' || req.method === 'HEAD')) { res.statusCode = 405; res.setHeader('Allow', [ 'GET', 'HEAD' ]); return this.renderError(null, req, res, path); } try { await this.sendStatic(req, res, path); } catch (error) { if (!(0, _isError).default(error)) throw error; const err = error; if (err.code === 'ENOENT' || err.statusCode === 404) { this.render404(req, res, parsedUrl); } else if (err.statusCode === 412) { res.statusCode = 412; return this.renderError(err, req, res, path); } else { throw err; } } } getStaticRoutes() { return this.hasStaticDir ? [ { // It's very important to keep this route's param optional. // (but it should support as many params as needed, separated by '/') // Otherwise this will lead to a pretty simple DOS attack. // See more: https://github.com/vercel/next.js/issues/2617 match: (0, _router).route('/static/:path*'), name: 'static catchall', fn: async (req, res, params, parsedUrl)=>{ const p = (0, _path).join(this.dir, 'static', ...params.path); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } }, ] : []; } isServeableUrl(untrustedFileUrl) { // This method mimics what the version of `send` we use does: // 1. decodeURIComponent: // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989 // https://github.com/pillarjs/send/blob/0.17.1/index.js#L518-L522 // 2. resolve: // https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 let decodedUntrustedFilePath; try { // (1) Decode the URL so we have the proper file name decodedUntrustedFilePath = decodeURIComponent(untrustedFileUrl); } catch { return false; } // (2) Resolve "up paths" to determine real request const untrustedFilePath = (0, _path).resolve(decodedUntrustedFilePath); // don't allow null bytes anywhere in the file path if (untrustedFilePath.indexOf('\x00') !== -1) { return false; } // Check if .next/static, static and public are in the path. // If not the path is not available. if ((untrustedFilePath.startsWith((0, _path).join(this.distDir, 'static') + _path.sep) || untrustedFilePath.startsWith((0, _path).join(this.dir, 'static') + _path.sep) || untrustedFilePath.startsWith((0, _path).join(this.dir, 'public') + _path.sep)) === false) { return false; } // Check against the real filesystem paths const filesystemUrls = this.getFilesystemPaths(); const resolved = (0, _path).relative(this.dir, untrustedFilePath); return filesystemUrls.has(resolved); } getMiddlewareInfo(page) { return (0, _require).getMiddlewareInfo({ dev: this.renderOpts.dev, page, distDir: this.distDir, serverless: this._isLikeServerless }); } getMiddlewareManifest() { if (!this.minimalMode) { const middlewareManifestPath = (0, _path).join((0, _path).join(this.distDir, _constants.SERVER_DIRECTORY), _constants.MIDDLEWARE_MANIFEST); return require(middlewareManifestPath); } return undefined; } generateRewrites({ restrictedRedirectPaths }) { let beforeFiles = []; let afterFiles = []; let fallback = []; if (!this.minimalMode) { const buildRewrite = (rewrite, check = true)=>{ const rewriteRoute = (0, _serverRouteUtils).getCustomRoute({ type: 'rewrite', rule: rewrite, restrictedRedirectPaths }); return { ...rewriteRoute, check, type: rewriteRoute.type, name: `Rewrite route ${rewriteRoute.source}`, match: rewriteRoute.match, fn: async (req, res, params, parsedUrl)=>{ const { newUrl , parsedDestination } = (0, _prepareDestination).prepareDestination({ appendParamsToQuery: true, destination: rewriteRoute.destination, params: params, query: parsedUrl.query }); // external rewrite, proxy it if (parsedDestination.protocol) { return this.proxyRequest(req, res, parsedDestination); } (0, _requestMeta).addRequestMeta(req, '_nextRewroteUrl', newUrl); (0, _requestMeta).addRequestMeta(req, '_nextDidRewrite', newUrl !== req.url); return { finished: false, pathname: newUrl, query: parsedDestination.query }; } }; }; if (Array.isArray(this.customRoutes.rewrites)) { afterFiles = this.customRoutes.rewrites.map((r)=>buildRewrite(r) ); } else { beforeFiles = this.customRoutes.rewrites.beforeFiles.map((r)=>buildRewrite(r, false) ); afterFiles = this.customRoutes.rewrites.afterFiles.map((r)=>buildRewrite(r) ); fallback = this.customRoutes.rewrites.fallback.map((r)=>buildRewrite(r) ); } } return { beforeFiles, afterFiles, fallback }; } generateCatchAllMiddlewareRoute() { if (this.minimalMode) return undefined; return { match: (0, _router).route('/:path*'), type: 'route', name: 'middleware catchall', fn: async (req, res, _params, parsed)=>{ var ref, ref1; if (!((ref = this.middleware) === null || ref === void 0 ? void 0 : ref.length)) { return { finished: false }; } const initUrl = (0, _requestMeta).getRequestMeta(req, '__NEXT_INIT_URL'); const parsedUrl = (0, _parseNextUrl).parseNextUrl({ url: initUrl, headers: req.headers, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash } }); const normalizedPathname = (0, _normalizeTrailingSlash).removePathTrailingSlash(parsedUrl.pathname); if (!((ref1 = this.middleware) === null || ref1 === void 0 ? void 0 : ref1.some((m)=>m.match(normalizedPathname) ))) { return { finished: false }; } let result = null; try { result = await this.runMiddleware({ request: req, response: res, parsedUrl: parsedUrl, parsed: parsed }); } catch (err) { if ((0, _isError).default(err) && err.code === 'ENOENT') { await this.render404(req, res, parsed); return { finished: true }; } const error = (0, _isError).getProperError(err); console.error(error); res.statusCode = 500; this.renderError(error, req, res, parsed.pathname || ''); return { finished: true }; } if (result === null) { return { finished: true }; } if (result.response.headers.has('x-middleware-rewrite')) { const value = result.response.headers.get('x-middleware-rewrite'); const rel = (0, _relativizeUrl).relativizeURL(value, initUrl); result.response.headers.set('x-middleware-rewrite', rel); } if (result.response.headers.has('Location')) { const value = result.response.headers.get('Location'); const rel = (0, _relativizeUrl).relativizeURL(value, initUrl); result.response.headers.set('Location', rel); } if (!result.response.headers.has('x-middleware-rewrite') && !result.response.headers.has('x-middleware-next') && !result.response.headers.has('Location')) { result.response.headers.set('x-middleware-refresh', '1'); } result.response.headers.delete('x-middleware-next'); for (const [key, value] of Object.entries((0, _utils1).toNodeHeaders(result.response.headers))){ if (key !== 'content-encoding' && value !== undefined) { res.setHeader(key, value); } } const preflight = req.method === 'HEAD' && req.headers['x-middleware-preflight']; if (preflight) { res.statusCode = 200; res.send(); return { finished: true }; } res.statusCode = result.response.status; res.statusMessage = result.response.statusText; const location = result.response.headers.get('Location'); if (location) { res.statusCode = result.response.status; if (res.statusCode === 308) { res.setHeader('Refresh', `0;url=${location}`); } res.body(location).send(); return { finished: true }; } if (result.response.headers.has('x-middleware-rewrite')) { const rewritePath = result.response.headers.get('x-middleware-rewrite'); const parsedDestination = (0, _parseUrl).parseUrl(rewritePath); const newUrl = parsedDestination.pathname; // TODO: remove after next minor version current `v12.0.9` this.warnIfQueryParametersWereDeleted(parsedUrl.query, parsedDestination.query); if (parsedDestination.protocol && (parsedDestination.port ? `${parsedDestination.hostname}:${parsedDestination.port}` : parsedDestination.hostname) !== req.headers.host) { return this.proxyRequest(req, res, parsedDestination); } if (this.nextConfig.i18n) { const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(newUrl, this.nextConfig.i18n.locales); if (localePathResult.detectedLocale) { parsedDestination.query.__nextLocale = localePathResult.detectedLocale; } } (0, _requestMeta).addRequestMeta(req, '_nextRewroteUrl', newUrl); (0, _requestMeta).addRequestMeta(req, '_nextDidRewrite', newUrl !== req.url); return { finished: false, pathname: newUrl, query: parsedDestination.query }; } if (result.response.headers.has('x-middleware-refresh')) { res.statusCode = result.response.status; for await (const chunk of result.response.body || []){ this.streamResponseChunk(res, chunk); } res.send(); return { finished: true }; } return { finished: false }; } }; } getMiddleware() { var ref, ref2; const middleware = ((ref = this.middlewareManifest) === null || ref === void 0 ? void 0 : ref.middleware) || {}; return ((ref2 = this.middlewareManifest) === null || ref2 === void 0 ? void 0 : ref2.sortedMiddleware.map((page)=>({ match: (0, _utils2).getRouteMatcher((0, _utils2).getMiddlewareRegex(page, _constants1.MIDDLEWARE_ROUTE.test(middleware[page].name))), page }) )) || []; } async runMiddleware(params) { this.middlewareBetaWarning(); const normalizedPathname = (0, _normalizeTrailingSlash).removePathTrailingSlash(params.parsedUrl.pathname); // For middleware to "fetch" we must always provide an absolute URL const url = (0, _requestMeta).getRequestMeta(params.request, '__NEXT_INIT_URL'); if (!url.startsWith('http')) { throw new Error('To use middleware you must provide a `hostname` and `port` to the Next.js Server'); } const page = {}; if (await this.hasPage(normalizedPathname)) { page.name = params.parsedUrl.pathname; } else if (this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes){ const matchParams = dynamicRoute.match(normalizedPathname); if (matchParams) { page.name = dynamicRoute.page; page.params = matchParams; break; } } } const allHeaders = new Headers(); let result = null; const method = (params.request.method || 'GET').toUpperCase(); let originalBody = method !== 'GET' && method !== 'HEAD' ? (0, _bodyStreams).clonableBodyForRequest(params.request.body) : undefined; for (const middleware of this.middleware || []){ if (middleware.match(normalizedPathname)) { if (!await this.hasMiddleware(middleware.page, middleware.ssr)) { console.warn(`The Edge Function for ${middleware.page} was not found`); continue; } await this.ensureMiddleware(middleware.page, middleware.ssr); const middlewareInfo = this.getMiddlewareInfo(middleware.page); result = await (0, _sandbox).run({ name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, wasm: middlewareInfo.wasm, request: { headers: params.request.headers, method, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash }, url: url, page: page, body: originalBody === null || originalBody === void 0 ? void 0 : originalBody.cloneBodyStream() }, useCache: !this.nextConfig.experimental.runtime, onWarning: (warning)=>{ if (params.onWarning) { warning.message += ` "./${middlewareInfo.name}"`; params.onWarning(warning); } } }); for (let [key, value] of result.response.headers){ if (key !== 'x-middleware-next') { allHeaders.append(key, value); } } if (!this.renderOpts.dev) { result.waitUntil.catch((error)=>{ console.error(`Uncaught: middleware waitUntil errored`, error); }); } if (!result.response.headers.has('x-middleware-next')) { break; } } } if (!result) { this.render404(params.request, params.response, params.parsed); } else { for (let [key, value] of allHeaders){ result.response.headers.set(key, value); } } await (originalBody === null || originalBody === void 0 ? void 0 : originalBody.finalize()); return result; } getPrerenderManifest() { if (this._cachedPreviewManifest) { return this._cachedPreviewManifest; } const manifest = require((0, _path).join(this.distDir, _constants.PRERENDER_MANIFEST)); return this._cachedPreviewManifest = manifest; } getRoutesManifest() { return require((0, _path).join(this.distDir, _constants.ROUTES_MANIFEST)); } // TODO: remove after next minor version current `v12.0.9` warnIfQueryParametersWereDeleted(incoming, rewritten) { const incomingQuery = (0, _querystring).urlQueryToSearchParams(incoming); const rewrittenQuery = (0, _querystring).urlQueryToSearchParams(rewritten); const missingKeys = [ ...incomingQuery.keys() ].filter((key)=>{ return !rewrittenQuery.has(key); }); if (missingKeys.length > 0) { Log.warn(`Query params are no longer automatically merged for rewrites in middleware, see more info here: https://nextjs.org/docs/messages/deleting-query-params-in-middlewares`); this.warnIfQueryParametersWereDeleted = ()=>{}; } } } exports.default = NextNodeServer; //# sourceMappingURL=next-server.js.map