145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
|
import * as plugins from '../../plugins.js';
|
||
|
import '../../core/models/socket-augmentation.js';
|
||
|
import type { IRouteContext, IHttpRouteContext, IHttp2RouteContext } from '../../core/models/route-context.js';
|
||
|
|
||
|
/**
|
||
|
* Context creator for NetworkProxy
|
||
|
* Creates route contexts for matching and function evaluation
|
||
|
*/
|
||
|
export class ContextCreator {
|
||
|
/**
|
||
|
* Create a route context from HTTP request information
|
||
|
*/
|
||
|
public createHttpRouteContext(req: any, options: {
|
||
|
tlsVersion?: string;
|
||
|
connectionId: string;
|
||
|
clientIp: string;
|
||
|
serverIp: string;
|
||
|
}): IHttpRouteContext {
|
||
|
// Parse headers
|
||
|
const headers: Record<string, string> = {};
|
||
|
for (const [key, value] of Object.entries(req.headers)) {
|
||
|
if (typeof value === 'string') {
|
||
|
headers[key.toLowerCase()] = value;
|
||
|
} else if (Array.isArray(value) && value.length > 0) {
|
||
|
headers[key.toLowerCase()] = value[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parse domain from Host header
|
||
|
const domain = headers['host']?.split(':')[0] || '';
|
||
|
|
||
|
// Parse URL
|
||
|
const url = new URL(`http://${domain}${req.url || '/'}`);
|
||
|
|
||
|
return {
|
||
|
// Connection basics
|
||
|
port: req.socket.localPort || 0,
|
||
|
domain,
|
||
|
clientIp: options.clientIp,
|
||
|
serverIp: options.serverIp,
|
||
|
|
||
|
// HTTP specifics
|
||
|
path: url.pathname,
|
||
|
query: url.search ? url.search.substring(1) : '',
|
||
|
headers,
|
||
|
|
||
|
// TLS information
|
||
|
isTls: !!req.socket.encrypted,
|
||
|
tlsVersion: options.tlsVersion,
|
||
|
|
||
|
// Request objects
|
||
|
req,
|
||
|
|
||
|
// Metadata
|
||
|
timestamp: Date.now(),
|
||
|
connectionId: options.connectionId
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a route context from HTTP/2 stream and headers
|
||
|
*/
|
||
|
public createHttp2RouteContext(
|
||
|
stream: plugins.http2.ServerHttp2Stream,
|
||
|
headers: plugins.http2.IncomingHttpHeaders,
|
||
|
options: {
|
||
|
connectionId: string;
|
||
|
clientIp: string;
|
||
|
serverIp: string;
|
||
|
}
|
||
|
): IHttp2RouteContext {
|
||
|
// Parse headers, excluding HTTP/2 pseudo-headers
|
||
|
const processedHeaders: Record<string, string> = {};
|
||
|
for (const [key, value] of Object.entries(headers)) {
|
||
|
if (!key.startsWith(':') && typeof value === 'string') {
|
||
|
processedHeaders[key.toLowerCase()] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get domain from :authority pseudo-header
|
||
|
const authority = headers[':authority'] as string || '';
|
||
|
const domain = authority.split(':')[0];
|
||
|
|
||
|
// Get path from :path pseudo-header
|
||
|
const path = headers[':path'] as string || '/';
|
||
|
|
||
|
// Parse the path to extract query string
|
||
|
const pathParts = path.split('?');
|
||
|
const pathname = pathParts[0];
|
||
|
const query = pathParts.length > 1 ? pathParts[1] : '';
|
||
|
|
||
|
// Get the socket from the session
|
||
|
const socket = (stream.session as any)?.socket;
|
||
|
|
||
|
return {
|
||
|
// Connection basics
|
||
|
port: socket?.localPort || 0,
|
||
|
domain,
|
||
|
clientIp: options.clientIp,
|
||
|
serverIp: options.serverIp,
|
||
|
|
||
|
// HTTP specifics
|
||
|
path: pathname,
|
||
|
query,
|
||
|
headers: processedHeaders,
|
||
|
|
||
|
// HTTP/2 specific properties
|
||
|
method: headers[':method'] as string,
|
||
|
stream,
|
||
|
|
||
|
// TLS information - HTTP/2 is always on TLS in browsers
|
||
|
isTls: true,
|
||
|
tlsVersion: socket?.getTLSVersion?.() || 'TLSv1.3',
|
||
|
|
||
|
// Metadata
|
||
|
timestamp: Date.now(),
|
||
|
connectionId: options.connectionId
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a basic route context from socket information
|
||
|
*/
|
||
|
public createSocketRouteContext(socket: plugins.net.Socket, options: {
|
||
|
domain?: string;
|
||
|
tlsVersion?: string;
|
||
|
connectionId: string;
|
||
|
}): IRouteContext {
|
||
|
return {
|
||
|
// Connection basics
|
||
|
port: socket.localPort || 0,
|
||
|
domain: options.domain,
|
||
|
clientIp: socket.remoteAddress?.replace('::ffff:', '') || '0.0.0.0',
|
||
|
serverIp: socket.localAddress?.replace('::ffff:', '') || '0.0.0.0',
|
||
|
|
||
|
// TLS information
|
||
|
isTls: options.tlsVersion !== undefined,
|
||
|
tlsVersion: options.tlsVersion,
|
||
|
|
||
|
// Metadata
|
||
|
timestamp: Date.now(),
|
||
|
connectionId: options.connectionId
|
||
|
};
|
||
|
}
|
||
|
}
|