Merge branch 'dev-issue-645-1' of https://github.com/mlutfy/KiwiIRC into mlutfy-dev...
[KiwiIRC.git] / server / httphandler.js
CommitLineData
bd299b17 1var url = require('url'),
0fa2ca42
JA
2 fs = require('fs'),
3 node_static = require('node-static'),
88a9b153 4 Negotiator = require('negotiator'),
840d6a6e 5 winston = require('winston'),
0ca6adac
D
6 SettingsGenerator = require('./settingsgenerator.js'),
7 Stats = require('./stats.js');
3eb6b5fd
D
8
9
10
a8bf3ea4 11
186531ed 12var HttpHandler = function (config) {
ceb43091
D
13 var public_http = config.public_http || 'client/';
14 this.file_server = new node_static.Server(public_http);
a8bf3ea4
JA
15};
16
186531ed
D
17module.exports.HttpHandler = HttpHandler;
18
19
20
21HttpHandler.prototype.serve = function (request, response) {
b65ad8f1 22 // The incoming requests base path (ie. /kiwiclient)
ba317e87
D
23 var base_path, base_check,
24 whitelisted_folders = ['/assets', '/src'],
25 is_whitelisted_folder = false;
b65ad8f1 26
ba317e87
D
27 // Trim off any trailing slashes from the base_path
28 base_path = global.config.http_base_path || '';
b65ad8f1
D
29 if (base_path.substr(base_path.length - 1) === '/') {
30 base_path = base_path.substr(0, base_path.length - 1);
31 }
0fa2ca42 32
f5114ea7
D
33 // Normalise the URL + remove query strings to compare against the base_path
34 base_check = request.url.split('?')[0];
ba317e87
D
35 if (base_check.substr(base_check.length - 1) !== '/') {
36 base_check += '/';
37 }
38
39 // Normalise the URL we use by removing the base path
40 if (base_check.indexOf(base_path + '/') === 0) {
41 request.url = request.url.replace(base_path, '');
42
43 } else if (base_check !== '/') {
44 // We don't handle requests outside of the base path and not /, so just 404
45 response.writeHead(404);
46 response.write('Not Found');
47 response.end();
48 return;
49 }
50
903f9288
D
51 // Map any whitelisted folders to the local directories
52 whitelisted_folders.forEach(function(folder) {
ba317e87
D
53 if (request.url.indexOf(folder) === 0) {
54 is_whitelisted_folder = true;
55 }
903f9288 56 });
c3511215 57
ba317e87
D
58 // Any requests not for whitelisted assets returns the index page
59 if (!is_whitelisted_folder) {
92f1ff73 60 request.url = '/index.html';
b075a0d6
D
61 }
62
0ca6adac
D
63 if (request.url === '/index.html') {
64 Stats.incr('http.homepage');
65 }
66
0fa2ca42 67 // If the 'magic' translation is requested, figure out the best language to use from
b722f3c6 68 // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation
28cde487
JA
69 if (request.url.substr(0, 16) === '/assets/locales/') {
70 if (request.url === '/assets/locales/magic.json') {
71 return serveMagicLocale.call(this, request, response);
72 } else {
73 response.setHeader('Content-Language', request.url.substr(16, request.url.indexOf('.') - 16));
74 }
cbcd1a23
JA
75 } else if (request.url.substr(0, 21) === '/assets/settings.json') {
76 return serveSettings.call(this, request, response);
0fa2ca42 77 }
0ae87edb 78
186531ed 79 this.file_server.serve(request, response, function (err) {
a8bf3ea4 80 if (err) {
bd299b17
JA
81 response.writeHead(err.status, err.headers);
82 response.end();
a8bf3ea4
JA
83 }
84 });
0fa2ca42
JA
85};
86
3eb6b5fd 87
88a9b153
D
88// Cached list of available translations
89var cached_available_locales = [];
90
91// Get a list of the available translations we have
e54fe284 92fs.readdir('../client/assets/locales', function (err, files) {
840d6a6e
D
93 if (err) {
94 if (err.code === 'ENOENT') {
95 winston.error('No locale files could be found at ' + err.path);
96 } else {
97 winston.error('Error reading locales.', err);
98 }
99 }
100
101 (files || []).forEach(function (file) {
88a9b153
D
102 if (file.substr(-5) === '.json') {
103 cached_available_locales.push(file.slice(0, -5));
104 }
105 });
106});
107
108
109
3eb6b5fd
D
110/**
111 * Handle the /assets/locales/magic.json request
112 * Find the closest translation we have for the language
113 * set in the browser.
114 **/
81f42bda 115function serveMagicLocale(request, response) {
88a9b153
D
116 var default_locale_id = 'en-gb',
117 found_locale, negotiator;
8e1ab29d 118
132ba3df
D
119 if (!request.headers['accept-language']) {
120 // No accept-language specified in the request so send the default
88a9b153 121 found_locale = default_locale_id;
cbc8feae 122
88a9b153
D
123 } else {
124 negotiator = new Negotiator(request);
125 found_locale = negotiator.language(cached_available_locales);
e539b24c
D
126
127 // If a locale couldn't be negotiated, use the default
128 found_locale = found_locale || default_locale_id;
88a9b153 129 }
3eb6b5fd 130
88a9b153
D
131 // Send a locale to the browser
132 this.file_server.serveFile('/assets/locales/' + found_locale + '.json', 200, {
3eb6b5fd 133 Vary: 'Accept-Language',
88a9b153 134 'Content-Language': found_locale
3eb6b5fd 135 }, request, response);
81f42bda 136}
cbcd1a23 137
3eb6b5fd 138
88a9b153 139
3eb6b5fd
D
140/**
141 * Handle the settings.json request
142 */
81f42bda 143function serveSettings(request, response) {
3eb6b5fd 144 var referrer_url,
81f42bda 145 debug = false;
3eb6b5fd
D
146
147 // Check the referrer for a debug option
81f42bda
JA
148 if (request.headers.referer) {
149 referrer_url = url.parse(request.headers.referer, true);
3eb6b5fd
D
150 if (referrer_url.query && referrer_url.query.debug) {
151 debug = true;
152 }
498afd32 153 }
cbe80532 154
81f42bda
JA
155 SettingsGenerator.get(debug, function(err, settings) {
156 if (err) {
157 winston.error('Error generating settings', err);
158 response.writeHead(500, 'Internal Server Error');
159 return response.end();
160 }
161
132ba3df
D
162 if (request.headers['if-none-match'] && request.headers['if-none-match'] === settings.hash) {
163 response.writeHead(304, 'Not Modified');
164 return response.end();
cbcd1a23
JA
165 }
166
132ba3df
D
167 response.writeHead(200, {
168 'ETag': settings.hash,
169 'Content-Type': 'application/json'
cbe80532 170 });
132ba3df 171 response.end(settings.settings);
cbe80532 172 });
81f42bda 173}