diff --git a/packages/sitecore-jss-proxy/src/index.ts b/packages/sitecore-jss-proxy/src/index.ts index bfb57b0fad..a24f67371a 100644 --- a/packages/sitecore-jss-proxy/src/index.ts +++ b/packages/sitecore-jss-proxy/src/index.ts @@ -1,4 +1,4 @@ -import { IncomingMessage, ServerResponse } from 'http'; +import { IncomingMessage, ServerResponse, ClientRequest } from 'http'; import proxy from 'http-proxy-middleware'; import HttpStatus from 'http-status-codes'; import setCookieParser from 'set-cookie-parser'; @@ -88,10 +88,7 @@ async function renderAppToResponse( return true; }; - if (request.method === 'HEAD') { - completeProxyResponse(null, proxyResponse.statusCode || HttpStatus.OK) - return - } + async function extractLayoutServiceDataFromProxyResponse(): Promise { if (proxyResponse.statusCode === HttpStatus.OK || proxyResponse.statusCode === HttpStatus.NOT_FOUND) { @@ -169,11 +166,19 @@ async function renderAppToResponse( } // we have to convert back to a buffer so that we can get the *byte count* (rather than character count) of the body - const content = Buffer.from(result.html); + let content = Buffer.from(result.html); // setting the content-length header is not absolutely necessary, but is recommended proxyResponse.headers['content-length'] = content.length.toString(10); + // if original request was a HEAD, we should not return a response body + if (request.method === 'HEAD') { + if (config.debug) { + console.log('DEBUG: Original request method was HEAD, clearing response body'); + } + content = Buffer.from([]); + } + if (result.redirect) { if (!result.status) { result.status = 302; @@ -381,7 +386,7 @@ function isUrlIgnored(originalUrl: string, config: ProxyConfig, noDebug: boolean ); if (!noDebug && config.debug) { - if (result) { + if (!result) { console.log( `DEBUG: URL ${originalUrl} did not match the proxy exclude list, and will be treated as a layout service route to render. Excludes:`, config.pathRewriteExcludeRoutes @@ -401,7 +406,7 @@ function isUrlIgnored(originalUrl: string, config: ProxyConfig, noDebug: boolean result = config.pathRewriteExcludePredicate(originalUrl); if (!noDebug && config.debug) { - if (result) { + if (!result) { console.log( `DEBUG: URL ${originalUrl} did not match the proxy exclude function, and will be treated as a layout service route to render.` ); @@ -418,6 +423,23 @@ function isUrlIgnored(originalUrl: string, config: ProxyConfig, noDebug: boolean return false; } +function handleProxyRequest(proxyReq: any, req: any, res: ServerResponse, config: ProxyConfig, + customOnProxyReq: ((proxyReq: ClientRequest, req: IncomingMessage, res: ServerResponse) => void) | undefined) { + + // if a HEAD request, we still need to issue a GET so we can return accurate headers + // proxyReq defined as 'any' to allow us to mutate this + if (proxyReq.method === 'HEAD' && !isUrlIgnored(req.originalUrl, config, true)) { + if (config.debug) { + console.log('DEBUG: Rewriting HEAD request to GET to create accurate headers'); + } + proxyReq.method = 'GET'; + } + // invoke custom onProxyReq + if (customOnProxyReq) { + customOnProxyReq(proxyReq, req, res); + } +} + function createOptions( renderer: AppRenderer, config: ProxyConfig, @@ -438,14 +460,16 @@ function createOptions( console.log('DEBUG: Final proxy config', config); } + const customOnProxyReq = config.proxyOptions?.onProxyReq; return { + ...config.proxyOptions, target: config.apiHost, changeOrigin: true, // required otherwise need to include CORS headers ws: true, pathRewrite: (reqPath, req) => rewriteRequestPath(reqPath, req, config, parseRouteUrl), logLevel: config.debug ? 'debug' : 'info', + onProxyReq: (proxyReq, req, res) => handleProxyRequest(proxyReq, req, res, config, customOnProxyReq), onProxyRes: (proxyRes, req, res) => handleProxyResponse(proxyRes, req, res, renderer, config), - ...config.proxyOptions, }; }