@push.rocks/smartproxy 16.0.2 → 16.0.4
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/models/index.d.ts +2 -0
- package/dist_ts/core/models/index.js +3 -1
- package/dist_ts/core/models/route-context.d.ts +62 -0
- package/dist_ts/core/models/route-context.js +43 -0
- package/dist_ts/core/models/socket-augmentation.d.ts +12 -0
- package/dist_ts/core/models/socket-augmentation.js +18 -0
- package/dist_ts/core/utils/event-system.d.ts +200 -0
- package/dist_ts/core/utils/event-system.js +224 -0
- package/dist_ts/core/utils/index.d.ts +7 -0
- package/dist_ts/core/utils/index.js +8 -1
- package/dist_ts/core/utils/route-manager.d.ts +118 -0
- package/dist_ts/core/utils/route-manager.js +383 -0
- package/dist_ts/core/utils/route-utils.d.ts +94 -0
- package/dist_ts/core/utils/route-utils.js +264 -0
- package/dist_ts/core/utils/security-utils.d.ts +111 -0
- package/dist_ts/core/utils/security-utils.js +212 -0
- package/dist_ts/core/utils/shared-security-manager.d.ts +110 -0
- package/dist_ts/core/utils/shared-security-manager.js +252 -0
- package/dist_ts/core/utils/template-utils.d.ts +37 -0
- package/dist_ts/core/utils/template-utils.js +104 -0
- package/dist_ts/core/utils/websocket-utils.d.ts +23 -0
- package/dist_ts/core/utils/websocket-utils.js +86 -0
- package/dist_ts/http/router/index.d.ts +5 -1
- package/dist_ts/http/router/index.js +4 -2
- package/dist_ts/http/router/route-router.d.ts +108 -0
- package/dist_ts/http/router/route-router.js +393 -0
- package/dist_ts/index.d.ts +8 -2
- package/dist_ts/index.js +10 -3
- package/dist_ts/proxies/index.d.ts +7 -2
- package/dist_ts/proxies/index.js +10 -4
- package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +21 -0
- package/dist_ts/proxies/network-proxy/certificate-manager.js +92 -1
- package/dist_ts/proxies/network-proxy/context-creator.d.ts +34 -0
- package/dist_ts/proxies/network-proxy/context-creator.js +108 -0
- package/dist_ts/proxies/network-proxy/function-cache.d.ts +90 -0
- package/dist_ts/proxies/network-proxy/function-cache.js +198 -0
- package/dist_ts/proxies/network-proxy/http-request-handler.d.ts +40 -0
- package/dist_ts/proxies/network-proxy/http-request-handler.js +256 -0
- package/dist_ts/proxies/network-proxy/http2-request-handler.d.ts +24 -0
- package/dist_ts/proxies/network-proxy/http2-request-handler.js +201 -0
- package/dist_ts/proxies/network-proxy/models/types.d.ts +73 -1
- package/dist_ts/proxies/network-proxy/models/types.js +242 -1
- package/dist_ts/proxies/network-proxy/network-proxy.d.ts +23 -20
- package/dist_ts/proxies/network-proxy/network-proxy.js +149 -60
- package/dist_ts/proxies/network-proxy/request-handler.d.ts +38 -5
- package/dist_ts/proxies/network-proxy/request-handler.js +584 -198
- package/dist_ts/proxies/network-proxy/security-manager.d.ts +65 -0
- package/dist_ts/proxies/network-proxy/security-manager.js +255 -0
- package/dist_ts/proxies/network-proxy/websocket-handler.d.ts +13 -2
- package/dist_ts/proxies/network-proxy/websocket-handler.js +238 -20
- package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +3 -5
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +56 -4
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +4 -57
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +19 -228
- package/dist_ts/proxies/smart-proxy/port-manager.d.ts +81 -0
- package/dist_ts/proxies/smart-proxy/port-manager.js +166 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +5 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +131 -15
- package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +3 -1
- package/dist_ts/proxies/smart-proxy/route-helpers/index.js +5 -3
- package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +5 -178
- package/dist_ts/proxies/smart-proxy/route-helpers.js +8 -296
- package/dist_ts/proxies/smart-proxy/route-manager.d.ts +11 -2
- package/dist_ts/proxies/smart-proxy/route-manager.js +79 -10
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +29 -2
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +48 -43
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +67 -1
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +120 -1
- package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +3 -3
- package/dist_ts/proxies/smart-proxy/utils/route-validators.js +27 -5
- package/package.json +1 -1
- package/readme.md +102 -14
- package/readme.plan.md +103 -168
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/models/index.ts +2 -0
- package/ts/core/models/route-context.ts +113 -0
- package/ts/core/models/socket-augmentation.ts +33 -0
- package/ts/core/utils/event-system.ts +376 -0
- package/ts/core/utils/index.ts +7 -0
- package/ts/core/utils/route-manager.ts +489 -0
- package/ts/core/utils/route-utils.ts +312 -0
- package/ts/core/utils/security-utils.ts +309 -0
- package/ts/core/utils/shared-security-manager.ts +333 -0
- package/ts/core/utils/template-utils.ts +124 -0
- package/ts/core/utils/websocket-utils.ts +81 -0
- package/ts/http/router/index.ts +8 -1
- package/ts/http/router/route-router.ts +482 -0
- package/ts/index.ts +14 -2
- package/ts/proxies/index.ts +12 -3
- package/ts/proxies/network-proxy/certificate-manager.ts +114 -10
- package/ts/proxies/network-proxy/context-creator.ts +145 -0
- package/ts/proxies/network-proxy/function-cache.ts +259 -0
- package/ts/proxies/network-proxy/http-request-handler.ts +330 -0
- package/ts/proxies/network-proxy/http2-request-handler.ts +255 -0
- package/ts/proxies/network-proxy/models/types.ts +312 -1
- package/ts/proxies/network-proxy/network-proxy.ts +197 -85
- package/ts/proxies/network-proxy/request-handler.ts +698 -246
- package/ts/proxies/network-proxy/security-manager.ts +298 -0
- package/ts/proxies/network-proxy/websocket-handler.ts +276 -33
- package/ts/proxies/smart-proxy/index.ts +2 -12
- package/ts/proxies/smart-proxy/models/interfaces.ts +7 -4
- package/ts/proxies/smart-proxy/models/route-types.ts +77 -10
- package/ts/proxies/smart-proxy/network-proxy-bridge.ts +20 -257
- package/ts/proxies/smart-proxy/port-manager.ts +195 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +156 -21
- package/ts/proxies/smart-proxy/route-manager.ts +98 -14
- package/ts/proxies/smart-proxy/smart-proxy.ts +56 -55
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +167 -1
- package/ts/proxies/smart-proxy/utils/route-validators.ts +24 -5
- package/ts/proxies/smart-proxy/domain-config-manager.ts.bak +0 -441
- package/ts/proxies/smart-proxy/route-helpers/index.ts +0 -9
- package/ts/proxies/smart-proxy/route-helpers.ts +0 -498
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import '../../core/models/socket-augmentation.js';
|
|
3
|
+
import type { IHttpRouteContext } from '../../core/models/route-context.js';
|
|
4
|
+
import type { ILogger } from './models/types.js';
|
|
5
|
+
import type { IMetricsTracker } from './request-handler.js';
|
|
6
|
+
import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
|
|
7
|
+
/**
|
|
8
|
+
* HTTP Request Handler Helper - handles requests with specific destinations
|
|
9
|
+
* This is a helper class for the main RequestHandler
|
|
10
|
+
*/
|
|
11
|
+
export declare class HttpRequestHandler {
|
|
12
|
+
/**
|
|
13
|
+
* Handle HTTP request with a specific destination
|
|
14
|
+
*/
|
|
15
|
+
static handleHttpRequestWithDestination(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse, destination: {
|
|
16
|
+
host: string;
|
|
17
|
+
port: number;
|
|
18
|
+
}, routeContext: IHttpRouteContext, startTime: number, logger: ILogger, metricsTracker?: IMetricsTracker | null, route?: IRouteConfig): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Apply URL rewriting based on route configuration
|
|
21
|
+
* Implements Phase 5.2: URL rewriting using route context
|
|
22
|
+
*
|
|
23
|
+
* @param req The request with the URL to rewrite
|
|
24
|
+
* @param route The route configuration containing rewrite rules
|
|
25
|
+
* @param routeContext Context for template variable resolution
|
|
26
|
+
* @param logger Logger for debugging information
|
|
27
|
+
* @returns True if URL was rewritten, false otherwise
|
|
28
|
+
*/
|
|
29
|
+
private static applyUrlRewriting;
|
|
30
|
+
/**
|
|
31
|
+
* Apply header modifications from route configuration to request headers
|
|
32
|
+
* Implements Phase 5.1: Route-based header manipulation for requests
|
|
33
|
+
*/
|
|
34
|
+
private static applyRouteHeaderModifications;
|
|
35
|
+
/**
|
|
36
|
+
* Apply header modifications from route configuration to response headers
|
|
37
|
+
* Implements Phase 5.1: Route-based header manipulation for responses
|
|
38
|
+
*/
|
|
39
|
+
private static applyResponseHeaderModifications;
|
|
40
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import '../../core/models/socket-augmentation.js';
|
|
3
|
+
import { TemplateUtils } from '../../core/utils/template-utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* HTTP Request Handler Helper - handles requests with specific destinations
|
|
6
|
+
* This is a helper class for the main RequestHandler
|
|
7
|
+
*/
|
|
8
|
+
export class HttpRequestHandler {
|
|
9
|
+
/**
|
|
10
|
+
* Handle HTTP request with a specific destination
|
|
11
|
+
*/
|
|
12
|
+
static async handleHttpRequestWithDestination(req, res, destination, routeContext, startTime, logger, metricsTracker, route) {
|
|
13
|
+
try {
|
|
14
|
+
// Apply URL rewriting if route config is provided
|
|
15
|
+
if (route) {
|
|
16
|
+
HttpRequestHandler.applyUrlRewriting(req, route, routeContext, logger);
|
|
17
|
+
HttpRequestHandler.applyRouteHeaderModifications(route, req, res, logger);
|
|
18
|
+
}
|
|
19
|
+
// Create options for the proxy request
|
|
20
|
+
const options = {
|
|
21
|
+
hostname: destination.host,
|
|
22
|
+
port: destination.port,
|
|
23
|
+
path: req.url,
|
|
24
|
+
method: req.method,
|
|
25
|
+
headers: { ...req.headers }
|
|
26
|
+
};
|
|
27
|
+
// Optionally rewrite host header to match target
|
|
28
|
+
if (options.headers && options.headers.host) {
|
|
29
|
+
// Only apply if host header rewrite is enabled or not explicitly disabled
|
|
30
|
+
const shouldRewriteHost = route?.action.options?.rewriteHostHeader !== false;
|
|
31
|
+
if (shouldRewriteHost) {
|
|
32
|
+
options.headers.host = `${destination.host}:${destination.port}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
logger.debug(`Proxying request to ${destination.host}:${destination.port}${req.url}`, { method: req.method });
|
|
36
|
+
// Create proxy request
|
|
37
|
+
const proxyReq = plugins.http.request(options, (proxyRes) => {
|
|
38
|
+
// Copy status code
|
|
39
|
+
res.statusCode = proxyRes.statusCode || 500;
|
|
40
|
+
// Copy headers from proxy response to client response
|
|
41
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
42
|
+
if (value !== undefined) {
|
|
43
|
+
res.setHeader(key, value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Apply response header modifications if route config is provided
|
|
47
|
+
if (route && route.headers?.response) {
|
|
48
|
+
HttpRequestHandler.applyResponseHeaderModifications(route, res, logger, routeContext);
|
|
49
|
+
}
|
|
50
|
+
// Pipe proxy response to client response
|
|
51
|
+
proxyRes.pipe(res);
|
|
52
|
+
// Increment served requests counter when the response finishes
|
|
53
|
+
res.on('finish', () => {
|
|
54
|
+
if (metricsTracker) {
|
|
55
|
+
metricsTracker.incrementRequestsServed();
|
|
56
|
+
}
|
|
57
|
+
// Log the completed request
|
|
58
|
+
const duration = Date.now() - startTime;
|
|
59
|
+
logger.debug(`Request completed in ${duration}ms: ${req.method} ${req.url} ${res.statusCode}`, { duration, statusCode: res.statusCode });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
// Handle proxy request errors
|
|
63
|
+
proxyReq.on('error', (error) => {
|
|
64
|
+
const duration = Date.now() - startTime;
|
|
65
|
+
logger.error(`Proxy error for ${req.method} ${req.url}: ${error.message}`, { duration, error: error.message });
|
|
66
|
+
// Increment failed requests counter
|
|
67
|
+
if (metricsTracker) {
|
|
68
|
+
metricsTracker.incrementFailedRequests();
|
|
69
|
+
}
|
|
70
|
+
// Check if headers have already been sent
|
|
71
|
+
if (!res.headersSent) {
|
|
72
|
+
res.statusCode = 502;
|
|
73
|
+
res.end(`Bad Gateway: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// If headers already sent, just close the connection
|
|
77
|
+
res.end();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Pipe request body to proxy request and handle client-side errors
|
|
81
|
+
req.pipe(proxyReq);
|
|
82
|
+
// Handle client disconnection
|
|
83
|
+
req.on('error', (error) => {
|
|
84
|
+
logger.debug(`Client connection error: ${error.message}`);
|
|
85
|
+
proxyReq.destroy();
|
|
86
|
+
// Increment failed requests counter on client errors
|
|
87
|
+
if (metricsTracker) {
|
|
88
|
+
metricsTracker.incrementFailedRequests();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// Handle response errors
|
|
92
|
+
res.on('error', (error) => {
|
|
93
|
+
logger.debug(`Response error: ${error.message}`);
|
|
94
|
+
proxyReq.destroy();
|
|
95
|
+
// Increment failed requests counter on response errors
|
|
96
|
+
if (metricsTracker) {
|
|
97
|
+
metricsTracker.incrementFailedRequests();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
// Handle any unexpected errors
|
|
103
|
+
logger.error(`Unexpected error handling request: ${error.message}`, { error: error.stack });
|
|
104
|
+
// Increment failed requests counter
|
|
105
|
+
if (metricsTracker) {
|
|
106
|
+
metricsTracker.incrementFailedRequests();
|
|
107
|
+
}
|
|
108
|
+
if (!res.headersSent) {
|
|
109
|
+
res.statusCode = 500;
|
|
110
|
+
res.end('Internal Server Error');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
res.end();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Apply URL rewriting based on route configuration
|
|
119
|
+
* Implements Phase 5.2: URL rewriting using route context
|
|
120
|
+
*
|
|
121
|
+
* @param req The request with the URL to rewrite
|
|
122
|
+
* @param route The route configuration containing rewrite rules
|
|
123
|
+
* @param routeContext Context for template variable resolution
|
|
124
|
+
* @param logger Logger for debugging information
|
|
125
|
+
* @returns True if URL was rewritten, false otherwise
|
|
126
|
+
*/
|
|
127
|
+
static applyUrlRewriting(req, route, routeContext, logger) {
|
|
128
|
+
// Check if route has URL rewriting configuration
|
|
129
|
+
if (!route.action.advanced?.urlRewrite) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const rewriteConfig = route.action.advanced.urlRewrite;
|
|
133
|
+
// Store original URL for logging
|
|
134
|
+
const originalUrl = req.url;
|
|
135
|
+
if (rewriteConfig.pattern && rewriteConfig.target) {
|
|
136
|
+
try {
|
|
137
|
+
// Create a RegExp from the pattern with optional flags
|
|
138
|
+
const regex = new RegExp(rewriteConfig.pattern, rewriteConfig.flags || '');
|
|
139
|
+
// Apply rewriting with template variable resolution
|
|
140
|
+
let target = rewriteConfig.target;
|
|
141
|
+
// Replace template variables in target with values from context
|
|
142
|
+
target = TemplateUtils.resolveTemplateVariables(target, routeContext);
|
|
143
|
+
// If onlyRewritePath is set, split URL into path and query parts
|
|
144
|
+
if (rewriteConfig.onlyRewritePath && req.url) {
|
|
145
|
+
const [path, query] = req.url.split('?');
|
|
146
|
+
const rewrittenPath = path.replace(regex, target);
|
|
147
|
+
req.url = query ? `${rewrittenPath}?${query}` : rewrittenPath;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Perform the replacement on the entire URL
|
|
151
|
+
req.url = req.url?.replace(regex, target);
|
|
152
|
+
}
|
|
153
|
+
logger.debug(`URL rewritten: ${originalUrl} -> ${req.url}`);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
logger.error(`Error in URL rewriting: ${err}`);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Apply header modifications from route configuration to request headers
|
|
165
|
+
* Implements Phase 5.1: Route-based header manipulation for requests
|
|
166
|
+
*/
|
|
167
|
+
static applyRouteHeaderModifications(route, req, res, logger) {
|
|
168
|
+
// Check if route has header modifications
|
|
169
|
+
if (!route.headers) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Apply request header modifications (these will be sent to the backend)
|
|
173
|
+
if (route.headers.request && req.headers) {
|
|
174
|
+
// Create routing context for template resolution
|
|
175
|
+
const routeContext = {
|
|
176
|
+
domain: req.headers.host || '',
|
|
177
|
+
path: req.url || '',
|
|
178
|
+
clientIp: req.socket.remoteAddress?.replace('::ffff:', '') || '',
|
|
179
|
+
serverIp: req.socket.localAddress?.replace('::ffff:', '') || '',
|
|
180
|
+
port: parseInt(req.socket.localPort?.toString() || '0', 10),
|
|
181
|
+
isTls: !!req.socket.encrypted,
|
|
182
|
+
headers: req.headers,
|
|
183
|
+
timestamp: Date.now(),
|
|
184
|
+
connectionId: `${Date.now()}-${Math.floor(Math.random() * 10000)}`,
|
|
185
|
+
};
|
|
186
|
+
for (const [key, value] of Object.entries(route.headers.request)) {
|
|
187
|
+
// Skip if header already exists and we're not overriding
|
|
188
|
+
if (req.headers[key.toLowerCase()] && !value.startsWith('!')) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
// Handle special delete directive (!delete)
|
|
192
|
+
if (value === '!delete') {
|
|
193
|
+
delete req.headers[key.toLowerCase()];
|
|
194
|
+
logger.debug(`Deleted request header: ${key}`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
// Handle forced override (!value)
|
|
198
|
+
let finalValue;
|
|
199
|
+
if (value.startsWith('!')) {
|
|
200
|
+
// Keep the ! but resolve any templates in the rest
|
|
201
|
+
const templateValue = value.substring(1);
|
|
202
|
+
finalValue = '!' + TemplateUtils.resolveTemplateVariables(templateValue, routeContext);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Resolve templates in the entire value
|
|
206
|
+
finalValue = TemplateUtils.resolveTemplateVariables(value, routeContext);
|
|
207
|
+
}
|
|
208
|
+
// Set the header
|
|
209
|
+
req.headers[key.toLowerCase()] = finalValue;
|
|
210
|
+
logger.debug(`Modified request header: ${key}=${finalValue}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Apply header modifications from route configuration to response headers
|
|
216
|
+
* Implements Phase 5.1: Route-based header manipulation for responses
|
|
217
|
+
*/
|
|
218
|
+
static applyResponseHeaderModifications(route, res, logger, routeContext) {
|
|
219
|
+
// Check if route has response header modifications
|
|
220
|
+
if (!route.headers?.response) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Apply response header modifications
|
|
224
|
+
for (const [key, value] of Object.entries(route.headers.response)) {
|
|
225
|
+
// Skip if header already exists and we're not overriding
|
|
226
|
+
if (res.hasHeader(key) && !value.startsWith('!')) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
// Handle special delete directive (!delete)
|
|
230
|
+
if (value === '!delete') {
|
|
231
|
+
res.removeHeader(key);
|
|
232
|
+
logger.debug(`Deleted response header: ${key}`);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
// Handle forced override (!value)
|
|
236
|
+
let finalValue;
|
|
237
|
+
if (value.startsWith('!') && value !== '!delete') {
|
|
238
|
+
// Keep the ! but resolve any templates in the rest
|
|
239
|
+
const templateValue = value.substring(1);
|
|
240
|
+
finalValue = routeContext
|
|
241
|
+
? '!' + TemplateUtils.resolveTemplateVariables(templateValue, routeContext)
|
|
242
|
+
: '!' + templateValue;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Resolve templates in the entire value
|
|
246
|
+
finalValue = routeContext
|
|
247
|
+
? TemplateUtils.resolveTemplateVariables(value, routeContext)
|
|
248
|
+
: value;
|
|
249
|
+
}
|
|
250
|
+
// Set the header
|
|
251
|
+
res.setHeader(key, finalValue);
|
|
252
|
+
logger.debug(`Modified response header: ${key}=${finalValue}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1yZXF1ZXN0LWhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm94aWVzL25ldHdvcmstcHJveHkvaHR0cC1yZXF1ZXN0LWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLDBDQUEwQyxDQUFDO0FBS2xELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUVuRTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sa0JBQWtCO0lBQzdCOztPQUVHO0lBQ0ksTUFBTSxDQUFDLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FDbEQsR0FBaUMsRUFDakMsR0FBZ0MsRUFDaEMsV0FBMkMsRUFDM0MsWUFBK0IsRUFDL0IsU0FBaUIsRUFDakIsTUFBZSxFQUNmLGNBQXVDLEVBQ3ZDLEtBQW9CO1FBRXBCLElBQUksQ0FBQztZQUNILGtEQUFrRDtZQUNsRCxJQUFJLEtBQUssRUFBRSxDQUFDO2dCQUNWLGtCQUFrQixDQUFDLGlCQUFpQixDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQyxDQUFDO2dCQUN2RSxrQkFBa0IsQ0FBQyw2QkFBNkIsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1RSxDQUFDO1lBRUQsdUNBQXVDO1lBQ3ZDLE1BQU0sT0FBTyxHQUFnQztnQkFDM0MsUUFBUSxFQUFFLFdBQVcsQ0FBQyxJQUFJO2dCQUMxQixJQUFJLEVBQUUsV0FBVyxDQUFDLElBQUk7Z0JBQ3RCLElBQUksRUFBRSxHQUFHLENBQUMsR0FBRztnQkFDYixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU07Z0JBQ2xCLE9BQU8sRUFBRSxFQUFFLEdBQUcsR0FBRyxDQUFDLE9BQU8sRUFBRTthQUM1QixDQUFDO1lBRUYsaURBQWlEO1lBQ2pELElBQUksT0FBTyxDQUFD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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IHttpRouteContext } from '../../core/models/route-context.js';
|
|
3
|
+
import type { ILogger } from './models/types.js';
|
|
4
|
+
import type { IMetricsTracker } from './request-handler.js';
|
|
5
|
+
/**
|
|
6
|
+
* HTTP/2 Request Handler Helper - handles HTTP/2 streams with specific destinations
|
|
7
|
+
* This is a helper class for the main RequestHandler
|
|
8
|
+
*/
|
|
9
|
+
export declare class Http2RequestHandler {
|
|
10
|
+
/**
|
|
11
|
+
* Handle HTTP/2 stream with direct HTTP/2 backend
|
|
12
|
+
*/
|
|
13
|
+
static handleHttp2WithHttp2Destination(stream: plugins.http2.ServerHttp2Stream, headers: plugins.http2.IncomingHttpHeaders, destination: {
|
|
14
|
+
host: string;
|
|
15
|
+
port: number;
|
|
16
|
+
}, routeContext: IHttpRouteContext, sessions: Map<string, plugins.http2.ClientHttp2Session>, logger: ILogger, metricsTracker?: IMetricsTracker | null): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Handle HTTP/2 stream with HTTP/1 backend
|
|
19
|
+
*/
|
|
20
|
+
static handleHttp2WithHttp1Destination(stream: plugins.http2.ServerHttp2Stream, headers: plugins.http2.IncomingHttpHeaders, destination: {
|
|
21
|
+
host: string;
|
|
22
|
+
port: number;
|
|
23
|
+
}, routeContext: IHttpRouteContext, logger: ILogger, metricsTracker?: IMetricsTracker | null): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* HTTP/2 Request Handler Helper - handles HTTP/2 streams with specific destinations
|
|
4
|
+
* This is a helper class for the main RequestHandler
|
|
5
|
+
*/
|
|
6
|
+
export class Http2RequestHandler {
|
|
7
|
+
/**
|
|
8
|
+
* Handle HTTP/2 stream with direct HTTP/2 backend
|
|
9
|
+
*/
|
|
10
|
+
static async handleHttp2WithHttp2Destination(stream, headers, destination, routeContext, sessions, logger, metricsTracker) {
|
|
11
|
+
const key = `${destination.host}:${destination.port}`;
|
|
12
|
+
// Get or create a client HTTP/2 session
|
|
13
|
+
let session = sessions.get(key);
|
|
14
|
+
if (!session || session.closed || session.destroyed) {
|
|
15
|
+
try {
|
|
16
|
+
// Connect to the backend HTTP/2 server
|
|
17
|
+
session = plugins.http2.connect(`http://${destination.host}:${destination.port}`);
|
|
18
|
+
sessions.set(key, session);
|
|
19
|
+
// Handle session errors and cleanup
|
|
20
|
+
session.on('error', (err) => {
|
|
21
|
+
logger.error(`HTTP/2 session error to ${key}: ${err.message}`);
|
|
22
|
+
sessions.delete(key);
|
|
23
|
+
});
|
|
24
|
+
session.on('close', () => {
|
|
25
|
+
logger.debug(`HTTP/2 session closed to ${key}`);
|
|
26
|
+
sessions.delete(key);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
logger.error(`Failed to establish HTTP/2 session to ${key}: ${err.message}`);
|
|
31
|
+
stream.respond({ ':status': 502 });
|
|
32
|
+
stream.end('Bad Gateway: Failed to establish connection to backend');
|
|
33
|
+
if (metricsTracker)
|
|
34
|
+
metricsTracker.incrementFailedRequests();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
// Build headers for backend HTTP/2 request
|
|
40
|
+
const h2Headers = {
|
|
41
|
+
':method': headers[':method'],
|
|
42
|
+
':path': headers[':path'],
|
|
43
|
+
':authority': `${destination.host}:${destination.port}`
|
|
44
|
+
};
|
|
45
|
+
// Copy other headers, excluding pseudo-headers
|
|
46
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
47
|
+
if (!key.startsWith(':') && typeof value === 'string') {
|
|
48
|
+
h2Headers[key] = value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
logger.debug(`Proxying HTTP/2 request to ${destination.host}:${destination.port}${headers[':path']}`, { method: headers[':method'] });
|
|
52
|
+
// Create HTTP/2 request stream to the backend
|
|
53
|
+
const h2Stream = session.request(h2Headers);
|
|
54
|
+
// Pipe client stream to backend stream
|
|
55
|
+
stream.pipe(h2Stream);
|
|
56
|
+
// Handle responses from the backend
|
|
57
|
+
h2Stream.on('response', (responseHeaders) => {
|
|
58
|
+
// Map status and headers to client response
|
|
59
|
+
const resp = {
|
|
60
|
+
':status': responseHeaders[':status']
|
|
61
|
+
};
|
|
62
|
+
// Copy non-pseudo headers
|
|
63
|
+
for (const [key, value] of Object.entries(responseHeaders)) {
|
|
64
|
+
if (!key.startsWith(':') && value !== undefined) {
|
|
65
|
+
resp[key] = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Send headers to client
|
|
69
|
+
stream.respond(resp);
|
|
70
|
+
// Pipe backend response to client
|
|
71
|
+
h2Stream.pipe(stream);
|
|
72
|
+
// Track successful requests
|
|
73
|
+
stream.on('end', () => {
|
|
74
|
+
if (metricsTracker)
|
|
75
|
+
metricsTracker.incrementRequestsServed();
|
|
76
|
+
logger.debug(`HTTP/2 request completed: ${headers[':method']} ${headers[':path']} ${responseHeaders[':status']}`, { method: headers[':method'], status: responseHeaders[':status'] });
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
// Handle backend errors
|
|
80
|
+
h2Stream.on('error', (err) => {
|
|
81
|
+
logger.error(`HTTP/2 stream error: ${err.message}`);
|
|
82
|
+
// Only send error response if headers haven't been sent
|
|
83
|
+
if (!stream.headersSent) {
|
|
84
|
+
stream.respond({ ':status': 502 });
|
|
85
|
+
stream.end(`Bad Gateway: ${err.message}`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
stream.end();
|
|
89
|
+
}
|
|
90
|
+
if (metricsTracker)
|
|
91
|
+
metricsTracker.incrementFailedRequests();
|
|
92
|
+
});
|
|
93
|
+
// Handle client stream errors
|
|
94
|
+
stream.on('error', (err) => {
|
|
95
|
+
logger.debug(`Client HTTP/2 stream error: ${err.message}`);
|
|
96
|
+
h2Stream.destroy();
|
|
97
|
+
if (metricsTracker)
|
|
98
|
+
metricsTracker.incrementFailedRequests();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
logger.error(`Error handling HTTP/2 request: ${err.message}`);
|
|
103
|
+
// Only send error response if headers haven't been sent
|
|
104
|
+
if (!stream.headersSent) {
|
|
105
|
+
stream.respond({ ':status': 500 });
|
|
106
|
+
stream.end('Internal Server Error');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
stream.end();
|
|
110
|
+
}
|
|
111
|
+
if (metricsTracker)
|
|
112
|
+
metricsTracker.incrementFailedRequests();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Handle HTTP/2 stream with HTTP/1 backend
|
|
117
|
+
*/
|
|
118
|
+
static async handleHttp2WithHttp1Destination(stream, headers, destination, routeContext, logger, metricsTracker) {
|
|
119
|
+
try {
|
|
120
|
+
// Build headers for HTTP/1 proxy request, excluding HTTP/2 pseudo-headers
|
|
121
|
+
const outboundHeaders = {};
|
|
122
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
123
|
+
if (typeof key === 'string' && typeof value === 'string' && !key.startsWith(':')) {
|
|
124
|
+
outboundHeaders[key] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Always rewrite host header to match target
|
|
128
|
+
outboundHeaders.host = `${destination.host}:${destination.port}`;
|
|
129
|
+
logger.debug(`Proxying HTTP/2 request to HTTP/1 backend ${destination.host}:${destination.port}${headers[':path']}`, { method: headers[':method'] });
|
|
130
|
+
// Create HTTP/1 proxy request
|
|
131
|
+
const proxyReq = plugins.http.request({
|
|
132
|
+
hostname: destination.host,
|
|
133
|
+
port: destination.port,
|
|
134
|
+
path: headers[':path'],
|
|
135
|
+
method: headers[':method'],
|
|
136
|
+
headers: outboundHeaders
|
|
137
|
+
}, (proxyRes) => {
|
|
138
|
+
// Map status and headers back to HTTP/2
|
|
139
|
+
const responseHeaders = {
|
|
140
|
+
':status': proxyRes.statusCode || 500
|
|
141
|
+
};
|
|
142
|
+
// Copy headers from HTTP/1 response to HTTP/2 response
|
|
143
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
144
|
+
if (value !== undefined) {
|
|
145
|
+
responseHeaders[key] = value;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Send headers to client
|
|
149
|
+
stream.respond(responseHeaders);
|
|
150
|
+
// Pipe HTTP/1 response to HTTP/2 stream
|
|
151
|
+
proxyRes.pipe(stream);
|
|
152
|
+
// Clean up when client disconnects
|
|
153
|
+
stream.on('close', () => proxyReq.destroy());
|
|
154
|
+
stream.on('error', () => proxyReq.destroy());
|
|
155
|
+
// Track successful requests
|
|
156
|
+
stream.on('end', () => {
|
|
157
|
+
if (metricsTracker)
|
|
158
|
+
metricsTracker.incrementRequestsServed();
|
|
159
|
+
logger.debug(`HTTP/2 to HTTP/1 request completed: ${headers[':method']} ${headers[':path']} ${proxyRes.statusCode}`, { method: headers[':method'], status: proxyRes.statusCode });
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
// Handle proxy request errors
|
|
163
|
+
proxyReq.on('error', (err) => {
|
|
164
|
+
logger.error(`HTTP/1 proxy error: ${err.message}`);
|
|
165
|
+
// Only send error response if headers haven't been sent
|
|
166
|
+
if (!stream.headersSent) {
|
|
167
|
+
stream.respond({ ':status': 502 });
|
|
168
|
+
stream.end(`Bad Gateway: ${err.message}`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
stream.end();
|
|
172
|
+
}
|
|
173
|
+
if (metricsTracker)
|
|
174
|
+
metricsTracker.incrementFailedRequests();
|
|
175
|
+
});
|
|
176
|
+
// Pipe client stream to proxy request
|
|
177
|
+
stream.pipe(proxyReq);
|
|
178
|
+
// Handle client stream errors
|
|
179
|
+
stream.on('error', (err) => {
|
|
180
|
+
logger.debug(`Client HTTP/2 stream error: ${err.message}`);
|
|
181
|
+
proxyReq.destroy();
|
|
182
|
+
if (metricsTracker)
|
|
183
|
+
metricsTracker.incrementFailedRequests();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
logger.error(`Error handling HTTP/2 to HTTP/1 request: ${err.message}`);
|
|
188
|
+
// Only send error response if headers haven't been sent
|
|
189
|
+
if (!stream.headersSent) {
|
|
190
|
+
stream.respond({ ':status': 500 });
|
|
191
|
+
stream.end('Internal Server Error');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
stream.end();
|
|
195
|
+
}
|
|
196
|
+
if (metricsTracker)
|
|
197
|
+
metricsTracker.incrementFailedRequests();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cDItcmVxdWVzdC1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9uZXR3b3JrLXByb3h5L2h0dHAyLXJlcXVlc3QtaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBSzVDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxtQkFBbUI7SUFDOUI7O09BRUc7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLCtCQUErQixDQUNqRCxNQUF1QyxFQUN2QyxPQUEwQyxFQUMxQyxXQUEyQyxFQUMzQyxZQUErQixFQUMvQixRQUF1RCxFQUN2RCxNQUFlLEVBQ2YsY0FBdUM7UUFFdkMsTUFBTSxHQUFHLEdBQUcsR0FBRyxXQUFXLENBQUMsSUFBSSxJQUFJLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUV0RCx3Q0FBd0M7UUFDeEMsSUFBSSxPQUFPLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxNQUFNLElBQUssT0FBZSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQzdELElBQUksQ0FBQztnQkFDSCx1Q0FBdUM7Z0JBQ3ZDLE9BQU8sR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLFdBQVcsQ0FBQyxJQUFJLElBQUksV0FBVyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQ2xGLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUUzQixvQ0FBb0M7Z0JBQ3BDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7b0JBQzFCLE1BQU0sQ0FBQyxLQUFLLENBQUMsMkJBQTJCLEdBQUcsS0FBSyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztvQkFD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
|