fix(network-proxy, route-utils, route-manager): Normalize IPv6-mapped IPv4 addresses in IP matching functions and remove deprecated legacy configuration methods in NetworkProxy. Update route-utils and route-manager to compare both canonical and IPv6-mapped IP forms, adjust tests accordingly, and clean up legacy exports.
This commit is contained in:
@@ -1,202 +1,207 @@
|
||||
import { expect } from '@push.rocks/tapbundle';
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import {
|
||||
EventSystem,
|
||||
ProxyEvents,
|
||||
ComponentType
|
||||
} from '../../../ts/core/utils/event-system.js';
|
||||
|
||||
// Test event system
|
||||
expect.describe('Event System', async () => {
|
||||
let eventSystem: EventSystem;
|
||||
let receivedEvents: any[] = [];
|
||||
// Setup function for creating a new event system
|
||||
function setupEventSystem(): { eventSystem: EventSystem, receivedEvents: any[] } {
|
||||
const eventSystem = new EventSystem(ComponentType.SMART_PROXY, 'test-id');
|
||||
const receivedEvents: any[] = [];
|
||||
return { eventSystem, receivedEvents };
|
||||
}
|
||||
|
||||
tap.test('Event System - certificate events with correct structure', async () => {
|
||||
const { eventSystem, receivedEvents } = setupEventSystem();
|
||||
|
||||
// Set up a new event system before each test
|
||||
expect.beforeEach(() => {
|
||||
eventSystem = new EventSystem(ComponentType.SMART_PROXY, 'test-id');
|
||||
receivedEvents = [];
|
||||
// Set up listeners
|
||||
eventSystem.on(ProxyEvents.CERTIFICATE_ISSUED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'issued',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
expect.it('should emit certificate events with correct structure', async () => {
|
||||
// Set up listeners
|
||||
eventSystem.on(ProxyEvents.CERTIFICATE_ISSUED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'issued',
|
||||
data
|
||||
});
|
||||
eventSystem.on(ProxyEvents.CERTIFICATE_RENEWED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'renewed',
|
||||
data
|
||||
});
|
||||
|
||||
eventSystem.on(ProxyEvents.CERTIFICATE_RENEWED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'renewed',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
// Emit events
|
||||
eventSystem.emitCertificateIssued({
|
||||
domain: 'example.com',
|
||||
certificate: 'cert-content',
|
||||
privateKey: 'key-content',
|
||||
expiryDate: new Date('2025-01-01')
|
||||
});
|
||||
|
||||
eventSystem.emitCertificateRenewed({
|
||||
domain: 'example.com',
|
||||
certificate: 'new-cert-content',
|
||||
privateKey: 'new-key-content',
|
||||
expiryDate: new Date('2026-01-01'),
|
||||
isRenewal: true
|
||||
});
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).to.equal(2);
|
||||
|
||||
// Check issuance event
|
||||
expect(receivedEvents[0].type).to.equal('issued');
|
||||
expect(receivedEvents[0].data.domain).to.equal('example.com');
|
||||
expect(receivedEvents[0].data.certificate).to.equal('cert-content');
|
||||
expect(receivedEvents[0].data.componentType).to.equal(ComponentType.SMART_PROXY);
|
||||
expect(receivedEvents[0].data.componentId).to.equal('test-id');
|
||||
expect(receivedEvents[0].data.timestamp).to.be.a('number');
|
||||
|
||||
// Check renewal event
|
||||
expect(receivedEvents[1].type).to.equal('renewed');
|
||||
expect(receivedEvents[1].data.domain).to.equal('example.com');
|
||||
expect(receivedEvents[1].data.isRenewal).to.be.true;
|
||||
expect(receivedEvents[1].data.expiryDate).to.deep.equal(new Date('2026-01-01'));
|
||||
});
|
||||
|
||||
expect.it('should emit component lifecycle events', async () => {
|
||||
// Set up listeners
|
||||
eventSystem.on(ProxyEvents.COMPONENT_STARTED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'started',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
eventSystem.on(ProxyEvents.COMPONENT_STOPPED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'stopped',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
// Emit events
|
||||
eventSystem.emitComponentStarted('TestComponent', '1.0.0');
|
||||
eventSystem.emitComponentStopped('TestComponent');
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).to.equal(2);
|
||||
|
||||
// Check started event
|
||||
expect(receivedEvents[0].type).to.equal('started');
|
||||
expect(receivedEvents[0].data.name).to.equal('TestComponent');
|
||||
expect(receivedEvents[0].data.version).to.equal('1.0.0');
|
||||
|
||||
// Check stopped event
|
||||
expect(receivedEvents[1].type).to.equal('stopped');
|
||||
expect(receivedEvents[1].data.name).to.equal('TestComponent');
|
||||
// Emit events
|
||||
eventSystem.emitCertificateIssued({
|
||||
domain: 'example.com',
|
||||
certificate: 'cert-content',
|
||||
privateKey: 'key-content',
|
||||
expiryDate: new Date('2025-01-01')
|
||||
});
|
||||
|
||||
expect.it('should emit connection events', async () => {
|
||||
// Set up listeners
|
||||
eventSystem.on(ProxyEvents.CONNECTION_ESTABLISHED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'established',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
eventSystem.on(ProxyEvents.CONNECTION_CLOSED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'closed',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
// Emit events
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-123',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443,
|
||||
isTls: true,
|
||||
domain: 'example.com'
|
||||
});
|
||||
|
||||
eventSystem.emitConnectionClosed({
|
||||
connectionId: 'conn-123',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).to.equal(2);
|
||||
|
||||
// Check established event
|
||||
expect(receivedEvents[0].type).to.equal('established');
|
||||
expect(receivedEvents[0].data.connectionId).to.equal('conn-123');
|
||||
expect(receivedEvents[0].data.clientIp).to.equal('192.168.1.1');
|
||||
expect(receivedEvents[0].data.port).to.equal(443);
|
||||
expect(receivedEvents[0].data.isTls).to.be.true;
|
||||
|
||||
// Check closed event
|
||||
expect(receivedEvents[1].type).to.equal('closed');
|
||||
expect(receivedEvents[1].data.connectionId).to.equal('conn-123');
|
||||
eventSystem.emitCertificateRenewed({
|
||||
domain: 'example.com',
|
||||
certificate: 'new-cert-content',
|
||||
privateKey: 'new-key-content',
|
||||
expiryDate: new Date('2026-01-01'),
|
||||
isRenewal: true
|
||||
});
|
||||
|
||||
expect.it('should support once and off subscription methods', async () => {
|
||||
// Set up a listener that should fire only once
|
||||
eventSystem.once(ProxyEvents.CONNECTION_ESTABLISHED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'once',
|
||||
data
|
||||
});
|
||||
// Verify events
|
||||
expect(receivedEvents.length).toEqual(2);
|
||||
|
||||
// Check issuance event
|
||||
expect(receivedEvents[0].type).toEqual('issued');
|
||||
expect(receivedEvents[0].data.domain).toEqual('example.com');
|
||||
expect(receivedEvents[0].data.certificate).toEqual('cert-content');
|
||||
expect(receivedEvents[0].data.componentType).toEqual(ComponentType.SMART_PROXY);
|
||||
expect(receivedEvents[0].data.componentId).toEqual('test-id');
|
||||
expect(typeof receivedEvents[0].data.timestamp).toEqual('number');
|
||||
|
||||
// Check renewal event
|
||||
expect(receivedEvents[1].type).toEqual('renewed');
|
||||
expect(receivedEvents[1].data.domain).toEqual('example.com');
|
||||
expect(receivedEvents[1].data.isRenewal).toEqual(true);
|
||||
expect(receivedEvents[1].data.expiryDate).toEqual(new Date('2026-01-01'));
|
||||
});
|
||||
|
||||
tap.test('Event System - component lifecycle events', async () => {
|
||||
const { eventSystem, receivedEvents } = setupEventSystem();
|
||||
|
||||
// Set up listeners
|
||||
eventSystem.on(ProxyEvents.COMPONENT_STARTED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'started',
|
||||
data
|
||||
});
|
||||
|
||||
// Set up a persistent listener
|
||||
const persistentHandler = (data: any) => {
|
||||
receivedEvents.push({
|
||||
type: 'persistent',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
eventSystem.on(ProxyEvents.CONNECTION_ESTABLISHED, persistentHandler);
|
||||
|
||||
// First event should trigger both listeners
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-1',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Second event should only trigger the persistent listener
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-2',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Unsubscribe the persistent listener
|
||||
eventSystem.off(ProxyEvents.CONNECTION_ESTABLISHED, persistentHandler);
|
||||
|
||||
// Third event should not trigger any listeners
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-3',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).to.equal(3);
|
||||
expect(receivedEvents[0].type).to.equal('once');
|
||||
expect(receivedEvents[0].data.connectionId).to.equal('conn-1');
|
||||
|
||||
expect(receivedEvents[1].type).to.equal('persistent');
|
||||
expect(receivedEvents[1].data.connectionId).to.equal('conn-1');
|
||||
|
||||
expect(receivedEvents[2].type).to.equal('persistent');
|
||||
expect(receivedEvents[2].data.connectionId).to.equal('conn-2');
|
||||
});
|
||||
});
|
||||
|
||||
eventSystem.on(ProxyEvents.COMPONENT_STOPPED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'stopped',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
// Emit events
|
||||
eventSystem.emitComponentStarted('TestComponent', '1.0.0');
|
||||
eventSystem.emitComponentStopped('TestComponent');
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).toEqual(2);
|
||||
|
||||
// Check started event
|
||||
expect(receivedEvents[0].type).toEqual('started');
|
||||
expect(receivedEvents[0].data.name).toEqual('TestComponent');
|
||||
expect(receivedEvents[0].data.version).toEqual('1.0.0');
|
||||
|
||||
// Check stopped event
|
||||
expect(receivedEvents[1].type).toEqual('stopped');
|
||||
expect(receivedEvents[1].data.name).toEqual('TestComponent');
|
||||
});
|
||||
|
||||
tap.test('Event System - connection events', async () => {
|
||||
const { eventSystem, receivedEvents } = setupEventSystem();
|
||||
|
||||
// Set up listeners
|
||||
eventSystem.on(ProxyEvents.CONNECTION_ESTABLISHED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'established',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
eventSystem.on(ProxyEvents.CONNECTION_CLOSED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'closed',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
// Emit events
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-123',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443,
|
||||
isTls: true,
|
||||
domain: 'example.com'
|
||||
});
|
||||
|
||||
eventSystem.emitConnectionClosed({
|
||||
connectionId: 'conn-123',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).toEqual(2);
|
||||
|
||||
// Check established event
|
||||
expect(receivedEvents[0].type).toEqual('established');
|
||||
expect(receivedEvents[0].data.connectionId).toEqual('conn-123');
|
||||
expect(receivedEvents[0].data.clientIp).toEqual('192.168.1.1');
|
||||
expect(receivedEvents[0].data.port).toEqual(443);
|
||||
expect(receivedEvents[0].data.isTls).toEqual(true);
|
||||
|
||||
// Check closed event
|
||||
expect(receivedEvents[1].type).toEqual('closed');
|
||||
expect(receivedEvents[1].data.connectionId).toEqual('conn-123');
|
||||
});
|
||||
|
||||
tap.test('Event System - once and off subscription methods', async () => {
|
||||
const { eventSystem, receivedEvents } = setupEventSystem();
|
||||
|
||||
// Set up a listener that should fire only once
|
||||
eventSystem.once(ProxyEvents.CONNECTION_ESTABLISHED, (data) => {
|
||||
receivedEvents.push({
|
||||
type: 'once',
|
||||
data
|
||||
});
|
||||
});
|
||||
|
||||
// Set up a persistent listener
|
||||
const persistentHandler = (data: any) => {
|
||||
receivedEvents.push({
|
||||
type: 'persistent',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
eventSystem.on(ProxyEvents.CONNECTION_ESTABLISHED, persistentHandler);
|
||||
|
||||
// First event should trigger both listeners
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-1',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Second event should only trigger the persistent listener
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-2',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Unsubscribe the persistent listener
|
||||
eventSystem.off(ProxyEvents.CONNECTION_ESTABLISHED, persistentHandler);
|
||||
|
||||
// Third event should not trigger any listeners
|
||||
eventSystem.emitConnectionEstablished({
|
||||
connectionId: 'conn-3',
|
||||
clientIp: '192.168.1.1',
|
||||
port: 443
|
||||
});
|
||||
|
||||
// Verify events
|
||||
expect(receivedEvents.length).toEqual(3);
|
||||
expect(receivedEvents[0].type).toEqual('once');
|
||||
expect(receivedEvents[0].data.connectionId).toEqual('conn-1');
|
||||
|
||||
expect(receivedEvents[1].type).toEqual('persistent');
|
||||
expect(receivedEvents[1].data.connectionId).toEqual('conn-1');
|
||||
|
||||
expect(receivedEvents[2].type).toEqual('persistent');
|
||||
expect(receivedEvents[2].data.connectionId).toEqual('conn-2');
|
||||
});
|
||||
|
||||
export default tap.start();
|
@@ -1,116 +1,110 @@
|
||||
import { expect } from '@push.rocks/tapbundle';
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as routeUtils from '../../../ts/core/utils/route-utils.js';
|
||||
|
||||
// Test domain matching
|
||||
expect.describe('Route Utils - Domain Matching', async () => {
|
||||
expect.it('should match exact domains', async () => {
|
||||
expect(routeUtils.matchDomain('example.com', 'example.com')).to.be.true;
|
||||
});
|
||||
tap.test('Route Utils - Domain Matching - exact domains', async () => {
|
||||
expect(routeUtils.matchDomain('example.com', 'example.com')).toEqual(true);
|
||||
});
|
||||
|
||||
expect.it('should match wildcard domains', async () => {
|
||||
expect(routeUtils.matchDomain('*.example.com', 'sub.example.com')).to.be.true;
|
||||
expect(routeUtils.matchDomain('*.example.com', 'another.sub.example.com')).to.be.true;
|
||||
expect(routeUtils.matchDomain('*.example.com', 'example.com')).to.be.false;
|
||||
});
|
||||
tap.test('Route Utils - Domain Matching - wildcard domains', async () => {
|
||||
expect(routeUtils.matchDomain('*.example.com', 'sub.example.com')).toEqual(true);
|
||||
expect(routeUtils.matchDomain('*.example.com', 'another.sub.example.com')).toEqual(true);
|
||||
expect(routeUtils.matchDomain('*.example.com', 'example.com')).toEqual(false);
|
||||
});
|
||||
|
||||
expect.it('should match domains case-insensitively', async () => {
|
||||
expect(routeUtils.matchDomain('example.com', 'EXAMPLE.com')).to.be.true;
|
||||
});
|
||||
tap.test('Route Utils - Domain Matching - case insensitivity', async () => {
|
||||
expect(routeUtils.matchDomain('example.com', 'EXAMPLE.com')).toEqual(true);
|
||||
});
|
||||
|
||||
expect.it('should match routes with multiple domain patterns', async () => {
|
||||
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'example.com')).to.be.true;
|
||||
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'sub.test.com')).to.be.true;
|
||||
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'something.else')).to.be.false;
|
||||
});
|
||||
tap.test('Route Utils - Domain Matching - multiple domain patterns', async () => {
|
||||
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'example.com')).toEqual(true);
|
||||
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'sub.test.com')).toEqual(true);
|
||||
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'something.else')).toEqual(false);
|
||||
});
|
||||
|
||||
// Test path matching
|
||||
expect.describe('Route Utils - Path Matching', async () => {
|
||||
expect.it('should match exact paths', async () => {
|
||||
expect(routeUtils.matchPath('/api/users', '/api/users')).to.be.true;
|
||||
});
|
||||
tap.test('Route Utils - Path Matching - exact paths', async () => {
|
||||
expect(routeUtils.matchPath('/api/users', '/api/users')).toEqual(true);
|
||||
});
|
||||
|
||||
expect.it('should match wildcard paths', async () => {
|
||||
expect(routeUtils.matchPath('/api/*', '/api/users')).to.be.true;
|
||||
expect(routeUtils.matchPath('/api/*', '/api/products')).to.be.true;
|
||||
expect(routeUtils.matchPath('/api/*', '/something/else')).to.be.false;
|
||||
});
|
||||
tap.test('Route Utils - Path Matching - wildcard paths', async () => {
|
||||
expect(routeUtils.matchPath('/api/*', '/api/users')).toEqual(true);
|
||||
expect(routeUtils.matchPath('/api/*', '/api/products')).toEqual(true);
|
||||
expect(routeUtils.matchPath('/api/*', '/something/else')).toEqual(false);
|
||||
});
|
||||
|
||||
expect.it('should match complex wildcard patterns', async () => {
|
||||
expect(routeUtils.matchPath('/api/*/details', '/api/users/details')).to.be.true;
|
||||
expect(routeUtils.matchPath('/api/*/details', '/api/products/details')).to.be.true;
|
||||
expect(routeUtils.matchPath('/api/*/details', '/api/users/other')).to.be.false;
|
||||
});
|
||||
tap.test('Route Utils - Path Matching - complex wildcard patterns', async () => {
|
||||
expect(routeUtils.matchPath('/api/*/details', '/api/users/details')).toEqual(true);
|
||||
expect(routeUtils.matchPath('/api/*/details', '/api/products/details')).toEqual(true);
|
||||
expect(routeUtils.matchPath('/api/*/details', '/api/users/other')).toEqual(false);
|
||||
});
|
||||
|
||||
// Test IP matching
|
||||
expect.describe('Route Utils - IP Matching', async () => {
|
||||
expect.it('should match exact IPs', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.1', '192.168.1.1')).to.be.true;
|
||||
});
|
||||
tap.test('Route Utils - IP Matching - exact IPs', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.1', '192.168.1.1')).toEqual(true);
|
||||
});
|
||||
|
||||
expect.it('should match wildcard IPs', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.*', '192.168.1.100')).to.be.true;
|
||||
expect(routeUtils.matchIpPattern('192.168.1.*', '192.168.2.1')).to.be.false;
|
||||
});
|
||||
tap.test('Route Utils - IP Matching - wildcard IPs', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.*', '192.168.1.100')).toEqual(true);
|
||||
expect(routeUtils.matchIpPattern('192.168.1.*', '192.168.2.1')).toEqual(false);
|
||||
});
|
||||
|
||||
expect.it('should match CIDR notation', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.0/24', '192.168.1.100')).to.be.true;
|
||||
expect(routeUtils.matchIpPattern('192.168.1.0/24', '192.168.2.1')).to.be.false;
|
||||
});
|
||||
tap.test('Route Utils - IP Matching - CIDR notation', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.0/24', '192.168.1.100')).toEqual(true);
|
||||
expect(routeUtils.matchIpPattern('192.168.1.0/24', '192.168.2.1')).toEqual(false);
|
||||
});
|
||||
|
||||
expect.it('should handle IPv6-mapped IPv4 addresses', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.1', '::ffff:192.168.1.1')).to.be.true;
|
||||
});
|
||||
tap.test('Route Utils - IP Matching - IPv6-mapped IPv4 addresses', async () => {
|
||||
expect(routeUtils.matchIpPattern('192.168.1.1', '::ffff:192.168.1.1')).toEqual(true);
|
||||
});
|
||||
|
||||
expect.it('should correctly authorize IPs based on allow/block lists', async () => {
|
||||
// With allow and block lists
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', ['192.168.1.*'], ['192.168.1.5'])).to.be.true;
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.5', ['192.168.1.*'], ['192.168.1.5'])).to.be.false;
|
||||
|
||||
// With only allow list
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', ['192.168.1.*'])).to.be.true;
|
||||
expect(routeUtils.isIpAuthorized('192.168.2.1', ['192.168.1.*'])).to.be.false;
|
||||
|
||||
// With only block list
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.5', undefined, ['192.168.1.5'])).to.be.false;
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', undefined, ['192.168.1.5'])).to.be.true;
|
||||
|
||||
// With wildcard in allow list
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', ['*'], ['192.168.1.5'])).to.be.true;
|
||||
});
|
||||
tap.test('Route Utils - IP Matching - IP authorization with allow/block lists', async () => {
|
||||
// With allow and block lists
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', ['192.168.1.*'], ['192.168.1.5'])).toEqual(true);
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.5', ['192.168.1.*'], ['192.168.1.5'])).toEqual(false);
|
||||
|
||||
// With only allow list
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', ['192.168.1.*'])).toEqual(true);
|
||||
expect(routeUtils.isIpAuthorized('192.168.2.1', ['192.168.1.*'])).toEqual(false);
|
||||
|
||||
// With only block list
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.5', undefined, ['192.168.1.5'])).toEqual(false);
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', undefined, ['192.168.1.5'])).toEqual(true);
|
||||
|
||||
// With wildcard in allow list
|
||||
expect(routeUtils.isIpAuthorized('192.168.1.1', ['*'], ['192.168.1.5'])).toEqual(true);
|
||||
});
|
||||
|
||||
// Test route specificity calculation
|
||||
expect.describe('Route Utils - Route Specificity', async () => {
|
||||
expect.it('should calculate route specificity correctly', async () => {
|
||||
const basicRoute = { domains: 'example.com' };
|
||||
const pathRoute = { domains: 'example.com', path: '/api' };
|
||||
const wildcardPathRoute = { domains: 'example.com', path: '/api/*' };
|
||||
const headerRoute = { domains: 'example.com', headers: { 'content-type': 'application/json' } };
|
||||
const complexRoute = {
|
||||
domains: 'example.com',
|
||||
path: '/api',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
clientIp: ['192.168.1.1']
|
||||
};
|
||||
|
||||
// Path routes should have higher specificity than domain-only routes
|
||||
expect(routeUtils.calculateRouteSpecificity(pathRoute))
|
||||
.to.be.greaterThan(routeUtils.calculateRouteSpecificity(basicRoute));
|
||||
|
||||
// Exact path routes should have higher specificity than wildcard path routes
|
||||
expect(routeUtils.calculateRouteSpecificity(pathRoute))
|
||||
.to.be.greaterThan(routeUtils.calculateRouteSpecificity(wildcardPathRoute));
|
||||
|
||||
// Routes with headers should have higher specificity than routes without
|
||||
expect(routeUtils.calculateRouteSpecificity(headerRoute))
|
||||
.to.be.greaterThan(routeUtils.calculateRouteSpecificity(basicRoute));
|
||||
|
||||
// Complex routes should have the highest specificity
|
||||
expect(routeUtils.calculateRouteSpecificity(complexRoute))
|
||||
.to.be.greaterThan(routeUtils.calculateRouteSpecificity(pathRoute));
|
||||
expect(routeUtils.calculateRouteSpecificity(complexRoute))
|
||||
.to.be.greaterThan(routeUtils.calculateRouteSpecificity(headerRoute));
|
||||
});
|
||||
});
|
||||
tap.test('Route Utils - Route Specificity - calculating correctly', async () => {
|
||||
const basicRoute = { domains: 'example.com' };
|
||||
const pathRoute = { domains: 'example.com', path: '/api' };
|
||||
const wildcardPathRoute = { domains: 'example.com', path: '/api/*' };
|
||||
const headerRoute = { domains: 'example.com', headers: { 'content-type': 'application/json' } };
|
||||
const complexRoute = {
|
||||
domains: 'example.com',
|
||||
path: '/api',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
clientIp: ['192.168.1.1']
|
||||
};
|
||||
|
||||
// Path routes should have higher specificity than domain-only routes
|
||||
expect(routeUtils.calculateRouteSpecificity(pathRoute) >
|
||||
routeUtils.calculateRouteSpecificity(basicRoute)).toEqual(true);
|
||||
|
||||
// Exact path routes should have higher specificity than wildcard path routes
|
||||
expect(routeUtils.calculateRouteSpecificity(pathRoute) >
|
||||
routeUtils.calculateRouteSpecificity(wildcardPathRoute)).toEqual(true);
|
||||
|
||||
// Routes with headers should have higher specificity than routes without
|
||||
expect(routeUtils.calculateRouteSpecificity(headerRoute) >
|
||||
routeUtils.calculateRouteSpecificity(basicRoute)).toEqual(true);
|
||||
|
||||
// Complex routes should have the highest specificity
|
||||
expect(routeUtils.calculateRouteSpecificity(complexRoute) >
|
||||
routeUtils.calculateRouteSpecificity(pathRoute)).toEqual(true);
|
||||
expect(routeUtils.calculateRouteSpecificity(complexRoute) >
|
||||
routeUtils.calculateRouteSpecificity(headerRoute)).toEqual(true);
|
||||
});
|
||||
|
||||
export default tap.start();
|
@@ -1,24 +1,22 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as plugins from '../ts/plugins.js';
|
||||
import { NetworkProxy } from '../ts/proxies/network-proxy/index.js';
|
||||
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
|
||||
import type { IRouteContext } from '../ts/core/models/route-context.js';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as http2 from 'http2';
|
||||
|
||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
// Declare variables for tests
|
||||
let networkProxy: NetworkProxy;
|
||||
let testServer: http.Server;
|
||||
let testServerHttp2: http2.Http2Server;
|
||||
let testServer: plugins.http.Server;
|
||||
let testServerHttp2: plugins.http2.Http2Server;
|
||||
let serverPort: number;
|
||||
let serverPortHttp2: number;
|
||||
|
||||
// Setup test environment
|
||||
tap.test('setup NetworkProxy function-based targets test environment', async () => {
|
||||
// Create simple HTTP server to respond to requests
|
||||
testServer = http.createServer((req, res) => {
|
||||
testServer = plugins.http.createServer((req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({
|
||||
url: req.url,
|
||||
@@ -29,7 +27,7 @@ tap.test('setup NetworkProxy function-based targets test environment', async ()
|
||||
});
|
||||
|
||||
// Create simple HTTP/2 server to respond to requests
|
||||
testServerHttp2 = http2.createServer();
|
||||
testServerHttp2 = plugins.http2.createServer();
|
||||
testServerHttp2.on('stream', (stream, headers) => {
|
||||
stream.respond({
|
||||
'content-type': 'application/json',
|
||||
@@ -82,10 +80,10 @@ tap.test('should support static host/port routes', async () => {
|
||||
const routes: IRouteConfig[] = [
|
||||
{
|
||||
name: 'static-route',
|
||||
domain: 'example.com',
|
||||
priority: 100,
|
||||
match: {
|
||||
domain: 'example.com'
|
||||
domains: 'example.com',
|
||||
ports: 0
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
@@ -124,10 +122,10 @@ tap.test('should support function-based host', async () => {
|
||||
const routes: IRouteConfig[] = [
|
||||
{
|
||||
name: 'function-host-route',
|
||||
domain: 'function.example.com',
|
||||
priority: 100,
|
||||
match: {
|
||||
domain: 'function.example.com'
|
||||
domains: 'function.example.com',
|
||||
ports: 0
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
@@ -169,10 +167,10 @@ tap.test('should support function-based port', async () => {
|
||||
const routes: IRouteConfig[] = [
|
||||
{
|
||||
name: 'function-port-route',
|
||||
domain: 'function-port.example.com',
|
||||
priority: 100,
|
||||
match: {
|
||||
domain: 'function-port.example.com'
|
||||
domains: 'function-port.example.com',
|
||||
ports: 0
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
@@ -214,10 +212,10 @@ tap.test('should support function-based host AND port', async () => {
|
||||
const routes: IRouteConfig[] = [
|
||||
{
|
||||
name: 'function-both-route',
|
||||
domain: 'function-both.example.com',
|
||||
priority: 100,
|
||||
match: {
|
||||
domain: 'function-both.example.com'
|
||||
domains: 'function-both.example.com',
|
||||
ports: 0
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
@@ -260,10 +258,10 @@ tap.test('should support context-based routing with path', async () => {
|
||||
const routes: IRouteConfig[] = [
|
||||
{
|
||||
name: 'context-path-route',
|
||||
domain: 'context.example.com',
|
||||
priority: 100,
|
||||
match: {
|
||||
domain: 'context.example.com'
|
||||
domains: 'context.example.com',
|
||||
ports: 0
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
@@ -338,10 +336,10 @@ tap.test('cleanup NetworkProxy function-based targets test environment', async (
|
||||
});
|
||||
|
||||
// Helper function to make HTTPS requests with self-signed certificate support
|
||||
async function makeRequest(options: http.RequestOptions): Promise<{ statusCode: number, headers: http.IncomingHttpHeaders, body: string }> {
|
||||
async function makeRequest(options: plugins.http.RequestOptions): Promise<{ statusCode: number, headers: plugins.http.IncomingHttpHeaders, body: string }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Use HTTPS with rejectUnauthorized: false to accept self-signed certificates
|
||||
const req = https.request({
|
||||
const req = plugins.https.request({
|
||||
...options,
|
||||
rejectUnauthorized: false, // Accept self-signed certificates
|
||||
}, (res) => {
|
||||
|
Reference in New Issue
Block a user