1+ const test = require('tap').test
2+ const path = require('path')
3+ const http = require('http')
4+ const httpServer = require('../lib/http-server')
5+ const WebSocket = require('ws')
6+
7+ // Prevent errors from being swallowed
8+ process.on('uncaughtException', console.error)
9+
10+ test('websocket proxy functionality', (t) => {
11+ new Promise((resolve) => {
12+ // Create a target server that will handle websocket connections
13+ const targetServer = http.createServer()
14+ const targetWss = new WebSocket.Server({ server: targetServer })
15+
16+ targetWss.on('connection', (ws) => {
17+ ws.on('message', (message) => {
18+ // Echo the message back
19+ ws.send(`Echo: ${message}`)
20+ })
21+ })
22+
23+ targetServer.listen(0, () => {
24+ const targetPort = targetServer.address().port
25+ const targetUrl = `http://localhost:${targetPort}`
26+
27+ // Create http-server with websocket proxy enabled
28+ const proxyServer = httpServer.createServer({
29+ proxy: targetUrl,
30+ websocket: true,
31+ root: path.join(__dirname, 'fixtures')
32+ })
33+
34+ proxyServer.listen(0, async () => {
35+ const proxyPort = proxyServer.server.address().port
36+ const proxyUrl = `http://localhost:${proxyPort}`
37+
38+ try {
39+ // Test 1: Verify websocket proxy is enabled when both proxy and websocket options are set
40+ t.ok(proxyServer.server.listeners('upgrade').length > 0, 'upgrade event listener should be registered')
41+
42+ // Test 2: Test websocket connection through proxy
43+ await new Promise((resolve, reject) => {
44+ const ws = new WebSocket(`ws://localhost:${proxyPort}`)
45+
46+ ws.on('open', () => {
47+ t.pass('websocket connection should be established through proxy')
48+
49+ // Send a test message
50+ ws.send('Hello WebSocket!')
51+ })
52+
53+ ws.on('message', (data) => {
54+ t.equal(data.toString(), 'Echo: Hello WebSocket!', 'should receive echoed message')
55+ ws.close()
56+ })
57+
58+ ws.on('close', () => {
59+ t.pass('websocket connection should close properly')
60+ resolve()
61+ })
62+
63+ ws.on('error', (err) => {
64+ t.fail(`websocket error: ${err.message}`)
65+ reject(err)
66+ })
67+
68+ // Set timeout to prevent hanging
69+ setTimeout(() => {
70+ ws.close()
71+ reject(new Error('WebSocket test timeout'))
72+ }, 5000)
73+ })
74+
75+ } catch (err) {
76+ t.fail(`websocket proxy test failed: ${err.message}`)
77+ } finally {
78+ proxyServer.close()
79+ targetServer.close()
80+ resolve()
81+ }
82+ })
83+ })
84+ })
85+ .then(() => t.end())
86+ .catch(err => {
87+ t.fail(err.toString())
88+ t.end()
89+ })
90+ })
91+
92+ test('websocket proxy without proxy configuration', (t) => {
93+ new Promise((resolve) => {
94+ // Create http-server with websocket enabled but no proxy
95+ const server = httpServer.createServer({
96+ websocket: true,
97+ root: path.join(__dirname, 'fixtures')
98+ })
99+
100+ server.listen(0, () => {
101+ try {
102+ // Test: Verify no upgrade event listener is registered when proxy is not set
103+ t.equal(server.server.listeners('upgrade').length, 0, 'no upgrade event listener should be registered when proxy is not set')
104+ t.pass('websocket option should be ignored when proxy is not configured')
105+ } catch (err) {
106+ t.fail(`test failed: ${err.message}`)
107+ } finally {
108+ server.close()
109+ resolve()
110+ }
111+ })
112+ })
113+ .then(() => t.end())
114+ .catch(err => {
115+ t.fail(err.toString())
116+ t.end()
117+ })
118+ })
119+
120+ test('ensure websocket proxy is not enabled when \'websocket\' is not set', (t) => {
121+ new Promise((resolve) => {
122+ // Create a target server that will handle websocket connections
123+ const targetServer = http.createServer()
124+ const targetWss = new WebSocket.Server({ server: targetServer })
125+
126+ targetWss.on('connection', (ws) => {
127+ ws.on('message', (message) => {
128+ // Echo the message back
129+ ws.send(`Echo: ${message}`)
130+ })
131+ })
132+
133+ targetServer.listen(0, () => {
134+ const targetPort = targetServer.address().port
135+ const targetUrl = `http://localhost:${targetPort}`
136+
137+ const proxyServer = httpServer.createServer({
138+ proxy: targetUrl,
139+ root: path.join(__dirname, 'fixtures')
140+ })
141+ try {
142+ t.equal(proxyServer.server.listeners('upgrade').length, 0, 'no upgrade event listener should be registered when websocket is not set')
143+ } catch (err) {
144+ t.fail(`test failed: ${err.message}`)
145+ } finally {
146+ proxyServer.close()
147+ targetServer.close()
148+ resolve()
149+ }
150+ })
151+ })
152+ .then(() => t.end())
153+ .catch(err => {
154+ t.fail(err.toString())
155+ t.end()
156+ })
157+ });
158+
159+ test('websocket proxy error handling', (t) => {
160+ new Promise((resolve) => {
161+ // Create http-server with invalid proxy target
162+ const proxyServer = httpServer.createServer({
163+ proxy: 'http://localhost:99999', // Invalid port
164+ websocket: true,
165+ root: path.join(__dirname, 'fixtures')
166+ })
167+
168+ proxyServer.listen(0, async () => {
169+ const proxyPort = proxyServer.server.address().port
170+
171+ try {
172+ // Test: Verify websocket proxy handles connection errors gracefully
173+ t.ok(proxyServer.server.listeners('upgrade').length > 0, 'upgrade event listener should be registered even with invalid proxy')
174+
175+ // Test websocket connection to invalid proxy target
176+ await new Promise((resolve, reject) => {
177+ const ws = new WebSocket(`ws://localhost:${proxyPort}`)
178+
179+ ws.on('open', () => {
180+ t.fail('websocket should not connect to invalid proxy target')
181+ ws.close()
182+ resolve()
183+ })
184+
185+ ws.on('error', (err) => {
186+ t.pass('websocket should error when proxy target is invalid')
187+ resolve() // This is expected
188+ })
189+
190+ ws.on('close', () => {
191+ t.pass('websocket should close on error')
192+ resolve()
193+ })
194+
195+ setTimeout(() => {
196+ ws.close()
197+ resolve() // Timeout is acceptable for this test
198+ }, 2000)
199+ })
200+
201+ } catch (err) {
202+ t.fail(`websocket proxy error handling test failed: ${err.message}`)
203+ } finally {
204+ proxyServer.close()
205+ resolve()
206+ }
207+ })
208+ })
209+ .then(() => t.end())
210+ .catch(err => {
211+ t.fail(err.toString())
212+ t.end()
213+ })
214+ })
0 commit comments