TLS connection fixes
[KiwiIRC.git] / server / httphandler.js
1 var url = require('url'),
2 fs = require('fs'),
3 node_static = require('node-static'),
4 _ = require('lodash'),
5 config = require('./configuration.js'),
6 SettingsGenerator = require('./settingsgenerator.js');
7
8
9
10
11 var HttpHandler = function (config) {
12 var public_http = config.public_http || 'client/';
13 this.file_server = new node_static.Server(public_http);
14 };
15
16 module.exports.HttpHandler = HttpHandler;
17
18
19
20 HttpHandler.prototype.serve = function (request, response) {
21 // The incoming requests base path (ie. /kiwiclient)
22 var base_path = global.config.http_base_path || '/kiwi',
23 base_path_regex;
24
25 // Trim of any trailing slashes
26 if (base_path.substr(base_path.length - 1) === '/') {
27 base_path = base_path.substr(0, base_path.length - 1);
28 }
29
30 // Build the regex to match the base_path
31 base_path_regex = base_path.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
32
33 // Any asset request to head into the asset dir
34 request.url = request.url.replace(base_path + '/assets/', '/assets/');
35
36 // Any src request to head into the src dir
37 request.url = request.url.replace(base_path + '/src/', '/src/');
38
39 // Any requests for /client to load the index file
40 if (request.url.match(new RegExp('^' + base_path_regex + '([/$]|$)', 'i'))) {
41 request.url = '/index.html';
42 }
43
44 // If the 'magic' translation is requested, figure out the best language to use from
45 // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation
46 if (request.url.substr(0, 16) === '/assets/locales/') {
47 if (request.url === '/assets/locales/magic.json') {
48 return serveMagicLocale.call(this, request, response);
49 } else {
50 response.setHeader('Content-Language', request.url.substr(16, request.url.indexOf('.') - 16));
51 }
52 } else if (request.url.substr(0, 21) === '/assets/settings.json') {
53 return serveSettings.call(this, request, response);
54 }
55
56 this.file_server.serve(request, response, function (err) {
57 if (err) {
58 response.writeHead(err.status, err.headers);
59 response.end();
60 }
61 });
62 };
63
64
65 /**
66 * Handle the /assets/locales/magic.json request
67 * Find the closest translation we have for the language
68 * set in the browser.
69 **/
70 var serveMagicLocale = function (request, response) {
71 var that = this,
72 default_locale_id = 'en-gb';
73
74 if (!request.headers['accept-language']) {
75 // No accept-language specified in the request so send the default
76 return serveLocale.call(this, request, response, default_locale_id);
77 }
78
79 fs.readdir('client/assets/locales', function (err, files) {
80 var available = [],
81 i = 0,
82 langs = request.headers['accept-language'].split(','), // Example: en-gb,en;q=0.5
83 found_locale = default_locale_id;
84
85 // Get a list of the available translations we have
86 files.forEach(function (file) {
87 if (file.substr(-5) === '.json') {
88 available.push(file.slice(0, -5));
89 }
90 });
91
92 // Sanitise the browsers accepted languages and the qualities
93 for (i = 0; i < langs.length; i++) {
94 langs[i]= langs[i].split(';q=');
95 langs[i][0] = langs[i][0].toLowerCase();
96 langs[i][1] = (typeof langs[i][1] === 'string') ? parseFloat(langs[i][1]) : 1.0;
97 }
98
99 // Sort the accepted languages by quality
100 langs.sort(function (a, b) {
101 return b[1] - a[1];
102 });
103
104 // Serve the first language we have a translation for
105 for (i = 0; i < langs.length; i++) {
106 if (langs[i][0] === '*') {
107 break;
108 } else if (_.contains(available, langs[i][0])) {
109 found_locale = langs[i][0];
110 break;
111 }
112 }
113
114 return serveLocale.call(that, request, response, found_locale);
115 });
116 };
117
118
119 /**
120 * Send a locale to the browser
121 */
122 var serveLocale = function (request, response, locale_id) {
123 this.file_server.serveFile('/assets/locales/' + locale_id + '.json', 200, {
124 Vary: 'Accept-Language',
125 'Content-Language': locale_id
126 }, request, response);
127 };
128
129
130 /**
131 * Handle the settings.json request
132 */
133 var serveSettings = function(request, response) {
134 var referrer_url,
135 debug = false,
136 settings;
137
138 // Check the referrer for a debug option
139 if (request.headers['referer']) {
140 referrer_url = url.parse(request.headers['referer'], true);
141 if (referrer_url.query && referrer_url.query.debug) {
142 debug = true;
143 }
144 }
145
146 SettingsGenerator.get(debug, function(settings) {
147 if (request.headers['if-none-match'] && request.headers['if-none-match'] === settings.hash) {
148 response.writeHead(304, 'Not Modified');
149 return response.end();
150 }
151
152 response.writeHead(200, {
153 'ETag': settings.hash,
154 'Content-Type': 'application/json'
155 });
156 response.end(settings.settings);
157 });
158 };