@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39

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 (149) hide show
  1. package/README.md +662 -658
  2. package/package.json +10 -3
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/index.js +26 -0
  100. package/src/services/lambda/server.js +12 -1
  101. package/src/services/lambda/simulator.js +4 -0
  102. package/src/services/opensearch/index.js +105 -0
  103. package/src/services/opensearch/models/document.js +115 -0
  104. package/src/services/opensearch/models/index.js +73 -0
  105. package/src/services/opensearch/server.js +310 -0
  106. package/src/services/opensearch/simulator.js +605 -0
  107. package/src/services/opensearch/store.js +162 -0
  108. package/src/services/organizations/control-tower-simulator.js +321 -0
  109. package/src/services/organizations/control-tower-store.js +182 -0
  110. package/src/services/organizations/iam-integration.js +413 -0
  111. package/src/services/organizations/index.js +172 -0
  112. package/src/services/organizations/models/index.js +13 -0
  113. package/src/services/organizations/models/invitation.js +48 -0
  114. package/src/services/organizations/models/organization.js +566 -0
  115. package/src/services/organizations/models/role.js +244 -0
  116. package/src/services/organizations/models/scp.js +131 -0
  117. package/src/services/organizations/server.js +736 -0
  118. package/src/services/organizations/simulator.js +593 -0
  119. package/src/services/organizations/store.js +235 -0
  120. package/src/services/organizations/vpc-integration.js +453 -0
  121. package/src/services/route53/index.js +67 -0
  122. package/src/services/route53/models/healthCheck.js +101 -0
  123. package/src/services/route53/models/hostedZone.js +37 -0
  124. package/src/services/route53/models/resourceRecord.js +106 -0
  125. package/src/services/route53/server.js +841 -0
  126. package/src/services/route53/simulator.js +350 -0
  127. package/src/services/s3/index.js +62 -0
  128. package/src/services/s3/presigned-url-generator.js +337 -0
  129. package/src/services/s3/server.js +231 -0
  130. package/src/services/s3/simulator.js +551 -4
  131. package/src/services/sns/index.js +31 -0
  132. package/src/services/sns/server.js +9 -0
  133. package/src/services/sqs/index.js +38 -0
  134. package/src/services/sqs/server.js +57 -4
  135. package/src/services/sqs/simulator.js +423 -33
  136. package/src/services/sts/simulator.js +209 -17
  137. package/src/services/vpc/README.md +459 -0
  138. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  139. package/src/services/vpc/index.js +90 -0
  140. package/src/services/vpc/models/internetGateway.js +71 -0
  141. package/src/services/vpc/models/routeTable.js +160 -0
  142. package/src/services/vpc/models/subnet.js +48 -0
  143. package/src/services/vpc/models/vpc.js +37 -0
  144. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  145. package/src/services/vpc/models/vpcGateway.js +134 -0
  146. package/src/services/vpc/server.js +836 -0
  147. package/src/services/vpc/simulator.js +613 -0
  148. package/src/utils/policy-cache.js +215 -0
  149. package/aws-config +0 -154
