info: socket.io started debug: client authorized info: handshake authorized Gej626w0ImW0-t55HLAc debug: setting request GET /socket.io/1/websocket/Gej626w0ImW0-t55HLAc debug: set heartbeat interval for client Gej626w0ImW0-t55HLAc debug: client authorized for debug: websocket writing 1:: debug: websocket writing 5:::{"name":"outgoing","args":["Hello over websockets"]}
Line | Hits | Source |
---|---|---|
1 | 1 | var http = require('http'), |
2 | https = require('https'), | |
3 | url = require('url'), | |
4 | httpProxy = require('./http-proxy/'), | |
5 | proxy = exports; | |
6 | ||
7 | /** | |
8 | * Creates the proxy server. | |
9 | * | |
10 | * Examples: | |
11 | * | |
12 | * httpProxy.createProxyServer({ .. }, 8000) | |
13 | * // => '{ web: [Function], ws: [Function] ... }' | |
14 | * | |
15 | * @param {Object} Options Config object passed to the proxy | |
16 | * | |
17 | * @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests | |
18 | * | |
19 | * @api public | |
20 | */ | |
21 | ||
22 | 1 | proxy.createProxyServer = proxy.createServer = function createProxyServer(options) { |
23 | /* | |
24 | * `options` is needed and it must have the following layout: | |
25 | * | |
26 | * { | |
27 | * target : <url string to be parsed with the url module> | |
28 | * forward: <url string to be parsed with the url module> | |
29 | * agent : <object to be passed to http(s).request> | |
30 | * ssl : <object to be passed to https.createServer()> | |
31 | * ws : <true/false, if you want to proxy websockets> | |
32 | * xfwd : <true/false, adds x-forward headers> | |
33 | * secure : <true/false, verify SSL certificate> | |
34 | * } | |
35 | * | |
36 | * NOTE: `options.ws` and `options.ssl` are optional. | |
37 | * `options.target and `options.forward` cannot be | |
38 | * both missing | |
39 | * } | |
40 | */ | |
41 | ||
42 | 16 | return new httpProxy.Server(options); |
43 | }; | |
44 | ||
45 |
Line | Hits | Source |
---|---|---|
1 | 1 | var common = exports, |
2 | extend = require('util')._extend; | |
3 | ||
4 | /** | |
5 | * Copies the right headers from `options` and `req` to | |
6 | * `outgoing` which is then used to fire the proxied | |
7 | * request. | |
8 | * | |
9 | * Examples: | |
10 | * | |
11 | * common.setupOutgoing(outgoing, options, req) | |
12 | * // => { host: ..., hostname: ...} | |
13 | * | |
14 | * @param {Object} Outgoing Base object to be filled with required properties | |
15 | * @param {Object} Options Config object passed to the proxy | |
16 | * @param {ClientRequest} Req Request Object | |
17 | * @param {String} Forward String to select forward or target | |
18 | * | |
19 | * @return {Object} Outgoing Object with all required properties set | |
20 | * | |
21 | * @api private | |
22 | */ | |
23 | ||
24 | 1 | common.setupOutgoing = function(outgoing, options, req, forward) { |
25 | 19 | outgoing.port = options[forward || 'target'].port || |
26 | (~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? 443 : 80); | |
27 | ||
28 | 19 | ['host', 'hostname', 'socketPath'].forEach( |
29 | 57 | function(e) { outgoing[e] = options[forward || 'target'][e]; } |
30 | ); | |
31 | ||
32 | 19 | ['method', 'headers'].forEach( |
33 | 38 | function(e) { outgoing[e] = req[e]; } |
34 | ); | |
35 | ||
36 | 19 | if (options.headers){ |
37 | 1 | extend(outgoing.headers, options.headers); |
38 | } | |
39 | ||
40 | 19 | if (options[forward || 'target'].protocol == 'https:') { |
41 | 4 | outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? true : options.secure; |
42 | } | |
43 | ||
44 | ||
45 | 19 | outgoing.agent = options.agent || false; |
46 | 19 | outgoing.path = req.url; |
47 | ||
48 | 19 | return outgoing; |
49 | }; | |
50 | ||
51 | /** | |
52 | * Set the proper configuration for sockets, | |
53 | * set no delay and set keep alive, also set | |
54 | * the timeout to 0. | |
55 | * | |
56 | * Examples: | |
57 | * | |
58 | * common.setupSocket(socket) | |
59 | * // => Socket | |
60 | * | |
61 | * @param {Socket} Socket instance to setup | |
62 | * | |
63 | * @return {Socket} Return the configured socket. | |
64 | * | |
65 | * @api private | |
66 | */ | |
67 | ||
68 | 1 | common.setupSocket = function(socket) { |
69 | 5 | socket.setTimeout(0); |
70 | 5 | socket.setNoDelay(true); |
71 | ||
72 | 5 | socket.setKeepAlive(true, 0); |
73 | ||
74 | 5 | return socket; |
75 | }; | |
76 |
Line | Hits | Source |
---|---|---|
1 | 1 | var httpProxy = exports, |
2 | extend = require('util')._extend, | |
3 | parse_url = require('url').parse, | |
4 | EE3 = require('eventemitter3').EventEmitter, | |
5 | http = require('http'), | |
6 | https = require('https'), | |
7 | web = require('./passes/web-incoming'), | |
8 | ws = require('./passes/ws-incoming'); | |
9 | ||
10 | 1 | httpProxy.Server = ProxyServer; |
11 | ||
12 | /** | |
13 | * Returns a function that creates the loader for | |
14 | * either `ws` or `web`'s passes. | |
15 | * | |
16 | * Examples: | |
17 | * | |
18 | * httpProxy.createRightProxy('ws') | |
19 | * // => [Function] | |
20 | * | |
21 | * @param {String} Type Either 'ws' or 'web' | |
22 | * | |
23 | * @return {Function} Loader Function that when called returns an iterator for the right passes | |
24 | * | |
25 | * @api private | |
26 | */ | |
27 | ||
28 | 1 | function createRightProxy(type) { |
29 | 32 | return function(options) { |
30 | 32 | return function(req, res /*, [head], [opts] */) { |
31 | 16 | var passes = (type === 'ws') ? this.wsPasses : this.webPasses, |
32 | args = [].slice.call(arguments), | |
33 | cntr = args.length - 1, | |
34 | head, cbl; | |
35 | ||
36 | /* optional args parse begin */ | |
37 | 16 | if(typeof args[cntr] === 'function') { |
38 | 1 | cbl = args[cntr]; |
39 | ||
40 | 1 | cntr--; |
41 | } | |
42 | ||
43 | 16 | if( |
44 | !(args[cntr] instanceof Buffer) && | |
45 | args[cntr] !== res | |
46 | ) { | |
47 | //Copy global options | |
48 | 1 | options = extend({}, options); |
49 | //Overwrite with request options | |
50 | 1 | extend(options, args[cntr]); |
51 | ||
52 | 1 | cntr--; |
53 | } | |
54 | ||
55 | 16 | if(args[cntr] instanceof Buffer) { |
56 | 2 | head = args[cntr]; |
57 | } | |
58 | ||
59 | /* optional args parse end */ | |
60 | ||
61 | 16 | ['target', 'forward'].forEach(function(e) { |
62 | 32 | if (typeof options[e] === 'string') |
63 | 15 | options[e] = parse_url(options[e]); |
64 | }); | |
65 | ||
66 | ||
67 | 16 | for(var i=0; i < passes.length; i++) { |
68 | /** | |
69 | * Call of passes functions | |
70 | * pass(req, res, options, head) | |
71 | * | |
72 | * In WebSockets case the `res` variable | |
73 | * refer to the connection socket | |
74 | * pass(req, socket, options, head) | |
75 | */ | |
76 | 64 | if(passes[i](req, res, options, head, cbl ? false : this, cbl)) { // passes can return a truthy value to halt the loop |
77 | 1 | break; |
78 | } | |
79 | } | |
80 | }; | |
81 | }; | |
82 | } | |
83 | ||
84 | ||
85 | 1 | function ProxyServer(options) { |
86 | 16 | EE3.call(this); |
87 | ||
88 | 16 | this.web = createRightProxy('web')(options); |
89 | 16 | this.ws = createRightProxy('ws')(options); |
90 | 16 | this.options = options; |
91 | ||
92 | 16 | this.webPasses = Object.keys(web).map(function(pass) { |
93 | 64 | return web[pass]; |
94 | }); | |
95 | ||
96 | 16 | this.wsPasses = Object.keys(ws).map(function(pass) { |
97 | 64 | return ws[pass]; |
98 | }); | |
99 | } | |
100 | ||
101 | 1 | require('util').inherits(ProxyServer, EE3); |
102 | ||
103 | 1 | ProxyServer.prototype.listen = function(port) { |
104 | 11 | var self = this, |
105 | 10 | closure = function(req, res) { self.web(req, res); }; |
106 | ||
107 | 11 | this._server = this.options.ssl ? |
108 | https.createServer(this.options.ssl, closure) : | |
109 | http.createServer(closure); | |
110 | ||
111 | 11 | if(this.options.ws) { |
112 | 4 | this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); |
113 | } | |
114 | ||
115 | 11 | this._server.listen(port); |
116 | ||
117 | 11 | return this; |
118 | }; | |
119 | ||
120 | 1 | ProxyServer.prototype.before = function(passName, callback) { |
121 | 0 | var i = false; |
122 | 0 | this.passes.forEach(function(v, idx) { |
123 | 0 | if(v.name === passName) i = idx; |
124 | }) | |
125 | ||
126 | 0 | if(!i) throw new Error('No such pass'); |
127 | ||
128 | 0 | this.passes.splice(i, 0, callback); |
129 | }; | |
130 | 1 | ProxyServer.prototype.after = function(passName, callback) { |
131 | 0 | var i = false; |
132 | 0 | this.passes.forEach(function(v, idx) { |
133 | 0 | if(v.name === passName) i = idx; |
134 | }) | |
135 | ||
136 | 0 | if(!i) throw new Error('No such pass'); |
137 | ||
138 | 0 | this.passes.splice(i++, 0, callback); |
139 | }; | |
140 |
Line | Hits | Source |
---|---|---|
1 | 1 | var http = require('http'), |
2 | https = require('https'), | |
3 | web_o = require('./web-outgoing'), | |
4 | common = require('../common'), | |
5 | passes = exports; | |
6 | ||
7 | 1 | web_o = Object.keys(web_o).map(function(pass) { |
8 | 4 | return web_o[pass]; |
9 | }); | |
10 | ||
11 | /*! | |
12 | * Array of passes. | |
13 | * | |
14 | * A `pass` is just a function that is executed on `req, res, options` | |
15 | * so that you can easily add new checks while still keeping the base | |
16 | * flexible. | |
17 | */ | |
18 | ||
19 | 1 | [ // <-- |
20 | ||
21 | /** | |
22 | * Sets `content-length` to '0' if request is of DELETE type. | |
23 | * | |
24 | * @param {ClientRequest} Req Request object | |
25 | * @param {IncomingMessage} Res Response object | |
26 | * @param {Object} Options Config object passed to the proxy | |
27 | * | |
28 | * @api private | |
29 | */ | |
30 | ||
31 | function deleteLength(req, res, options) { | |
32 | 15 | if(req.method === 'DELETE' && !req.headers['content-length']) { |
33 | 1 | req.headers['content-length'] = '0'; |
34 | } | |
35 | }, | |
36 | ||
37 | /** | |
38 | * Sets timeout in request socket if it was specified in options. | |
39 | * | |
40 | * @param {ClientRequest} Req Request object | |
41 | * @param {IncomingMessage} Res Response object | |
42 | * @param {Object} Options Config object passed to the proxy | |
43 | * | |
44 | * @api private | |
45 | */ | |
46 | ||
47 | function timeout(req, res, options) { | |
48 | 15 | if(options.timeout) { |
49 | 2 | req.socket.setTimeout(options.timeout); |
50 | } | |
51 | }, | |
52 | ||
53 | /** | |
54 | * Sets `x-forwarded-*` headers if specified in config. | |
55 | * | |
56 | * @param {ClientRequest} Req Request object | |
57 | * @param {IncomingMessage} Res Response object | |
58 | * @param {Object} Options Config object passed to the proxy | |
59 | * | |
60 | * @api private | |
61 | */ | |
62 | ||
63 | function XHeaders(req, res, options) { | |
64 | 29 | if(!options.xfwd) return; |
65 | ||
66 | 1 | var values = { |
67 | for : req.connection.remoteAddress || req.socket.remoteAddress, | |
68 | port : req.connection.remotePort || req.socket.remotePort, | |
69 | proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http') | |
70 | }; | |
71 | ||
72 | 1 | ['for', 'port', 'proto'].forEach(function(header) { |
73 | 3 | req.headers['x-forwarded-' + header] = |
74 | (req.headers['x-forwarded-' + header] || '') + | |
75 | (req.headers['x-forwarded-' + header] ? ',' : '') + | |
76 | values[header]; | |
77 | }); | |
78 | }, | |
79 | ||
80 | /** | |
81 | * Does the actual proxying. If `forward` is enabled fires up | |
82 | * a ForwardStream, same happens for ProxyStream. The request | |
83 | * just dies otherwise. | |
84 | * | |
85 | * @param {ClientRequest} Req Request object | |
86 | * @param {IncomingMessage} Res Response object | |
87 | * @param {Object} Options Config object passed to the proxy | |
88 | * | |
89 | * @api private | |
90 | */ | |
91 | ||
92 | function stream(req, res, options, _, server, clb) { | |
93 | 14 | if(options.forward) { |
94 | // If forward enable, so just pipe the request | |
95 | 1 | var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( |
96 | common.setupOutgoing(options.ssl || {}, options, req, 'forward') | |
97 | ); | |
98 | 1 | (options.buffer || req).pipe(forwardReq); |
99 | 2 | if(!options.target) { return res.end(); } |
100 | } | |
101 | ||
102 | // Request initalization | |
103 | 13 | var proxyReq = (options.target.protocol === 'https:' ? https : http).request( |
104 | common.setupOutgoing(options.ssl || {}, options, req) | |
105 | ); | |
106 | ||
107 | // Error Handler | |
108 | 13 | proxyReq.on('error', function(err){ |
109 | 4 | if(options.buffer) { options.buffer.destroy(); } |
110 | 4 | if (clb) { |
111 | 1 | clb(err); |
112 | } else { | |
113 | 3 | server.emit('error', err, req, res); |
114 | } | |
115 | }); | |
116 | ||
117 | 13 | (options.buffer || req).pipe(proxyReq); |
118 | ||
119 | 13 | proxyReq.on('response', function(proxyRes) { |
120 | 7 | server.emit('proxyRes', proxyRes); |
121 | 7 | for(var i=0; i < web_o.length; i++) { |
122 | 28 | if(web_o[i](req, res, proxyRes)) { break; } |
123 | } | |
124 | ||
125 | 7 | proxyRes.pipe(res); |
126 | }); | |
127 | ||
128 | //proxyReq.end(); | |
129 | } | |
130 | ||
131 | ] // <-- | |
132 | .forEach(function(func) { | |
133 | 4 | passes[func.name] = func; |
134 | }); | |
135 |
Line | Hits | Source |
---|---|---|
1 | 1 | var passes = exports; |
2 | ||
3 | /*! | |
4 | * Array of passes. | |
5 | * | |
6 | * A `pass` is just a function that is executed on `req, res, options` | |
7 | * so that you can easily add new checks while still keeping the base | |
8 | * flexible. | |
9 | */ | |
10 | ||
11 | 1 | [ // <-- |
12 | ||
13 | /** | |
14 | * If is a HTTP 1.0 request, remove chunk headers | |
15 | * | |
16 | * @param {ClientRequest} Req Request object | |
17 | * @param {IncomingMessage} Res Response object | |
18 | * @param {proxyResponse} Res Response object from the proxy request | |
19 | * | |
20 | * @api private | |
21 | */ | |
22 | function removeChunked(req, res, proxyRes) { | |
23 | 8 | if(req.httpVersion === '1.0') { |
24 | 1 | delete proxyRes.headers['transfer-encoding']; |
25 | } | |
26 | }, | |
27 | ||
28 | /** | |
29 | * If is a HTTP 1.0 request, set the correct connection header | |
30 | * or if connection header not present, then use `keep-alive` | |
31 | * | |
32 | * @param {ClientRequest} Req Request object | |
33 | * @param {IncomingMessage} Res Response object | |
34 | * @param {proxyResponse} Res Response object from the proxy request | |
35 | * | |
36 | * @api private | |
37 | */ | |
38 | function setConnection(req, res, proxyRes) { | |
39 | 11 | if (req.httpVersion === '1.0') { |
40 | 2 | proxyRes.headers.connection = req.headers.connection || 'close'; |
41 | 9 | } else if (!proxyRes.headers.connection) { |
42 | 2 | proxyRes.headers.connection = req.headers.connection || 'keep-alive'; |
43 | } | |
44 | }, | |
45 | ||
46 | /** | |
47 | * Copy headers from proxyResponse to response | |
48 | * set each header in response object. | |
49 | * | |
50 | * @param {ClientRequest} Req Request object | |
51 | * @param {IncomingMessage} Res Response object | |
52 | * @param {proxyResponse} Res Response object from the proxy request | |
53 | * | |
54 | * @api private | |
55 | */ | |
56 | function writeHeaders(req, res, proxyRes) { | |
57 | 8 | Object.keys(proxyRes.headers).forEach(function(key) { |
58 | 29 | res.setHeader(key, proxyRes.headers[key]); |
59 | }); | |
60 | }, | |
61 | ||
62 | /** | |
63 | * Set the statusCode from the proxyResponse | |
64 | * | |
65 | * @param {ClientRequest} Req Request object | |
66 | * @param {IncomingMessage} Res Response object | |
67 | * @param {proxyResponse} Res Response object from the proxy request | |
68 | * | |
69 | * @api private | |
70 | */ | |
71 | function writeStatusCode(req, res, proxyRes) { | |
72 | 8 | res.writeHead(proxyRes.statusCode); |
73 | } | |
74 | ||
75 | ] // <-- | |
76 | .forEach(function(func) { | |
77 | 4 | passes[func.name] = func; |
78 | }); | |
79 |
Line | Hits | Source |
---|---|---|
1 | 1 | var http = require('http'), |
2 | https = require('https'), | |
3 | common = require('../common'), | |
4 | passes = exports; | |
5 | ||
6 | /*! | |
7 | * Array of passes. | |
8 | * | |
9 | * A `pass` is just a function that is executed on `req, socket, options` | |
10 | * so that you can easily add new checks while still keeping the base | |
11 | * flexible. | |
12 | */ | |
13 | ||
14 | /* | |
15 | * Websockets Passes | |
16 | * | |
17 | */ | |
18 | ||
19 | 1 | var passes = exports; |
20 | ||
21 | 1 | [ |
22 | /** | |
23 | * WebSocket requests must have the `GET` method and | |
24 | * the `upgrade:websocket` header | |
25 | * | |
26 | * @param {ClientRequest} Req Request object | |
27 | * @param {Socket} Websocket | |
28 | * | |
29 | * @api private | |
30 | */ | |
31 | ||
32 | function checkMethodAndHeader (req, socket) { | |
33 | 6 | if (req.method !== 'GET' || !req.headers.upgrade) { |
34 | 2 | socket.destroy(); |
35 | 2 | return true; |
36 | } | |
37 | ||
38 | 4 | if (req.headers.upgrade.toLowerCase() !== 'websocket') { |
39 | 1 | socket.destroy(); |
40 | 1 | return true; |
41 | } | |
42 | }, | |
43 | ||
44 | /** | |
45 | * Set the proper configuration for sockets, | |
46 | * set no delay and set keep alive, also set | |
47 | * the timeout to 0. | |
48 | * | |
49 | * @param {ClientRequest} Req Request object | |
50 | * @param {Socket} Websocket | |
51 | * | |
52 | * @api private | |
53 | */ | |
54 | ||
55 | function setupSocket(req, socket) { | |
56 | 3 | socket.setTimeout(0); |
57 | 3 | socket.setNoDelay(true); |
58 | ||
59 | 3 | socket.setKeepAlive(true, 0); |
60 | }, | |
61 | ||
62 | /** | |
63 | * Sets `x-forwarded-*` headers if specified in config. | |
64 | * | |
65 | * @param {ClientRequest} Req Request object | |
66 | * @param {Socket} Websocket | |
67 | * @param {Object} Options Config object passed to the proxy | |
68 | * | |
69 | * @api private | |
70 | */ | |
71 | ||
72 | function XHeaders(req, socket, options) { | |
73 | 8 | if(!options.xfwd) return; |
74 | ||
75 | 2 | var values = { |
76 | for : req.connection.remoteAddress || req.socket.remoteAddress, | |
77 | port : req.connection.remotePort || req.socket.remotePort, | |
78 | proto: req.connection.pair ? 'wss' : 'ws' | |
79 | }; | |
80 | ||
81 | 2 | ['for', 'port', 'proto'].forEach(function(header) { |
82 | 6 | req.headers['x-forwarded-' + header] = |
83 | (req.headers['x-forwarded-' + header] || '') + | |
84 | (req.headers['x-forwarded-' + header] ? ',' : '') + | |
85 | values[header]; | |
86 | }); | |
87 | }, | |
88 | ||
89 | /** | |
90 | * Does the actual proxying. Make the request and upgrade it | |
91 | * send the Switching Protocols request and pipe the sockets. | |
92 | * | |
93 | * @param {ClientRequest} Req Request object | |
94 | * @param {Socket} Websocket | |
95 | * @param {Object} Options Config object passed to the proxy | |
96 | * | |
97 | * @api private | |
98 | */ | |
99 | function stream(req, socket, options, server, head, clb) { | |
100 | 2 | common.setupSocket(socket); |
101 | ||
102 | 2 | if (head && head.length) socket.unshift(head); |
103 | ||
104 | ||
105 | 2 | var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request( |
106 | common.setupOutgoing(options.ssl || {}, options, req) | |
107 | ); | |
108 | // Error Handler | |
109 | 2 | proxyReq.on('error', onError); |
110 | ||
111 | 2 | proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { |
112 | 2 | proxySocket.on('error', onError); |
113 | ||
114 | 2 | common.setupSocket(proxySocket); |
115 | ||
116 | 3 | if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); |
117 | ||
118 | 2 | socket.write('HTTP/1.1 101 Switching Protocols\r\n'); |
119 | 2 | socket.write(Object.keys(proxyRes.headers).map(function(i) { |
120 | 6 | return i + ": " + proxyRes.headers[i]; |
121 | }).join('\r\n') + '\r\n\r\n'); | |
122 | 2 | proxySocket.pipe(socket).pipe(proxySocket); |
123 | }); | |
124 | ||
125 | 2 | return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT |
126 | ||
127 | 0 | function onError(err) { |
128 | 0 | if (clb) { |
129 | 0 | clb(err); |
130 | } else { | |
131 | 0 | server.emit('error', err, req, socket); |
132 | } | |
133 | } | |
134 | } | |
135 | ||
136 | ] // <-- | |
137 | .forEach(function(func) { | |
138 | 4 | passes[func.name] = func; |
139 | }); | |
140 |