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