1
+ /*
2
+ relay-proxy.js: http proxy for node.js
3
+
4
+ Copyright (c) 2010 Nathan Ridley
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ */
26
+
27
+ var httpProxy = require ( 'http-proxy' ) ,
28
+ net = require ( 'net' ) ,
29
+ http = require ( 'http' ) ,
30
+ util = require ( 'util' ) ;
31
+
32
+ function RelayProxy ( ) {
33
+
34
+ var authHandler = function defaultAuthHandler ( req , username , password , callback ) {
35
+ // @arg 1: error object
36
+ // @arg 2: true if authorized, otherwise false
37
+ callback ( null , true ) ;
38
+ }
39
+
40
+ var proxySelector = function defaultProxySelector ( req , username , callback ) {
41
+ // @arg 1: error object
42
+ // @arg 2: a proxy options object, or false to decline the request,
43
+ // or null to forward directly to the remote host
44
+ // options: {
45
+ // host: 'x.x.x.x',
46
+ // port: 9999,
47
+ // username: 'username', // optional
48
+ // password: 'password' // optional
49
+ // }
50
+ callback ( null , null ) ;
51
+ }
52
+
53
+ function getAuth ( req ) {
54
+
55
+ var authHeader = req . headers [ 'proxy-authorization' ] ;
56
+ if ( authHeader ) {
57
+ var authMatch = ( new Buffer ( authHeader . substr ( 6 ) , 'base64' ) ) . toString ( ) . match ( / ^ ( [ ^ : ] * ) : ( .* ) $ / ) ;
58
+ if ( authMatch && authMatch [ 1 ] && authMatch [ 2 ] ) {
59
+ return {
60
+ username : authMatch [ 1 ] ,
61
+ password : authMatch [ 2 ]
62
+ } ;
63
+ }
64
+ }
65
+ return null ;
66
+ }
67
+
68
+ function createAuthHeader ( username , password ) {
69
+ return 'Basic ' + new Buffer ( username + ':' + password ) . toString ( 'base64' ) ;
70
+ }
71
+
72
+ function getRemoteProxy ( req , callback ) {
73
+
74
+ var auth = getAuth ( req ) || { } ;
75
+ authHandler ( req , auth . username , auth . password , function onAuthHandlerCallback ( err , authorized ) {
76
+
77
+ if ( ! authorized ) {
78
+ req . connection . end ( 'HTTP/1.0 407 Proxy authentication required\r\nProxy-authenticate: Basic realm="remotehost"\r\n\r\n' ) ;
79
+ callback ( new Error ( 'Proxy authorization not supplied (407 response sent)' ) )
80
+ return ;
81
+ } ;
82
+
83
+ proxySelector ( req , auth . username , function onProxySelectorCallback ( err , options ) {
84
+
85
+ if ( options === false ) {
86
+ req . connection . end ( 'HTTP/1.0 429 Too Many Requests\r\n\r\nNo proxy available to service request' ) ;
87
+ callback ( new Error ( 'No remote proxies available (429 response sent)' ) ) ;
88
+ return ;
89
+ }
90
+
91
+ callback ( null , options ) ;
92
+ } ) ;
93
+
94
+ } ) ;
95
+ }
96
+
97
+ function onHttpRequest ( req , res , proxy ) {
98
+
99
+ var buffer = httpProxy . buffer ( req ) ;
100
+
101
+ console . log ( 'HTTP --> ' + req . url ) ;
102
+
103
+ getRemoteProxy ( req , function ( err , remoteProxy ) {
104
+
105
+ if ( err ) {
106
+ console . log ( err . message ) ;
107
+ return ;
108
+ }
109
+
110
+ if ( ! remoteProxy ) {
111
+ var parts = req . headers . host . split ( ':' ) ;
112
+ proxy . proxyRequest ( req , res , {
113
+ host : parts [ 0 ] ,
114
+ port : parts [ 1 ] || 80
115
+ } ) ;
116
+ return ;
117
+ }
118
+
119
+ req . path = req . url ;
120
+ if ( remoteProxy . username && remoteProxy . password )
121
+ req . headers [ 'proxy-authorization' ] = createAuthHeader ( remoteProxy . username , remoteProxy . password ) ;
122
+ var options = {
123
+ host : remoteProxy . host ,
124
+ port : remoteProxy . port ,
125
+ buffer : buffer
126
+ }
127
+ proxy . proxyRequest ( req , res , options ) ;
128
+ } ) ;
129
+ }
130
+
131
+ function onHttpsRequest ( req , socket , head ) {
132
+
133
+ console . log ( 'HTTPS --> ' + req . url ) ;
134
+
135
+ getRemoteProxy ( req , function ( err , remoteProxy ) {
136
+
137
+ if ( err ) {
138
+ console . log ( err . message ) ;
139
+ return ;
140
+ }
141
+
142
+ if ( ! remoteProxy ) {
143
+ var parts = req . url . split ( ':' ) ;
144
+ var conn = net . connect ( parts [ 1 ] , parts [ 0 ] , function ( ) {
145
+ socket . write ( 'HTTP/1.1 200 OK\r\n\r\n' ) ;
146
+ conn . pipe ( socket ) ;
147
+ socket . pipe ( conn ) ;
148
+ } ) ;
149
+ return ;
150
+ }
151
+
152
+ var conn = net . connect ( remoteProxy . port , remoteProxy . host , function ( ) {
153
+
154
+ var headers
155
+ = 'CONNECT ' + req . url + ' HTTP/1.1\r\n'
156
+ + 'Proxy-Authorization: ' + createAuthHeader ( remoteProxy . username , remoteProxy . password ) + '\r\n\r\n' ;
157
+ conn . write ( headers ) ;
158
+
159
+ } ) . once ( 'data' , function ( buffer ) {
160
+
161
+ var ok = / ^ H T T P \/ 1 .[ 0 1 ] 2 0 0 / i. test ( buffer . toString ( 'utf8' ) ) ;
162
+ if ( ! ok )
163
+ socket . end ( 'HTTP/1.1 401 Unauthorized\r\n\r\nUpstream proxy rejected the request' ) ;
164
+ else {
165
+ socket . write ( 'HTTP/1.1 200 OK\r\n\r\n' ) ;
166
+ socket . pipe ( conn ) ;
167
+ conn . pipe ( socket ) ;
168
+ }
169
+ } ) ;
170
+ } )
171
+ }
172
+
173
+ this . authorize = function authorize ( handler ) {
174
+ authHandler = handler ;
175
+ }
176
+
177
+ this . selectForwardProxy = function selectForwardProxy ( handler ) {
178
+ proxySelector = handler ;
179
+ }
180
+
181
+ this . listen = function listen ( port ) {
182
+ port || ( port = 3333 ) ;
183
+ this . server = httpProxy
184
+ . createServer ( onHttpRequest )
185
+ . on ( 'connect' , onHttpsRequest )
186
+ . listen ( port ) ;
187
+
188
+ console . log ( 'Proxy server now listening on port ' + port + '.' ) ;
189
+ }
190
+ }
191
+
192
+ module . exports = RelayProxy ;
0 commit comments