Skip to content

Commit

Permalink
[sitecore-jss-nextjs]: A condition for prefetch requests has been ad…
Browse files Browse the repository at this point in the history
…ded to improve the performance of redirects.

Additionally, a condition has been added to handle Netlify requests to reduce server load.
  • Loading branch information
Ruslan Matkovskyi committed Dec 20, 2024
1 parent 9585e19 commit f67d456
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,7 @@ describe('RedirectsMiddleware', () => {
origin: 'http://localhost:3000',
clone: cloneUrl,
},
headerValues: { 'cdn-loop': 'netlify' },
},
});
setupRedirectStub(301);
Expand Down
96 changes: 43 additions & 53 deletions packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CacheClient, debug, MemoryCacheClient } from '@sitecore-jss/sitecore-jss';
import { debug } from '@sitecore-jss/sitecore-jss';
import {
GraphQLRedirectsService,
GraphQLRedirectsServiceConfig,
Expand All @@ -16,6 +16,7 @@ import { MiddlewareBase, MiddlewareBaseConfig } from './middleware';

const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
const NAME_NETLIFY = 'netlify';

type RedirectResult = RedirectInfo & { matchedQueryString?: string };

Expand All @@ -37,7 +38,6 @@ export type RedirectsMiddlewareConfig = Omit<GraphQLRedirectsServiceConfig, 'fet
export class RedirectsMiddleware extends MiddlewareBase {
private redirectsService: GraphQLRedirectsService;
private locales: string[];
private cache: CacheClient<RedirectResult | boolean | undefined>;

/**
* @param {RedirectsMiddlewareConfig} [config] redirects middleware config
Expand All @@ -49,10 +49,6 @@ export class RedirectsMiddleware extends MiddlewareBase {
// (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
this.redirectsService = new GraphQLRedirectsService({ ...config, fetch: fetch });
this.locales = config.locales;
this.cache = new MemoryCacheClient<RedirectResult | boolean | undefined>({
cacheEnabled: config.cacheEnabled,
cacheTimeout: config.cacheTimeout,
});
}

/**
Expand Down Expand Up @@ -85,15 +81,24 @@ export class RedirectsMiddleware extends MiddlewareBase {
});

const createResponse = async () => {
const response = res || NextResponse.next();

if (this.config.disabled && this.config.disabled(req, res || NextResponse.next())) {
debug.redirects('skipped (redirects middleware is disabled)');
return res || NextResponse.next();
return response;
}

if (this.isPreview(req) || this.excludeRoute(pathname)) {
debug.redirects('skipped (%s)', this.isPreview(req) ? 'preview' : 'route excluded');

return res || NextResponse.next();
return response;
}

// Skip prefetch requests
if (this.isPrefetch(req)) {
debug.redirects('skipped (prefetch)');
response.headers.set('x-middleware-cache', 'no-cache');
return response;
}

site = this.getSite(req, res);
Expand All @@ -104,7 +109,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
if (!existsRedirect) {
debug.redirects('skipped (redirect does not exist)');

return res || NextResponse.next();
return response;
}

// Find context site language and replace token
Expand Down Expand Up @@ -160,16 +165,16 @@ export class RedirectsMiddleware extends MiddlewareBase {
/** return Response redirect with http code of redirect type */
switch (existsRedirect.redirectType) {
case REDIRECT_TYPE_301: {
return this.createRedirectResponse(url, res, 301, 'Moved Permanently');
return this.createRedirectResponse(url, response, 301, 'Moved Permanently');
}
case REDIRECT_TYPE_302: {
return this.createRedirectResponse(url, res, 302, 'Found');
return this.createRedirectResponse(url, response, 302, 'Found');
}
case REDIRECT_TYPE_SERVER_TRANSFER: {
return this.rewrite(url.href, req, res || NextResponse.next());
return this.rewrite(url.href, req, response);
}
default:
return res || NextResponse.next();
return response;
}
};

Expand Down Expand Up @@ -199,29 +204,14 @@ export class RedirectsMiddleware extends MiddlewareBase {
const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(
req.nextUrl.clone()
);
const cacheKey = `${targetURL}-${targetQS}-${locale}`;
const cachedRedirect = this.cache.getCacheValue(cacheKey);

if (cachedRedirect !== null) {
return typeof cachedRedirect === 'boolean' ? undefined : cachedRedirect;
}

const normalizedPath = targetURL.replace(/\/*$/gi, '');
const redirects = await this.redirectsService.fetchRedirects(siteName);

const language = this.getLanguage(req);
const modifyRedirects = structuredClone(redirects);
let matchedQueryString: string | undefined;

const result = modifyRedirects.length
return modifyRedirects.length
? modifyRedirects.find((redirect: RedirectResult) => {
// generate cache key for the current pattern
const chachedPatternResultKey = `${cacheKey}-${redirect.pattern}-${redirect.target}`;
// Check if the result is already cached
const chachedPatternResult = this.cache.getCacheValue(chachedPatternResultKey);

if (chachedPatternResult !== null) {
return chachedPatternResult;
}

// Modify the redirect pattern to ignore the language prefix in the path
// And escapes non-special "?" characters in a string or regex.
redirect.pattern = this.escapeNonSpecialQuestionMarks(
Expand Down Expand Up @@ -260,34 +250,35 @@ export class RedirectsMiddleware extends MiddlewareBase {
* it returns `undefined`. The `matchedQueryString` is later used to indicate whether the query
* string contributed to a successful redirect match.
*/
const matchedQueryString = this.isPermutedQueryMatch({
pathname: targetURL,
queryString: targetQS,
pattern: redirect.pattern,
locale,
});
if (req.headers.get('cdn-loop') === NAME_NETLIFY) {
matchedQueryString = this.getPermutedQueryMatch({
pathname: normalizedPath,
queryString: targetQS,
pattern: redirect.pattern,
locale,
});
} else {
matchedQueryString = [
regexParser(redirect.pattern).test(`${normalizedPath}${targetQS}`),
regexParser(redirect.pattern).test(`/${locale}${normalizedPath}${targetQS}`),
].some(Boolean)
? targetQS
: undefined;
}

// Save the matched query string (if found) into the redirect object
redirect.matchedQueryString = matchedQueryString || '';

const matchedPatterResult =
// Return the redirect if the URL path or any query string permutation matches the pattern
return (
!!(
regexParser(redirect.pattern).test(targetURL) ||
regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
matchedQueryString
) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true);

// Save cache the result for the current pattern
this.cache.setCacheValue(chachedPatternResultKey, matchedPatterResult);

// Return the redirect if the URL path or any query string permutation matches the pattern
return matchedPatterResult;
) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true)
);
})
: undefined;

this.cache.setCacheValue(cacheKey, result ? result : undefined);

return result;
}

/**
Expand Down Expand Up @@ -372,7 +363,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
* @param {string} [params.locale] - The locale prefix to include in the URL if present.
* @returns {string | undefined} - return query string if any of the query permutations match the provided pattern, undefined otherwise.
*/
private isPermutedQueryMatch({
private getPermutedQueryMatch({
pathname,
queryString,
pattern,
Expand All @@ -389,11 +380,10 @@ export class RedirectsMiddleware extends MiddlewareBase {
'?' + permutation.map(([key, value]) => `${key}=${value}`).join('&')
);

const normalizedPath = pathname.replace(/\/*$/gi, '');
return listOfPermuted.find((query: string) =>
[
regexParser(pattern).test(`${normalizedPath}${query}`),
regexParser(pattern).test(`/${locale}${normalizedPath}${query}`),
regexParser(pattern).test(`${pathname}${query}`),
regexParser(pattern).test(`/${locale}${pathname}${query}`),
].some(Boolean)
);
}
Expand Down

0 comments on commit f67d456

Please sign in to comment.