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((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((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();