UNPKG

@azure/static-web-apps-cli

Version:
177 lines 7.77 kB
import chalk from "chalk"; import { isHttpUrl } from "../../core/utils/net.js"; import { logger } from "../../core/utils/logger.js"; import { isSWAConfigFileUrl } from "../../core/utils/user-config.js"; import { IS_APP_DEV_SERVER } from "../../core/constants.js"; import { handleDataApiRequest } from "../handlers/dab.handler.js"; import { handleErrorPage } from "../handlers/error-page.handler.js"; import { handleFunctionRequest, isFunctionRequest } from "../handlers/function.handler.js"; import { applyRedirectResponse, tryFindFileForRequest } from "../routes-engine/rules/routes.js"; import { getHeadersForRoute, updateResponseHeaders } from "../routes-engine/rules/headers.js"; import { getMimeTypeForExtension } from "../routes-engine/rules/mime-types.js"; import { isRequestPathExcludedFromNavigationFallback } from "../routes-engine/rules/navigation-fallback.js"; import { parseQueryParams } from "../routes-engine/route-processor.js"; export function getResponse(req, res, matchedRoute, userConfig, isFunctionRequest, isDataApiRequest) { const statusCodeToServe = parseInt(`${matchedRoute?.statusCode}`, 10); const redirect = matchedRoute?.redirect; const rewrite = matchedRoute?.rewrite; logger.silly(`using userConfig`); logger.silly({ userConfig }); if (redirect) { logger.silly(` - redirect rule detected. Exit`); applyRedirectResponse(req, res, matchedRoute); return false; } // We should always set the x-ms-original-url to be the full request URL. req.headers["x-ms-original-url"] = new URL(req.url, `http://${req.headers.host}`).href; if (rewrite) { req.url = rewrite; } if ([403, 401].includes(statusCodeToServe)) { logger.silly(` - ${statusCodeToServe} code detected. Exit`); handleErrorPage(req, res, statusCodeToServe, userConfig?.responseOverrides); return false; } if (isFunctionRequest) { handleFunctionRequest(req, res); return true; } if (isDataApiRequest) { if (req.url?.startsWith("/data-api/")) { req.url = req.url?.replace("/data-api", ""); } handleDataApiRequest(req, res); return true; } const storageResult = getStorageContent(req, res, rewrite, matchedRoute, userConfig?.responseOverrides, userConfig?.navigationFallback, userConfig?.mimeTypes, matchedRoute?.headers, userConfig?.globalHeaders); if (storageResult.isFunctionFallbackRequest) { req.url = userConfig?.navigationFallback.rewrite; handleFunctionRequest(req, res); return true; } if (statusCodeToServe) { res.statusCode = statusCodeToServe; } return false; } export function getStorageContent(req, res, pathToServe, matchedRoute, responseOverridesRule, navigationFallbackRule, mimeTypeRule, routeHeaders, globalHeaders) { logger.silly(`checking storage content`); // don't serve staticwebapp.config.json / routes.json if (isSWAConfigFileUrl(req)) { logger.silly(` - request to config file detected. Exit`); handleErrorPage(req, res, 404, responseOverridesRule); return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: false, }; } let requestPath = req.url; let filePathFromRequest = null; let decodedRequestPath = req.url; const { urlPathnameWithoutQueryParams } = parseQueryParams(req, matchedRoute); // we only process if the user is NOT connecting to a remote dev server. // if the user is connecting to a remote dev server, we skip the following logic. if (IS_APP_DEV_SERVER()) { logger.silly(`remote dev server detected.`); return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: true, }; } else { requestPath = urlPathnameWithoutQueryParams; decodedRequestPath = urlPathnameWithoutQueryParams; if (pathToServe) { requestPath = pathToServe; decodedRequestPath = decodeURI(pathToServe); } filePathFromRequest = tryFindFileForRequest(requestPath); } if (!filePathFromRequest) { let shouldDisplayNotFoundPage = true; if (navigationFallbackRule?.rewrite) { const isFunctionFallback = isFunctionRequest(req, navigationFallbackRule.rewrite); logger.silly(` - isFunctionFallback: ${chalk.yellow(isFunctionFallback)}`); if (isFunctionFallback) { return { isFunctionFallbackRequest: true, isSuccessfulSiteHit: false, }; } else { const navigationFallbackRewrite = navigationFallbackRule.rewrite; logger.silly(`validating navigation fallback rewrite rule`); logger.silly(` - rewrite: ${chalk.yellow(navigationFallbackRewrite)}`); const isNavigationFallbackWritePathExists = tryFindFileForRequest(navigationFallbackRewrite); if (isNavigationFallbackWritePathExists && !isRequestPathExcludedFromNavigationFallback(decodedRequestPath, navigationFallbackRule, matchedRoute)) { shouldDisplayNotFoundPage = false; } if (navigationFallbackRewrite) { filePathFromRequest = navigationFallbackRewrite; if (!shouldDisplayNotFoundPage) { logger.silly(`rewrite request to ${chalk.yellow(filePathFromRequest)}`); req.url = filePathFromRequest; } } } } logger.silly(` - shouldDisplayNotFoundPage: ${chalk.yellow(shouldDisplayNotFoundPage)}`); if (shouldDisplayNotFoundPage) { handleErrorPage(req, res, 404, responseOverridesRule); return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: false, }; } } if (!filePathFromRequest) { return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: false, }; } // if the file path is a remote HTTP request, this means we are connecting to a dev server. // exist here and let the remote server handle the request. if (isHttpUrl(filePathFromRequest)) { return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: true, }; } // mime type const mimeType = getMimeTypeForExtension(filePathFromRequest, mimeTypeRule); res.setHeader("Content-Type", mimeType); // compute both global and route headers const matchingRouteHeaders = getHeadersForRoute(routeHeaders, globalHeaders); if (responseOverridesRule) { // Handle HEAD request if (req.method === "HEAD") { updateResponseHeaders(res, matchingRouteHeaders); res.statusCode = 200; return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: true, }; } // Handle OPTIONS request if (req.method === "OPTIONS") { updateResponseHeaders(res, matchingRouteHeaders); const allowStr = "GET, HEAD, OPTIONS"; res.setHeader("Allow", allowStr); res.statusCode = 204; // No Content return { isFunctionFallbackRequest: false, isSuccessfulSiteHit: true, }; } } // Handle GET request updateResponseHeaders(res, matchingRouteHeaders); req.url = filePathFromRequest; return { isSuccessfulSiteHit: true, isFunctionFallbackRequest: false, }; } //# sourceMappingURL=response.middleware.js.map