Skip to content

Commit

Permalink
[sitecore-jss-proxy] Setting "followRedirects" to true" breaks HEAD r…
Browse files Browse the repository at this point in the history
…equests.
  • Loading branch information
illiakovalenko committed Oct 12, 2023
1 parent 6498247 commit c651417
Showing 1 changed file with 47 additions and 28 deletions.
75 changes: 47 additions & 28 deletions packages/sitecore-jss-proxy/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IncomingMessage, ServerResponse, ClientRequest, IncomingHttpHeaders } from 'http';
import { Request, Response } from 'express';
import { Request, RequestHandler, Response } from 'express';
import { ServerOptions } from 'http-proxy';
import { createProxyMiddleware, Options } from 'http-proxy-middleware';
import HttpStatus from 'http-status-codes';
Expand All @@ -11,19 +11,9 @@ import { RenderResponse } from './RenderResponse';
import { RouteUrlParser } from './RouteUrlParser';
import { buildQueryString, tryParseJson } from './util';

/**
* Extends IncomingMessage as it should contain these properties but they are not provided in types
*/
export interface ProxyIncomingMessage extends IncomingMessage {
originalUrl: string;
query: { [key: string]: string | number | boolean };
}

/**
* Extends ClientRequest as it should contain `method` but it's not provided in types
*/
interface ExtendedClientRequest extends ClientRequest {
method: string;
interface ExtendedRequest extends Request {
// Custom property we set to keep an original method when we swap 'HEAD' with 'GET'
originalMethod?: string;
}

// For some reason, every other response returned by Sitecore contains the 'set-cookie' header with the SC_ANALYTICS_GLOBAL_COOKIE value as an empty string.
Expand Down Expand Up @@ -330,8 +320,8 @@ function handleProxyResponse(

if (config.debug) {
console.log('DEBUG: request url', request.url);
console.log('DEBUG: request query', (request as ProxyIncomingMessage).query);
console.log('DEBUG: request original url', (request as ProxyIncomingMessage).originalUrl);
console.log('DEBUG: request query', request.query);
console.log('DEBUG: request original url', request.originalUrl);
console.log('DEBUG: proxied request response code', proxyResponse.statusCode);
console.log('DEBUG: RAW request headers', JSON.stringify(request.headers, null, 2));
console.log(
Expand All @@ -342,7 +332,7 @@ function handleProxyResponse(

// if the request URL contains any of the excluded rewrite routes, we assume the response does not need to be server rendered.
// instead, the response should just be relayed as usual.
if (isUrlIgnored((request as ProxyIncomingMessage).originalUrl, config, true)) {
if (isUrlIgnored(request.originalUrl, config, true)) {
return Promise.resolve(undefined);
}

Expand Down Expand Up @@ -385,7 +375,7 @@ export function rewriteRequestPath(
const qsIndex = finalReqPath.indexOf('?');
let qs = '';
if (qsIndex > -1 || Object.keys(req.query).length) {
qs = buildQueryString((req as ProxyIncomingMessage).query);
qs = buildQueryString(req.query as { [key: string]: string | number | boolean });
// Splice qs part when url contains that
if (qsIndex > -1) finalReqPath = finalReqPath.slice(0, qsIndex);
}
Expand Down Expand Up @@ -509,24 +499,34 @@ function isUrlIgnored(originalUrl: string, config: ProxyConfig, noDebug = false)
*/
function handleProxyRequest(
proxyReq: ClientRequest,
req: Request,
req: ExtendedRequest,
res: Response,
options: ServerOptions,
config: ProxyConfig,
customOnProxyReq:
| ((proxyReq: ClientRequest, req: Request, res: Response, options: ServerOptions) => void)
| undefined
) {
// if a HEAD request, we still need to issue a GET so we can return accurate headers
if (
(proxyReq as ExtendedClientRequest).method === 'HEAD' &&
!isUrlIgnored((req as Request).originalUrl, config, true)
) {
if (config.debug) {
console.log('DEBUG: Rewriting HEAD request to GET to create accurate headers');
if (!isUrlIgnored(req.originalUrl, config, true)) {
// In case 'followRedirects' is enabled, and before the proxy was initialized we had set 'originalMethod'
// now we need to set req.method back to original one, since proxyReq is already initialized.
// See more info in 'preProxyHandler'
if (options.followRedirects && req.originalMethod === 'HEAD') {
req.method = req.originalMethod;
delete req.originalMethod;

if (config.debug) {
console.log('DEBUG: Rewriting HEAD request to GET to create accurate headers');
}
} else if (proxyReq.method === 'HEAD' && !isUrlIgnored(req.originalUrl, config, true)) {
if (config.debug) {
console.log('DEBUG: Rewriting HEAD request to GET to create accurate headers');
}
// if a HEAD request, we still need to issue a GET so we can return accurate headers
proxyReq.method = 'GET';
}
(proxyReq as ExtendedClientRequest).method = 'GET';
}

// invoke custom onProxyReq
if (customOnProxyReq) {
customOnProxyReq(proxyReq, req, res, options);
Expand Down Expand Up @@ -583,7 +583,26 @@ export default function scProxy(
parseRouteUrl: RouteUrlParser
) {
const options = createOptions(renderer, config, parseRouteUrl);
return createProxyMiddleware(options);

const preProxyHandler: RequestHandler = (req, _res, next) => {
// When 'followRedirects' is enabled, 'onProxyReq' is executed after 'proxyReq' is initialized based on original 'req'
// and there are no public properties/methods to modify Redirectable 'proxyReq'.
// so, we need to set 'HEAD' req as 'GET' before the proxy is initialized.
// During the 'onProxyReq' event we will set 'req.method' back as 'HEAD'.
// if a HEAD request, we need to issue a GET so we can return accurate headers
if (
req.method === 'HEAD' &&
options.followRedirects &&
!isUrlIgnored(req.originalUrl, config, true)
) {
req.method = 'GET';
(req as ExtendedRequest).originalMethod = 'HEAD';
}

next();
};

return [preProxyHandler, createProxyMiddleware(options)];
}

export { ProxyConfig, ServerBundle };

0 comments on commit c651417

Please sign in to comment.