1
1
/*
2
- node-http-proxy.js: http proxy for node.js
2
+ node-http-proxy.js: http proxy for node.js with pooling and event buffering
3
3
4
- Copyright (c) 2010 Charlie Robbins & Marak Squires http://github.com/nodejitsu/node-http-proxy
4
+ Copyright (c) 2010 Mikeal Rogers, Charlie Robbins
5
5
6
6
Permission is hereby granted, free of charge, to any person obtaining
7
7
a copy of this software and associated documentation files (the
24
24
25
25
*/
26
26
27
- var sys = require ( 'sys' ) ,
28
- http = require ( 'http' ) ,
29
- events = require ( 'events' ) ;
30
-
31
- exports . HttpProxy = function ( ) {
32
- this . emitter = new ( events . EventEmitter ) ;
33
- this . events = { } ;
34
- this . listeners = { } ;
35
- this . collisions = { } ;
36
- } ;
27
+ var sys = require ( 'sys' ) ,
28
+ http = require ( 'http' ) ,
29
+ pool = require ( 'pool' ) ,
30
+ url = require ( 'url' ) ,
31
+ events = require ( 'events' ) ,
32
+ min = 0 ,
33
+ max = 100 ;
34
+
35
+ // Setup the PoolManager
36
+ var manager = pool . createPoolManager ( ) ;
37
+ manager . setMinClients ( min ) ;
38
+ manager . setMaxClients ( max ) ;
37
39
38
40
exports . createServer = function ( ) {
39
- // Initialize the nodeProxy to start proxying requests
40
- var proxy = new ( exports . HttpProxy ) ;
41
- return proxy . createServer . apply ( proxy , arguments ) ;
42
- } ;
43
-
44
- exports . HttpProxy . prototype = {
45
- toArray : function ( obj ) {
46
- var len = obj . length ,
47
- arr = new Array ( len ) ;
48
- for ( var i = 0 ; i < len ; ++ i ) {
49
- arr [ i ] = obj [ i ] ;
50
- }
51
- return arr ;
52
- } ,
41
+ var args , action , port , host ;
42
+ args = Array . prototype . slice . call ( arguments ) ;
43
+ action = typeof args [ args . length - 1 ] === 'function' && args . pop ( ) ;
44
+ if ( args [ 0 ] ) port = args [ 0 ] ;
45
+ if ( args [ 1 ] ) host = args [ 1 ] ;
53
46
54
- createServer : function ( ) {
55
- var self = this ,
56
- server ,
57
- port ,
58
- callback ;
59
-
60
- if ( typeof ( arguments [ 0 ] ) === "function" ) {
61
- callback = arguments [ 0 ] ;
62
- }
47
+ var proxy = createProxy ( ) ;
48
+ proxy . on ( 'route' , function ( req , res , callback ) {
49
+ var uri = url . parse ( req . url ) ;
50
+ if ( action ) {
51
+ action ( req , res , callback ) ;
52
+ }
63
53
else {
64
- port = arguments [ 0 ] ;
65
- server = arguments [ 1 ] ;
54
+ port = port ? port : uri . port ? uri . port : 80 ;
55
+ host = host ? host : uri . hostname ;
56
+ callback ( port , host ) ;
66
57
}
67
-
68
- var proxyServer = http . createServer ( function ( req , res ) {
69
- self . watch ( req , res ) ;
70
-
71
- // If we were passed a callback to process the request
72
- // or response in some way, then call it.
73
- if ( callback ) {
74
- callback ( req , res , self ) ;
75
- }
76
- else {
77
- self . proxyRequest ( port , server , req , res ) ;
78
- }
79
- } ) ;
80
-
81
- return proxyServer ;
82
- } ,
83
-
84
- watch : function ( req , res ) {
85
- var self = this ;
86
-
87
- // Create a unique id for this request so
88
- // we can reference it later.
89
- var id = new Date ( ) . getTime ( ) . toString ( ) ;
90
-
91
- // If we get a request in the same tick, we need to
92
- // append to the id so it stays unique.
93
- if ( typeof this . collisions [ id ] === 'undefined' ) {
94
- this . collisions [ id ] = 0 ;
95
- }
96
- else {
97
- this . collisions [ id ] ++ ;
98
- id += this . collisions [ id ] ;
99
- }
100
-
101
- req . id = id ;
102
- this . events [ req . id ] = [ ] ;
103
-
104
- this . listeners [ req . id ] = {
105
- onData : function ( ) {
106
- self . events [ req . id ] . push ( [ 'data' ] . concat ( self . toArray ( arguments ) ) ) ;
107
- } ,
108
- onEnd : function ( ) {
109
- self . events [ req . id ] . push ( [ 'end' ] . concat ( self . toArray ( arguments ) ) ) ;
110
- }
111
- } ;
112
-
113
- req . addListener ( 'data' , this . listeners [ req . id ] . onData ) ;
114
- req . addListener ( 'end' , this . listeners [ req . id ] . onEnd ) ;
115
-
116
- } ,
117
-
118
- unwatch : function ( req , res ) {
119
- req . removeListener ( 'data' , this . listeners [ req . id ] . onData ) ;
120
- req . removeListener ( 'end' , this . listeners [ req . id ] . onEnd ) ;
121
-
122
- // Rebroadcast any events that have been buffered
123
- while ( this . events [ req . id ] . length > 0 ) {
124
- var args = this . events [ req . id ] . shift ( ) ;
125
- req . emit . apply ( req , args ) ;
126
- }
127
-
128
- // Remove the data from the event and listeners hashes
129
- delete this . listeners [ req . id ] ;
130
- delete this . events [ req . id ] ;
131
-
132
- // If this request id is a base time, delete it
133
- if ( typeof this . collisions [ req . id ] !== 'undefined' ) {
134
- delete this . collisions [ req . id ] ;
135
- }
136
- } ,
137
-
138
- proxyRequest : function ( port , server , req , res ) {
139
- // Remark: nodeProxy.body exists solely for testability
140
- this . body = '' ;
141
- var self = this ;
142
-
143
- // Open new HTTP request to internal resource with will act as a reverse proxy pass
144
- var c = http . createClient ( port , server ) ;
145
-
146
- // Make request to internal server, passing along the method and headers
147
- var reverse_proxy = c . request ( req . method , req . url , req . headers ) ;
148
-
149
- // Add a listener for the connection timeout event
150
- reverse_proxy . connection . addListener ( 'error' , function ( err ) {
151
- res . writeHead ( 200 , { 'Content-Type' : 'text/plain' } ) ;
152
-
153
- if ( req . method !== 'HEAD' ) {
154
- res . write ( 'An error has occurred: ' + sys . puts ( JSON . stringify ( err ) ) ) ;
155
- }
58
+ } ) ;
59
+ return proxy ;
60
+ } ;
156
61
157
- res . end ( ) ;
158
- } ) ;
62
+ exports . setMin = function ( value ) {
63
+ min = value ;
64
+ manager . setMinClients ( min ) ;
65
+ } ;
159
66
67
+ exports . setMax = function ( value ) {
68
+ max = value ;
69
+ manager . setMaxClients ( max ) ;
70
+ }
160
71
161
- // Add a listener for the reverse_proxy response event
162
- reverse_proxy . addListener ( 'response' , function ( response ) {
163
- // Set the response headers of the client response
164
- res . writeHead ( response . statusCode , response . headers ) ;
72
+ var createProxy = function ( ) {
73
+ var server = http . createServer ( function ( req , res ) {
74
+ var buffers = [ ] ,
75
+ b = function ( chunk ) { buffers . push ( chunk ) } ,
76
+ e = function ( ) { e = false } ;
77
+
78
+ req . on ( 'data' , b ) ;
79
+ req . on ( 'end' , e ) ;
80
+
81
+ server . emit ( 'route' , req , res , function ( port , hostname ) {
82
+ var p = manager . getPool ( port , hostname ) ;
165
83
166
- // Add event handler for the proxied response in chunks
167
- response . addListener ( 'data' , function ( chunk ) {
168
- if ( req . method !== 'HEAD' ) {
169
- res . write ( chunk , 'binary' ) ;
170
- self . body += chunk ;
84
+ p . request ( req . method , req . url , req . headers , function ( reverse_proxy ) {
85
+ var data = '' ;
86
+ reverse_proxy . on ( 'error' , function ( err ) {
87
+ res . writeHead ( 500 , { 'Content-Type' : 'text/plain' } ) ;
88
+
89
+ if ( req . method !== 'HEAD' ) {
90
+ res . write ( 'An error has occurred: ' + sys . puts ( JSON . stringify ( err ) ) ) ;
91
+ }
92
+
93
+ res . end ( ) ;
94
+ } ) ;
95
+
96
+ buffers . forEach ( function ( c ) {
97
+ data += c ;
98
+ reverse_proxy . write ( c ) ;
99
+ } ) ;
100
+
101
+ buffers = null ;
102
+ req . removeListener ( 'data' , b ) ;
103
+ sys . pump ( req , reverse_proxy ) ;
104
+
105
+ if ( e ) {
106
+ req . removeListener ( 'end' , e ) ;
107
+ req . addListener ( 'end' , function ( ) { reverse_proxy . end ( ) } ) ;
108
+ }
109
+ else {
110
+ reverse_proxy . end ( ) ;
171
111
}
172
- } ) ;
173
112
174
- // Add event listener for end of proxied response
175
- response . addListener ( 'end' , function ( ) {
176
- // Remark: Emit the end event for testability
177
- self . emitter . emit ( 'end' , null , self . body ) ;
178
-
179
- res . end ( ) ;
113
+ // Add a listener for the reverse_proxy response event
114
+ reverse_proxy . addListener ( 'response' , function ( response ) {
115
+ // These two listeners are for testability and observation
116
+ // of what's passed back from the target server
117
+ response . addListener ( 'data' , function ( chunk ) {
118
+ data += chunk ;
119
+ } ) ;
120
+
121
+ response . addListener ( 'end' , function ( ) {
122
+ server . emit ( 'proxy' , null , data ) ;
123
+ } ) ;
124
+
125
+ // Set the response headers of the client response
126
+ res . writeHead ( response . statusCode , response . headers ) ;
127
+ sys . pump ( response , res ) ;
128
+ } ) ;
180
129
} ) ;
181
130
} ) ;
182
-
183
- // Chunk the client request body as chunks from the proxied request come in
184
- req . addListener ( 'data' , function ( chunk ) {
185
- reverse_proxy . write ( chunk , 'binary' ) ;
186
- } )
187
-
188
- // At the end of the client request, we are going to stop the proxied request
189
- req . addListener ( 'end' , function ( ) {
190
- reverse_proxy . end ( ) ;
191
- } ) ;
192
-
193
- this . unwatch ( req , res ) ;
194
- }
195
- } ;
131
+ } )
132
+ return server ;
133
+ } ;
0 commit comments