@@ -10,6 +10,7 @@ const httpMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
1010const urlPattern = / ^ h t t p s ? : \/ \/ /
1111const kWs = Symbol ( 'ws' )
1212const kWsHead = Symbol ( 'wsHead' )
13+ const kWsUpgradeListener = Symbol ( 'wsUpgradeListener' )
1314
1415function liftErrorCode ( code ) {
1516 /* istanbul ignore next */
@@ -74,32 +75,46 @@ function proxyWebSockets (source, target) {
7475 target . on ( 'unexpected-response' , ( ) => close ( 1011 , 'unexpected response' ) )
7576}
7677
78+ function handleUpgrade ( fastify , rawRequest , socket , head ) {
79+ // Save a reference to the socket and then dispatch the request through the normal fastify router so that it will invoke hooks and then eventually a route handler that might upgrade the socket.
80+ rawRequest [ kWs ] = socket
81+ rawRequest [ kWsHead ] = head
82+
83+ const rawResponse = new ServerResponse ( rawRequest )
84+ rawResponse . assignSocket ( socket )
85+ fastify . routing ( rawRequest , rawResponse )
86+
87+ rawResponse . on ( 'finish' , ( ) => {
88+ socket . destroy ( )
89+ } )
90+ }
91+
7792class WebSocketProxy {
78- constructor ( fastify , wsServerOptions ) {
93+ constructor ( fastify , { wsServerOptions, wsClientOptions , upstream , wsUpstream , replyOptions : { getUpstream } = { } } ) {
7994 this . logger = fastify . log
95+ this . wsClientOptions = {
96+ rewriteRequestHeaders : defaultWsHeadersRewrite ,
97+ headers : { } ,
98+ ...wsClientOptions
99+ }
100+ this . upstream = convertUrlToWebSocket ( upstream )
101+ this . wsUpstream = wsUpstream ? convertUrlToWebSocket ( wsUpstream ) : ''
102+ this . getUpstream = getUpstream
80103
81104 const wss = new WebSocket . Server ( {
82105 noServer : true ,
83106 ...wsServerOptions
84107 } )
85108
86- fastify . server . on ( 'upgrade' , ( rawRequest , socket , head ) => {
87- // Save a reference to the socket and then dispatch the request through the normal fastify router so that it will invoke hooks and then eventually a route handler that might upgrade the socket.
88- rawRequest [ kWs ] = socket
89- rawRequest [ kWsHead ] = head
90-
91- const rawResponse = new ServerResponse ( rawRequest )
92- rawResponse . assignSocket ( socket )
93- fastify . routing ( rawRequest , rawResponse )
94-
95- rawResponse . on ( 'finish' , ( ) => {
96- socket . destroy ( )
97- } )
98- } )
109+ if ( ! fastify . server [ kWsUpgradeListener ] ) {
110+ fastify . server [ kWsUpgradeListener ] = ( rawRequest , socket , head ) =>
111+ handleUpgrade ( fastify , rawRequest , socket , head )
112+ fastify . server . on ( 'upgrade' , fastify . server [ kWsUpgradeListener ] )
113+ }
99114
100- this . handleUpgrade = ( request , cb ) => {
115+ this . handleUpgrade = ( request , dest , cb ) => {
101116 wss . handleUpgrade ( request . raw , request . raw [ kWs ] , request . raw [ kWsHead ] , ( socket ) => {
102- this . handleConnection ( socket , request )
117+ this . handleConnection ( socket , request , dest )
103118 cb ( )
104119 } )
105120 }
@@ -134,53 +149,41 @@ class WebSocketProxy {
134149 this . prefixList = [ ]
135150 }
136151
137- addUpstream ( prefix , rewritePrefix , upstream , wsUpstream , wsClientOptions ) {
138- this . prefixList . push ( {
139- prefix : new URL ( prefix , 'ws://127.0.0.1' ) . pathname ,
140- rewritePrefix,
141- upstream : convertUrlToWebSocket ( upstream ) ,
142- wsUpstream : wsUpstream ? convertUrlToWebSocket ( wsUpstream ) : '' ,
143- wsClientOptions
144- } )
152+ findUpstream ( request , dest ) {
153+ const search = new URL ( request . url , 'ws://127.0.0.1' ) . search
145154
146- // sort by decreasing prefix length, so that findUpstreamUrl() does longest prefix match
147- this . prefixList . sort ( ( a , b ) => b . prefix . length - a . prefix . length )
148- }
149-
150- findUpstream ( request ) {
151- const source = new URL ( request . url , 'ws://127.0.0.1' )
152-
153- for ( const { prefix, rewritePrefix, upstream, wsUpstream, wsClientOptions } of this . prefixList ) {
154- if ( wsUpstream ) {
155- const target = new URL ( wsUpstream )
156- target . search = source . search
157- return { target, wsClientOptions }
158- }
155+ if ( typeof this . wsUpstream === 'string' && this . wsUpstream !== '' ) {
156+ const target = new URL ( this . wsUpstream )
157+ target . search = search
158+ return target
159+ }
159160
160- if ( source . pathname . startsWith ( prefix ) ) {
161- const target = new URL ( source . pathname . replace ( prefix , rewritePrefix ) , upstream )
162- target . search = source . search
163- return { target, wsClientOptions }
164- }
161+ if ( typeof this . upstream === 'string' && this . upstream !== '' ) {
162+ const target = new URL ( dest , this . upstream )
163+ target . search = search
164+ return target
165165 }
166166
167+ const upstream = this . getUpstream ( request , '' )
168+ const target = new URL ( dest , upstream )
167169 /* istanbul ignore next */
168- throw new Error ( `no upstream found for ${ request . url } . this should not happened. Please report to https://github.com/fastify/fastify-http-proxy` )
170+ target . protocol = upstream . indexOf ( 'http:' ) === 0 ? 'ws:' : 'wss'
171+ target . search = search
172+ return target
169173 }
170174
171- handleConnection ( source , request ) {
172- const upstream = this . findUpstream ( request )
173- const { target : url , wsClientOptions } = upstream
174- const rewriteRequestHeaders = wsClientOptions ?. rewriteRequestHeaders || defaultWsHeadersRewrite
175- const headersToRewrite = wsClientOptions ?. headers || { }
175+ handleConnection ( source , request , dest ) {
176+ const url = this . findUpstream ( request , dest )
177+ const rewriteRequestHeaders = this . wsClientOptions . rewriteRequestHeaders
178+ const headersToRewrite = this . wsClientOptions . headers
176179
177180 const subprotocols = [ ]
178181 if ( source . protocol ) {
179182 subprotocols . push ( source . protocol )
180183 }
181184
182185 const headers = rewriteRequestHeaders ( headersToRewrite , request )
183- const optionsWs = { ...( wsClientOptions || { } ) , headers }
186+ const optionsWs = { ...this . wsClientOptions , headers }
184187
185188 const target = new WebSocket ( url , subprotocols , optionsWs )
186189 this . logger . debug ( { url : url . href } , 'proxy websocket' )
@@ -195,41 +198,6 @@ function defaultWsHeadersRewrite (headers, request) {
195198 return { ...headers }
196199}
197200
198- const httpWss = new WeakMap ( ) // http.Server => WebSocketProxy
199-
200- function setupWebSocketProxy ( fastify , options , rewritePrefix ) {
201- let wsProxy = httpWss . get ( fastify . server )
202- if ( ! wsProxy ) {
203- wsProxy = new WebSocketProxy ( fastify , options . wsServerOptions )
204- httpWss . set ( fastify . server , wsProxy )
205- }
206-
207- if (
208- ( typeof options . wsUpstream === 'string' && options . wsUpstream !== '' ) ||
209- ( typeof options . upstream === 'string' && options . upstream !== '' )
210- ) {
211- wsProxy . addUpstream (
212- fastify . prefix ,
213- rewritePrefix ,
214- options . upstream ,
215- options . wsUpstream ,
216- options . wsClientOptions
217- )
218- // The else block is validate earlier in the code
219- } else {
220- wsProxy . findUpstream = function ( request ) {
221- const source = new URL ( request . url , 'ws://127.0.0.1' )
222- const upstream = options . replyOptions . getUpstream ( request , '' )
223- const target = new URL ( source . pathname , upstream )
224- /* istanbul ignore next */
225- target . protocol = upstream . indexOf ( 'http:' ) === 0 ? 'ws:' : 'wss'
226- target . search = source . search
227- return { target, wsClientOptions : options . wsClientOptions }
228- }
229- }
230- return wsProxy
231- }
232-
233201function generateRewritePrefix ( prefix , opts ) {
234202 let rewritePrefix = opts . rewritePrefix || ( opts . upstream ? new URL ( opts . upstream ) . pathname : '/' )
235203
@@ -303,7 +271,7 @@ async function fastifyHttpProxy (fastify, opts) {
303271 let wsProxy
304272
305273 if ( opts . websocket ) {
306- wsProxy = setupWebSocketProxy ( fastify , opts , rewritePrefix )
274+ wsProxy = new WebSocketProxy ( fastify , opts )
307275 }
308276
309277 function extractUrlComponents ( urlString ) {
@@ -321,16 +289,6 @@ async function fastifyHttpProxy (fastify, opts) {
321289 }
322290
323291 function handler ( request , reply ) {
324- if ( request . raw [ kWs ] ) {
325- reply . hijack ( )
326- try {
327- wsProxy . handleUpgrade ( request , noop )
328- } catch ( err ) {
329- /* istanbul ignore next */
330- request . log . warn ( { err } , 'websocket proxy error' )
331- }
332- return
333- }
334292 const { path, queryParams } = extractUrlComponents ( request . url )
335293 let dest = path
336294
@@ -350,6 +308,17 @@ async function fastifyHttpProxy (fastify, opts) {
350308 } else {
351309 dest = dest . replace ( this . prefix , rewritePrefix )
352310 }
311+
312+ if ( request . raw [ kWs ] ) {
313+ reply . hijack ( )
314+ try {
315+ wsProxy . handleUpgrade ( request , dest || '/' , noop )
316+ } catch ( err ) {
317+ /* istanbul ignore next */
318+ request . log . warn ( { err } , 'websocket proxy error' )
319+ }
320+ return
321+ }
353322 reply . from ( dest || '/' , replyOpts )
354323 }
355324}
0 commit comments