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