Commit | Line | Data |
---|---|---|
bd299b17 | 1 | var 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 | 12 | var 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 |
17 | module.exports.HttpHandler = HttpHandler; |
18 | ||
19 | ||
20 | ||
21 | HttpHandler.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 |
89 | var cached_available_locales = []; | |
90 | ||
91 | // Get a list of the available translations we have | |
e54fe284 | 92 | fs.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 | 115 | function 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 | 143 | function 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 | } |