@@ -0,0 +1,512 @@
1
+ const express = require('express');
2
+ const { v4: uuidv4 } = require('uuid');
3
+ const CloudFrontSimulator = require('./simulator');
4
+ const { validate: validateConfig } = require('./models');
5
+
6
+ /**
7
+ * CloudFront REST API Server
8
+ * Implements CloudFront API endpoints for distribution management and content delivery
9
+ */
10
+ class CloudFrontServer {
11
+ constructor(options = {}) {
12
+ this.app = express();
13
+ this.simulator = new CloudFrontSimulator(options);
14
+ this.setupMiddleware();
15
+ this.setupRoutes();
16
+ }
17
+
18
+ setupMiddleware() {
19
+ // CORS middleware
20
+ this.app.use((req, res, next) => {
21
+ console.log('[CORS] Request:', req.method, req.path);
22
+ res.set('Access-Control-Allow-Origin', '*');
23
+ res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
24
+ res.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Amz-Date, X-Amz-Security-Token');
25
+
26
+ if (req.method === 'OPTIONS') {
27
+ res.status(204).end();
28
+ return;
29
+ }
30
+
31
+ next();
32
+ });
33
+
34
+ this.app.use(express.json());
35
+
36
+ // Error handling middleware
37
+ this.app.use((err, req, res, next) => {
38
+ console.error('CloudFront Error:', err);
39
+ res.status(err.status || 500).json({
40
+ Error: {
41
+ Code: err.code || 'InternalError',
42
+ Message: err.message || 'Internal server error'
43
+ }
44
+ });
45
+ });
46
+ }
47
+
48
+ setupRoutes() {
49
+ // Distributions
50
+ this.app.post('/distribution', this.createDistribution.bind(this));
51
+ this.app.get('/distribution/:id', this.getDistribution.bind(this));
52
+ this.app.get('/distribution', this.listDistributions.bind(this));
53
+ this.app.put('/distribution/:id', this.updateDistribution.bind(this));
54
+ this.app.delete('/distribution/:id', this.deleteDistribution.bind(this));
55
+
56
+ // Distribution Config
57
+ this.app.get('/distribution/:id/config', this.getDistributionConfig.bind(this));
58
+ this.app.put('/distribution/:id/config', this.updateDistributionConfig.bind(this));
59
+
60
+ // Invalidations
61
+ this.app.post('/distribution/:id/invalidation', this.createInvalidation.bind(this));
62
+ this.app.get('/distribution/:id/invalidation/:invalidationId', this.getInvalidation.bind(this));
63
+
64
+ // Statistics
65
+ this.app.get('/distribution/:id/stats', this.getDistributionStats.bind(this));
66
+ this.app.get('/cache/stats', this.getCacheStats.bind(this));
67
+
68
+ // Signed URLs
69
+ this.app.post('/distribution/:id/signed-url', this.generateSignedUrl.bind(this));
70
+
71
+ // ── JSON routes for React frontend ────────────────────────────────────
72
+ this.app.get('/json/cloudfront/distributions', (req, res, next) => {
73
+ try {
74
+ const distributions = this.simulator.listDistributions();
75
+ res.json(distributions.map(d => d.toAWSFormat?.() || d));
76
+ } catch (error) {
77
+ next(error);
78
+ }
79
+ });
80
+
81
+ this.app.get('/json/cloudfront/distribution/:id', (req, res, next) => {
82
+ try {
83
+ const distribution = this.simulator.getDistribution(req.params.id);
84
+ if (!distribution) {
85
+ return res.status(404).json({ error: 'Distribution not found' });
86
+ }
87
+ res.json(distribution.toAWSFormat?.() || distribution);
88
+ } catch (error) {
89
+ next(error);
90
+ }
91
+ });
92
+
93
+ this.app.post('/json/cloudfront/distribution', (req, res, next) => {
94
+ this.createDistribution(req, res, next);
95
+ });
96
+
97
+ this.app.delete('/json/cloudfront/distribution/:id', (req, res, next) => {
98
+ this.deleteDistribution(req, res, next);
99
+ });
100
+
101
+ this.app.get('/json/cloudfront/distribution/:id/invalidations', (req, res, next) => {
102
+ try {
103
+ const distribution = this.simulator.getDistribution(req.params.id);
104
+ if (!distribution) {
105
+ return res.status(404).json({ error: 'Distribution not found' });
106
+ }
107
+ const invalidations = distribution.invalidations || [];
108
+ res.json(invalidations);
109
+ } catch (error) {
110
+ next(error);
111
+ }
112
+ });
113
+
114
+ this.app.post('/json/cloudfront/distribution/:id/invalidation', (req, res, next) => {
115
+ this.createInvalidation(req, res, next);
116
+ });
117
+
118
+ // Content delivery (GET requests) - must come AFTER /json routes
119
+ this.app.get('/:distributionId/*', this.deliverContent.bind(this));
120
+
121
+ // Health check
122
+ this.app.get('/health', (req, res) => {
123
+ res.json({
124
+ status: 'healthy',
125
+ service: 'cloudfront',
126
+ timestamp: new Date().toISOString()
127
+ });
128
+ });
129
+ }
130
+
131
+ /**
132
+ * POST /distribution
133
+ * Create a new distribution
134
+ */
135
+ createDistribution(req, res, next) {
136
+ try {
137
+ const config = req.body;
138
+
139
+ // Validate configuration
140
+ const validation = validateConfig(config);
141
+ if (!validation.isValid) {
142
+ return res.status(400).json({
143
+ Error: {
144
+ Code: 'InvalidDistributionConfig',
145
+ Message: validation.errors.join('; ')
146
+ }
147
+ });
148
+ }
149
+
150
+ const distribution = this.simulator.createDistribution(config);
151
+
152
+ res.status(201).json({
153
+ Distribution: distribution.toAWSFormat(),
154
+ ETag: this._generateETag()
155
+ });
156
+ } catch (error) {
157
+ next(error);
158
+ }
159
+ }
160
+
161
+ /**
162
+ * GET /distribution/:id
163
+ * Get a distribution
164
+ */
165
+ getDistribution(req, res, next) {
166
+ try {
167
+ const distribution = this.simulator.getDistribution(req.params.id);
168
+
169
+ if (!distribution) {
170
+ return res.status(404).json({
171
+ Error: {
172
+ Code: 'NoSuchDistribution',
173
+ Message: `Distribution with id ${req.params.id} not found`
174
+ }
175
+ });
176
+ }
177
+
178
+ res.json({
179
+ Distribution: distribution.toAWSFormat(),
180
+ ETag: this._generateETag()
181
+ });
182
+ } catch (error) {
183
+ next(error);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * GET /distribution
189
+ * List distributions
190
+ */
191
+ listDistributions(req, res, next) {
192
+ try {
193
+ const distributions = this.simulator.listDistributions();
194
+
195
+ res.json({
196
+ DistributionList: {
197
+ IsTruncated: false,
198
+ Items: distributions.map(d => d.toAWSFormat()),
199
+ Marker: '',
200
+ MaxItems: 100,
201
+ Quantity: distributions.length
202
+ }
203
+ });
204
+ } catch (error) {
205
+ next(error);
206
+ }
207
+ }
208
+
209
+ /**
210
+ * PUT /distribution/:id
211
+ * Update a distribution
212
+ */
213
+ updateDistribution(req, res, next) {
214
+ try {
215
+ const { config, ETag } = req.body;
216
+
217
+ // In a real system, ETag would be checked for concurrency
218
+ const distribution = this.simulator.getDistribution(req.params.id);
219
+ if (!distribution) {
220
+ return res.status(404).json({
221
+ Error: {
222
+ Code: 'NoSuchDistribution',
223
+ Message: `Distribution not found`
224
+ }
225
+ });
226
+ }
227
+
228
+ // Update distribution (simplified - would merge config)
229
+ const updated = this.simulator.createDistribution({
230
+ ...distribution,
231
+ ...config,
232
+ id: req.params.id
233
+ });
234
+
235
+ res.json({
236
+ Distribution: updated.toAWSFormat(),
237
+ ETag: this._generateETag()
238
+ });
239
+ } catch (error) {
240
+ next(error);
241
+ }
242
+ }
243
+
244
+ /**
245
+ * DELETE /distribution/:id
246
+ * Delete a distribution
247
+ */
248
+ deleteDistribution(req, res, next) {
249
+ try {
250
+ const success = this.simulator.deleteDistribution(req.params.id);
251
+
252
+ if (!success) {
253
+ return res.status(404).json({
254
+ Error: {
255
+ Code: 'NoSuchDistribution',
256
+ Message: 'Distribution not found'
257
+ }
258
+ });
259
+ }
260
+
261
+ res.status(204).send();
262
+ } catch (error) {
263
+ next(error);
264
+ }
265
+ }
266
+
267
+ /**
268
+ * GET /distribution/:id/config
269
+ * Get distribution config
270
+ */
271
+ getDistributionConfig(req, res, next) {
272
+ try {
273
+ const distribution = this.simulator.getDistribution(req.params.id);
274
+ if (!distribution) {
275
+ return res.status(404).json({
276
+ Error: { Code: 'NoSuchDistribution' }
277
+ });
278
+ }
279
+
280
+ res.json({
281
+ DistributionConfig: distribution.toAWSFormat().DistributionConfig,
282
+ ETag: this._generateETag()
283
+ });
284
+ } catch (error) {
285
+ next(error);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * PUT /distribution/:id/config
291
+ * Update distribution config
292
+ */
293
+ updateDistributionConfig(req, res, next) {
294
+ try {
295
+ const { DistributionConfig, ETag } = req.body;
296
+
297
+ const distribution = this.simulator.getDistribution(req.params.id);
298
+ if (!distribution) {
299
+ return res.status(404).json({
300
+ Error: { Code: 'NoSuchDistribution' }
301
+ });
302
+ }
303
+
304
+ // Update config
305
+ const updated = this.simulator.createDistribution({
306
+ ...DistributionConfig,
307
+ id: req.params.id
308
+ });
309
+
310
+ res.json({
311
+ Distribution: updated.toAWSFormat(),
312
+ ETag: this._generateETag()
313
+ });
314
+ } catch (error) {
315
+ next(error);
316
+ }
317
+ }
318
+
319
+ /**
320
+ * POST /distribution/:id/invalidation
321
+ * Create invalidation
322
+ */
323
+ createInvalidation(req, res, next) {
324
+ try {
325
+ const { paths } = req.body;
326
+
327
+ if (!paths || paths.length === 0) {
328
+ return res.status(400).json({
329
+ Error: {
330
+ Code: 'InvalidRequest',
331
+ Message: 'At least one path must be specified for invalidation'
332
+ }
333
+ });
334
+ }
335
+
336
+ const result = this.simulator.invalidateDistribution(req.params.id, paths);
337
+
338
+ if (result.error) {
339
+ return res.status(404).json({
340
+ Error: {
341
+ Code: 'NoSuchDistribution',
342
+ Message: result.error
343
+ }
344
+ });
345
+ }
346
+
347
+ res.status(201).json({
348
+ Invalidation: result,
349
+ Location: `/distribution/${req.params.id}/invalidation/${result.invalidationId}`
350
+ });
351
+ } catch (error) {
352
+ next(error);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * GET /distribution/:id/invalidation/:invalidationId
358
+ * Get invalidation status
359
+ */
360
+ getInvalidation(req, res, next) {
361
+ try {
362
+ // Simplified - would look up actual invalidation
363
+ res.json({
364
+ InvalidationList: {
365
+ IsTruncated: false,
366
+ Items: [{
367
+ Id: req.params.invalidationId,
368
+ CreateTime: new Date().toISOString(),
369
+ Status: 'Completed'
370
+ }],
371
+ Marker: '',
372
+ MaxItems: 100,
373
+ Quantity: 1
374
+ }
375
+ });
376
+ } catch (error) {
377
+ next(error);
378
+ }
379
+ }
380
+
381
+ /**
382
+ * GET /distribution/:id/stats
383
+ * Get distribution statistics
384
+ */
385
+ getDistributionStats(req, res, next) {
386
+ try {
387
+ const stats = this.simulator.getDistributionStats(req.params.id);
388
+
389
+ if (!stats) {
390
+ return res.status(404).json({
391
+ Error: { Code: 'NoSuchDistribution' }
392
+ });
393
+ }
394
+
395
+ res.json({ stats });
396
+ } catch (error) {
397
+ next(error);
398
+ }
399
+ }
400
+
401
+ /**
402
+ * GET /cache/stats
403
+ * Get cache statistics
404
+ */
405
+ getCacheStats(req, res, next) {
406
+ try {
407
+ const stats = this.simulator.getCacheStats();
408
+ res.json({ stats });
409
+ } catch (error) {
410
+ next(error);
411
+ }
412
+ }
413
+
414
+ /**
415
+ * POST /distribution/:id/signed-url
416
+ * Generate a signed URL
417
+ */
418
+ generateSignedUrl(req, res, next) {
419
+ try {
420
+ const { resourcePath, privateKey, keyPairId, expirationTime } = req.body;
421
+
422
+ if (!resourcePath || !privateKey || !keyPairId || !expirationTime) {
423
+ return res.status(400).json({
424
+ Error: {
425
+ Code: 'InvalidRequest',
426
+ Message: 'Missing required parameters'
427
+ }
428
+ });
429
+ }
430
+
431
+ const signedUrl = this.simulator.generateSignedUrl(
432
+ req.params.id,
433
+ resourcePath,
434
+ privateKey,
435
+ keyPairId,
436
+ expirationTime
437
+ );
438
+
439
+ res.json({
440
+ signedUrl: signedUrl,
441
+ expiresIn: expirationTime,
442
+ expiresAt: new Date(Date.now() + expirationTime * 1000)
443
+ });
444
+ } catch (error) {
445
+ next(error);
446
+ }
447
+ }
448
+
449
+ /**
450
+ * GET /:distributionId/*
451
+ * Deliver content from distribution
452
+ */
453
+ deliverContent(req, res, next) {
454
+ try {
455
+ const { distributionId } = req.params;
456
+ const path = '/' + (req.params[0] || '');
457
+
458
+ const response = this.simulator.processRequest({
459
+ distributionId: distributionId,
460
+ path: path,
461
+ method: req.method,
462
+ headers: req.headers,
463
+ clientIp: req.ip,
464
+ protocol: req.protocol,
465
+ url: req.originalUrl
466
+ });
467
+
468
+ res.status(response.status || 200);
469
+
470
+ if (response.headers) {
471
+ Object.entries(response.headers).forEach(([key, value]) => {
472
+ res.set(key, value);
473
+ });
474
+ }
475
+
476
+ res.set('X-Cache', response.cacheStatus || 'MISS');
477
+ res.set('X-Amz-Cf-Id', response.xAmzCfId);
478
+
479
+ res.send(response.body || response.error || 'OK');
480
+ } catch (error) {
481
+ next(error);
482
+ }
483
+ }
484
+
485
+ /**
486
+ * Start the server
487
+ * @param {number} port - Port to listen on
488
+ */
489
+ listen(port) {
490
+ this.server = this.app.listen(port, () => {
491
+ console.log(`CloudFront server listening on port ${port}`);
492
+ });
493
+ return this.server;
494
+ }
495
+
496
+ /**
497
+ * Get Express app instance
498
+ */
499
+ getApp() {
500
+ return this.app;
501
+ }
502
+
503
+ /**
504
+ * Private Methods
505
+ */
506
+
507
+ _generateETag() {
508
+ return '"' + Math.random().toString(36).substring(2, 15) + '"';
509
+ }
510
+ }
511
+
512
+ module.exports = CloudFrontServer;