Merge branch 'settings' of https://github.com/M2Ys4U/KiwiIRC into settings
[KiwiIRC.git] / server / weblistener.js
CommitLineData
a8bf3ea4
JA
1var ws = require('socket.io'),
2 events = require('events'),
3 http = require('http'),
4 https = require('https'),
5 util = require('util'),
6 fs = require('fs'),
7 dns = require('dns'),
186531ed 8 url = require('url'),
f9ff7686 9 _ = require('lodash'),
8f2efe03 10 spdy = require('spdy'),
e7af0cbb 11 ipaddr = require('ipaddr.js'),
1286229a
D
12 Client = require('./client.js').Client,
13 HttpHandler = require('./httphandler.js').HttpHandler,
e7af0cbb 14 rehash = require('./rehash.js');
1286229a
D
15
16
17
18rehash.on('rehashed', function (files) {
19 Client = require('./client.js').Client;
2f1e8a71 20 HttpHandler = require('./httphandler.js').HttpHandler;
1286229a
D
21});
22
186531ed
D
23
24// Instance of HttpHandler
25var http_handler;
26
a8bf3ea4 27
8b0eb787 28var WebListener = function (web_config, transports) {
71c81a3b 29 var hs, opts, ws_opts,
a8bf3ea4
JA
30 that = this;
31
71c81a3b 32
a8bf3ea4 33 events.EventEmitter.call(this);
b156e01a 34
8b0eb787 35 http_handler = new HttpHandler(web_config);
b156e01a 36
71c81a3b
D
37 // Standard options for the socket.io connections
38 ws_opts = {
39 'log level': 0,
40 'log colors': 0
41 };
42
43
8b0eb787 44 if (web_config.ssl) {
a8bf3ea4 45 opts = {
562ecbe0
PV
46 key: fs.readFileSync(web_config.ssl_key),
47 cert: fs.readFileSync(web_config.ssl_cert)
a8bf3ea4 48 };
186531ed 49
a8bf3ea4 50 // Do we have an intermediate certificate?
480552a2
D
51 if (typeof web_config.ssl_ca !== 'undefined') {
52 // An array of them?
53 if (typeof web_config.ssl_ca.map !== 'undefined') {
54 opts.ca = web_config.ssl_ca.map(function (f) { return fs.readFileSync(f); });
186531ed 55
480552a2
D
56 } else {
57 opts.ca = fs.readFileSync(web_config.ssl_ca);
58 }
59 }
186531ed 60
8f2efe03
JA
61 hs = spdy.createServer(opts, handleHttpRequest);
62
186531ed 63 // Start socket.io listening on this weblistener
71c81a3b 64 this.ws = ws.listen(hs, _.extend({ssl: true}, ws_opts));
e380bc9e
D
65 hs.listen(web_config.port, web_config.address, function () {
66 that.emit('listening');
67 });
a8bf3ea4 68 } else {
186531ed 69
a8bf3ea4 70 // Start some plain-text server up
186531ed
D
71 hs = http.createServer(handleHttpRequest);
72
73 // Start socket.io listening on this weblistener
71c81a3b 74 this.ws = ws.listen(hs, _.extend({ssl: false}, ws_opts));
e380bc9e
D
75 hs.listen(web_config.port, web_config.address, function () {
76 that.emit('listening');
77 });
a8bf3ea4 78 }
d558508c
JA
79
80 hs.on('error', function (err) {
81 that.emit('error', err);
b156e01a
JA
82 });
83
a8bf3ea4
JA
84 this.ws.enable('browser client minification');
85 this.ws.enable('browser client etag');
86 this.ws.set('transports', transports);
b737610b 87 this.ws.set('resource', (global.config.http_base_path || '') + '/transport');
a8bf3ea4 88
c6e3ed44
D
89 this.ws.of('/kiwi').authorization(authoriseConnection)
90 .on('connection', function () {
91 newConnection.apply(that, arguments);
92 }
93 );
a8bf3ea4
JA
94 this.ws.of('/kiwi').on('error', console.log);
95};
96util.inherits(WebListener, events.EventEmitter);
97
186531ed
D
98
99
100function handleHttpRequest(request, response) {
101 var uri = url.parse(request.url, true);
e7af0cbb 102
186531ed
D
103 // If this isn't a socket.io request, pass it onto the http handler
104 if (uri.pathname.substr(0, 10) !== '/socket.io') {
105 http_handler.serve(request, response);
106 }
107}
108
e7af0cbb
JA
109function rangeCheck(addr, range) {
110 var i, ranges, parts;
111 ranges = (!_.isArray(range)) ? [range] : range;
112 for (i = 0; i < ranges.length; i++) {
113 parts = ranges[i].split('/');
114 if (ipaddr.process(addr).match(ipaddr.process(parts[0]), parts[1])) {
115 return true;
116 }
117 }
118 return false;
119}
120
a8bf3ea4 121
15fefff7
D
122/**
123 * Get the reverse DNS entry for this connection.
124 * Used later on for webirc, etc functionality
125 */
772a4bb6 126function authoriseConnection(handshakeData, callback) {
c6e3ed44
D
127 var address = handshakeData.address.address;
128
129 // If a forwarded-for header is found, switch the source address
f2fb32b7 130 if (handshakeData.headers[global.config.http_proxy_ip_header || 'x-forwarded-for']) {
c6e3ed44 131 // Check we're connecting from a whitelisted proxy
e7af0cbb 132 if (!global.config.http_proxies || !rangeCheck(address, global.config.http_proxies)) {
c6e3ed44
D
133 console.log('Unlisted proxy:', address);
134 callback(null, false);
135 return;
136 }
137
138 // We're sent from a whitelisted proxy, replace the hosts
139 address = handshakeData.headers['x-forwarded-for'];
140 }
141
c36ed4eb 142 handshakeData.real_address = address;
b156e01a 143
13d7faa3 144 // If enabled, don't go over the connection limit
b737610b
JA
145 if (global.config.max_client_conns && global.config.max_client_conns > 0) {
146 if (global.clients.numOnAddress(address) + 1 > global.config.max_client_conns) {
13d7faa3
D
147 return callback(null, false);
148 }
c36ed4eb 149 }
be481108
D
150
151
152 try {
153 dns.reverse(address, function (err, domains) {
154 if (err || domains.length === 0) {
155 handshakeData.revdns = address;
156 } else {
157 handshakeData.revdns = _.first(domains) || address;
158 }
b156e01a 159
be481108
D
160 // All is well, authorise the connection
161 callback(null, true);
162 });
163 } catch (err) {
164 handshakeData.revdns = address;
a8bf3ea4 165 callback(null, true);
be481108 166 }
15fefff7 167}
a8bf3ea4 168
772a4bb6 169function newConnection(websocket) {
c36ed4eb
JA
170 var client, that = this;
171 client = new Client(websocket);
57566ca2
D
172 client.on('dispose', function () {
173 that.emit('client_dispose', this);
c36ed4eb
JA
174 });
175 this.emit('connection', client);
15fefff7 176}
186531ed
D
177
178
179
180
181
562ecbe0 182module.exports = WebListener;