Родитель
4674b70b3b
Коммит
c26746d47f
|
@ -46,3 +46,4 @@ dist
|
|||
*.tgz
|
||||
*.orig
|
||||
cypress/videos/
|
||||
cypress/screenshots/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
const PROVIDERS = ["github", "twitter", "facebook", "google", "aad"];
|
||||
const PROVIDERS = ["github", "twitter", "facebook", "aad"];
|
||||
|
||||
context("/.auth/me", () => {
|
||||
let clientPrincipal;
|
||||
|
@ -51,6 +51,17 @@ context("/.auth/me", () => {
|
|||
});
|
||||
|
||||
context(`/.auth/login/<provider>`, () => {
|
||||
// google has a special config (check staticwebapp.config.json)
|
||||
// { "route": "/*.google", "redirect": "https://www.google.com/" }
|
||||
describe(`when using provider: google`, () => {
|
||||
it(`should redirect to https://www.google.com/`, async () => {
|
||||
cy.visit("http://0.0.0.0:1234/.auth/login/google").then((response) => {
|
||||
expect(response.status).to.be(302);
|
||||
expect(response.headers.get("location")).to.be("https://www.google.com/");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
for (let index = 0; index < PROVIDERS.length; index++) {
|
||||
const provider = PROVIDERS[index];
|
||||
describe(`when using provider: ${provider}`, () => {
|
||||
|
|
|
@ -1,55 +1,52 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout: 20000 /* set this for Windows */ }, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit("http://0.0.0.0:1234");
|
||||
});
|
||||
|
||||
it("root returns /index.html", async () => {
|
||||
cy.request("/").then(() => {
|
||||
cy.visit("http://0.0.0.0:1234/").then(() => {
|
||||
cy.title().should("eq", "/index.html");
|
||||
});
|
||||
});
|
||||
|
||||
it("/index.html returns /index.html", async () => {
|
||||
cy.request("/index.html").then(() => {
|
||||
cy.visit("http://0.0.0.0:1234/index.html").then(() => {
|
||||
cy.title().should("eq", "/index.html");
|
||||
});
|
||||
});
|
||||
|
||||
it("folder returns folder/index.html", async () => {
|
||||
cy.request("/folder/").then(() => {
|
||||
cy.visit("http://0.0.0.0:1234/folder/").then(() => {
|
||||
cy.title().should("eq", "/folder/index.html");
|
||||
});
|
||||
});
|
||||
|
||||
it("rewrite to file returns correct content", async () => {
|
||||
cy.request("/rewrite_index2").then(() => {
|
||||
cy.visit("http://0.0.0.0:1234/rewrite_index2").then(() => {
|
||||
cy.title().should("eq", "/index2.html");
|
||||
});
|
||||
});
|
||||
|
||||
it("rewrite to function returns function response", async () => {
|
||||
cy.request("/rewrite-to-function").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/rewrite-to-function").then((response) => {
|
||||
expect(response).to.have.property("x-swa-custom");
|
||||
expect(response["x-swa-custom"]).to.be("/api/headers");
|
||||
});
|
||||
});
|
||||
|
||||
it("content response contains global headers", async () => {
|
||||
cy.request("/").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/").then((response) => {
|
||||
expect(response.headers.get("a")).to.be("b");
|
||||
});
|
||||
});
|
||||
|
||||
it("route headers override global headers", async () => {
|
||||
cy.request("/rewrite_index2").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/rewrite_index2").then((response) => {
|
||||
expect(response.headers.get("a")).to.be("c");
|
||||
});
|
||||
});
|
||||
|
||||
it("default redirect returns 302 with correct location", async () => {
|
||||
cy.request("/redirect/foo").as("response");
|
||||
cy.visit("http://0.0.0.0:1234/redirect/foo").as("response");
|
||||
|
||||
cy.get("@response")
|
||||
.its("headers")
|
||||
|
@ -62,7 +59,7 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
|
||||
it("redirect with statusCode 302 returns 302 with correct location", async () => {
|
||||
cy.request("/redirect/302").as("response");
|
||||
cy.visit("http://0.0.0.0:1234/redirect/302").as("response");
|
||||
|
||||
cy.get("@response")
|
||||
.its("headers")
|
||||
|
@ -75,7 +72,7 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
|
||||
it("redirect with statusCode 301 returns 301 with correct location", async () => {
|
||||
cy.request("/redirect/301").as("response");
|
||||
cy.visit("http://0.0.0.0:1234/redirect/301").as("response");
|
||||
|
||||
cy.get("@response")
|
||||
.its("headers")
|
||||
|
@ -88,27 +85,27 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
|
||||
it("setting mimetype of unknown file type returns correct mime type", async () => {
|
||||
cy.request("/test.swaconfig").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/test.swaconfig").then((response) => {
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.headers.get("content-type")).to.be("application/json");
|
||||
});
|
||||
});
|
||||
|
||||
it("navigation fallback returns /index.html", async () => {
|
||||
cy.request("/does_not_exist.html").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/does_not_exist.html").then((response) => {
|
||||
expect(response.status).to.be(200);
|
||||
cy.title().should("eq", "/index.html");
|
||||
});
|
||||
});
|
||||
|
||||
it("navigation fallback that's excluded returns 404", async () => {
|
||||
cy.request("/does_not_exist.txt").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/does_not_exist.txt").then((response) => {
|
||||
expect(response.status).to.be(404);
|
||||
});
|
||||
});
|
||||
|
||||
it("/*.foo matches extension", async () => {
|
||||
cy.request("/thing.foo").as("response");
|
||||
cy.visit("http://0.0.0.0:1234/thing.foo").as("response");
|
||||
|
||||
cy.get("@response")
|
||||
.its("headers")
|
||||
|
@ -121,7 +118,7 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
|
||||
it("/*.{jpg} matches extension", async () => {
|
||||
cy.request("/thing.jpg").as("response");
|
||||
cy.visit("http://0.0.0.0:1234/thing.jpg").as("response");
|
||||
|
||||
cy.get("@response")
|
||||
.its("headers")
|
||||
|
@ -134,7 +131,7 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
|
||||
it("/*.{png,gif} matches multiple extensions", async () => {
|
||||
cy.request("/thing.png").as("response1");
|
||||
cy.visit("http://0.0.0.0:1234/thing.png").as("response1");
|
||||
|
||||
cy.get("@response1")
|
||||
.its("headers")
|
||||
|
@ -145,7 +142,7 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
});
|
||||
|
||||
cy.request("/thing.gif").as("response2");
|
||||
cy.visit("http://0.0.0.0:1234/thing.gif").as("response2");
|
||||
|
||||
cy.get("@response2")
|
||||
.its("headers")
|
||||
|
@ -158,14 +155,14 @@ context("route rules engine", { failOnStatusCode: false, defaultCommandTimeout:
|
|||
});
|
||||
|
||||
it("redirect can redirect to external URL", async () => {
|
||||
cy.request("/something.google").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/something.google").then((response) => {
|
||||
expect(response.status).to.be(302);
|
||||
expect(response.headers.get("location")).to.be("https://www.google.com/");
|
||||
});
|
||||
});
|
||||
|
||||
it("rewrite to folder returns folder's default file", async () => {
|
||||
cy.request("/folder/somefile.html").then((response) => {
|
||||
cy.visit("http://0.0.0.0:1234/folder/somefile.html").then((response) => {
|
||||
expect(response.status).to.be(200);
|
||||
cy.title().should("eq", "/folder/index.html");
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ describe("validateCookie()", () => {
|
|||
});
|
||||
|
||||
it("cookies = 'abc'", () => {
|
||||
expect(validateCookie("")).toBe(false);
|
||||
expect(validateCookie("abc")).toBe(false);
|
||||
});
|
||||
|
||||
it("cookies = 'foo=bar'", () => {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import { globToRegExp } from "./glob";
|
||||
|
||||
describe("globToRegExp()", () => {
|
||||
it("glob = <EMPTY>", () => {
|
||||
expect(globToRegExp("")).toBe("");
|
||||
});
|
||||
|
||||
it("glob = abc", () => {
|
||||
expect(globToRegExp("abc")).toBe("abc");
|
||||
});
|
||||
|
||||
it("glob = foo=bar", () => {
|
||||
expect(globToRegExp("foo=bar")).toBe("foo=bar");
|
||||
});
|
||||
|
||||
it("glob = *", () => {
|
||||
expect(globToRegExp("*")).toBe("*");
|
||||
});
|
||||
it("glob = /*", () => {
|
||||
expect(globToRegExp("/*")).toBe("\\/.*");
|
||||
});
|
||||
|
||||
it("glob = /foo/*", () => {
|
||||
expect(globToRegExp("/foo/*")).toBe("\\/foo\\/.*");
|
||||
});
|
||||
|
||||
it("glob = /*.{jpg}", () => {
|
||||
expect(globToRegExp("/*.{jpg}")).toBe("\\/.*(jpg)");
|
||||
});
|
||||
|
||||
it("glob = /*.{jpg,gif}", () => {
|
||||
expect(globToRegExp("/*.{jpg,gif}")).toBe("\\/.*(jpg|gif)");
|
||||
});
|
||||
|
||||
it("glob = /foo/*.{jpg,gif}", () => {
|
||||
expect(globToRegExp("/foo/*.{jpg,gif}")).toBe("\\/foo\\/.*(jpg|gif)");
|
||||
});
|
||||
|
||||
it("glob = {foo,bar}.json", () => {
|
||||
expect(globToRegExp("{foo,bar}.json")).toBe("(foo|bar).json");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Turn expression into a valid regex
|
||||
*/
|
||||
export function globToRegExp(glob: string) {
|
||||
const filesExtensionMatch = glob.match(/{.*}/);
|
||||
if (filesExtensionMatch) {
|
||||
const filesExtensionExpression = filesExtensionMatch[0];
|
||||
if (filesExtensionExpression) {
|
||||
// build a regex group (png|jpg|gif)
|
||||
const filesExtensionRegEx = filesExtensionExpression.replace(/\,/g, "|").replace("{", "(").replace("}", ")");
|
||||
glob = glob.replace(filesExtensionExpression, filesExtensionRegEx);
|
||||
}
|
||||
}
|
||||
|
||||
return glob.replace(/\//g, "\\/").replace("*.", ".*").replace("/*", "/.*");
|
||||
}
|
|
@ -23,8 +23,6 @@ export async function applyRules(req: IncomingMessage, res: ServerResponse, user
|
|||
const filepath = path.join(process.env.SWA_CLI_OUTPUT_LOCATION!, req.url!);
|
||||
const isFileFound = fs.existsSync(filepath);
|
||||
|
||||
logger.silly("checking rules...");
|
||||
|
||||
// note: these rules are mutating the req and res objects
|
||||
|
||||
await navigationFallback(req, res, userConfig.navigationFallback);
|
||||
|
@ -40,6 +38,7 @@ export async function applyRules(req: IncomingMessage, res: ServerResponse, user
|
|||
matchedRoute: userDefinedRoute,
|
||||
filepath,
|
||||
isFileFound,
|
||||
navigationFallback: { statusCode: res.statusCode, url: req.url },
|
||||
statusCode: res.statusCode,
|
||||
url: req.url,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -180,7 +180,8 @@ describe("customRoutes()", () => {
|
|||
};
|
||||
await customRoutes(req, res, userRouteConfig);
|
||||
|
||||
expect(res.writeHead).toHaveBeenCalledWith(302, { Location: "/bar.html" });
|
||||
expect(res.setHeader).toHaveBeenCalledWith("Location", "/bar.html");
|
||||
expect(res.statusCode).toBe(302);
|
||||
});
|
||||
|
||||
it("should serve with redirect (statusCode=302)", async () => {
|
||||
|
@ -191,7 +192,8 @@ describe("customRoutes()", () => {
|
|||
};
|
||||
await customRoutes(req, res, userRouteConfig);
|
||||
|
||||
expect(res.writeHead).toHaveBeenCalledWith(302, { Location: "/bar" });
|
||||
expect(res.setHeader).toHaveBeenCalledWith("Location", "/bar");
|
||||
expect(res.statusCode).toBe(302);
|
||||
});
|
||||
|
||||
it("should serve with redirect (statusCode=301)", async () => {
|
||||
|
@ -202,7 +204,8 @@ describe("customRoutes()", () => {
|
|||
};
|
||||
await customRoutes(req, res, userRouteConfig);
|
||||
|
||||
expect(res.writeHead).toHaveBeenCalledWith(301, { Location: "/bar" });
|
||||
expect(res.setHeader).toHaveBeenCalledWith("Location", "/bar");
|
||||
expect(res.statusCode).toBe(301);
|
||||
});
|
||||
|
||||
it("should not serve with redirect (statusCode=200)", async () => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import globalyzer from "globalyzer";
|
||||
import globrex from "globrex";
|
||||
// import globalyzer from "globalyzer";
|
||||
// import globrex from "globrex";
|
||||
import http from "http";
|
||||
import { decodeCookie } from "../../../core";
|
||||
import { decodeCookie, logger } from "../../../core";
|
||||
import { globToRegExp } from "../../../core/utils/glob";
|
||||
|
||||
export const matchRoute = (req: http.IncomingMessage, isLegacyConfigFile: boolean) => {
|
||||
const sanitizedUrl = new URL(req.url!, `http://${req?.headers?.host}`);
|
||||
|
@ -28,30 +29,9 @@ export const matchRoute = (req: http.IncomingMessage, isLegacyConfigFile: boolea
|
|||
}
|
||||
|
||||
// we don't support full globs in the config file.
|
||||
// add this little workaround to convert a wildcard into a valid glob pattern
|
||||
filter = filter.replace("/*", "/**/*");
|
||||
|
||||
// extract glob metadata
|
||||
const globSegments = globalyzer(filter);
|
||||
// if filter and url segments don't have a commom base path
|
||||
// don't process regex, just return false
|
||||
if (originlUrl.startsWith(globSegments.base)) {
|
||||
const { regex } = globrex(globSegments.glob, { globstar: true, extended: true });
|
||||
|
||||
// extract the last segment (what comes after the base) from the URL:
|
||||
// / => <empty string>
|
||||
// /bar.gif => bar.gif
|
||||
// /images/foo/bar.gif => bar.gif
|
||||
let lastSegmentFromUrl = originlUrl.replace(`${globSegments.base}`, "");
|
||||
|
||||
// globrex generates regex that doesn't match leading forwardslash, so we remove it
|
||||
// before processing the regex
|
||||
lastSegmentFromUrl = lastSegmentFromUrl.replace(/^\//, "");
|
||||
|
||||
return regex.test(lastSegmentFromUrl!);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// add this little utility to convert a wildcard into a valid glob pattern
|
||||
const regexp = new RegExp(`^${globToRegExp(filter)}$`);
|
||||
return regexp.test(originlUrl!);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -61,6 +41,9 @@ export const customRoutes = async (req: http.IncomingMessage, res: http.ServerRe
|
|||
}
|
||||
|
||||
if (userDefinedRoute) {
|
||||
logger.silly("checking routes rule...");
|
||||
logger.silly({ userDefinedRoute });
|
||||
|
||||
// set headers
|
||||
if (userDefinedRoute.headers) {
|
||||
for (const header in userDefinedRoute.headers) {
|
||||
|
@ -106,10 +89,8 @@ export const customRoutes = async (req: http.IncomingMessage, res: http.ServerRe
|
|||
// redirects
|
||||
// note: adding checks to avoid ERR_TOO_MANY_REDIRECTS
|
||||
if (route !== req.url) {
|
||||
res.writeHead(Number(userDefinedRoute.statusCode) || 302, {
|
||||
Location: route,
|
||||
});
|
||||
res.end();
|
||||
res.setHeader("Location", route);
|
||||
res.statusCode = Number(userDefinedRoute.statusCode) || 302;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
import http from "http";
|
||||
import type http from "http";
|
||||
import { logger } from "../../../core";
|
||||
|
||||
// See: https://docs.microsoft.com/en-us/azure/static-web-apps/configuration#global-headers
|
||||
export const globalHeaders = async (_req: http.IncomingMessage, res: http.ServerResponse, globalHeaders: SWAConfigFileGlobalHeaders) => {
|
||||
logger.silly("checking globalHeaders rule...");
|
||||
|
||||
for (const header in globalHeaders) {
|
||||
if (globalHeaders[header] === "") {
|
||||
res.removeHeader(header);
|
||||
|
||||
logger.silly(` - removing header: ${header}`);
|
||||
} else {
|
||||
res.setHeader(header, globalHeaders[header]);
|
||||
|
||||
logger.silly(` - adding header: ${header}=${globalHeaders[header]}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import fs from "fs";
|
||||
import globalyzer from "globalyzer";
|
||||
import globrex from "globrex";
|
||||
// import globalyzer from "globalyzer";
|
||||
// import globrex from "globrex";
|
||||
import http from "http";
|
||||
import path from "path";
|
||||
import { logger } from "../../../core";
|
||||
import { globToRegExp } from "../../../core/utils/glob";
|
||||
|
||||
// See: https://docs.microsoft.com/en-us/azure/static-web-apps/configuration#fallback-routes
|
||||
|
||||
|
@ -24,6 +25,8 @@ export const navigationFallback = async (
|
|||
return;
|
||||
}
|
||||
|
||||
logger.silly("checking navigationFallback rule...");
|
||||
|
||||
// make sure we have a leading / in the URL
|
||||
if (navigationFallback.rewrite.startsWith("/") === false) {
|
||||
navigationFallback.rewrite = `/${navigationFallback.rewrite}`;
|
||||
|
@ -31,29 +34,27 @@ export const navigationFallback = async (
|
|||
|
||||
// is the requested file available on disk?
|
||||
const filename = originlUrl?.endsWith("/") ? `${originlUrl}/index.html` : originlUrl;
|
||||
|
||||
const filepath = path.join(process.env.SWA_CLI_OUTPUT_LOCATION!, filename!);
|
||||
|
||||
const isFileFoundOnDisk = fs.existsSync(filepath);
|
||||
|
||||
logger.silly(` - url ${originlUrl}`);
|
||||
|
||||
// parse the exclusion rules and match at least one rule
|
||||
const isMatchedFilter = navigationFallback.exclude.some((filter) => {
|
||||
const isMatchedFilter = navigationFallback?.exclude.some((filter) => {
|
||||
// we don't support full globs in the config file.
|
||||
// add this little workaround to convert a wildcard into a valid glob pattern
|
||||
filter = filter.replace("*", "**/*");
|
||||
// add this little utility to convert a wildcard into a valid glob pattern
|
||||
const regexp = new RegExp(`^${globToRegExp(filter)}$`);
|
||||
const isMatch = regexp.test(originlUrl!);
|
||||
|
||||
// extract glob metadata
|
||||
const globSegments = globalyzer(filter);
|
||||
logger.silly(` - filter= ${filter}`);
|
||||
logger.silly(` - regexp= ${regexp}`);
|
||||
logger.silly(` - match= ${isMatch}`);
|
||||
|
||||
const { regex } = globrex(globSegments.glob, { globstar: true, extended: true, filepath: true });
|
||||
|
||||
// globrex generates regex that doesn't match leading forwardslash, so we remove it
|
||||
// before processing the regex
|
||||
let originlUrlWithoutLeadingSlash = originlUrl?.replace(/^\//, "");
|
||||
|
||||
return regex.test(originlUrlWithoutLeadingSlash!);
|
||||
return isMatch;
|
||||
});
|
||||
|
||||
logger.silly(` - isMatchedFilter=${isMatchedFilter}`);
|
||||
|
||||
// rules logic:
|
||||
// 1. if no exclude rules are provided, rewrite by default
|
||||
// 2. if a file exists on disk, and match exclusion => return it
|
||||
|
@ -66,24 +67,38 @@ export const navigationFallback = async (
|
|||
// 1.
|
||||
if (!navigationFallback.exclude || navigationFallback.exclude.length === 0) {
|
||||
newUrl = navigationFallback.rewrite;
|
||||
|
||||
logger.silly(` - no exclude rules are provided (rewrite by default)`);
|
||||
logger.silly(` - url=${newUrl}`);
|
||||
}
|
||||
// 2.
|
||||
else if (isFileFoundOnDisk === true && isMatchedFilter === true) {
|
||||
newUrl = req.url;
|
||||
|
||||
logger.silly(` - file exists on disk, and match exclusion`);
|
||||
logger.silly(` - url=${newUrl}`);
|
||||
}
|
||||
// 3.
|
||||
else if (isFileFoundOnDisk === false && isMatchedFilter === true) {
|
||||
res.statusCode = 404;
|
||||
|
||||
logger.silly(` - file doesn't exist on disk, and match exclusion`);
|
||||
logger.silly(` - statusCode=404`);
|
||||
}
|
||||
// 4.
|
||||
else if (isFileFoundOnDisk === true && isMatchedFilter === false) {
|
||||
newUrl = navigationFallback.rewrite;
|
||||
|
||||
logger.silly(` - file exists on disk, and doesn't match exclusion`);
|
||||
logger.silly(` - url=${newUrl}`);
|
||||
}
|
||||
// 5.
|
||||
else if (isFileFoundOnDisk === false && isMatchedFilter === false) {
|
||||
newUrl = navigationFallback.rewrite;
|
||||
|
||||
logger.silly(` - file doesn't exist on disk, and doesn't match exclusion`);
|
||||
logger.silly(` - url=${newUrl}`);
|
||||
}
|
||||
|
||||
logger.silly({ filepath, isMatchedFilter });
|
||||
req.url = newUrl;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import http from "http";
|
||||
import { DEFAULT_CONFIG } from "../../../config";
|
||||
import { logger } from "../../../core";
|
||||
|
||||
// See: https://docs.microsoft.com/en-us/azure/static-web-apps/configuration#response-overrides
|
||||
export const responseOverrides = async (req: http.IncomingMessage, res: http.ServerResponse, responseOverrides: SWAConfigFileResponseOverrides) => {
|
||||
|
@ -9,14 +10,22 @@ export const responseOverrides = async (req: http.IncomingMessage, res: http.Ser
|
|||
const overridenStatusCode = responseOverrides?.[`${statusCode}`];
|
||||
|
||||
if (overridenStatusCode) {
|
||||
logger.silly("checking responseOverrides rule...");
|
||||
|
||||
if (overridenStatusCode.statusCode) {
|
||||
res.statusCode = overridenStatusCode.statusCode;
|
||||
|
||||
logger.silly(` - statusCode: ${statusCode}`);
|
||||
}
|
||||
if (overridenStatusCode.redirect) {
|
||||
res.setHeader("Location", overridenStatusCode.redirect);
|
||||
|
||||
logger.silly(` - Location: ${overridenStatusCode.redirect}`);
|
||||
}
|
||||
if (overridenStatusCode.rewrite && req.url !== overridenStatusCode.rewrite) {
|
||||
req.url = `${DEFAULT_CONFIG.customUrlScheme}${overridenStatusCode.rewrite}`;
|
||||
|
||||
logger.silly(` - url: ${req.url}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,6 +141,12 @@ const requestHandler = (userConfig: SWAConfigFile | null) =>
|
|||
if (userConfig) {
|
||||
await applyRules(req, res, userConfig);
|
||||
|
||||
// in case a redirect rule has been applied, flush response
|
||||
if (res.getHeader("Location")) {
|
||||
logRequest(req, null, res.statusCode);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
if ([401, 403, 404].includes(res.statusCode)) {
|
||||
const isCustomUrl = req.url.startsWith(DEFAULT_CONFIG.customUrlScheme!);
|
||||
|
||||
|
@ -167,7 +173,7 @@ const requestHandler = (userConfig: SWAConfigFile | null) =>
|
|||
}
|
||||
}
|
||||
|
||||
// don't serve user custom routes file
|
||||
// don't serve staticwebapp.config.json / routes.json
|
||||
if (req.url.endsWith(DEFAULT_CONFIG.swaConfigFilename!) || req.url.endsWith(DEFAULT_CONFIG.swaConfigFilenameLegacy!)) {
|
||||
req.url = "404.html";
|
||||
res.statusCode = 404;
|
||||
|
|
Загрузка…
Ссылка в новой задаче