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