@gugananuvem/aws-local-simulator 1.0.34 → 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.
- package/README.md +662 -658
- package/package.json +11 -4
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/handler-loader.js +50 -5
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +18 -12
- package/src/services/lambda/simulator.js +73 -17
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- package/aws-config +0 -154
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
const { Distribution } = require('./models');
|
|
2
|
+
const CacheEngine = require('./cache-engine');
|
|
3
|
+
const SignedUrlGenerator = require('./signed-url-generator');
|
|
4
|
+
const OriginValidator = require('./origin-validator');
|
|
5
|
+
const GeoRestrictions = require('./geo-restrictions');
|
|
6
|
+
const OriginRouter = require('./origin-router');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* CloudFront Simulator
|
|
10
|
+
* Simulates CloudFront distribution behavior including caching, routing, and content delivery
|
|
11
|
+
*/
|
|
12
|
+
class CloudFrontSimulator {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.distributions = new Map();
|
|
15
|
+
this.cacheEngine = new CacheEngine(options.cacheOptions);
|
|
16
|
+
this.signedUrlGenerator = new SignedUrlGenerator();
|
|
17
|
+
this.originValidator = new OriginValidator();
|
|
18
|
+
this.geoRestrictions = new GeoRestrictions();
|
|
19
|
+
this.originRouter = new OriginRouter(options.servicesRegistry || {});
|
|
20
|
+
this.requestStats = {
|
|
21
|
+
totalRequests: 0,
|
|
22
|
+
cacheHits: 0,
|
|
23
|
+
cacheMisses: 0,
|
|
24
|
+
blockedRequests: 0
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register services for origin routing
|
|
30
|
+
* @param {string} serviceName - Service name (s3, apigateway, ec2, lambda, etc)
|
|
31
|
+
* @param {object} serviceInstance - Service instance
|
|
32
|
+
*/
|
|
33
|
+
registerService(serviceName, serviceInstance) {
|
|
34
|
+
this.originRouter.registerService(serviceName, serviceInstance);
|
|
35
|
+
}
|
|
36
|
+
createDistribution(distributionConfig) {
|
|
37
|
+
const distribution = new Distribution(distributionConfig);
|
|
38
|
+
|
|
39
|
+
// Validate all origins
|
|
40
|
+
for (const origin of distribution.origins) {
|
|
41
|
+
const validation = this.originValidator.validateOrigin(origin);
|
|
42
|
+
if (!validation.isValid) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Invalid origin '${origin.id}': ${validation.error}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Validate geo-restrictions if present
|
|
50
|
+
if (distribution.restrictions?.geoRestriction) {
|
|
51
|
+
const geoValidation = this.geoRestrictions.validateGeoRestriction(
|
|
52
|
+
distribution.restrictions.geoRestriction
|
|
53
|
+
);
|
|
54
|
+
if (!geoValidation.isValid) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Invalid geo-restriction: ${geoValidation.errors.join(', ')}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.distributions.set(distribution.id, distribution);
|
|
62
|
+
return distribution;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get a distribution by ID
|
|
67
|
+
* @param {string} distributionId - Distribution ID
|
|
68
|
+
* @returns {object|null} Distribution or null
|
|
69
|
+
*/
|
|
70
|
+
getDistribution(distributionId) {
|
|
71
|
+
return this.distributions.get(distributionId) || null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* List all distributions
|
|
76
|
+
* @returns {object[]} Array of distributions
|
|
77
|
+
*/
|
|
78
|
+
listDistributions() {
|
|
79
|
+
return Array.from(this.distributions.values());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Delete a distribution
|
|
84
|
+
* @param {string} distributionId - Distribution ID
|
|
85
|
+
* @returns {boolean} True if deleted
|
|
86
|
+
*/
|
|
87
|
+
deleteDistribution(distributionId) {
|
|
88
|
+
// Invalidate all cached content for this distribution
|
|
89
|
+
this.cacheEngine.invalidateDistribution(distributionId);
|
|
90
|
+
return this.distributions.delete(distributionId);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Process a request through CloudFront
|
|
95
|
+
* Handles routing, caching, geo-restrictions, signed URLs, etc.
|
|
96
|
+
* @param {object} request - Request object
|
|
97
|
+
* @param {string} request.distributionId - Distribution to route through
|
|
98
|
+
* @param {string} request.path - Request path
|
|
99
|
+
* @param {string} [request.method] - HTTP method (default: GET)
|
|
100
|
+
* @param {object} [request.headers] - Request headers
|
|
101
|
+
* @param {string} [request.clientIp] - Client IP for geo-restrictions
|
|
102
|
+
* @param {string} [request.protocol] - Request protocol (http/https)
|
|
103
|
+
* @returns {object} Response object with caching info
|
|
104
|
+
*/
|
|
105
|
+
processRequest(request) {
|
|
106
|
+
this.requestStats.totalRequests++;
|
|
107
|
+
|
|
108
|
+
const distribution = this.getDistribution(request.distributionId);
|
|
109
|
+
if (!distribution) {
|
|
110
|
+
return {
|
|
111
|
+
status: 404,
|
|
112
|
+
error: 'Distribution not found',
|
|
113
|
+
cached: false
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if distribution is enabled
|
|
118
|
+
if (!distribution.enabled) {
|
|
119
|
+
return {
|
|
120
|
+
status: 403,
|
|
121
|
+
error: 'Distribution is disabled',
|
|
122
|
+
cached: false
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check geo-restrictions
|
|
127
|
+
if (distribution.restrictions?.geoRestriction && request.clientIp) {
|
|
128
|
+
const clientCountry = this._getCountryFromIp(request.clientIp);
|
|
129
|
+
const geoCheck = this.geoRestrictions.checkGeoRestriction(
|
|
130
|
+
distribution.restrictions.geoRestriction,
|
|
131
|
+
clientCountry,
|
|
132
|
+
{ ip: request.clientIp, distributionId: request.distributionId }
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (!geoCheck.isAllowed) {
|
|
136
|
+
this.requestStats.blockedRequests++;
|
|
137
|
+
return {
|
|
138
|
+
status: 403,
|
|
139
|
+
error: 'Access denied by geo-restriction',
|
|
140
|
+
reason: geoCheck.reason,
|
|
141
|
+
cached: false
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check signed URL if required
|
|
147
|
+
if (distribution.defaultBehavior?.requireSignedUrls) {
|
|
148
|
+
const urlValidation = this._validateSignedUrl(request, distribution);
|
|
149
|
+
if (!urlValidation.isValid) {
|
|
150
|
+
this.requestStats.blockedRequests++;
|
|
151
|
+
return {
|
|
152
|
+
status: 403,
|
|
153
|
+
error: 'Invalid or missing signed URL',
|
|
154
|
+
reason: urlValidation.reason,
|
|
155
|
+
cached: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Find matching behavior
|
|
161
|
+
const behavior = distribution.matchBehavior(request.path);
|
|
162
|
+
if (!behavior) {
|
|
163
|
+
return {
|
|
164
|
+
status: 404,
|
|
165
|
+
error: 'No matching behavior found',
|
|
166
|
+
cached: false
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check cache
|
|
171
|
+
const cacheKey = this._generateCacheKey(request.distributionId, request.path);
|
|
172
|
+
let cachedResponse = this.cacheEngine.get(cacheKey);
|
|
173
|
+
|
|
174
|
+
if (cachedResponse) {
|
|
175
|
+
this.requestStats.cacheHits++;
|
|
176
|
+
return {
|
|
177
|
+
...cachedResponse,
|
|
178
|
+
cached: true,
|
|
179
|
+
cacheStatus: 'HIT',
|
|
180
|
+
xAmzCfId: this._generateCfId()
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.requestStats.cacheMisses++;
|
|
185
|
+
|
|
186
|
+
// Get origin
|
|
187
|
+
const origin = distribution.origins.find(o => o.id === behavior.targetOriginId);
|
|
188
|
+
if (!origin) {
|
|
189
|
+
return {
|
|
190
|
+
status: 502,
|
|
191
|
+
error: 'Origin not found',
|
|
192
|
+
cached: false
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Request to origin (may be async)
|
|
197
|
+
const originResponse = this._simulateOriginRequest(origin, request, behavior);
|
|
198
|
+
|
|
199
|
+
// Handle promise (async origin routing)
|
|
200
|
+
if (originResponse && typeof originResponse.then === 'function') {
|
|
201
|
+
return originResponse.then(response => {
|
|
202
|
+
// Cache response if cacheable
|
|
203
|
+
if (behavior.isCacheable(request.method || 'GET')) {
|
|
204
|
+
const ttl = behavior.getCachePolicy()?.defaultTtl || 3600;
|
|
205
|
+
this.cacheEngine.set(cacheKey, response, ttl, request.distributionId);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
...response,
|
|
210
|
+
cached: false,
|
|
211
|
+
cacheStatus: 'MISS',
|
|
212
|
+
xAmzCfId: this._generateCfId()
|
|
213
|
+
};
|
|
214
|
+
}).catch(error => ({
|
|
215
|
+
status: 502,
|
|
216
|
+
error: 'Origin request failed',
|
|
217
|
+
message: error.message,
|
|
218
|
+
cached: false
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Synchronous response
|
|
223
|
+
// Cache response if cacheable
|
|
224
|
+
if (behavior.isCacheable(request.method || 'GET')) {
|
|
225
|
+
const ttl = behavior.getCachePolicy()?.defaultTtl || 3600;
|
|
226
|
+
this.cacheEngine.set(cacheKey, originResponse, ttl, request.distributionId);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
...originResponse,
|
|
231
|
+
cached: false,
|
|
232
|
+
cacheStatus: 'MISS',
|
|
233
|
+
xAmzCfId: this._generateCfId()
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Invalidate distribution content
|
|
239
|
+
* @param {string} distributionId - Distribution ID
|
|
240
|
+
* @param {string|string[]} paths - Paths to invalidate
|
|
241
|
+
* @returns {object} Invalidation result
|
|
242
|
+
*/
|
|
243
|
+
invalidateDistribution(distributionId, paths) {
|
|
244
|
+
const distribution = this.getDistribution(distributionId);
|
|
245
|
+
if (!distribution) {
|
|
246
|
+
return { error: 'Distribution not found' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const pathsArray = Array.isArray(paths) ? paths : [paths];
|
|
250
|
+
let invalidatedCount = 0;
|
|
251
|
+
|
|
252
|
+
for (const path of pathsArray) {
|
|
253
|
+
const cacheKey = this._generateCacheKey(distributionId, path);
|
|
254
|
+
this.cacheEngine.invalidate(cacheKey);
|
|
255
|
+
invalidatedCount++;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
invalidationId: this._generateInvalidationId(),
|
|
260
|
+
distributionId: distributionId,
|
|
261
|
+
paths: pathsArray,
|
|
262
|
+
invalidatedCount: invalidatedCount,
|
|
263
|
+
status: 'InProgress'
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get distribution statistics
|
|
269
|
+
* @param {string} distributionId - Distribution ID
|
|
270
|
+
* @returns {object} Distribution stats
|
|
271
|
+
*/
|
|
272
|
+
getDistributionStats(distributionId) {
|
|
273
|
+
const distribution = this.getDistribution(distributionId);
|
|
274
|
+
if (!distribution) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const cacheStats = this.cacheEngine.getStats();
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
distributionId: distributionId,
|
|
282
|
+
enabled: distribution.enabled,
|
|
283
|
+
domainName: distribution.domainName,
|
|
284
|
+
caching: {
|
|
285
|
+
entries: cacheStats.entries,
|
|
286
|
+
maxSize: cacheStats.maxSize,
|
|
287
|
+
usedSize: cacheStats.usedSize,
|
|
288
|
+
hitRate: this.requestStats.totalRequests > 0
|
|
289
|
+
? (this.requestStats.cacheHits / this.requestStats.totalRequests * 100).toFixed(2) + '%'
|
|
290
|
+
: '0%'
|
|
291
|
+
},
|
|
292
|
+
global: this.requestStats
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Generate a signed URL
|
|
298
|
+
* @param {string} distributionId - Distribution ID
|
|
299
|
+
* @param {string} resourcePath - Resource path
|
|
300
|
+
* @param {string} privateKey - Private key for signing
|
|
301
|
+
* @param {string} keyPairId - Key pair ID
|
|
302
|
+
* @param {number} expirationTime - Expiration time (seconds from now)
|
|
303
|
+
* @returns {string} Signed URL
|
|
304
|
+
*/
|
|
305
|
+
generateSignedUrl(
|
|
306
|
+
distributionId,
|
|
307
|
+
resourcePath,
|
|
308
|
+
privateKey,
|
|
309
|
+
keyPairId,
|
|
310
|
+
expirationTime
|
|
311
|
+
) {
|
|
312
|
+
const distribution = this.getDistribution(distributionId);
|
|
313
|
+
if (!distribution) {
|
|
314
|
+
throw new Error('Distribution not found');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const expirationDate = new Date(Date.now() + expirationTime * 1000);
|
|
318
|
+
return this.signedUrlGenerator.generateCannedUrl(
|
|
319
|
+
distribution.domainName,
|
|
320
|
+
resourcePath,
|
|
321
|
+
privateKey,
|
|
322
|
+
keyPairId,
|
|
323
|
+
expirationDate
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get cache engine stats
|
|
329
|
+
* @returns {object} Cache statistics
|
|
330
|
+
*/
|
|
331
|
+
getCacheStats() {
|
|
332
|
+
return this.cacheEngine.getStats();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Clear all caches
|
|
337
|
+
*/
|
|
338
|
+
clearAllCaches() {
|
|
339
|
+
this.cacheEngine.clear();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get global request statistics
|
|
344
|
+
* @returns {object} Request statistics
|
|
345
|
+
*/
|
|
346
|
+
getRequestStats() {
|
|
347
|
+
const hitRate = this.requestStats.totalRequests > 0
|
|
348
|
+
? (this.requestStats.cacheHits / this.requestStats.totalRequests * 100).toFixed(2)
|
|
349
|
+
: 0;
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
...this.requestStats,
|
|
353
|
+
hitRate: hitRate + '%'
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Private Methods
|
|
359
|
+
*/
|
|
360
|
+
|
|
361
|
+
_generateCacheKey(distributionId, path) {
|
|
362
|
+
return `${distributionId}:${path}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
_generateCfId() {
|
|
366
|
+
return 'X-Amz-Cf-Id-' + Math.random().toString(36).substring(2, 15);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
_generateInvalidationId() {
|
|
370
|
+
return 'I' + Math.random().toString(36).substring(2, 15).toUpperCase();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
_getCountryFromIp(ip) {
|
|
374
|
+
// Simplified - in production this would use GeoIP database
|
|
375
|
+
// For demo: return country code based on IP ranges
|
|
376
|
+
const ipParts = ip.split('.');
|
|
377
|
+
const firstOctet = parseInt(ipParts[0]);
|
|
378
|
+
|
|
379
|
+
// Rough mapping for demo
|
|
380
|
+
if (firstOctet >= 1 && firstOctet <= 39) return 'US';
|
|
381
|
+
if (firstOctet >= 40 && firstOctet <= 79) return 'GB';
|
|
382
|
+
if (firstOctet >= 80 && firstOctet <= 119) return 'DE';
|
|
383
|
+
if (firstOctet >= 120 && firstOctet <= 159) return 'JP';
|
|
384
|
+
if (firstOctet >= 160 && firstOctet <= 199) return 'BR';
|
|
385
|
+
if (firstOctet >= 200 && firstOctet <= 255) return 'CN';
|
|
386
|
+
|
|
387
|
+
return 'UNKNOWN';
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
_validateSignedUrl(request, distribution) {
|
|
391
|
+
const url = request.url || request.path;
|
|
392
|
+
|
|
393
|
+
// Look for signature parameters in URL or headers
|
|
394
|
+
const hasSignature = url?.includes('Signature=') ||
|
|
395
|
+
url?.includes('Policy=') ||
|
|
396
|
+
request.headers?.['CloudFront-Policy'];
|
|
397
|
+
|
|
398
|
+
if (!hasSignature) {
|
|
399
|
+
return {
|
|
400
|
+
isValid: false,
|
|
401
|
+
reason: 'MISSING_SIGNATURE'
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Full verification would require the public key
|
|
406
|
+
// For now, just check if signature is present
|
|
407
|
+
return {
|
|
408
|
+
isValid: true
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
_simulateOriginRequest(origin, request, behavior) {
|
|
413
|
+
// Rotate to real origin (S3, API Gateway, ALB, Lambda, HTTP)
|
|
414
|
+
return this.originRouter.routeToOrigin(origin, request.path || '/', request)
|
|
415
|
+
.then(response => response)
|
|
416
|
+
.catch(error => ({
|
|
417
|
+
status: 502,
|
|
418
|
+
headers: { 'Content-Type': 'application/json' },
|
|
419
|
+
body: JSON.stringify({ error: 'Bad Gateway', message: error.message }),
|
|
420
|
+
timestamp: new Date().toISOString()
|
|
421
|
+
}));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
module.exports = CloudFrontSimulator;
|