1 var url
= require('url'),
3 node_static
= require('node-static'),
4 Negotiator
= require('negotiator'),
5 winston
= require('winston'),
6 SettingsGenerator
= require('./settingsgenerator.js'),
7 Stats
= require('./stats.js');
11 // Cached list of available translations
12 var cached_available_locales
= null;
16 var HttpHandler = function (config
) {
17 var public_http
= config
.public_http
|| 'client/';
18 this.file_server
= new node_static
.Server(public_http
);
20 if (!cached_available_locales
) {
25 module
.exports
.HttpHandler
= HttpHandler
;
29 HttpHandler
.prototype.serve = function (request
, response
) {
30 // The incoming requests base path (ie. /kiwiclient)
31 var base_path
, base_check
,
32 whitelisted_folders
= ['/assets', '/src'],
33 is_whitelisted_folder
= false;
35 // Trim off any trailing slashes from the base_path
36 base_path
= global
.config
.http_base_path
|| '';
37 if (base_path
.substr(base_path
.length
- 1) === '/') {
38 base_path
= base_path
.substr(0, base_path
.length
- 1);
41 // Normalise the URL + remove query strings to compare against the base_path
42 base_check
= request
.url
.split('?')[0];
43 if (base_check
.substr(base_check
.length
- 1) !== '/') {
47 // Normalise the URL we use by removing the base path
48 if (base_check
.indexOf(base_path
+ '/') === 0) {
49 request
.url
= request
.url
.replace(base_path
, '');
51 } else if (base_check
!== '/') {
52 // We don't handle requests outside of the base path and not /, so just 404
53 response
.writeHead(404);
54 response
.write('Not Found');
59 // Map any whitelisted folders to the local directories
60 whitelisted_folders
.forEach(function(folder
) {
61 if (request
.url
.indexOf(folder
) === 0) {
62 is_whitelisted_folder
= true;
66 // Any requests not for whitelisted assets returns the index page
67 if (!is_whitelisted_folder
) {
68 request
.url
= '/index.html';
71 if (request
.url
=== '/index.html') {
72 Stats
.incr('http.homepage');
75 // If the 'magic' translation is requested, figure out the best language to use from
76 // the Accept-Language HTTP header. If nothing is suitible, fallback to our en-gb default translation
77 if (request
.url
.substr(0, 16) === '/assets/locales/') {
78 if (request
.url
=== '/assets/locales/magic.json') {
79 return serveMagicLocale
.call(this, request
, response
);
81 response
.setHeader('Content-Language', request
.url
.substr(16, request
.url
.indexOf('.') - 16));
83 } else if (request
.url
.substr(0, 21) === '/assets/settings.json') {
84 return serveSettings
.call(this, request
, response
);
87 this.file_server
.serve(request
, response
, function (err
) {
89 response
.writeHead(err
.status
, err
.headers
);
99 * Cache the available locales we have so we don't read the same directory for each request
101 function updateLocalesCache() {
102 cached_available_locales
= [];
104 fs
.readdir(global
.config
.public_http
+ '/assets/locales', function (err
, files
) {
106 if (err
.code
=== 'ENOENT') {
107 winston
.error('No locale files could be found at ' + err
.path
);
109 winston
.error('Error reading locales.', err
);
113 (files
|| []).forEach(function (file
) {
114 if (file
.substr(-5) === '.json') {
115 cached_available_locales
.push(file
.slice(0, -5));
124 * Handle the /assets/locales/magic.json request
125 * Find the closest translation we have for the language
126 * set in the browser.
128 function serveMagicLocale(request
, response
) {
129 var default_locale_id
= 'en-gb',
130 found_locale
, negotiator
;
132 if (!request
.headers
['accept-language']) {
133 // No accept-language specified in the request so send the default
134 found_locale
= default_locale_id
;
137 negotiator
= new Negotiator(request
);
138 found_locale
= negotiator
.language(cached_available_locales
);
140 // If a locale couldn't be negotiated, use the default
141 found_locale
= found_locale
|| default_locale_id
;
144 // Send a locale to the browser
145 this.file_server
.serveFile('/assets/locales/' + found_locale
+ '.json', 200, {
146 Vary
: 'Accept-Language',
147 'Content-Language': found_locale
148 }, request
, response
);
154 * Handle the settings.json request
156 function serveSettings(request
, response
) {
160 // Check the referrer for a debug option
161 if (request
.headers
.referer
) {
162 referrer_url
= url
.parse(request
.headers
.referer
, true);
163 if (referrer_url
.query
&& referrer_url
.query
.debug
) {
168 SettingsGenerator
.get(debug
, function(err
, settings
) {
170 winston
.error('Error generating settings', err
);
171 response
.writeHead(500, 'Internal Server Error');
172 return response
.end();
175 if (request
.headers
['if-none-match'] && request
.headers
['if-none-match'] === settings
.hash
) {
176 response
.writeHead(304, 'Not Modified');
177 return response
.end();
180 response
.writeHead(200, {
181 'ETag': settings
.hash
,
182 'Content-Type': 'application/json'
184 response
.end(settings
.settings
);