@sitecore-jss/sitecore-jss-proxy
Advanced tools
Comparing version 6.0.0 to 7.0.0
@@ -1,1 +0,234 @@ | ||
'use strict';var _extends=Object.assign||function(a){for(var b,c=1;c<arguments.length;c++)for(var d in b=arguments[c],b)Object.prototype.hasOwnProperty.call(b,d)&&(a[d]=b[d]);return a},_httpProxyMiddleware=require('http-proxy-middleware'),_httpProxyMiddleware2=_interopRequireDefault(_httpProxyMiddleware),_setCookieParser=require('set-cookie-parser'),_setCookieParser2=_interopRequireDefault(_setCookieParser),_zlib=require('zlib'),_zlib2=_interopRequireDefault(_zlib),_util=require('./util');Object.defineProperty(exports,'__esModule',{value:!0}),exports.rewriteRequestPath=exports.removeEmptyAnalyticsCookie=void 0;function _interopRequireDefault(a){return a&&a.__esModule?a:{default:a}}var removeEmptyAnalyticsCookie=exports.removeEmptyAnalyticsCookie=function(a){var b=_setCookieParser2.default.parse(a.headers['set-cookie']);if(b){var c=b.findIndex(function(a){return'SC_ANALYTICS_GLOBAL_COOKIE'===a.name});if(-1!==c){var d=b[c];d&&''===d.value&&(b.splice(c,1),a.headers['set-cookie']=b)}}},renderAppToResponse=function(a,b,c,d,e){var f=c.writeHead,g=c.write,h=c.end;delete a.headers['content-length'],a.headers['content-type']='text/html; charset=utf-8';var i=a.headers['content-encoding'];i&&(-1!==i.indexOf('gzip')||-1!==i.indexOf('deflate'))&&delete a.headers['content-encoding'],c.writeHead=function(){};var j=new Buffer('');c.write=function(a,b){if(j=Buffer.isBuffer(a)?Buffer.concat([j,a]):Buffer.concat([j,new Buffer(a,b)]),10485760<j.length)throw new Error('Document too large')},c.end=function(){var k;if(i&&(-1!==i.indexOf('gzip')||-1!==i.indexOf('deflate'))){var n=_zlib2.default.unzipSync(j);n&&(k=n.toString('utf-8'))}else k=j.toString('utf-8');var l=(0,_util.tryParseJson)(k)||null,m=function(b,d){var i=new Buffer(d.html);a.headers['content-length']=i.length,e.debug&&console.log('FINAL response headers for output',JSON.stringify(a.headers,!0,2)),f.apply(c,[a.statusCode,a.headers]),g.call(c,i),h.call(c)};try{d(m,b.originalUrl,l,{})}catch(a){console.error(a);var o={statusCode:500,content:'Internal Server Error'};e.onError&&(o=e.onError(a)),f.apply(c,[o.statusCode,{}]),g.call(c,new Buffer(o.content)),h.call(c)}}},handleProxyResponse=function(a,b,c,d,e){removeEmptyAnalyticsCookie(a),e.debug&&(console.log('request url',b.url),console.log('request query',b.query),console.log('request original url',b.originalUrl),console.log('RAW request headers',JSON.stringify(b.headers,!0,2)),console.log('RAW response headers from the target',JSON.stringify(a.headers,!0,2)));e.pathRewriteExcludeRoutes.find(function(a){return-1!==b.originalUrl.indexOf(a)})||renderAppToResponse(a,b,c,d,e)},rewriteRequestPath=exports.rewriteRequestPath=function(a,b,c,d){if(c.pathRewriteExcludeRoutes.find(function(b){return-1!==a.indexOf(b)}))return a;if(-1!==a.indexOf(c.layoutServiceRoute))return a;var e,f=a.indexOf('?');-1<f&&(e=(0,_util.buildQueryString)(b.query),a=a.slice(0,f));var g;if(d){var i=d(a);i&&(a=i.sitecoreRoute?i.sitecoreRoute:'/',!a.startsWith('/')&&(a='/'+a),g=i.lang)}var h=c.layoutServiceRoute+'?item='+encodeURIComponent(a)+'&sc_apikey='+c.apiKey;return g&&(h=h+'&sc_lang='+g),e&&(h=h+'&'+e),h},createOptions=function(a,b,c){return _extends({target:b.apiHost,changeOrigin:!0,ws:!0,pathRewrite:function pathRewrite(a,d){return rewriteRequestPath(a,d,b,c)},logLevel:b.debug?'debug':'info',onProxyRes:function onProxyRes(c,d,e){return handleProxyResponse(c,d,e,a,b)}},b.proxyOptions)};exports.default=function(a,b,c){var d=createOptions(a,b,c);return(0,_httpProxyMiddleware2.default)(d)}; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.rewriteRequestPath = exports.removeEmptyAnalyticsCookie = undefined; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // node.js standard lib | ||
var _httpProxyMiddleware = require('http-proxy-middleware'); | ||
var _httpProxyMiddleware2 = _interopRequireDefault(_httpProxyMiddleware); | ||
var _setCookieParser = require('set-cookie-parser'); | ||
var _setCookieParser2 = _interopRequireDefault(_setCookieParser); | ||
var _zlib = require('zlib'); | ||
var _zlib2 = _interopRequireDefault(_zlib); | ||
var _util = require('./util'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// 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. | ||
// This effectively sets the cookie to empty on the client as well, so if a user were to close their browser | ||
// after one of these 'empty value' responses, they would not be tracked as a returning visitor after re-opening their browser. | ||
// To address this, we simply parse the response cookies and if the analytics cookie is present but has an empty value, then we | ||
// remove it from the response header. This means the existing cookie in the browser remains intact. | ||
var removeEmptyAnalyticsCookie = exports.removeEmptyAnalyticsCookie = function removeEmptyAnalyticsCookie(proxyResponse) { | ||
var cookies = _setCookieParser2.default.parse(proxyResponse.headers['set-cookie']); | ||
if (cookies) { | ||
var analyticsCookieIndex = cookies.findIndex(function (c) { | ||
return c.name === 'SC_ANALYTICS_GLOBAL_COOKIE'; | ||
}); | ||
if (analyticsCookieIndex !== -1) { | ||
var analyticsCookie = cookies[analyticsCookieIndex]; | ||
if (analyticsCookie && analyticsCookie.value === '') { | ||
cookies.splice(analyticsCookieIndex, 1); | ||
/* eslint-disable no-param-reassign */ | ||
proxyResponse.headers['set-cookie'] = cookies; | ||
/* eslint-enable no-param-reassign */ | ||
} | ||
} | ||
} | ||
}; | ||
// inspired by: http://stackoverflow.com/a/22487927/9324 | ||
var renderAppToResponse = function renderAppToResponse(proxyResponse, request, serverResponse, renderer, config) { | ||
/* eslint-disable no-param-reassign */ | ||
// monkey-patch FTW | ||
var originalWriteHead = serverResponse.writeHead; | ||
var originalWrite = serverResponse.write; | ||
var originalEnd = serverResponse.end; | ||
// these lines are necessary and must happen before we do any writing to the response | ||
// still don't fully understand why they have to be done at this point | ||
// ideally, we'd set the content-type header when we render the app below and set the content-length header | ||
// unfortunately, that doesn't work | ||
// same with deleting the content-length header. not sure why it needs to happen at this point as opposed to just overwriting it later | ||
// but again, unfortunately, that doesn't work | ||
delete proxyResponse.headers['content-length']; | ||
proxyResponse.headers['content-type'] = 'text/html; charset=utf-8'; | ||
var contentEncoding = proxyResponse.headers['content-encoding']; | ||
if (contentEncoding && (contentEncoding.indexOf('gzip') !== -1 || contentEncoding.indexOf('deflate') !== -1)) { | ||
delete proxyResponse.headers['content-encoding']; | ||
} | ||
// we are going to set our own status code if rendering fails | ||
serverResponse.writeHead = function () {}; | ||
// buffer the response body as it is written for later processing | ||
var buf = new Buffer(''); | ||
serverResponse.write = function (data, encoding) { | ||
if (Buffer.isBuffer(data)) { | ||
buf = Buffer.concat([buf, data]); // append raw buffer | ||
} else { | ||
buf = Buffer.concat([buf, new Buffer(data, encoding)]); // append string with optional character encoding (default utf8) | ||
} | ||
// sanity check: if the response is huge, bail. | ||
// ...we don't want to let someone bring down the server by filling up all our RAM. | ||
if (buf.length > 10 * 1024 * 1024) { | ||
throw new Error('Document too large'); | ||
} | ||
}; | ||
// as the response is ending, we parse the current response body which is JSON, then | ||
// render the app using that JSON, but return HTML to the final response. | ||
serverResponse.end = function () { | ||
var bufStr = void 0; | ||
if (contentEncoding && (contentEncoding.indexOf('gzip') !== -1 || contentEncoding.indexOf('deflate') !== -1)) { | ||
var decoded = _zlib2.default.unzipSync(buf); | ||
if (decoded) { | ||
bufStr = decoded.toString('utf-8'); | ||
} | ||
} else { | ||
bufStr = buf.toString('utf-8'); | ||
} | ||
var data = (0, _util.tryParseJson)(bufStr) || null; | ||
var handleResult = function handleResult(emptyArg, result) { | ||
var content = new Buffer(result.html); // we have to convert back to a buffer so that we can get the *byte count* (rather than character count) of the body | ||
// setting the content-length header is not absolutely necessary, but is recommended | ||
proxyResponse.headers['content-length'] = content.length; | ||
if (config.debug) { | ||
console.log('FINAL response headers for output', JSON.stringify(proxyResponse.headers, true, 2)); | ||
} | ||
originalWriteHead.apply(serverResponse, [proxyResponse.statusCode, proxyResponse.headers]); | ||
originalWrite.call(serverResponse, content); | ||
originalEnd.call(serverResponse); | ||
}; | ||
try { | ||
renderer(handleResult, request.originalUrl, data, {}); | ||
} catch (err) { | ||
console.error(err); | ||
var errorResponse = { | ||
statusCode: 500, | ||
content: "Internal Server Error" | ||
}; | ||
if (config.onError) { | ||
errorResponse = config.onError(err); | ||
} | ||
originalWriteHead.apply(serverResponse, [errorResponse.statusCode, {}]); | ||
originalWrite.call(serverResponse, new Buffer(errorResponse.content)); | ||
originalEnd.call(serverResponse); | ||
} | ||
}; | ||
/* eslint-enable no-param-reassign */ | ||
}; | ||
var handleProxyResponse = function handleProxyResponse(proxyResponse, request, serverResponse, renderer, config) { | ||
removeEmptyAnalyticsCookie(proxyResponse); | ||
if (config.debug) { | ||
console.log('request url', request.url); | ||
console.log('request query', request.query); | ||
console.log('request original url', request.originalUrl); | ||
console.log('RAW request headers', JSON.stringify(request.headers, true, 2)); | ||
console.log('RAW response headers from the target', JSON.stringify(proxyResponse.headers, true, 2)); | ||
} | ||
// 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 (config.pathRewriteExcludeRoutes.find(function (r) { | ||
return request.originalUrl.indexOf(r) !== -1; | ||
})) { | ||
return; | ||
} | ||
// your first thought might be: why do we need to render the app here? why not just pass the JSON response to another piece of middleware that will render the app? | ||
// the answer: the proxy middleware ends the response and does not "chain" | ||
renderAppToResponse(proxyResponse, request, serverResponse, renderer, config); | ||
}; | ||
var rewriteRequestPath = exports.rewriteRequestPath = function rewriteRequestPath(reqPath, req, config, parseRouteUrl) { | ||
// necessary to make sure proxy doesn't fail if target server sends gzipped | ||
// delete req.headers['accept-encoding']; | ||
// if the request URL contains a path/route that should not be re-written, then just pass it along as-is | ||
if (config.pathRewriteExcludeRoutes.find(function (r) { | ||
return reqPath.indexOf(r) !== -1; | ||
})) { | ||
return reqPath; | ||
} | ||
// if the request URL doesn't contain the layout service controller path, assume we need to rewrite the request URL so that it does | ||
// if this seems redundant, it is. the config.pathRewriteExcludeRoutes should contain the layout service path, but can't always assume that it will... | ||
if (reqPath.indexOf(config.layoutServiceRoute) !== -1) { | ||
return reqPath; | ||
} | ||
var qsIndex = reqPath.indexOf('?'); | ||
var qs = void 0; | ||
if (qsIndex > -1) { | ||
qs = (0, _util.buildQueryString)(req.query); | ||
reqPath = reqPath.slice(0, qsIndex); | ||
} | ||
var lang = void 0; | ||
if (parseRouteUrl) { | ||
var routeParams = parseRouteUrl(reqPath); | ||
if (routeParams) { | ||
if (routeParams.sitecoreRoute) { | ||
reqPath = routeParams.sitecoreRoute; | ||
} else { | ||
reqPath = '/'; | ||
} | ||
if (!reqPath.startsWith('/')) { | ||
reqPath = '/' + reqPath; | ||
} | ||
lang = routeParams.lang; | ||
} | ||
} | ||
var path = config.layoutServiceRoute + '?item=' + encodeURIComponent(reqPath) + '&sc_apikey=' + config.apiKey; | ||
if (lang) { | ||
path = path + '&sc_lang=' + lang; | ||
} | ||
if (qs) { | ||
path = path + '&' + qs; | ||
} | ||
return path; | ||
}; | ||
var createOptions = function createOptions(renderer, config, parseRouteUrl) { | ||
return _extends({ | ||
target: config.apiHost, | ||
changeOrigin: true, // required otherwise need to include CORS headers | ||
ws: true, | ||
pathRewrite: function pathRewrite(reqPath, req) { | ||
return rewriteRequestPath(reqPath, req, config, parseRouteUrl); | ||
}, | ||
logLevel: config.debug ? 'debug' : 'info', | ||
onProxyRes: function onProxyRes(proxyRes, req, res) { | ||
return handleProxyResponse(proxyRes, req, res, renderer, config); | ||
} | ||
}, config.proxyOptions); | ||
}; | ||
exports.default = function (renderer, config, parseRouteUrl) { | ||
var options = createOptions(renderer, config, parseRouteUrl); | ||
return (0, _httpProxyMiddleware2.default)(options); | ||
}; |
@@ -1,1 +0,28 @@ | ||
'use strict';var _typeof='function'==typeof Symbol&&'symbol'==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&'function'==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?'symbol':typeof a},tryParseJson=exports.tryParseJson=function(a){try{var b=JSON.parse(a);if(b&&'object'===('undefined'==typeof b?'undefined':_typeof(b))&&null!==b)return b}catch(b){console.error('error parsing json string \''+a+'\'',b)}return!1},buildQueryString=exports.buildQueryString=function(a){return Object.keys(a).map(function(b){return encodeURIComponent(b)+'='+encodeURIComponent(a[b])}).join('&')};Object.defineProperty(exports,'__esModule',{value:!0}); | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var tryParseJson = exports.tryParseJson = function tryParseJson(jsonString) { | ||
try { | ||
var json = JSON.parse(jsonString); | ||
// handle non-exception-throwing cases | ||
if (json && (typeof json === 'undefined' ? 'undefined' : _typeof(json)) === 'object' && json !== null) { | ||
return json; | ||
} | ||
} catch (e) { | ||
// todo: write to stdout or callback | ||
console.error('error parsing json string \'' + jsonString + '\'', e); | ||
} | ||
return false; | ||
}; | ||
var buildQueryString = exports.buildQueryString = function buildQueryString(params) { | ||
return Object.keys(params).map(function (k) { | ||
return encodeURIComponent(k) + '=' + encodeURIComponent(params[k]); | ||
}).join('&'); | ||
}; |
{ | ||
"name": "@sitecore-jss/sitecore-jss-proxy", | ||
"version": "6.0.0", | ||
"version": "7.0.0", | ||
"description": "Proxy middleware for express.js server.", | ||
@@ -40,2 +40,2 @@ "main": "dist/index.js", | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Trivial Package
Supply chain riskPackages less than 10 lines of code are easily copied into your own project and may not warrant the additional supply chain risk of an external dependency.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
20178
223
0