Cloudflare Containers Proxy
The @zintrust/cloudflare-containers-proxy package provides a Cloudflare Containers proxy adapter for ZinTrust, enabling seamless integration with Cloudflare's container runtime environment.
Installation
bash
npm install @zintrust/cloudflare-containers-proxyConfiguration
Add the Cloudflare Containers proxy configuration to your environment:
typescript
// config/cloudflare.ts
import { CloudflareConfig } from '@zintrust/core';
export const cloudflare: CloudflareConfig = {
containers: {
enabled: true,
proxy: {
target: process.env.CONTAINER_TARGET || 'http://localhost:8080',
timeout: 30000,
retries: 3,
healthCheck: {
path: '/health',
interval: 30000,
timeout: 5000,
},
},
routing: {
enabled: true,
prefix: '/api/container',
stripPrefix: true,
},
},
};Environment Variables
bash
CONTAINER_TARGET=http://localhost:8080
CONTAINER_PROXY_ENABLED=true
CONTAINER_HEALTH_CHECK_PATH=/healthUsage
typescript
import { CloudflareContainersProxy } from '@zintrust/cloudflare-containers-proxy';
// Initialize proxy
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
timeout: 30000,
});
// Proxy requests
app.use('/api/container', proxy.middleware());
// Direct proxy calls
const response = await proxy.request('/users', {
method: 'GET',
headers: { 'Authorization': 'Bearer token' },
});
// Health check
const isHealthy = await proxy.healthCheck();Features
- Container Integration: Seamless integration with Cloudflare Containers
- Request Proxying: HTTP request forwarding to container services
- Health Monitoring: Container health check capabilities
- Load Balancing: Multiple container instance support
- Circuit Breaker: Automatic failover for unhealthy containers
- Request Routing: Intelligent request routing and filtering
- Performance Monitoring: Request/response metrics and logging
- Security: Request validation and sanitization
Advanced Configuration
Multiple Container Targets
typescript
export const cloudflare: CloudflareConfig = {
containers: {
enabled: true,
proxy: {
targets: [
{ url: 'http://container-1:8080', weight: 1 },
{ url: 'http://container-2:8080', weight: 1 },
{ url: 'http://container-3:8080', weight: 2 },
],
loadBalancing: {
strategy: 'round-robin', // or 'weighted', 'least-connections'
healthCheck: {
path: '/health',
interval: 15000,
timeout: 3000,
unhealthyThreshold: 3,
healthyThreshold: 2,
},
},
},
},
};Request Routing
typescript
export const cloudflare: CloudflareConfig = {
containers: {
enabled: true,
routing: {
rules: [
{
path: '/api/users/*',
target: 'http://user-service:8080',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
{
path: '/api/orders/*',
target: 'http://order-service:8080',
methods: ['GET', 'POST'],
},
{
path: '/api/notifications/*',
target: 'http://notification-service:8080',
timeout: 10000, // Custom timeout for notifications
},
],
},
},
};Circuit Breaker Configuration
typescript
export const cloudflare: CloudflareConfig = {
containers: {
enabled: true,
circuitBreaker: {
enabled: true,
threshold: 5, // Fail after 5 consecutive failures
timeout: 60000, // Reset after 60 seconds
resetTimeout: 30000, // Try to reset after 30 seconds
monitoringPeriod: 10000, // Check every 10 seconds
},
},
};Request/Response Handling
Request Transformation
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
requestTransform: (req) => {
// Add custom headers
req.headers['X-Request-ID'] = generateRequestId();
req.headers['X-Forwarded-For'] = req.ip;
// Transform request body
if (req.body && typeof req.body === 'object') {
req.body.timestamp = new Date().toISOString();
}
return req;
},
responseTransform: (res, req) => {
// Add response headers
res.headers['X-Response-Time'] = Date.now() - req.startTime;
// Transform response body
if (res.data && typeof res.data === 'object') {
res.data.processedAt = new Date().toISOString();
}
return res;
},
});Error Handling
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
errorHandler: (error, req, res) => {
console.log('Proxy error:', error.message);
if (error.code === 'ECONNREFUSED') {
res.status(503).json({
error: 'Service Unavailable',
message: 'Container service is not responding',
});
} else if (error.code === 'ETIMEDOUT') {
res.status(504).json({
error: 'Gateway Timeout',
message: 'Container service timed out',
});
} else {
res.status(500).json({
error: 'Internal Server Error',
message: 'Proxy request failed',
});
}
},
});Health Monitoring
Health Checks
typescript
import { ContainerHealthMonitor } from '@zintrust/cloudflare-containers-proxy';
const monitor = new ContainerHealthMonitor({
targets: [
{ url: 'http://container-1:8080', name: 'container-1' },
{ url: 'http://container-2:8080', name: 'container-2' },
],
healthCheck: {
path: '/health',
interval: 30000,
timeout: 5000,
expectedStatus: 200,
},
});
// Get health status
const health = await monitor.getHealth();
// Returns: { healthy: boolean, targets: Array<{ name: string, healthy: boolean, responseTime: number }> }
// Health events
monitor.on('health-change', (target, healthy) => {
console.log(`Target ${target.name} is now ${healthy ? 'healthy' : 'unhealthy'}`);
});
monitor.on('all-unhealthy', () => {
console.log('All targets are unhealthy - triggering alert');
sendAlert('All container targets are down');
});Custom Health Checks
typescript
const monitor = new ContainerHealthMonitor({
targets: [{ url: 'http://container:8080', name: 'main' }],
healthCheck: {
custom: async (target) => {
try {
const response = await fetch(`${target.url}/health/detailed`);
const data = await response.json();
return {
healthy: response.ok && data.status === 'healthy',
responseTime: response.headers.get('x-response-time'),
details: data,
};
} catch (error) {
return { healthy: false, error: error.message };
}
},
},
});Performance Optimization
Connection Pooling
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
connectionPool: {
enabled: true,
maxConnections: 100,
minConnections: 10,
acquireTimeoutMillis: 30000,
idleTimeoutMillis: 30000,
},
});Caching
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
cache: {
enabled: true,
ttl: 300000, // 5 minutes
maxSize: 1000, // Max 1000 cached responses
keyGenerator: (req) => {
return `${req.method}:${req.url}:${JSON.stringify(req.body)}`;
},
shouldCache: (req, res) => {
return req.method === 'GET' && res.status === 200;
},
},
});Compression
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
compression: {
enabled: true,
threshold: 1024, // Compress responses larger than 1KB
algorithms: ['gzip', 'br'], // gzip and brotli
},
});Security
Request Validation
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
security: {
requestValidation: {
maxPayloadSize: 10 * 1024 * 1024, // 10MB
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
rateLimit: {
windowMs: 60000, // 1 minute
max: 100, // 100 requests per minute
},
},
sanitization: {
removeHeaders: ['cookie', 'x-forwarded-for'],
sanitizeBody: true,
},
},
});Authentication
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
authentication: {
type: 'jwt',
secret: process.env.JWT_SECRET,
algorithms: ['HS256'],
required: false, // Optional authentication
onVerified: (payload, req) => {
req.user = payload;
req.headers['X-User-ID'] = payload.sub;
},
},
});Monitoring and Metrics
Request Metrics
typescript
import { ContainerMetrics } from '@zintrust/cloudflare-containers-proxy';
const metrics = new ContainerMetrics();
// Get request metrics
const requestMetrics = await metrics.getRequestMetrics();
// Returns: { totalRequests: number, averageResponseTime: number, errorRate: number, requestsPerSecond: number }
// Get target-specific metrics
const targetMetrics = await metrics.getTargetMetrics('container-1');
// Returns: { requests: number, errors: number, averageResponseTime: number, uptime: number }
// Custom metrics
metrics.recordCustomMetric('business-logic-calls', 1, {
target: 'container-1',
operation: 'process-order',
});Performance Monitoring
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
monitoring: {
enabled: true,
metrics: {
requestCount: true,
responseTime: true,
errorRate: true,
targetHealth: true,
},
logging: {
level: 'info',
includeRequestBody: false,
includeResponseBody: false,
customFields: ['request-id', 'user-id'],
},
},
});Testing
Mock Container
typescript
import { ContainerMock } from '@zintrust/cloudflare-containers-proxy';
// Use mock for testing
const mockContainer = new ContainerMock({
responses: {
'/health': { status: 200, body: { healthy: true } },
'/users': { status: 200, body: [{ id: 1, name: 'John' }] },
},
});
// Test proxy with mock
const proxy = new CloudflareContainersProxy({
target: mockContainer.url,
});
const response = await proxy.request('/users');
expect(response.data).toEqual([{ id: 1, name: 'John' }]);Integration Testing
typescript
import { TestContainer } from '@zintrust/cloudflare-containers-proxy';
// Use test container for integration tests
const testContainer = new TestContainer({
image: 'your-container-image:latest',
port: 8080,
environment: {
NODE_ENV: 'test',
DATABASE_URL: 'sqlite::memory:',
},
});
// Start test container
await testContainer.start();
// Run tests
const proxy = new CloudflareContainersProxy({
target: testContainer.url,
});
// Cleanup
await testContainer.stop();Error Handling
Circuit Breaker Events
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
circuitBreaker: {
enabled: true,
onCircuitOpen: () => {
console.log('Circuit breaker opened - failing fast');
sendAlert('Container service circuit breaker opened');
},
onCircuitClose: () => {
console.log('Circuit breaker closed - requests allowed');
},
onHalfOpen: () => {
console.log('Circuit breaker half-open - testing connection');
},
},
});Retry Logic
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
retry: {
enabled: true,
attempts: 3,
delay: 1000,
maxDelay: 10000,
backoff: 'exponential',
retryCondition: (error) => {
return error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT';
},
},
});Best Practices
- Health Monitoring: Implement comprehensive health checks
- Circuit Breaker: Use circuit breakers to prevent cascading failures
- Load Balancing: Distribute load across multiple container instances
- Timeout Configuration: Set appropriate timeouts for different operations
- Monitoring: Monitor request metrics and container health
- Security: Validate and sanitize all requests
- Error Handling: Implement robust error handling and retry logic
- Performance: Use connection pooling and caching for better performance
Limitations
- Network Latency: Network latency between Cloudflare and containers
- Cold Starts: Container cold start latency
- Resource Limits: Container resource limitations
- Connection Limits: Maximum concurrent connections
- Protocol Support: Limited to HTTP/HTTPS protocols
- Memory Usage: Proxy memory usage for large requests/responses
Troubleshooting
Common Issues
- Connection Refused: Check if container is running and accessible
- Timeout Errors: Increase timeout values or optimize container performance
- Health Check Failures: Verify health check endpoint and expected responses
- Circuit Breaker Tripping: Check container health and adjust thresholds
- High Latency: Optimize container performance or use edge caching
Debug Mode
typescript
const proxy = new CloudflareContainersProxy({
target: 'http://container-service:8080',
debug: process.env.NODE_ENV === 'development',
logging: {
level: 'debug',
logRequests: true,
logResponses: true,
logErrors: true,
},
});