162 lines
4.8 KiB
TypeScript
162 lines
4.8 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { SmartProxy, SocketHandlers } from '../ts/index.js';
|
|
import * as net from 'net';
|
|
|
|
// Test that HTTP-01 challenges are properly processed when the initial data arrives
|
|
tap.test('should correctly handle HTTP-01 challenge requests with initial data chunk', async (tapTest) => {
|
|
// Prepare test data
|
|
const challengeToken = 'test-acme-http01-challenge-token';
|
|
const challengeResponse = 'mock-response-for-challenge';
|
|
const challengePath = `/.well-known/acme-challenge/${challengeToken}`;
|
|
|
|
// Create a socket handler that responds to ACME challenges using httpServer
|
|
const acmeHandler = SocketHandlers.httpServer((req, res) => {
|
|
// Log request details for debugging
|
|
console.log(`Received request: ${req.method} ${req.url}`);
|
|
|
|
// Check if this is an ACME challenge request
|
|
if (req.url?.startsWith('/.well-known/acme-challenge/')) {
|
|
const token = req.url.substring('/.well-known/acme-challenge/'.length);
|
|
|
|
// If the token matches our test token, return the response
|
|
if (token === challengeToken) {
|
|
res.header('Content-Type', 'text/plain');
|
|
res.send(challengeResponse);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// For any other requests, return 404
|
|
res.status(404);
|
|
res.header('Content-Type', 'text/plain');
|
|
res.send('Not found');
|
|
});
|
|
|
|
// Create a proxy with the ACME challenge route
|
|
const proxy = new SmartProxy({
|
|
routes: [{
|
|
name: 'acme-challenge-route',
|
|
match: {
|
|
ports: 8080,
|
|
path: '/.well-known/acme-challenge/*'
|
|
},
|
|
action: {
|
|
type: 'socket-handler',
|
|
socketHandler: acmeHandler
|
|
}
|
|
}]
|
|
});
|
|
|
|
await proxy.start();
|
|
|
|
// Create a client to test the HTTP-01 challenge
|
|
const testClient = new net.Socket();
|
|
let responseData = '';
|
|
|
|
// Set up client handlers
|
|
testClient.on('data', (data) => {
|
|
responseData += data.toString();
|
|
});
|
|
|
|
// Connect to the proxy and send the HTTP-01 challenge request
|
|
await new Promise<void>((resolve, reject) => {
|
|
testClient.connect(8080, 'localhost', () => {
|
|
// Send HTTP request for the challenge token
|
|
testClient.write(
|
|
`GET ${challengePath} HTTP/1.1\r\n` +
|
|
'Host: test.example.com\r\n' +
|
|
'User-Agent: ACME Challenge Test\r\n' +
|
|
'Accept: */*\r\n' +
|
|
'\r\n'
|
|
);
|
|
resolve();
|
|
});
|
|
|
|
testClient.on('error', reject);
|
|
});
|
|
|
|
// Wait for the response
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Verify that we received a valid HTTP response with the challenge token
|
|
expect(responseData).toContain('HTTP/1.1 200');
|
|
expect(responseData).toContain('Content-Type: text/plain');
|
|
expect(responseData).toContain(challengeResponse);
|
|
|
|
// Cleanup
|
|
testClient.destroy();
|
|
await proxy.stop();
|
|
});
|
|
|
|
// Test that non-existent challenge tokens return 404
|
|
tap.test('should return 404 for non-existent challenge tokens', async (tapTest) => {
|
|
// Create a socket handler that behaves like a real ACME handler
|
|
const acmeHandler = SocketHandlers.httpServer((req, res) => {
|
|
if (req.url?.startsWith('/.well-known/acme-challenge/')) {
|
|
const token = req.url.substring('/.well-known/acme-challenge/'.length);
|
|
// In this test, we only recognize one specific token
|
|
if (token === 'valid-token') {
|
|
res.header('Content-Type', 'text/plain');
|
|
res.send('valid-response');
|
|
return;
|
|
}
|
|
}
|
|
|
|
// For all other paths or unrecognized tokens, return 404
|
|
res.status(404);
|
|
res.header('Content-Type', 'text/plain');
|
|
res.send('Not found');
|
|
});
|
|
|
|
// Create a proxy with the ACME challenge route
|
|
const proxy = new SmartProxy({
|
|
routes: [{
|
|
name: 'acme-challenge-route',
|
|
match: {
|
|
ports: 8081,
|
|
path: '/.well-known/acme-challenge/*'
|
|
},
|
|
action: {
|
|
type: 'socket-handler',
|
|
socketHandler: acmeHandler
|
|
}
|
|
}]
|
|
});
|
|
|
|
await proxy.start();
|
|
|
|
// Create a client to test the invalid challenge request
|
|
const testClient = new net.Socket();
|
|
let responseData = '';
|
|
|
|
testClient.on('data', (data) => {
|
|
responseData += data.toString();
|
|
});
|
|
|
|
// Connect and send a request for a non-existent token
|
|
await new Promise<void>((resolve, reject) => {
|
|
testClient.connect(8081, 'localhost', () => {
|
|
testClient.write(
|
|
'GET /.well-known/acme-challenge/invalid-token HTTP/1.1\r\n' +
|
|
'Host: test.example.com\r\n' +
|
|
'\r\n'
|
|
);
|
|
resolve();
|
|
});
|
|
|
|
testClient.on('error', reject);
|
|
});
|
|
|
|
// Wait for the response
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Verify we got a 404 Not Found
|
|
expect(responseData).toContain('HTTP/1.1 404');
|
|
expect(responseData).toContain('Not found');
|
|
|
|
// Cleanup
|
|
testClient.destroy();
|
|
await proxy.stop();
|
|
});
|
|
|
|
tap.start(); |