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.
Files changed (235) hide show
  1. package/README.md +3 -3
  2. package/package.json +33 -57
  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/CHANGELOG.md +0 -78
  131. package/dist/ServerlessOffline.js +0 -508
  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 -12
  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/authValidateContext.js +0 -53
  141. package/dist/events/http/Endpoint.js +0 -173
  142. package/dist/events/http/Http.js +0 -77
  143. package/dist/events/http/HttpEventDefinition.js +0 -36
  144. package/dist/events/http/HttpServer.js +0 -1370
  145. package/dist/events/http/OfflineEndpoint.js +0 -38
  146. package/dist/events/http/authJWTSettingsExtractor.js +0 -76
  147. package/dist/events/http/createAuthScheme.js +0 -184
  148. package/dist/events/http/createJWTAuthScheme.js +0 -159
  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 -87
  152. package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +0 -246
  153. package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +0 -225
  154. package/dist/events/http/lambda-events/VelocityContext.js +0 -170
  155. package/dist/events/http/lambda-events/index.js +0 -39
  156. package/dist/events/http/lambda-events/renderVelocityTemplateObject.js +0 -111
  157. package/dist/events/http/payloadSchemaValidator.js +0 -13
  158. package/dist/events/schedule/Schedule.js +0 -183
  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 -114
  163. package/dist/events/websocket/WebSocket.js +0 -78
  164. package/dist/events/websocket/WebSocketClients.js +0 -577
  165. package/dist/events/websocket/WebSocketEventDefinition.js +0 -32
  166. package/dist/events/websocket/WebSocketServer.js +0 -139
  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 -15
  181. package/dist/lambda/HttpServer.js +0 -124
  182. package/dist/lambda/Lambda.js +0 -117
  183. package/dist/lambda/LambdaContext.js +0 -53
  184. package/dist/lambda/LambdaFunction.js +0 -390
  185. package/dist/lambda/LambdaFunctionPool.js +0 -127
  186. package/dist/lambda/handler-runner/HandlerRunner.js +0 -195
  187. package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +0 -124
  188. package/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js +0 -49
  189. package/dist/lambda/handler-runner/child-process-runner/index.js +0 -15
  190. package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +0 -515
  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 -230
  195. package/dist/lambda/handler-runner/go-runner/index.js +0 -15
  196. package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +0 -228
  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 -153
  200. package/dist/lambda/handler-runner/java-runner/index.js +0 -15
  201. package/dist/lambda/handler-runner/python-runner/PythonRunner.js +0 -185
  202. package/dist/lambda/handler-runner/python-runner/index.js +0 -15
  203. package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -147
  204. package/dist/lambda/handler-runner/ruby-runner/index.js +0 -15
  205. package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +0 -92
  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 -31
  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 -40
  225. package/dist/utils/index.js +0 -165
  226. package/dist/utils/jsonPath.js +0 -21
  227. package/dist/utils/lowerCaseKeys.js +0 -14
  228. package/dist/utils/parseHeaders.js +0 -23
  229. package/dist/utils/parseMultiValueHeaders.js +0 -36
  230. package/dist/utils/parseMultiValueQueryStringParameters.js +0 -40
  231. package/dist/utils/parseQueryStringParameters.js +0 -26
  232. package/dist/utils/resolveJoins.js +0 -36
  233. package/dist/utils/satisfiesVersionRange.js +0 -20
  234. package/dist/utils/splitHandlerPathAndName.js +0 -37
  235. 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;