serverless-offline 8.8.1 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +33 -57
- package/src/ServerlessOffline.js +412 -0
- package/src/config/commandOptions.js +155 -0
- package/src/config/constants.js +22 -0
- package/{dist → src}/config/defaultOptions.js +8 -17
- package/src/config/index.js +4 -0
- package/src/config/supportedRuntimes.js +47 -0
- package/src/events/authCanExecuteResource.js +35 -0
- package/src/events/authFunctionNameExtractor.js +75 -0
- package/src/events/authMatchPolicyResource.js +71 -0
- package/src/events/authValidateContext.js +51 -0
- package/src/events/http/Endpoint.js +135 -0
- package/src/events/http/Http.js +50 -0
- package/src/events/http/HttpEventDefinition.js +20 -0
- package/src/events/http/HttpServer.js +1277 -0
- package/src/events/http/OfflineEndpoint.js +33 -0
- package/src/events/http/authJWTSettingsExtractor.js +70 -0
- package/src/events/http/createAuthScheme.js +176 -0
- package/src/events/http/createJWTAuthScheme.js +106 -0
- package/src/events/http/index.js +1 -0
- package/src/events/http/javaHelpers.js +102 -0
- package/src/events/http/lambda-events/LambdaIntegrationEvent.js +57 -0
- package/src/events/http/lambda-events/LambdaProxyIntegrationEvent.js +233 -0
- package/src/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +190 -0
- package/src/events/http/lambda-events/VelocityContext.js +147 -0
- package/src/events/http/lambda-events/index.js +4 -0
- package/src/events/http/lambda-events/renderVelocityTemplateObject.js +93 -0
- package/{dist → src}/events/http/parseResources.js +73 -78
- package/src/events/http/payloadSchemaValidator.js +13 -0
- package/{dist → src}/events/http/templates/offline-default.req.vm +0 -0
- package/{dist → src}/events/http/templates/offline-default.res.vm +0 -0
- package/src/events/schedule/Schedule.js +131 -0
- package/src/events/schedule/ScheduleEvent.js +18 -0
- package/src/events/schedule/ScheduleEventDefinition.js +21 -0
- package/src/events/schedule/index.js +1 -0
- package/src/events/websocket/HttpServer.js +69 -0
- package/src/events/websocket/WebSocket.js +52 -0
- package/src/events/websocket/WebSocketClients.js +462 -0
- package/src/events/websocket/WebSocketEventDefinition.js +18 -0
- package/src/events/websocket/WebSocketServer.js +73 -0
- package/src/events/websocket/http-routes/_catchAll/catchAllRoute.js +16 -0
- package/src/events/websocket/http-routes/_catchAll/index.js +1 -0
- package/src/events/websocket/http-routes/connections/ConnectionsController.js +28 -0
- package/src/events/websocket/http-routes/connections/connectionsRoutes.js +70 -0
- package/src/events/websocket/http-routes/connections/index.js +1 -0
- package/src/events/websocket/http-routes/index.js +2 -0
- package/src/events/websocket/index.js +1 -0
- package/src/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +65 -0
- package/src/events/websocket/lambda-events/WebSocketConnectEvent.js +68 -0
- package/src/events/websocket/lambda-events/WebSocketDisconnectEvent.js +31 -0
- package/src/events/websocket/lambda-events/WebSocketEvent.js +29 -0
- package/src/events/websocket/lambda-events/WebSocketRequestContext.js +67 -0
- package/src/events/websocket/lambda-events/index.js +4 -0
- package/src/index.js +12 -0
- package/src/lambda/HttpServer.js +108 -0
- package/src/lambda/Lambda.js +68 -0
- package/src/lambda/LambdaContext.js +33 -0
- package/src/lambda/LambdaFunction.js +308 -0
- package/src/lambda/LambdaFunctionPool.js +109 -0
- package/src/lambda/__tests__/LambdaContext.test.js +30 -0
- package/src/lambda/__tests__/LambdaFunction.test.js +196 -0
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsJSONObject.fixture.js +47 -0
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js +46 -0
- package/src/lambda/__tests__/fixtures/Lambda/package.json +3 -0
- package/src/lambda/__tests__/fixtures/lambdaFunction.fixture.js +145 -0
- package/src/lambda/__tests__/fixtures/package.json +3 -0
- package/src/lambda/__tests__/routes/invocations/InvocationsController.test.js +42 -0
- package/src/lambda/handler-runner/HandlerRunner.js +136 -0
- package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +72 -0
- package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +42 -0
- package/src/lambda/handler-runner/child-process-runner/index.js +1 -0
- package/src/lambda/handler-runner/docker-runner/DockerContainer.js +417 -0
- package/src/lambda/handler-runner/docker-runner/DockerImage.js +35 -0
- package/src/lambda/handler-runner/docker-runner/DockerRunner.js +63 -0
- package/src/lambda/handler-runner/docker-runner/index.js +1 -0
- package/src/lambda/handler-runner/go-runner/GoRunner.js +166 -0
- package/src/lambda/handler-runner/go-runner/index.js +1 -0
- package/src/lambda/handler-runner/in-process-runner/InProcessRunner.js +125 -0
- package/src/lambda/handler-runner/in-process-runner/index.js +1 -0
- package/src/lambda/handler-runner/index.js +1 -0
- package/src/lambda/handler-runner/java-runner/JavaRunner.js +114 -0
- package/src/lambda/handler-runner/java-runner/index.js +1 -0
- package/src/lambda/handler-runner/python-runner/PythonRunner.js +138 -0
- package/src/lambda/handler-runner/python-runner/index.js +1 -0
- package/{dist → src}/lambda/handler-runner/python-runner/invoke.py +0 -0
- package/src/lambda/handler-runner/ruby-runner/RubyRunner.js +107 -0
- package/src/lambda/handler-runner/ruby-runner/index.js +1 -0
- package/{dist → src}/lambda/handler-runner/ruby-runner/invoke.rb +0 -0
- package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +70 -0
- package/src/lambda/handler-runner/worker-thread-runner/index.js +1 -0
- package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +29 -0
- package/src/lambda/index.js +1 -0
- package/src/lambda/routes/index.js +2 -0
- package/src/lambda/routes/invocations/InvocationsController.js +102 -0
- package/src/lambda/routes/invocations/index.js +1 -0
- package/src/lambda/routes/invocations/invocationsRoute.js +77 -0
- package/src/lambda/routes/invoke-async/InvokeAsyncController.js +20 -0
- package/src/lambda/routes/invoke-async/index.js +1 -0
- package/src/lambda/routes/invoke-async/invokeAsyncRoute.js +33 -0
- package/src/utils/__tests__/createUniqueId.test.js +18 -0
- package/src/utils/__tests__/formatToClfTime.test.js +14 -0
- package/src/utils/__tests__/generateHapiPath.test.js +46 -0
- package/src/utils/__tests__/lowerCaseKeys.test.js +30 -0
- package/src/utils/__tests__/parseHeaders.test.js +13 -0
- package/src/utils/__tests__/parseMultiValueHeaders.test.js +24 -0
- package/src/utils/__tests__/parseMultiValueQueryStringParameters.test.js +159 -0
- package/src/utils/__tests__/parseQueryStringParameters.test.js +15 -0
- package/src/utils/__tests__/splitHandlerPathAndName.test.js +54 -0
- package/src/utils/__tests__/unflatten.test.js +32 -0
- package/src/utils/checkDockerDaemon.js +19 -0
- package/src/utils/checkGoVersion.js +16 -0
- package/src/utils/createApiKey.js +5 -0
- package/src/utils/createUniqueId.js +5 -0
- package/src/utils/detectExecutable.js +11 -0
- package/{dist → src}/utils/formatToClfTime.js +6 -14
- package/src/utils/generateHapiPath.js +26 -0
- package/src/utils/getHttpApiCorsConfig.js +28 -0
- package/src/utils/index.js +42 -0
- package/src/utils/jsonPath.js +13 -0
- package/src/utils/logRoutes.js +64 -0
- package/src/utils/lowerCaseKeys.js +6 -0
- package/src/utils/parseHeaders.js +14 -0
- package/src/utils/parseMultiValueHeaders.js +27 -0
- package/src/utils/parseMultiValueQueryStringParameters.js +31 -0
- package/src/utils/parseQueryStringParameters.js +15 -0
- package/src/utils/resolveJoins.js +29 -0
- package/src/utils/splitHandlerPathAndName.js +31 -0
- package/src/utils/unflatten.js +11 -0
- package/CHANGELOG.md +0 -78
- package/dist/ServerlessOffline.js +0 -508
- package/dist/config/commandOptions.js +0 -149
- package/dist/config/constants.js +0 -30
- package/dist/config/index.js +0 -55
- package/dist/config/supportedRuntimes.js +0 -40
- package/dist/debugLog.js +0 -12
- package/dist/events/authCanExecuteResource.js +0 -35
- package/dist/events/authFunctionNameExtractor.js +0 -87
- package/dist/events/authMatchPolicyResource.js +0 -62
- package/dist/events/authValidateContext.js +0 -53
- package/dist/events/http/Endpoint.js +0 -173
- package/dist/events/http/Http.js +0 -77
- package/dist/events/http/HttpEventDefinition.js +0 -36
- package/dist/events/http/HttpServer.js +0 -1370
- package/dist/events/http/OfflineEndpoint.js +0 -38
- package/dist/events/http/authJWTSettingsExtractor.js +0 -76
- package/dist/events/http/createAuthScheme.js +0 -184
- package/dist/events/http/createJWTAuthScheme.js +0 -159
- package/dist/events/http/index.js +0 -15
- package/dist/events/http/javaHelpers.js +0 -99
- package/dist/events/http/lambda-events/LambdaIntegrationEvent.js +0 -87
- package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +0 -246
- package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +0 -225
- package/dist/events/http/lambda-events/VelocityContext.js +0 -170
- package/dist/events/http/lambda-events/index.js +0 -39
- package/dist/events/http/lambda-events/renderVelocityTemplateObject.js +0 -111
- package/dist/events/http/payloadSchemaValidator.js +0 -13
- package/dist/events/schedule/Schedule.js +0 -183
- package/dist/events/schedule/ScheduleEvent.js +0 -27
- package/dist/events/schedule/ScheduleEventDefinition.js +0 -36
- package/dist/events/schedule/index.js +0 -15
- package/dist/events/websocket/HttpServer.js +0 -114
- package/dist/events/websocket/WebSocket.js +0 -78
- package/dist/events/websocket/WebSocketClients.js +0 -577
- package/dist/events/websocket/WebSocketEventDefinition.js +0 -32
- package/dist/events/websocket/WebSocketServer.js +0 -139
- package/dist/events/websocket/http-routes/_catchAll/catchAllRoute.js +0 -33
- package/dist/events/websocket/http-routes/_catchAll/index.js +0 -15
- package/dist/events/websocket/http-routes/connections/ConnectionsController.js +0 -45
- package/dist/events/websocket/http-routes/connections/connectionsRoutes.js +0 -95
- package/dist/events/websocket/http-routes/connections/index.js +0 -15
- package/dist/events/websocket/http-routes/index.js +0 -23
- package/dist/events/websocket/index.js +0 -15
- package/dist/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +0 -99
- package/dist/events/websocket/lambda-events/WebSocketConnectEvent.js +0 -101
- package/dist/events/websocket/lambda-events/WebSocketDisconnectEvent.js +0 -47
- package/dist/events/websocket/lambda-events/WebSocketEvent.js +0 -54
- package/dist/events/websocket/lambda-events/WebSocketRequestContext.js +0 -98
- package/dist/events/websocket/lambda-events/index.js +0 -39
- package/dist/index.js +0 -15
- package/dist/lambda/HttpServer.js +0 -124
- package/dist/lambda/Lambda.js +0 -117
- package/dist/lambda/LambdaContext.js +0 -53
- package/dist/lambda/LambdaFunction.js +0 -390
- package/dist/lambda/LambdaFunctionPool.js +0 -127
- package/dist/lambda/handler-runner/HandlerRunner.js +0 -195
- package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +0 -124
- package/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js +0 -49
- package/dist/lambda/handler-runner/child-process-runner/index.js +0 -15
- package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +0 -515
- package/dist/lambda/handler-runner/docker-runner/DockerImage.js +0 -67
- package/dist/lambda/handler-runner/docker-runner/DockerRunner.js +0 -74
- package/dist/lambda/handler-runner/docker-runner/index.js +0 -15
- package/dist/lambda/handler-runner/go-runner/GoRunner.js +0 -230
- package/dist/lambda/handler-runner/go-runner/index.js +0 -15
- package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +0 -228
- package/dist/lambda/handler-runner/in-process-runner/index.js +0 -15
- package/dist/lambda/handler-runner/index.js +0 -15
- package/dist/lambda/handler-runner/java-runner/JavaRunner.js +0 -153
- package/dist/lambda/handler-runner/java-runner/index.js +0 -15
- package/dist/lambda/handler-runner/python-runner/PythonRunner.js +0 -185
- package/dist/lambda/handler-runner/python-runner/index.js +0 -15
- package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -147
- package/dist/lambda/handler-runner/ruby-runner/index.js +0 -15
- package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +0 -92
- package/dist/lambda/handler-runner/worker-thread-runner/index.js +0 -15
- package/dist/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +0 -31
- package/dist/lambda/index.js +0 -15
- package/dist/lambda/routes/index.js +0 -23
- package/dist/lambda/routes/invocations/InvocationsController.js +0 -142
- package/dist/lambda/routes/invocations/index.js +0 -15
- package/dist/lambda/routes/invocations/invocationsRoute.js +0 -90
- package/dist/lambda/routes/invoke-async/InvokeAsyncController.js +0 -38
- package/dist/lambda/routes/invoke-async/index.js +0 -15
- package/dist/lambda/routes/invoke-async/invokeAsyncRoute.js +0 -43
- package/dist/main.js +0 -11
- package/dist/serverlessLog.js +0 -91
- package/dist/utils/checkDockerDaemon.js +0 -27
- package/dist/utils/checkGoVersion.js +0 -27
- package/dist/utils/createApiKey.js +0 -12
- package/dist/utils/createUniqueId.js +0 -14
- package/dist/utils/detectExecutable.js +0 -21
- package/dist/utils/generateHapiPath.js +0 -28
- package/dist/utils/getHttpApiCorsConfig.js +0 -40
- package/dist/utils/index.js +0 -165
- package/dist/utils/jsonPath.js +0 -21
- package/dist/utils/lowerCaseKeys.js +0 -14
- package/dist/utils/parseHeaders.js +0 -23
- package/dist/utils/parseMultiValueHeaders.js +0 -36
- package/dist/utils/parseMultiValueQueryStringParameters.js +0 -40
- package/dist/utils/parseQueryStringParameters.js +0 -26
- package/dist/utils/resolveJoins.js +0 -36
- package/dist/utils/satisfiesVersionRange.js +0 -20
- package/dist/utils/splitHandlerPathAndName.js +0 -37
- package/dist/utils/unflatten.js +0 -18
|
@@ -1,1370 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
|
|
8
|
-
var _buffer = require("buffer");
|
|
9
|
-
|
|
10
|
-
var _fs = require("fs");
|
|
11
|
-
|
|
12
|
-
var pathUtils = _interopRequireWildcard(require("path"));
|
|
13
|
-
|
|
14
|
-
var _process = _interopRequireWildcard(require("process"));
|
|
15
|
-
|
|
16
|
-
var _h2o = _interopRequireDefault(require("@hapi/h2o2"));
|
|
17
|
-
|
|
18
|
-
var _hapi = require("@hapi/hapi");
|
|
19
|
-
|
|
20
|
-
var _module = require("module");
|
|
21
|
-
|
|
22
|
-
var _authFunctionNameExtractor = _interopRequireDefault(require("../authFunctionNameExtractor.js"));
|
|
23
|
-
|
|
24
|
-
var _authJWTSettingsExtractor = _interopRequireDefault(require("./authJWTSettingsExtractor.js"));
|
|
25
|
-
|
|
26
|
-
var _createAuthScheme = _interopRequireDefault(require("./createAuthScheme.js"));
|
|
27
|
-
|
|
28
|
-
var _createJWTAuthScheme = _interopRequireDefault(require("./createJWTAuthScheme.js"));
|
|
29
|
-
|
|
30
|
-
var _Endpoint = _interopRequireDefault(require("./Endpoint.js"));
|
|
31
|
-
|
|
32
|
-
var _index = require("./lambda-events/index.js");
|
|
33
|
-
|
|
34
|
-
var _parseResources = _interopRequireDefault(require("./parseResources.js"));
|
|
35
|
-
|
|
36
|
-
var _payloadSchemaValidator = _interopRequireDefault(require("./payloadSchemaValidator.js"));
|
|
37
|
-
|
|
38
|
-
var _debugLog = _interopRequireDefault(require("../../debugLog.js"));
|
|
39
|
-
|
|
40
|
-
var _serverlessLog = _interopRequireWildcard(require("../../serverlessLog.js"));
|
|
41
|
-
|
|
42
|
-
var _index2 = require("../../utils/index.js");
|
|
43
|
-
|
|
44
|
-
var _LambdaProxyIntegrationEventV = _interopRequireDefault(require("./lambda-events/LambdaProxyIntegrationEventV2.js"));
|
|
45
|
-
|
|
46
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
47
|
-
|
|
48
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
49
|
-
|
|
50
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
51
|
-
|
|
52
|
-
function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
|
|
53
|
-
|
|
54
|
-
var id = 0;
|
|
55
|
-
|
|
56
|
-
function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
|
|
57
|
-
|
|
58
|
-
const {
|
|
59
|
-
parse,
|
|
60
|
-
stringify
|
|
61
|
-
} = JSON;
|
|
62
|
-
const {
|
|
63
|
-
assign,
|
|
64
|
-
entries,
|
|
65
|
-
keys
|
|
66
|
-
} = Object;
|
|
67
|
-
|
|
68
|
-
var _lambda = /*#__PURE__*/_classPrivateFieldLooseKey("lambda");
|
|
69
|
-
|
|
70
|
-
var _lastRequestOptions = /*#__PURE__*/_classPrivateFieldLooseKey("lastRequestOptions");
|
|
71
|
-
|
|
72
|
-
var _options = /*#__PURE__*/_classPrivateFieldLooseKey("options");
|
|
73
|
-
|
|
74
|
-
var _serverless = /*#__PURE__*/_classPrivateFieldLooseKey("serverless");
|
|
75
|
-
|
|
76
|
-
var _server = /*#__PURE__*/_classPrivateFieldLooseKey("server");
|
|
77
|
-
|
|
78
|
-
var _terminalInfo = /*#__PURE__*/_classPrivateFieldLooseKey("terminalInfo");
|
|
79
|
-
|
|
80
|
-
class HttpServer {
|
|
81
|
-
constructor(serverless, options, lambda, v3Utils) {
|
|
82
|
-
Object.defineProperty(this, _lambda, {
|
|
83
|
-
writable: true,
|
|
84
|
-
value: null
|
|
85
|
-
});
|
|
86
|
-
Object.defineProperty(this, _lastRequestOptions, {
|
|
87
|
-
writable: true,
|
|
88
|
-
value: null
|
|
89
|
-
});
|
|
90
|
-
Object.defineProperty(this, _options, {
|
|
91
|
-
writable: true,
|
|
92
|
-
value: null
|
|
93
|
-
});
|
|
94
|
-
Object.defineProperty(this, _serverless, {
|
|
95
|
-
writable: true,
|
|
96
|
-
value: null
|
|
97
|
-
});
|
|
98
|
-
Object.defineProperty(this, _server, {
|
|
99
|
-
writable: true,
|
|
100
|
-
value: null
|
|
101
|
-
});
|
|
102
|
-
Object.defineProperty(this, _terminalInfo, {
|
|
103
|
-
writable: true,
|
|
104
|
-
value: []
|
|
105
|
-
});
|
|
106
|
-
_classPrivateFieldLooseBase(this, _lambda)[_lambda] = lambda;
|
|
107
|
-
_classPrivateFieldLooseBase(this, _options)[_options] = options;
|
|
108
|
-
_classPrivateFieldLooseBase(this, _serverless)[_serverless] = serverless;
|
|
109
|
-
|
|
110
|
-
if (v3Utils) {
|
|
111
|
-
this.log = v3Utils.log;
|
|
112
|
-
this.progress = v3Utils.progress;
|
|
113
|
-
this.writeText = v3Utils.writeText;
|
|
114
|
-
this.v3Utils = v3Utils;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const {
|
|
118
|
-
enforceSecureCookies,
|
|
119
|
-
host,
|
|
120
|
-
httpPort,
|
|
121
|
-
httpsProtocol,
|
|
122
|
-
noStripTrailingSlashInUrl
|
|
123
|
-
} = _classPrivateFieldLooseBase(this, _options)[_options];
|
|
124
|
-
|
|
125
|
-
const serverOptions = {
|
|
126
|
-
host,
|
|
127
|
-
port: httpPort,
|
|
128
|
-
router: {
|
|
129
|
-
// allows for paths with trailing slashes to be the same as without
|
|
130
|
-
// e.g. : /my-path is the same as /my-path/
|
|
131
|
-
stripTrailingSlash: !noStripTrailingSlashInUrl
|
|
132
|
-
},
|
|
133
|
-
state: enforceSecureCookies ? {
|
|
134
|
-
isHttpOnly: true,
|
|
135
|
-
isSameSite: false,
|
|
136
|
-
isSecure: true
|
|
137
|
-
} : {
|
|
138
|
-
isHttpOnly: false,
|
|
139
|
-
isSameSite: false,
|
|
140
|
-
isSecure: false
|
|
141
|
-
}
|
|
142
|
-
}; // HTTPS support
|
|
143
|
-
|
|
144
|
-
if (typeof httpsProtocol === 'string' && httpsProtocol.length > 0) {
|
|
145
|
-
serverOptions.tls = {
|
|
146
|
-
cert: (0, _fs.readFileSync)((0, pathUtils.resolve)(httpsProtocol, 'cert.pem'), 'ascii'),
|
|
147
|
-
key: (0, _fs.readFileSync)((0, pathUtils.resolve)(httpsProtocol, 'key.pem'), 'ascii')
|
|
148
|
-
};
|
|
149
|
-
} // Hapijs server creation
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
_classPrivateFieldLooseBase(this, _server)[_server] = new _hapi.Server(serverOptions); // Enable CORS preflight response
|
|
153
|
-
|
|
154
|
-
_classPrivateFieldLooseBase(this, _server)[_server].ext('onPreResponse', (request, h) => {
|
|
155
|
-
if (request.headers.origin) {
|
|
156
|
-
const response = request.response.isBoom ? request.response.output : request.response;
|
|
157
|
-
const explicitlySetHeaders = { ...response.headers
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (_classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.httpApi && _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.httpApi.cors) {
|
|
161
|
-
const httpApiCors = (0, _index2.getHttpApiCorsConfig)(_classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.httpApi.cors, this);
|
|
162
|
-
|
|
163
|
-
if (request.method === 'options') {
|
|
164
|
-
response.statusCode = 204;
|
|
165
|
-
const allowAllOrigins = httpApiCors.allowedOrigins.length === 1 && httpApiCors.allowedOrigins[0] === '*';
|
|
166
|
-
|
|
167
|
-
if (!allowAllOrigins && !httpApiCors.allowedOrigins.includes(request.headers.origin)) {
|
|
168
|
-
return h.continue;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
response.headers['access-control-allow-origin'] = request.headers.origin;
|
|
173
|
-
|
|
174
|
-
if (httpApiCors.allowCredentials) {
|
|
175
|
-
response.headers['access-control-allow-credentials'] = 'true';
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (httpApiCors.maxAge) {
|
|
179
|
-
response.headers['access-control-max-age'] = httpApiCors.maxAge;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (httpApiCors.exposedResponseHeaders) {
|
|
183
|
-
response.headers['access-control-expose-headers'] = httpApiCors.exposedResponseHeaders.join(',');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (httpApiCors.allowedMethods) {
|
|
187
|
-
response.headers['access-control-allow-methods'] = httpApiCors.allowedMethods.join(',');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (httpApiCors.allowedHeaders) {
|
|
191
|
-
response.headers['access-control-allow-headers'] = httpApiCors.allowedHeaders.join(',');
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
response.headers['access-control-allow-origin'] = request.headers.origin;
|
|
195
|
-
response.headers['access-control-allow-credentials'] = 'true';
|
|
196
|
-
|
|
197
|
-
if (request.method === 'options') {
|
|
198
|
-
response.statusCode = 200;
|
|
199
|
-
|
|
200
|
-
if (request.headers['access-control-expose-headers']) {
|
|
201
|
-
response.headers['access-control-expose-headers'] = request.headers['access-control-expose-headers'];
|
|
202
|
-
} else {
|
|
203
|
-
response.headers['access-control-expose-headers'] = 'content-type, content-length, etag';
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
response.headers['access-control-max-age'] = 60 * 10;
|
|
207
|
-
|
|
208
|
-
if (request.headers['access-control-request-headers']) {
|
|
209
|
-
response.headers['access-control-allow-headers'] = request.headers['access-control-request-headers'];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (request.headers['access-control-request-method']) {
|
|
213
|
-
response.headers['access-control-allow-methods'] = request.headers['access-control-request-method'];
|
|
214
|
-
}
|
|
215
|
-
} // Override default headers with headers that have been explicitly set
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
keys(explicitlySetHeaders).forEach(key => {
|
|
219
|
-
const value = explicitlySetHeaders[key];
|
|
220
|
-
|
|
221
|
-
if (value) {
|
|
222
|
-
response.headers[key] = value;
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return h.continue;
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async start() {
|
|
233
|
-
const {
|
|
234
|
-
host,
|
|
235
|
-
httpPort,
|
|
236
|
-
httpsProtocol
|
|
237
|
-
} = _classPrivateFieldLooseBase(this, _options)[_options];
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
await _classPrivateFieldLooseBase(this, _server)[_server].start();
|
|
241
|
-
} catch (err) {
|
|
242
|
-
if (this.log) {
|
|
243
|
-
this.log.error(`Unexpected error while starting serverless-offline server on port ${httpPort}:`, err);
|
|
244
|
-
} else {
|
|
245
|
-
console.error(`Unexpected error while starting serverless-offline server on port ${httpPort}:`, err);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
(0, _process.exit)(1);
|
|
249
|
-
} // TODO move the following block
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`;
|
|
253
|
-
|
|
254
|
-
if (this.log) {
|
|
255
|
-
this.log.notice(`Server ready: ${server} 🚀`);
|
|
256
|
-
this.log.notice();
|
|
257
|
-
this.log.notice('Enter "rp" to replay the last request');
|
|
258
|
-
} else {
|
|
259
|
-
(0, _serverlessLog.default)(`[HTTP] server ready: ${server} 🚀`);
|
|
260
|
-
(0, _serverlessLog.default)(''); // serverlessLog('OpenAPI/Swagger documentation:')
|
|
261
|
-
// logRoute('GET', server, '/documentation')
|
|
262
|
-
// serverlessLog('')
|
|
263
|
-
|
|
264
|
-
(0, _serverlessLog.default)('Enter "rp" to replay the last request');
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (_process.env.NODE_ENV !== 'test') {
|
|
268
|
-
_process.default.openStdin().addListener('data', data => {
|
|
269
|
-
// note: data is an object, and when converted to a string it will
|
|
270
|
-
// end with a linefeed. so we (rather crudely) account for that
|
|
271
|
-
// with toString() and then trim()
|
|
272
|
-
if (data.toString().trim() === 'rp') {
|
|
273
|
-
this._injectLastRequest();
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
} // stops the server
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
stop(timeout) {
|
|
281
|
-
return _classPrivateFieldLooseBase(this, _server)[_server].stop({
|
|
282
|
-
timeout
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async registerPlugins() {
|
|
287
|
-
try {
|
|
288
|
-
await _classPrivateFieldLooseBase(this, _server)[_server].register([_h2o.default]);
|
|
289
|
-
} catch (err) {
|
|
290
|
-
if (this.log) {
|
|
291
|
-
this.log.error(err);
|
|
292
|
-
} else {
|
|
293
|
-
(0, _serverlessLog.default)(err);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
} // // TODO unused:
|
|
297
|
-
// get server() {
|
|
298
|
-
// return this.#server.listener
|
|
299
|
-
// }
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
_printBlankLine() {
|
|
303
|
-
if (_process.env.NODE_ENV !== 'test') {
|
|
304
|
-
if (this.log) {
|
|
305
|
-
this.log.notice();
|
|
306
|
-
} else {
|
|
307
|
-
console.log();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
_logPluginIssue() {
|
|
313
|
-
if (this.log) {
|
|
314
|
-
this.log.notice('If you think this is an issue with the plugin please submit it, thanks!\nhttps://github.com/dherault/serverless-offline/issues');
|
|
315
|
-
this.log.notice();
|
|
316
|
-
} else {
|
|
317
|
-
(0, _serverlessLog.default)('If you think this is an issue with the plugin please submit it, thanks!');
|
|
318
|
-
(0, _serverlessLog.default)('https://github.com/dherault/serverless-offline/issues');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
_extractJWTAuthSettings(endpoint) {
|
|
323
|
-
const result = (0, _authJWTSettingsExtractor.default)(endpoint, _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider, _classPrivateFieldLooseBase(this, _options)[_options].ignoreJWTSignature, this);
|
|
324
|
-
return result.unsupportedAuth ? null : result;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
_configureJWTAuthorization(endpoint, functionKey, method, path) {
|
|
328
|
-
if (!endpoint.authorizer) {
|
|
329
|
-
return null;
|
|
330
|
-
} // right now _configureJWTAuthorization only handles AWS HttpAPI Gateway JWT
|
|
331
|
-
// authorizers that are defined in the serverless file
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (_classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.name !== 'aws' || !endpoint.isHttpApi) {
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const jwtSettings = this._extractJWTAuthSettings(endpoint);
|
|
339
|
-
|
|
340
|
-
if (!jwtSettings) {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (this.log) {
|
|
345
|
-
this.log.notice(`Configuring JWT Authorization: ${method} ${path}`);
|
|
346
|
-
} else {
|
|
347
|
-
(0, _serverlessLog.default)(`Configuring JWT Authorization: ${method} ${path}`);
|
|
348
|
-
} // Create a unique scheme per endpoint
|
|
349
|
-
// This allows the methodArn on the event property to be set appropriately
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const authKey = `${functionKey}-${jwtSettings.authorizerName}-${method}-${path}`;
|
|
353
|
-
const authSchemeName = `scheme-${authKey}`;
|
|
354
|
-
const authStrategyName = `strategy-${authKey}`; // set strategy name for the route config
|
|
355
|
-
|
|
356
|
-
if (this.log) {
|
|
357
|
-
this.log.debug(`Creating Authorization scheme for ${authKey}`);
|
|
358
|
-
} else {
|
|
359
|
-
(0, _debugLog.default)(`Creating Authorization scheme for ${authKey}`);
|
|
360
|
-
} // Create the Auth Scheme for the endpoint
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const scheme = (0, _createJWTAuthScheme.default)(jwtSettings, this); // Set the auth scheme and strategy on the server
|
|
364
|
-
|
|
365
|
-
_classPrivateFieldLooseBase(this, _server)[_server].auth.scheme(authSchemeName, scheme);
|
|
366
|
-
|
|
367
|
-
_classPrivateFieldLooseBase(this, _server)[_server].auth.strategy(authStrategyName, authSchemeName);
|
|
368
|
-
|
|
369
|
-
return authStrategyName;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
_extractAuthFunctionName(endpoint) {
|
|
373
|
-
const result = (0, _authFunctionNameExtractor.default)(endpoint, null, this);
|
|
374
|
-
return result.unsupportedAuth ? null : result.authorizerName;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
_configureAuthorization(endpoint, functionKey, method, path) {
|
|
378
|
-
if (!endpoint.authorizer) {
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const authFunctionName = this._extractAuthFunctionName(endpoint);
|
|
383
|
-
|
|
384
|
-
if (!authFunctionName) {
|
|
385
|
-
return null;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (this.log) {
|
|
389
|
-
this.log.notice(`Configuring Authorization: ${path} ${authFunctionName}`);
|
|
390
|
-
} else {
|
|
391
|
-
(0, _serverlessLog.default)(`Configuring Authorization: ${path} ${authFunctionName}`);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const authFunction = _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.getFunction(authFunctionName);
|
|
395
|
-
|
|
396
|
-
if (!authFunction) {
|
|
397
|
-
if (this.log) {
|
|
398
|
-
this.log.error(`Authorization function ${authFunctionName} does not exist`);
|
|
399
|
-
} else {
|
|
400
|
-
(0, _serverlessLog.default)(`WARNING: Authorization function ${authFunctionName} does not exist`);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return null;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const authorizerOptions = {
|
|
407
|
-
identitySource: 'method.request.header.Authorization',
|
|
408
|
-
identityValidationExpression: '(.*)',
|
|
409
|
-
resultTtlInSeconds: '300'
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
if (typeof endpoint.authorizer === 'string') {
|
|
413
|
-
authorizerOptions.name = authFunctionName;
|
|
414
|
-
} else {
|
|
415
|
-
assign(authorizerOptions, endpoint.authorizer);
|
|
416
|
-
} // Create a unique scheme per endpoint
|
|
417
|
-
// This allows the methodArn on the event property to be set appropriately
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const authKey = `${functionKey}-${authFunctionName}-${method}-${path}`;
|
|
421
|
-
const authSchemeName = `scheme-${authKey}`;
|
|
422
|
-
const authStrategyName = `strategy-${authKey}`; // set strategy name for the route config
|
|
423
|
-
|
|
424
|
-
if (this.log) {
|
|
425
|
-
this.log.debug(`Creating Authorization scheme for ${authKey}`);
|
|
426
|
-
} else {
|
|
427
|
-
(0, _debugLog.default)(`Creating Authorization scheme for ${authKey}`);
|
|
428
|
-
} // Create the Auth Scheme for the endpoint
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const scheme = (0, _createAuthScheme.default)(authorizerOptions, _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider, _classPrivateFieldLooseBase(this, _lambda)[_lambda], this); // Set the auth scheme and strategy on the server
|
|
432
|
-
|
|
433
|
-
_classPrivateFieldLooseBase(this, _server)[_server].auth.scheme(authSchemeName, scheme);
|
|
434
|
-
|
|
435
|
-
_classPrivateFieldLooseBase(this, _server)[_server].auth.strategy(authStrategyName, authSchemeName);
|
|
436
|
-
|
|
437
|
-
return authStrategyName;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
_setAuthorizationStrategy(endpoint, functionKey, method, path) {
|
|
441
|
-
var _customizations$offli;
|
|
442
|
-
|
|
443
|
-
/*
|
|
444
|
-
* The authentication strategy can be provided outside of this project
|
|
445
|
-
* by injecting the provider through a custom variable in the serverless.yml.
|
|
446
|
-
*
|
|
447
|
-
* see the example in the tests for more details
|
|
448
|
-
* /tests/integration/custom-authentication
|
|
449
|
-
*/
|
|
450
|
-
const customizations = _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.custom;
|
|
451
|
-
|
|
452
|
-
if (customizations && (_customizations$offli = customizations.offline) !== null && _customizations$offli !== void 0 && _customizations$offli.customAuthenticationProvider) {
|
|
453
|
-
const root = pathUtils.resolve(_classPrivateFieldLooseBase(this, _serverless)[_serverless].serviceDir, 'require-resolver');
|
|
454
|
-
const customRequire = (0, _module.createRequire)(root);
|
|
455
|
-
const provider = customRequire(customizations.offline.customAuthenticationProvider);
|
|
456
|
-
const strategy = provider(endpoint, functionKey, method, path);
|
|
457
|
-
|
|
458
|
-
_classPrivateFieldLooseBase(this, _server)[_server].auth.scheme(strategy.scheme, strategy.getAuthenticateFunction);
|
|
459
|
-
|
|
460
|
-
_classPrivateFieldLooseBase(this, _server)[_server].auth.strategy(strategy.name, strategy.scheme);
|
|
461
|
-
|
|
462
|
-
return strategy.name;
|
|
463
|
-
} // If the endpoint has an authorization function, create an authStrategy for the route
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const authStrategyName = _classPrivateFieldLooseBase(this, _options)[_options].noAuth ? null : this._configureJWTAuthorization(endpoint, functionKey, method, path) || this._configureAuthorization(endpoint, functionKey, method, path);
|
|
467
|
-
return authStrategyName;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
createRoutes(functionKey, httpEvent, handler) {
|
|
471
|
-
const [handlerPath] = (0, _index2.splitHandlerPathAndName)(handler);
|
|
472
|
-
let method;
|
|
473
|
-
let path;
|
|
474
|
-
let hapiPath;
|
|
475
|
-
|
|
476
|
-
if (httpEvent.isHttpApi) {
|
|
477
|
-
if (httpEvent.routeKey === '$default') {
|
|
478
|
-
method = 'ANY';
|
|
479
|
-
path = httpEvent.routeKey;
|
|
480
|
-
hapiPath = '/{default*}';
|
|
481
|
-
} else {
|
|
482
|
-
;
|
|
483
|
-
[method, path] = httpEvent.routeKey.split(' ');
|
|
484
|
-
hapiPath = (0, _index2.generateHapiPath)(path, { ..._classPrivateFieldLooseBase(this, _options)[_options],
|
|
485
|
-
noPrependStageInUrl: true // Serverless always uses the $default stage
|
|
486
|
-
|
|
487
|
-
}, _classPrivateFieldLooseBase(this, _serverless)[_serverless]);
|
|
488
|
-
}
|
|
489
|
-
} else {
|
|
490
|
-
method = httpEvent.method.toUpperCase();
|
|
491
|
-
({
|
|
492
|
-
path
|
|
493
|
-
} = httpEvent);
|
|
494
|
-
hapiPath = (0, _index2.generateHapiPath)(path, _classPrivateFieldLooseBase(this, _options)[_options], _classPrivateFieldLooseBase(this, _serverless)[_serverless]);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const endpoint = new _Endpoint.default((0, pathUtils.join)(_classPrivateFieldLooseBase(this, _serverless)[_serverless].config.servicePath, handlerPath), httpEvent, this.v3Utils);
|
|
498
|
-
const stage = endpoint.isHttpApi ? '$default' : _classPrivateFieldLooseBase(this, _options)[_options].stage || _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.stage;
|
|
499
|
-
const protectedRoutes = [];
|
|
500
|
-
|
|
501
|
-
if (httpEvent.private) {
|
|
502
|
-
protectedRoutes.push(`${method}#${hapiPath}`);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const {
|
|
506
|
-
host,
|
|
507
|
-
httpPort,
|
|
508
|
-
httpsProtocol
|
|
509
|
-
} = _classPrivateFieldLooseBase(this, _options)[_options];
|
|
510
|
-
|
|
511
|
-
const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`;
|
|
512
|
-
|
|
513
|
-
_classPrivateFieldLooseBase(this, _terminalInfo)[_terminalInfo].push({
|
|
514
|
-
method,
|
|
515
|
-
path: hapiPath,
|
|
516
|
-
server,
|
|
517
|
-
stage: endpoint.isHttpApi || _classPrivateFieldLooseBase(this, _options)[_options].noPrependStageInUrl ? null : stage,
|
|
518
|
-
invokePath: `/2015-03-31/functions/${functionKey}/invocations`
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
const authStrategyName = this._setAuthorizationStrategy(endpoint, functionKey, method, path);
|
|
522
|
-
|
|
523
|
-
let cors = null;
|
|
524
|
-
|
|
525
|
-
if (endpoint.cors) {
|
|
526
|
-
cors = {
|
|
527
|
-
credentials: endpoint.cors.credentials || _classPrivateFieldLooseBase(this, _options)[_options].corsConfig.credentials,
|
|
528
|
-
exposedHeaders: _classPrivateFieldLooseBase(this, _options)[_options].corsConfig.exposedHeaders,
|
|
529
|
-
headers: endpoint.cors.headers || _classPrivateFieldLooseBase(this, _options)[_options].corsConfig.headers,
|
|
530
|
-
origin: endpoint.cors.origins || _classPrivateFieldLooseBase(this, _options)[_options].corsConfig.origin
|
|
531
|
-
};
|
|
532
|
-
} else if (_classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.httpApi && _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.httpApi.cors) {
|
|
533
|
-
const httpApiCors = (0, _index2.getHttpApiCorsConfig)(_classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.httpApi.cors, this);
|
|
534
|
-
cors = {
|
|
535
|
-
origin: httpApiCors.allowedOrigins || [],
|
|
536
|
-
credentials: httpApiCors.allowCredentials,
|
|
537
|
-
exposedHeaders: httpApiCors.exposedResponseHeaders || [],
|
|
538
|
-
maxAge: httpApiCors.maxAge,
|
|
539
|
-
headers: httpApiCors.allowedHeaders || []
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
const hapiMethod = method === 'ANY' ? '*' : method;
|
|
544
|
-
const state = _classPrivateFieldLooseBase(this, _options)[_options].disableCookieValidation ? {
|
|
545
|
-
failAction: 'ignore',
|
|
546
|
-
parse: false
|
|
547
|
-
} : {
|
|
548
|
-
failAction: 'error',
|
|
549
|
-
parse: true
|
|
550
|
-
};
|
|
551
|
-
const hapiOptions = {
|
|
552
|
-
auth: authStrategyName,
|
|
553
|
-
cors,
|
|
554
|
-
state,
|
|
555
|
-
timeout: {
|
|
556
|
-
socket: false
|
|
557
|
-
}
|
|
558
|
-
}; // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
|
|
559
|
-
// for more details, check https://github.com/dherault/serverless-offline/issues/204
|
|
560
|
-
|
|
561
|
-
if (hapiMethod === 'HEAD') {
|
|
562
|
-
if (this.log) {
|
|
563
|
-
this.log.notice('HEAD method event detected. Skipping HAPI server route mapping');
|
|
564
|
-
} else {
|
|
565
|
-
(0, _serverlessLog.default)('HEAD method event detected. Skipping HAPI server route mapping ...');
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (hapiMethod !== 'HEAD' && hapiMethod !== 'GET') {
|
|
572
|
-
// maxBytes: Increase request size from 1MB default limit to 10MB.
|
|
573
|
-
// Cf AWS API GW payload limits.
|
|
574
|
-
hapiOptions.payload = {
|
|
575
|
-
maxBytes: 1024 * 1024 * 10,
|
|
576
|
-
parse: false
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const additionalRequestContext = {};
|
|
581
|
-
|
|
582
|
-
if (httpEvent.operationId) {
|
|
583
|
-
additionalRequestContext.operationName = httpEvent.operationId;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
hapiOptions.tags = ['api'];
|
|
587
|
-
|
|
588
|
-
const hapiHandler = async (request, h) => {
|
|
589
|
-
var _endpoint$request;
|
|
590
|
-
|
|
591
|
-
// Here we go
|
|
592
|
-
// Store current request as the last one
|
|
593
|
-
_classPrivateFieldLooseBase(this, _lastRequestOptions)[_lastRequestOptions] = {
|
|
594
|
-
headers: request.headers,
|
|
595
|
-
method: request.method,
|
|
596
|
-
payload: request.payload,
|
|
597
|
-
url: request.url.href
|
|
598
|
-
};
|
|
599
|
-
const requestPath = endpoint.isHttpApi || _classPrivateFieldLooseBase(this, _options)[_options].noPrependStageInUrl ? request.path : request.path.substr(`/${stage}`.length);
|
|
600
|
-
|
|
601
|
-
if (request.auth.credentials && request.auth.strategy) {
|
|
602
|
-
_classPrivateFieldLooseBase(this, _lastRequestOptions)[_lastRequestOptions].auth = request.auth;
|
|
603
|
-
} // Payload processing
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const encoding = (0, _index2.detectEncoding)(request);
|
|
607
|
-
request.payload = request.payload && request.payload.toString(encoding);
|
|
608
|
-
request.rawPayload = request.payload; // Incomming request message
|
|
609
|
-
|
|
610
|
-
this._printBlankLine();
|
|
611
|
-
|
|
612
|
-
if (this.log) {
|
|
613
|
-
this.log.notice();
|
|
614
|
-
this.log.notice(`${method} ${request.path} (λ: ${functionKey})`);
|
|
615
|
-
} else {
|
|
616
|
-
(0, _serverlessLog.default)(`${method} ${request.path} (λ: ${functionKey})`);
|
|
617
|
-
} // Check for APIKey
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if ((protectedRoutes.includes(`${hapiMethod}#${hapiPath}`) || protectedRoutes.includes(`ANY#${hapiPath}`)) && !_classPrivateFieldLooseBase(this, _options)[_options].noAuth) {
|
|
621
|
-
const errorResponse = () => h.response({
|
|
622
|
-
message: 'Forbidden'
|
|
623
|
-
}).code(403).type('application/json').header('x-amzn-ErrorType', 'ForbiddenException');
|
|
624
|
-
|
|
625
|
-
const requestToken = request.headers['x-api-key'];
|
|
626
|
-
|
|
627
|
-
if (requestToken) {
|
|
628
|
-
if (requestToken !== _classPrivateFieldLooseBase(this, _options)[_options].apiKey) {
|
|
629
|
-
(0, _debugLog.default)(`Method ${method} of function ${functionKey} token ${requestToken} not valid`);
|
|
630
|
-
return errorResponse();
|
|
631
|
-
}
|
|
632
|
-
} else if (request.auth && request.auth.credentials && request.auth.credentials.usageIdentifierKey) {
|
|
633
|
-
const {
|
|
634
|
-
usageIdentifierKey
|
|
635
|
-
} = request.auth.credentials;
|
|
636
|
-
|
|
637
|
-
if (usageIdentifierKey !== _classPrivateFieldLooseBase(this, _options)[_options].apiKey) {
|
|
638
|
-
(0, _debugLog.default)(`Method ${method} of function ${functionKey} token ${usageIdentifierKey} not valid`);
|
|
639
|
-
return errorResponse();
|
|
640
|
-
}
|
|
641
|
-
} else {
|
|
642
|
-
if (this.log) {
|
|
643
|
-
this.log.debug(`Missing x-api-key on private function ${functionKey}`);
|
|
644
|
-
} else {
|
|
645
|
-
(0, _debugLog.default)(`Missing x-api-key on private function ${functionKey}`);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
return errorResponse();
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const response = h.response();
|
|
653
|
-
const contentType = request.mime || 'application/json'; // default content type
|
|
654
|
-
|
|
655
|
-
const {
|
|
656
|
-
integration,
|
|
657
|
-
requestTemplates
|
|
658
|
-
} = endpoint; // default request template to '' if we don't have a definition pushed in from serverless or endpoint
|
|
659
|
-
|
|
660
|
-
const requestTemplate = typeof requestTemplates !== 'undefined' && integration === 'AWS' ? requestTemplates[contentType] : '';
|
|
661
|
-
const schema = typeof (endpoint === null || endpoint === void 0 ? void 0 : (_endpoint$request = endpoint.request) === null || _endpoint$request === void 0 ? void 0 : _endpoint$request.schema) !== 'undefined' ? endpoint.request.schema[contentType] : ''; // https://hapijs.com/api#route-configuration doesn't seem to support selectively parsing
|
|
662
|
-
// so we have to do it ourselves
|
|
663
|
-
|
|
664
|
-
const contentTypesThatRequirePayloadParsing = ['application/json', 'application/vnd.api+json'];
|
|
665
|
-
|
|
666
|
-
if (contentTypesThatRequirePayloadParsing.includes(contentType) && request.payload && request.payload.length > 1) {
|
|
667
|
-
try {
|
|
668
|
-
if (!request.payload || request.payload.length < 1) {
|
|
669
|
-
request.payload = '{}';
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
request.payload = parse(request.payload);
|
|
673
|
-
} catch (err) {
|
|
674
|
-
if (this.log) {
|
|
675
|
-
this.log.debug('error in converting request.payload to JSON:', err);
|
|
676
|
-
} else {
|
|
677
|
-
(0, _debugLog.default)('error in converting request.payload to JSON:', err);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
if (this.log) {
|
|
683
|
-
this.log.debug('contentType:', contentType);
|
|
684
|
-
this.log.debug('requestTemplate:', requestTemplate);
|
|
685
|
-
this.log.debug('payload:', request.payload);
|
|
686
|
-
} else {
|
|
687
|
-
(0, _debugLog.default)('contentType:', contentType);
|
|
688
|
-
(0, _debugLog.default)('requestTemplate:', requestTemplate);
|
|
689
|
-
(0, _debugLog.default)('payload:', request.payload);
|
|
690
|
-
}
|
|
691
|
-
/* REQUEST PAYLOAD SCHEMA VALIDATION */
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (schema) {
|
|
695
|
-
if (this.log) {
|
|
696
|
-
this.log.debug('schema:', schema);
|
|
697
|
-
} else {
|
|
698
|
-
(0, _debugLog.default)('schema:', schema);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
try {
|
|
702
|
-
_payloadSchemaValidator.default.validate(schema, request.payload);
|
|
703
|
-
} catch (err) {
|
|
704
|
-
return this._reply400(response, err.message, err);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
/* REQUEST TEMPLATE PROCESSING (event population) */
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
let event = {};
|
|
711
|
-
|
|
712
|
-
if (integration === 'AWS') {
|
|
713
|
-
if (requestTemplate) {
|
|
714
|
-
try {
|
|
715
|
-
if (this.log) {
|
|
716
|
-
this.log.debug('_____ REQUEST TEMPLATE PROCESSING _____');
|
|
717
|
-
} else {
|
|
718
|
-
(0, _debugLog.default)('_____ REQUEST TEMPLATE PROCESSING _____');
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
event = new _index.LambdaIntegrationEvent(request, stage, requestTemplate, requestPath, this.v3Utils).create();
|
|
722
|
-
} catch (err) {
|
|
723
|
-
return this._reply502(response, `Error while parsing template "${contentType}" for ${functionKey}`, err);
|
|
724
|
-
}
|
|
725
|
-
} else if (typeof request.payload === 'object') {
|
|
726
|
-
event = request.payload || {};
|
|
727
|
-
}
|
|
728
|
-
} else if (integration === 'AWS_PROXY') {
|
|
729
|
-
const stageVariables = _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.custom ? _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.custom.stageVariables : null;
|
|
730
|
-
const lambdaProxyIntegrationEvent = endpoint.isHttpApi && endpoint.payload === '2.0' ? new _LambdaProxyIntegrationEventV.default(request, stage, endpoint.routeKey, stageVariables, additionalRequestContext, this.v3Utils) : new _index.LambdaProxyIntegrationEvent(request, stage, requestPath, stageVariables, endpoint.isHttpApi ? endpoint.routeKey : null, additionalRequestContext, this.v3Utils);
|
|
731
|
-
event = lambdaProxyIntegrationEvent.create();
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (this.log) {
|
|
735
|
-
this.log.debug('event:', event);
|
|
736
|
-
} else {
|
|
737
|
-
(0, _debugLog.default)('event:', event);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
const lambdaFunction = _classPrivateFieldLooseBase(this, _lambda)[_lambda].get(functionKey);
|
|
741
|
-
|
|
742
|
-
lambdaFunction.setEvent(event);
|
|
743
|
-
let result;
|
|
744
|
-
let err;
|
|
745
|
-
|
|
746
|
-
try {
|
|
747
|
-
result = await lambdaFunction.runHandler();
|
|
748
|
-
} catch (_err) {
|
|
749
|
-
err = _err;
|
|
750
|
-
} // const processResponse = (err, data) => {
|
|
751
|
-
// Everything in this block happens once the lambda function has resolved
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
if (this.log) {
|
|
755
|
-
this.log.debug('_____ HANDLER RESOLVED _____');
|
|
756
|
-
} else {
|
|
757
|
-
(0, _debugLog.default)('_____ HANDLER RESOLVED _____');
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
let responseName = 'default';
|
|
761
|
-
const {
|
|
762
|
-
contentHandling,
|
|
763
|
-
responseContentType
|
|
764
|
-
} = endpoint;
|
|
765
|
-
/* RESPONSE SELECTION (among endpoint's possible responses) */
|
|
766
|
-
// Failure handling
|
|
767
|
-
|
|
768
|
-
let errorStatusCode = '502';
|
|
769
|
-
|
|
770
|
-
if (err) {
|
|
771
|
-
// Since the --useChildProcesses option loads the handler in
|
|
772
|
-
// a separate process and serverless-offline communicates with it
|
|
773
|
-
// over IPC, we are unable to catch JavaScript unhandledException errors
|
|
774
|
-
// when the handler code contains bad JavaScript. Instead, we "catch"
|
|
775
|
-
// it here and reply in the same way that we would have above when
|
|
776
|
-
// we lazy-load the non-IPC handler function.
|
|
777
|
-
if (_classPrivateFieldLooseBase(this, _options)[_options].useChildProcesses && err.ipcException) {
|
|
778
|
-
return this._reply502(response, `Error while loading ${functionKey}`, err);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
const errorMessage = (err.message || err).toString();
|
|
782
|
-
const re = /\[(\d{3})]/;
|
|
783
|
-
const found = errorMessage.match(re);
|
|
784
|
-
|
|
785
|
-
if (found && found.length > 1) {
|
|
786
|
-
;
|
|
787
|
-
[, errorStatusCode] = found;
|
|
788
|
-
} else {
|
|
789
|
-
errorStatusCode = '502';
|
|
790
|
-
} // Mocks Lambda errors
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
result = {
|
|
794
|
-
errorMessage,
|
|
795
|
-
errorType: err.constructor.name,
|
|
796
|
-
stackTrace: this._getArrayStackTrace(err.stack)
|
|
797
|
-
};
|
|
798
|
-
|
|
799
|
-
if (this.log) {
|
|
800
|
-
this.log.error(errorMessage);
|
|
801
|
-
} else {
|
|
802
|
-
(0, _serverlessLog.default)(`Failure: ${errorMessage}`);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
if (!_classPrivateFieldLooseBase(this, _options)[_options].hideStackTraces) {
|
|
806
|
-
if (this.log) {
|
|
807
|
-
this.log.error(err.stack);
|
|
808
|
-
} else {
|
|
809
|
-
console.error(err.stack);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
for (const [key, value] of entries(endpoint.responses)) {
|
|
814
|
-
if (key !== 'default' && errorMessage.match(`^${value.selectionPattern || key}$`)) {
|
|
815
|
-
responseName = key;
|
|
816
|
-
break;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
if (this.log) {
|
|
822
|
-
this.log.debug(`Using response '${responseName}'`);
|
|
823
|
-
} else {
|
|
824
|
-
(0, _debugLog.default)(`Using response '${responseName}'`);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
const chosenResponse = endpoint.responses[responseName];
|
|
828
|
-
/* RESPONSE PARAMETERS PROCCESSING */
|
|
829
|
-
|
|
830
|
-
const {
|
|
831
|
-
responseParameters
|
|
832
|
-
} = chosenResponse;
|
|
833
|
-
|
|
834
|
-
if (responseParameters) {
|
|
835
|
-
const responseParametersKeys = keys(responseParameters);
|
|
836
|
-
|
|
837
|
-
if (this.log) {
|
|
838
|
-
this.log.debug('_____ RESPONSE PARAMETERS PROCCESSING _____');
|
|
839
|
-
this.log.debug(`Found ${responseParametersKeys.length} responseParameters for '${responseName}' response`);
|
|
840
|
-
} else {
|
|
841
|
-
(0, _debugLog.default)('_____ RESPONSE PARAMETERS PROCCESSING _____');
|
|
842
|
-
(0, _debugLog.default)();
|
|
843
|
-
} // responseParameters use the following shape: "key": "value"
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
entries(responseParameters).forEach(([key, value]) => {
|
|
847
|
-
const keyArray = key.split('.'); // eg: "method.response.header.location"
|
|
848
|
-
|
|
849
|
-
const valueArray = value.split('.'); // eg: "integration.response.body.redirect.url"
|
|
850
|
-
|
|
851
|
-
if (this.log) {
|
|
852
|
-
this.log.debug(`Processing responseParameter "${key}": "${value}"`);
|
|
853
|
-
} else {
|
|
854
|
-
(0, _debugLog.default)(`Processing responseParameter "${key}": "${value}"`);
|
|
855
|
-
} // For now the plugin only supports modifying headers
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
if (key.startsWith('method.response.header') && keyArray[3]) {
|
|
859
|
-
const headerName = keyArray.slice(3).join('.');
|
|
860
|
-
let headerValue;
|
|
861
|
-
|
|
862
|
-
if (this.log) {
|
|
863
|
-
this.log.debug('Found header in left-hand:', headerName);
|
|
864
|
-
} else {
|
|
865
|
-
(0, _debugLog.default)('Found header in left-hand:', headerName);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
if (value.startsWith('integration.response')) {
|
|
869
|
-
if (valueArray[2] === 'body') {
|
|
870
|
-
if (this.log) {
|
|
871
|
-
this.log.debug('Found body in right-hand');
|
|
872
|
-
} else {
|
|
873
|
-
(0, _debugLog.default)('Found body in right-hand');
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
headerValue = valueArray[3] ? (0, _index2.jsonPath)(result, valueArray.slice(3).join('.')) : result;
|
|
877
|
-
|
|
878
|
-
if (typeof headerValue === 'undefined' || headerValue === null) {
|
|
879
|
-
headerValue = '';
|
|
880
|
-
} else {
|
|
881
|
-
headerValue = headerValue.toString();
|
|
882
|
-
}
|
|
883
|
-
} else {
|
|
884
|
-
this._printBlankLine();
|
|
885
|
-
|
|
886
|
-
if (this.log) {
|
|
887
|
-
this.log.warning();
|
|
888
|
-
this.log.warning(`Offline plugin only supports "integration.response.body[.JSON_path]" right-hand responseParameter. Found "${value}" (for "${key}"") instead. Skipping.`);
|
|
889
|
-
} else {
|
|
890
|
-
(0, _serverlessLog.default)(`Warning: while processing responseParameter "${key}": "${value}"`);
|
|
891
|
-
(0, _serverlessLog.default)(`Offline plugin only supports "integration.response.body[.JSON_path]" right-hand responseParameter. Found "${value}" instead. Skipping.`);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
this._logPluginIssue();
|
|
895
|
-
|
|
896
|
-
this._printBlankLine();
|
|
897
|
-
}
|
|
898
|
-
} else {
|
|
899
|
-
headerValue = value.match(/^'.*'$/) ? value.slice(1, -1) : value; // See #34
|
|
900
|
-
} // Applies the header;
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (headerValue === '') {
|
|
904
|
-
if (this.log) {
|
|
905
|
-
this.log.warning(`Empty value for responseParameter "${key}": "${value}", it won't be set`);
|
|
906
|
-
} else {
|
|
907
|
-
(0, _serverlessLog.default)(`Warning: empty value for responseParameter "${key}": "${value}", it won't be set`);
|
|
908
|
-
}
|
|
909
|
-
} else {
|
|
910
|
-
if (this.log) {
|
|
911
|
-
this.log.debug(`Will assign "${headerValue}" to header "${headerName}"`);
|
|
912
|
-
} else {
|
|
913
|
-
(0, _debugLog.default)(`Will assign "${headerValue}" to header "${headerName}"`);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
response.header(headerName, headerValue);
|
|
917
|
-
}
|
|
918
|
-
} else {
|
|
919
|
-
this._printBlankLine();
|
|
920
|
-
|
|
921
|
-
if (this.log) {
|
|
922
|
-
this.log.warning();
|
|
923
|
-
this.log.warning(`Offline plugin only supports "method.response.header.PARAM_NAME" left-hand responseParameter. Found "${key}" instead. Skipping.`);
|
|
924
|
-
} else {
|
|
925
|
-
(0, _serverlessLog.default)(`Warning: while processing responseParameter "${key}": "${value}"`);
|
|
926
|
-
(0, _serverlessLog.default)(`Offline plugin only supports "method.response.header.PARAM_NAME" left-hand responseParameter. Found "${key}" instead. Skipping.`);
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
this._logPluginIssue();
|
|
930
|
-
|
|
931
|
-
this._printBlankLine();
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
let statusCode = 200;
|
|
937
|
-
|
|
938
|
-
if (integration === 'AWS') {
|
|
939
|
-
const endpointResponseHeaders = endpoint.response && endpoint.response.headers || {};
|
|
940
|
-
entries(endpointResponseHeaders).filter(([, value]) => typeof value === 'string' && /^'.*?'$/.test(value)).forEach(([key, value]) => response.header(key, value.slice(1, -1)));
|
|
941
|
-
/* LAMBDA INTEGRATION RESPONSE TEMPLATE PROCCESSING */
|
|
942
|
-
// If there is a responseTemplate, we apply it to the result
|
|
943
|
-
|
|
944
|
-
const {
|
|
945
|
-
responseTemplates
|
|
946
|
-
} = chosenResponse;
|
|
947
|
-
|
|
948
|
-
if (typeof responseTemplates === 'object') {
|
|
949
|
-
const responseTemplatesKeys = keys(responseTemplates);
|
|
950
|
-
|
|
951
|
-
if (responseTemplatesKeys.length) {
|
|
952
|
-
// BAD IMPLEMENTATION: first key in responseTemplates
|
|
953
|
-
const responseTemplate = responseTemplates[responseContentType];
|
|
954
|
-
|
|
955
|
-
if (responseTemplate && responseTemplate !== '\n') {
|
|
956
|
-
if (this.log) {
|
|
957
|
-
this.log.debug('_____ RESPONSE TEMPLATE PROCCESSING _____');
|
|
958
|
-
this.log.debug(`Using responseTemplate '${responseContentType}'`);
|
|
959
|
-
} else {
|
|
960
|
-
(0, _debugLog.default)('_____ RESPONSE TEMPLATE PROCCESSING _____');
|
|
961
|
-
(0, _debugLog.default)(`Using responseTemplate '${responseContentType}'`);
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
try {
|
|
965
|
-
const reponseContext = new _index.VelocityContext(request, stage, result).getContext();
|
|
966
|
-
result = (0, _index.renderVelocityTemplateObject)({
|
|
967
|
-
root: responseTemplate
|
|
968
|
-
}, reponseContext, this.v3Utils).root;
|
|
969
|
-
} catch (error) {
|
|
970
|
-
if (this.log) {
|
|
971
|
-
this.log.error(`Error while parsing responseTemplate '${responseContentType}' for lambda ${functionKey}:\n${error.stack}`);
|
|
972
|
-
} else {
|
|
973
|
-
(0, _serverlessLog.default)(`Error while parsing responseTemplate '${responseContentType}' for lambda ${functionKey}:`);
|
|
974
|
-
console.log(error.stack);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
/* LAMBDA INTEGRATION HAPIJS RESPONSE CONFIGURATION */
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
statusCode = chosenResponse.statusCode || 200;
|
|
984
|
-
|
|
985
|
-
if (err) {
|
|
986
|
-
statusCode = errorStatusCode;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
if (!chosenResponse.statusCode) {
|
|
990
|
-
this._printBlankLine();
|
|
991
|
-
|
|
992
|
-
if (this.log) {
|
|
993
|
-
this.log.warning();
|
|
994
|
-
this.log.warning(`No statusCode found for response "${responseName}".`);
|
|
995
|
-
} else {
|
|
996
|
-
(0, _serverlessLog.default)(`Warning: No statusCode found for response "${responseName}".`);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
response.header('Content-Type', responseContentType, {
|
|
1001
|
-
override: false // Maybe a responseParameter set it already. See #34
|
|
1002
|
-
|
|
1003
|
-
});
|
|
1004
|
-
response.statusCode = statusCode;
|
|
1005
|
-
|
|
1006
|
-
if (contentHandling === 'CONVERT_TO_BINARY') {
|
|
1007
|
-
response.encoding = 'binary';
|
|
1008
|
-
response.source = _buffer.Buffer.from(result, 'base64');
|
|
1009
|
-
response.variety = 'buffer';
|
|
1010
|
-
} else if (typeof result === 'string') {
|
|
1011
|
-
response.source = stringify(result);
|
|
1012
|
-
} else if (result && result.body && typeof result.body !== 'string') {
|
|
1013
|
-
return this._reply502(response, 'According to the API Gateway specs, the body content must be stringified. Check your Lambda response and make sure you are invoking JSON.stringify(YOUR_CONTENT) on your body object', {});
|
|
1014
|
-
} else {
|
|
1015
|
-
response.source = result;
|
|
1016
|
-
}
|
|
1017
|
-
} else if (integration === 'AWS_PROXY') {
|
|
1018
|
-
/* LAMBDA PROXY INTEGRATION HAPIJS RESPONSE CONFIGURATION */
|
|
1019
|
-
if (endpoint.isHttpApi && endpoint.payload === '2.0' && (typeof result === 'string' || !result.statusCode)) {
|
|
1020
|
-
const body = typeof result === 'string' ? result : stringify(result);
|
|
1021
|
-
result = {
|
|
1022
|
-
isBase64Encoded: false,
|
|
1023
|
-
statusCode: 200,
|
|
1024
|
-
body,
|
|
1025
|
-
headers: {
|
|
1026
|
-
'Content-Type': 'application/json'
|
|
1027
|
-
}
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
if (result && !result.errorType) {
|
|
1032
|
-
statusCode = result.statusCode || 200;
|
|
1033
|
-
} else {
|
|
1034
|
-
statusCode = 502;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
response.statusCode = statusCode;
|
|
1038
|
-
const headers = {};
|
|
1039
|
-
|
|
1040
|
-
if (result && result.headers) {
|
|
1041
|
-
keys(result.headers).forEach(header => {
|
|
1042
|
-
headers[header] = (headers[header] || []).concat(result.headers[header]);
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
if (result && result.multiValueHeaders) {
|
|
1047
|
-
keys(result.multiValueHeaders).forEach(header => {
|
|
1048
|
-
headers[header] = (headers[header] || []).concat(result.multiValueHeaders[header]);
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
if (this.log) {
|
|
1053
|
-
this.log.debug('headers', headers);
|
|
1054
|
-
} else {
|
|
1055
|
-
(0, _debugLog.default)('headers', headers);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
const parseCookies = headerValue => {
|
|
1059
|
-
const cookieName = headerValue.slice(0, headerValue.indexOf('='));
|
|
1060
|
-
const cookieValue = headerValue.slice(headerValue.indexOf('=') + 1);
|
|
1061
|
-
h.state(cookieName, cookieValue, {
|
|
1062
|
-
encoding: 'none',
|
|
1063
|
-
strictHeader: false
|
|
1064
|
-
});
|
|
1065
|
-
};
|
|
1066
|
-
|
|
1067
|
-
keys(headers).forEach(header => {
|
|
1068
|
-
if (header.toLowerCase() === 'set-cookie') {
|
|
1069
|
-
headers[header].forEach(parseCookies);
|
|
1070
|
-
} else {
|
|
1071
|
-
headers[header].forEach(headerValue => {
|
|
1072
|
-
// it looks like Hapi doesn't support multiple headers with the same name,
|
|
1073
|
-
// appending values is the closest we can come to the AWS behavior.
|
|
1074
|
-
response.header(header, headerValue, {
|
|
1075
|
-
append: true
|
|
1076
|
-
});
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
if (endpoint.isHttpApi && endpoint.payload === '2.0' && result.cookies) {
|
|
1082
|
-
result.cookies.forEach(parseCookies);
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
response.header('Content-Type', 'application/json', {
|
|
1086
|
-
duplicate: false,
|
|
1087
|
-
override: false
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
if (typeof result === 'string') {
|
|
1091
|
-
response.source = stringify(result);
|
|
1092
|
-
} else if (result && typeof result.body !== 'undefined') {
|
|
1093
|
-
if (result.isBase64Encoded) {
|
|
1094
|
-
response.encoding = 'binary';
|
|
1095
|
-
response.source = _buffer.Buffer.from(result.body, 'base64');
|
|
1096
|
-
response.variety = 'buffer';
|
|
1097
|
-
} else {
|
|
1098
|
-
if (result && result.body && typeof result.body !== 'string') {
|
|
1099
|
-
return this._reply502(response, 'According to the API Gateway specs, the body content must be stringified. Check your Lambda response and make sure you are invoking JSON.stringify(YOUR_CONTENT) on your body object', {});
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
response.source = result.body;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
} // Log response
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
let whatToLog = result;
|
|
1109
|
-
|
|
1110
|
-
try {
|
|
1111
|
-
whatToLog = stringify(result);
|
|
1112
|
-
} catch (error) {// nothing
|
|
1113
|
-
} finally {
|
|
1114
|
-
if (_classPrivateFieldLooseBase(this, _options)[_options].printOutput) {
|
|
1115
|
-
if (this.log) {
|
|
1116
|
-
this.log.notice(err ? `Replying ${statusCode}` : `[${statusCode}] ${whatToLog}`);
|
|
1117
|
-
} else {
|
|
1118
|
-
(0, _serverlessLog.default)(err ? `Replying ${statusCode}` : `[${statusCode}] ${whatToLog}`);
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
} // Bon voyage!
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
return response;
|
|
1125
|
-
};
|
|
1126
|
-
|
|
1127
|
-
_classPrivateFieldLooseBase(this, _server)[_server].route({
|
|
1128
|
-
handler: hapiHandler,
|
|
1129
|
-
method: hapiMethod,
|
|
1130
|
-
options: hapiOptions,
|
|
1131
|
-
path: hapiPath
|
|
1132
|
-
});
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
_replyError(statusCode, response, message, error) {
|
|
1136
|
-
(0, _serverlessLog.default)(message);
|
|
1137
|
-
|
|
1138
|
-
if (this.log) {
|
|
1139
|
-
this.log.error(error);
|
|
1140
|
-
} else {
|
|
1141
|
-
console.error(error);
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
response.header('Content-Type', 'application/json');
|
|
1145
|
-
response.statusCode = statusCode;
|
|
1146
|
-
response.source = {
|
|
1147
|
-
errorMessage: message,
|
|
1148
|
-
errorType: error.constructor.name,
|
|
1149
|
-
offlineInfo: 'If you believe this is an issue with serverless-offline please submit it, thanks. https://github.com/dherault/serverless-offline/issues',
|
|
1150
|
-
stackTrace: this._getArrayStackTrace(error.stack)
|
|
1151
|
-
};
|
|
1152
|
-
return response;
|
|
1153
|
-
} // Bad news
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
_reply502(response, message, error) {
|
|
1157
|
-
// APIG replies 502 by default on failures;
|
|
1158
|
-
return this._replyError(502, response, message, error);
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
_reply400(response, message, error) {
|
|
1162
|
-
return this._replyError(400, response, message, error);
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
createResourceRoutes() {
|
|
1166
|
-
const resourceRoutesOptions = _classPrivateFieldLooseBase(this, _options)[_options].resourceRoutes;
|
|
1167
|
-
|
|
1168
|
-
if (!resourceRoutesOptions) {
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
const resourceRoutes = (0, _parseResources.default)(_classPrivateFieldLooseBase(this, _serverless)[_serverless].service.resources);
|
|
1173
|
-
|
|
1174
|
-
if (!resourceRoutes || !keys(resourceRoutes).length) {
|
|
1175
|
-
return;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
this._printBlankLine();
|
|
1179
|
-
|
|
1180
|
-
if (this.log) {
|
|
1181
|
-
this.log.notice();
|
|
1182
|
-
this.log.notice('Routes defined in resources:');
|
|
1183
|
-
} else {
|
|
1184
|
-
(0, _serverlessLog.default)('Routes defined in resources:');
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
entries(resourceRoutes).forEach(([methodId, resourceRoutesObj]) => {
|
|
1188
|
-
const {
|
|
1189
|
-
isProxy,
|
|
1190
|
-
method,
|
|
1191
|
-
pathResource,
|
|
1192
|
-
proxyUri
|
|
1193
|
-
} = resourceRoutesObj;
|
|
1194
|
-
|
|
1195
|
-
if (!isProxy) {
|
|
1196
|
-
if (this.log) {
|
|
1197
|
-
this.log.warning(`Only HTTP_PROXY is supported. Path '${pathResource}' is ignored.`);
|
|
1198
|
-
} else {
|
|
1199
|
-
(0, _serverlessLog.default)(`WARNING: Only HTTP_PROXY is supported. Path '${pathResource}' is ignored.`);
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
if (!pathResource) {
|
|
1206
|
-
if (this.log) {
|
|
1207
|
-
this.log.warning(`Could not resolve path for '${methodId}'.`);
|
|
1208
|
-
} else {
|
|
1209
|
-
(0, _serverlessLog.default)(`WARNING: Could not resolve path for '${methodId}'.`);
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
return;
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
const hapiPath = (0, _index2.generateHapiPath)(pathResource, _classPrivateFieldLooseBase(this, _options)[_options], _classPrivateFieldLooseBase(this, _serverless)[_serverless]);
|
|
1216
|
-
const proxyUriOverwrite = resourceRoutesOptions[methodId] || {};
|
|
1217
|
-
const proxyUriInUse = proxyUriOverwrite.Uri || proxyUri;
|
|
1218
|
-
|
|
1219
|
-
if (!proxyUriInUse) {
|
|
1220
|
-
if (this.log) {
|
|
1221
|
-
this.log.warning(`Could not load Proxy Uri for '${methodId}'`);
|
|
1222
|
-
} else {
|
|
1223
|
-
(0, _serverlessLog.default)(`WARNING: Could not load Proxy Uri for '${methodId}'`);
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
const hapiMethod = method === 'ANY' ? '*' : method;
|
|
1230
|
-
const state = _classPrivateFieldLooseBase(this, _options)[_options].disableCookieValidation ? {
|
|
1231
|
-
failAction: 'ignore',
|
|
1232
|
-
parse: false
|
|
1233
|
-
} : {
|
|
1234
|
-
failAction: 'error',
|
|
1235
|
-
parse: true
|
|
1236
|
-
};
|
|
1237
|
-
const hapiOptions = {
|
|
1238
|
-
cors: _classPrivateFieldLooseBase(this, _options)[_options].corsConfig,
|
|
1239
|
-
state
|
|
1240
|
-
}; // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
|
|
1241
|
-
// for more details, check https://github.com/dherault/serverless-offline/issues/204
|
|
1242
|
-
|
|
1243
|
-
if (hapiMethod === 'HEAD') {
|
|
1244
|
-
if (this.log) {
|
|
1245
|
-
this.log.notice('HEAD method event detected. Skipping HAPI server route mapping');
|
|
1246
|
-
} else {
|
|
1247
|
-
(0, _serverlessLog.default)('HEAD method event detected. Skipping HAPI server route mapping ...');
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
if (hapiMethod !== 'GET' && hapiMethod !== 'HEAD') {
|
|
1254
|
-
hapiOptions.payload = {
|
|
1255
|
-
parse: false
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
if (this.log) {
|
|
1260
|
-
this.log.notice(`${method} ${hapiPath} -> ${proxyUriInUse}`);
|
|
1261
|
-
} else {
|
|
1262
|
-
(0, _serverlessLog.default)(`${method} ${hapiPath} -> ${proxyUriInUse}`);
|
|
1263
|
-
} // hapiOptions.tags = ['api']
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
const {
|
|
1267
|
-
log
|
|
1268
|
-
} = this;
|
|
1269
|
-
const route = {
|
|
1270
|
-
handler(request, h) {
|
|
1271
|
-
const {
|
|
1272
|
-
params
|
|
1273
|
-
} = request;
|
|
1274
|
-
let resultUri = proxyUriInUse;
|
|
1275
|
-
entries(params).forEach(([key, value]) => {
|
|
1276
|
-
resultUri = resultUri.replace(`{${key}}`, value);
|
|
1277
|
-
});
|
|
1278
|
-
|
|
1279
|
-
if (request.url.search !== null) {
|
|
1280
|
-
resultUri += request.url.search; // search is empty string by default
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
if (log) {
|
|
1284
|
-
log.notice(`PROXY ${request.method} ${request.url.pathname} -> ${resultUri}`);
|
|
1285
|
-
} else {
|
|
1286
|
-
(0, _serverlessLog.default)(`PROXY ${request.method} ${request.url.pathname} -> ${resultUri}`);
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
return h.proxy({
|
|
1290
|
-
passThrough: true,
|
|
1291
|
-
uri: resultUri
|
|
1292
|
-
});
|
|
1293
|
-
},
|
|
1294
|
-
|
|
1295
|
-
method: hapiMethod,
|
|
1296
|
-
options: hapiOptions,
|
|
1297
|
-
path: hapiPath
|
|
1298
|
-
};
|
|
1299
|
-
|
|
1300
|
-
_classPrivateFieldLooseBase(this, _server)[_server].route(route);
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
create404Route() {
|
|
1305
|
-
// If a {proxy+} or $default route exists, don't conflict with it
|
|
1306
|
-
if (_classPrivateFieldLooseBase(this, _server)[_server].match('*', '/{p*}')) {
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
const existingRoutes = _classPrivateFieldLooseBase(this, _server)[_server].table() // Exclude this (404) route
|
|
1311
|
-
.filter(route => route.path !== '/{p*}') // Sort by path
|
|
1312
|
-
.sort((a, b) => a.path <= b.path ? -1 : 1) // Human-friendly result
|
|
1313
|
-
.map(route => `${route.method} - ${route.path}`);
|
|
1314
|
-
|
|
1315
|
-
const route = {
|
|
1316
|
-
handler(request, h) {
|
|
1317
|
-
const response = h.response({
|
|
1318
|
-
currentRoute: `${request.method} - ${request.path}`,
|
|
1319
|
-
error: 'Serverless-offline: route not found.',
|
|
1320
|
-
existingRoutes,
|
|
1321
|
-
statusCode: 404
|
|
1322
|
-
});
|
|
1323
|
-
response.statusCode = 404;
|
|
1324
|
-
return response;
|
|
1325
|
-
},
|
|
1326
|
-
|
|
1327
|
-
method: '*',
|
|
1328
|
-
options: {
|
|
1329
|
-
cors: _classPrivateFieldLooseBase(this, _options)[_options].corsConfig
|
|
1330
|
-
},
|
|
1331
|
-
path: '/{p*}'
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
_classPrivateFieldLooseBase(this, _server)[_server].route(route);
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
_getArrayStackTrace(stack) {
|
|
1338
|
-
if (!stack) return null;
|
|
1339
|
-
const splittedStack = stack.split('\n');
|
|
1340
|
-
return splittedStack.slice(0, splittedStack.findIndex(item => item.match(/server.route.handler.LambdaContext/))).map(line => line.trim());
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
_injectLastRequest() {
|
|
1344
|
-
if (_classPrivateFieldLooseBase(this, _lastRequestOptions)[_lastRequestOptions]) {
|
|
1345
|
-
if (this.log) {
|
|
1346
|
-
this.log.notice('Replaying HTTP last request');
|
|
1347
|
-
|
|
1348
|
-
_classPrivateFieldLooseBase(this, _server)[_server].inject(_classPrivateFieldLooseBase(this, _lastRequestOptions)[_lastRequestOptions]);
|
|
1349
|
-
} else {
|
|
1350
|
-
(0, _serverlessLog.default)('Replaying HTTP last request');
|
|
1351
|
-
}
|
|
1352
|
-
} else if (this.log) {
|
|
1353
|
-
this.log.notice('No last HTTP request to replay!');
|
|
1354
|
-
} else {
|
|
1355
|
-
(0, _serverlessLog.default)('No last HTTP request to replay!');
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
writeRoutesTerminal() {
|
|
1360
|
-
(0, _serverlessLog.logRoutes)(_classPrivateFieldLooseBase(this, _terminalInfo)[_terminalInfo]);
|
|
1361
|
-
} // TEMP FIXME quick fix to expose gateway server for testing, look for better solution
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
getServer() {
|
|
1365
|
-
return _classPrivateFieldLooseBase(this, _server)[_server];
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
exports.default = HttpServer;
|