23
23
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
25
25
*/
26
-
26
+
27
27
var sys = require ( 'sys' ) ,
28
28
http = require ( 'http' ) ,
29
29
events = require ( 'events' ) ,
30
30
pool = require ( 'pool' ) ,
31
- min = 0 ,
31
+ min = 0 ,
32
32
max = 100 ;
33
33
34
34
// Setup the PoolManager
@@ -42,24 +42,28 @@ exports.createServer = function () {
42
42
callback = typeof args [ args . length - 1 ] === 'function' && args . pop ( ) ;
43
43
if ( args [ 0 ] ) port = args [ 0 ] ;
44
44
if ( args [ 1 ] ) host = args [ 1 ] ;
45
-
45
+
46
46
var server = http . createServer ( function ( req , res ) {
47
47
var proxy = new HttpProxy ( req , res ) ;
48
-
48
+
49
49
proxy . emitter . on ( 'proxy' , function ( err , body ) {
50
50
server . emit ( 'proxy' , err , body ) ;
51
51
} ) ;
52
-
52
+
53
53
// If we were passed a callback to process the request
54
54
// or response in some way, then call it.
55
55
if ( callback ) {
56
56
callback ( req , res , proxy ) ;
57
57
}
58
- else {
58
+ else {
59
59
proxy . proxyRequest ( port , server ) ;
60
60
}
61
61
} ) ;
62
-
62
+
63
+ // WebSocket support
64
+ server . on ( 'update' , function ( ) {
65
+ } ) ;
66
+
63
67
return server ;
64
68
} ;
65
69
@@ -73,12 +77,21 @@ exports.setMax = function (value) {
73
77
manager . setMaxClients ( max ) ;
74
78
} ;
75
79
76
- var HttpProxy = function ( req , res ) {
80
+ var HttpProxy = function ( req , res , /* optional */ head ) {
77
81
this . emitter = new ( events . EventEmitter ) ;
78
82
this . events = { } ;
79
83
this . req = req ;
80
- this . res = res ;
81
- this . watch ( req ) ;
84
+ // If this request is upgrade request
85
+ // No response will be passed
86
+ if ( ! req . headers . upgrade ) {
87
+ this . res = res ;
88
+ this . watch ( req ) ;
89
+ } else {
90
+ // Second argument will be socket
91
+ this . sock = res ;
92
+ this . head = head ;
93
+ this . watch ( res ) ;
94
+ }
82
95
} ;
83
96
84
97
HttpProxy . prototype = {
@@ -90,7 +103,7 @@ HttpProxy.prototype = {
90
103
}
91
104
return arr ;
92
105
} ,
93
-
106
+
94
107
watch : function ( req ) {
95
108
this . events = [ ] ;
96
109
var self = this ;
@@ -105,11 +118,11 @@ HttpProxy.prototype = {
105
118
req . addListener ( 'data' , this . onData ) ;
106
119
req . addListener ( 'end' , this . onEnd ) ;
107
120
} ,
108
-
121
+
109
122
unwatch : function ( req ) {
110
123
req . removeListener ( 'data' , this . onData ) ;
111
124
req . removeListener ( 'end' , this . onEnd ) ;
112
-
125
+
113
126
// Rebroadcast any events that have been buffered
114
127
for ( var i = 0 , len = this . events . length ; i < len ; ++ i ) {
115
128
req . emit . apply ( req , this . events [ i ] ) ;
@@ -120,13 +133,13 @@ HttpProxy.prototype = {
120
133
// Remark: nodeProxy.body exists solely for testability
121
134
var self = this , req = this . req , res = this . res ;
122
135
self . body = '' ;
123
-
136
+
124
137
// Open new HTTP request to internal resource with will act as a reverse proxy pass
125
138
var p = manager . getPool ( port , server ) ;
126
-
139
+
127
140
p . on ( 'error' , function ( err ) {
128
141
// Remark: We should probably do something here
129
- // but this is a hot-fix because I don't think 'pool'
142
+ // but this is a hot-fix because I don't think 'pool'
130
143
// should be emitting this event.
131
144
} ) ;
132
145
@@ -141,7 +154,7 @@ HttpProxy.prototype = {
141
154
142
155
res . end ( ) ;
143
156
} ;
144
-
157
+
145
158
// Add a listener for the connection timeout event
146
159
reverse_proxy . addListener ( 'error' , error ) ;
147
160
@@ -155,6 +168,13 @@ HttpProxy.prototype = {
155
168
// Set the response headers of the client response
156
169
res . writeHead ( response . statusCode , response . headers ) ;
157
170
171
+ // Status code = 304
172
+ // No 'data' event and no 'end'
173
+ if ( response . statusCode === 304 ) {
174
+ res . end ( ) ;
175
+ return ;
176
+ }
177
+
158
178
// Add event handler for the proxied response in chunks
159
179
response . addListener ( 'data' , function ( chunk ) {
160
180
if ( req . method !== 'HEAD' ) {
@@ -184,6 +204,173 @@ HttpProxy.prototype = {
184
204
185
205
self . unwatch ( req ) ;
186
206
} ) ;
207
+ } ,
208
+
209
+ /**
210
+ * WebSocket Tunnel realization
211
+ * Copyright (c) 2010 Fedor Indutny : http://github.com/donnerjack13589
212
+ */
213
+ proxyWebSocketRequest : function ( port , server , host /* optional */ ) {
214
+ var self = this , update = self . update , req = self . req , socket = self . sock ,
215
+ head = self . head , headers = new _headers ( req . headers ) , CRLF = '\r\n' ,
216
+ listeners = { } ;
217
+
218
+ // Will generate clone of headers
219
+ // To not change original
220
+ function _headers ( headers ) {
221
+ var h = { } ;
222
+ for ( var i in headers ) {
223
+ h [ i ] = headers [ i ] ;
224
+ }
225
+ return h ;
226
+ }
227
+
228
+ // WebSocket requests has
229
+ // method = GET
230
+ if ( req . method !== 'GET' || headers . upgrade . toLowerCase ( ) !== 'websocket' ) {
231
+ // This request is not WebSocket request
232
+ return ;
233
+ }
234
+
235
+ // Turn of all bufferings
236
+ // For server set KeepAlive
237
+ // For client set encoding
238
+ function _socket ( socket , server ) {
239
+ socket . setTimeout ( 0 ) ;
240
+ socket . setNoDelay ( true ) ;
241
+ if ( server ) {
242
+ socket . setKeepAlive ( true , 0 ) ;
243
+ } else {
244
+ socket . setEncoding ( 'utf8' ) ;
245
+ }
246
+ }
247
+
248
+ // Client socket
249
+ _socket ( socket ) ;
250
+
251
+ // If host is undefined
252
+ // Get it from headers
253
+ if ( ! host ) {
254
+ host = headers . Host ;
255
+ }
256
+ // Remote host address
257
+ var remote_host = server + ( port - 80 === 0 ? '' : ':' + port ) ;
258
+
259
+ // Change headers
260
+ headers . Host = remote_host ;
261
+ headers . Origin = 'http://' + remote_host ;
262
+
263
+ // Open request
264
+ var p = manager . getPool ( port , server ) ;
265
+
266
+ p . getClient ( function ( client ) {
267
+ // Based on 'pool/main.js'
268
+ var request = client . request ( 'GET' , req . url , headers ) ;
269
+
270
+ var errorListener = function ( error ) {
271
+ p . emit ( 'error' , error ) ;
272
+ request . emit ( 'error' , error ) ;
273
+ socket . end ( ) ;
274
+ }
275
+
276
+ // Not disconnect on update
277
+ client . on ( 'upgrade' , function ( request , remote_socket , head ) {
278
+ // Prepare socket
279
+ _socket ( remote_socket , true ) ;
280
+
281
+ // Emit event
282
+ onUpgrade ( remote_socket ) ;
283
+ } ) ;
284
+
285
+ client . on ( 'error' , errorListener ) ;
286
+ request . on ( 'response' , function ( response ) {
287
+ response . on ( 'end' , function ( ) {
288
+ client . removeListener ( 'error' , errorListener ) ;
289
+ client . busy = false ;
290
+ p . onFree ( client ) ;
291
+ } )
292
+ } )
293
+ client . busy = true ;
294
+
295
+ var t ;
296
+ request . socket . on ( 'data' , t = function ( data ) {
297
+ // Handshaking
298
+
299
+ // Ok, kind of harmfull part of code
300
+ // Socket.IO is sending hash at the end of handshake
301
+ // If protocol = 76
302
+ // But we need to replace 'host' and 'origin' in response
303
+ // So we split data to printable data and to non-printable
304
+ // (Non-printable will come after double-CRLF)
305
+ var sdata = data . toString ( ) ;
306
+
307
+ // Get Printable
308
+ sdata = sdata
309
+ . substr ( 0 , sdata . search ( CRLF + CRLF ) ) ;
310
+
311
+ // Get Non-Printable
312
+ data = data . slice ( Buffer . byteLength ( sdata ) , data . length ) ;
313
+
314
+ // Replace host and origin
315
+ sdata = sdata
316
+ . replace ( remote_host , host )
317
+ . replace ( remote_host , host ) ;
318
+
319
+ // Write printable
320
+ socket . write ( sdata ) ;
321
+
322
+ // Write non-printable
323
+ socket . write ( data ) ;
324
+
325
+ // Remove data listener
326
+ request . socket . removeListener ( 'data' , t ) ;
327
+ } ) ;
328
+
329
+ // Write upgrade-head
330
+ request . write ( head ) ;
331
+ self . unwatch ( socket ) ;
332
+ } ) ;
333
+
334
+ // Request
335
+
336
+ function onUpgrade ( reverse_proxy ) {
337
+ // We're now connected to the server
338
+ // So lets change server socket
339
+
340
+ reverse_proxy . on ( 'data' , listeners . _r_data = function ( data ) {
341
+ // Pass data to client
342
+ if ( socket . writable ) {
343
+ socket . write ( data ) ;
344
+ }
345
+ } ) ;
346
+
347
+ socket . on ( 'data' , listeners . _data = function ( data ) {
348
+ // Pass data from client to server
349
+ // Socket thougth that it isn't writable
350
+ reverse_proxy . write ( data ) ;
351
+ } ) ;
352
+
353
+ // Detach event listeners from reverse_proxy
354
+ function detach ( ) {
355
+ reverse_proxy . removeListener ( 'close' , listeners . _r_close ) ;
356
+ reverse_proxy . removeListener ( 'data' , listeners . _r_data ) ;
357
+ socket . removeListener ( 'data' , listeners . _data ) ;
358
+ socket . removeListener ( 'close' , listeners . _close ) ;
359
+ }
360
+
361
+ // Hook disconnections
362
+ reverse_proxy . on ( 'close' , listeners . _r_close = function ( ) {
363
+ socket . end ( ) ;
364
+ detach ( ) ;
365
+ } ) ;
366
+
367
+ socket . on ( 'close' , listeners . _close = function ( ) {
368
+ reverse_proxy . end ( ) ;
369
+ detach ( ) ;
370
+ } ) ;
371
+
372
+ } ;
373
+
187
374
}
188
375
} ;
189
376
0 commit comments