Mercurial > hg > nodescore
comparison node_modules/socket.io/lib/static.js @ 69:333afcfd3f3a
added node_modules to project and fixed path to chronometer
also added deps to installer script
author | tzara <rc-web@kiben.net> |
---|---|
date | Sat, 26 Oct 2013 14:12:50 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
68:b076cd17638c | 69:333afcfd3f3a |
---|---|
1 | |
2 /*! | |
3 * socket.io-node | |
4 * Copyright(c) 2011 LearnBoost <dev@learnboost.com> | |
5 * MIT Licensed | |
6 */ | |
7 | |
8 /** | |
9 * Module dependencies. | |
10 */ | |
11 | |
12 var client = require('socket.io-client') | |
13 , cp = require('child_process') | |
14 , fs = require('fs') | |
15 , util = require('./util'); | |
16 | |
17 /** | |
18 * File type details. | |
19 * | |
20 * @api private | |
21 */ | |
22 | |
23 var mime = { | |
24 js: { | |
25 type: 'application/javascript' | |
26 , encoding: 'utf8' | |
27 , gzip: true | |
28 } | |
29 , swf: { | |
30 type: 'application/x-shockwave-flash' | |
31 , encoding: 'binary' | |
32 , gzip: false | |
33 } | |
34 }; | |
35 | |
36 /** | |
37 * Regexp for matching custom transport patterns. Users can configure their own | |
38 * socket.io bundle based on the url structure. Different transport names are | |
39 * concatinated using the `+` char. /socket.io/socket.io+websocket.js should | |
40 * create a bundle that only contains support for the websocket. | |
41 * | |
42 * @api private | |
43 */ | |
44 | |
45 var bundle = /\+((?:\+)?[\w\-]+)*(?:\.v\d+\.\d+\.\d+)?(?:\.js)$/ | |
46 , versioning = /\.v\d+\.\d+\.\d+(?:\.js)$/; | |
47 | |
48 /** | |
49 * Export the constructor | |
50 */ | |
51 | |
52 exports = module.exports = Static; | |
53 | |
54 /** | |
55 * Static constructor | |
56 * | |
57 * @api public | |
58 */ | |
59 | |
60 function Static (manager) { | |
61 this.manager = manager; | |
62 this.cache = {}; | |
63 this.paths = {}; | |
64 | |
65 this.init(); | |
66 } | |
67 | |
68 /** | |
69 * Initialize the Static by adding default file paths. | |
70 * | |
71 * @api public | |
72 */ | |
73 | |
74 Static.prototype.init = function () { | |
75 /** | |
76 * Generates a unique id based the supplied transports array | |
77 * | |
78 * @param {Array} transports The array with transport types | |
79 * @api private | |
80 */ | |
81 function id (transports) { | |
82 var id = transports.join('').split('').map(function (char) { | |
83 return ('' + char.charCodeAt(0)).split('').pop(); | |
84 }).reduce(function (char, id) { | |
85 return char +id; | |
86 }); | |
87 | |
88 return client.version + ':' + id; | |
89 } | |
90 | |
91 /** | |
92 * Generates a socket.io-client file based on the supplied transports. | |
93 * | |
94 * @param {Array} transports The array with transport types | |
95 * @param {Function} callback Callback for the static.write | |
96 * @api private | |
97 */ | |
98 | |
99 function build (transports, callback) { | |
100 client.builder(transports, { | |
101 minify: self.manager.enabled('browser client minification') | |
102 }, function (err, content) { | |
103 callback(err, content ? new Buffer(content) : null, id(transports)); | |
104 } | |
105 ); | |
106 } | |
107 | |
108 var self = this; | |
109 | |
110 // add our default static files | |
111 this.add('/static/flashsocket/WebSocketMain.swf', { | |
112 file: client.dist + '/WebSocketMain.swf' | |
113 }); | |
114 | |
115 this.add('/static/flashsocket/WebSocketMainInsecure.swf', { | |
116 file: client.dist + '/WebSocketMainInsecure.swf' | |
117 }); | |
118 | |
119 // generates dedicated build based on the available transports | |
120 this.add('/socket.io.js', function (path, callback) { | |
121 build(self.manager.get('transports'), callback); | |
122 }); | |
123 | |
124 this.add('/socket.io.v', { mime: mime.js }, function (path, callback) { | |
125 build(self.manager.get('transports'), callback); | |
126 }); | |
127 | |
128 // allow custom builds based on url paths | |
129 this.add('/socket.io+', { mime: mime.js }, function (path, callback) { | |
130 var available = self.manager.get('transports') | |
131 , matches = path.match(bundle) | |
132 , transports = []; | |
133 | |
134 if (!matches) return callback('No valid transports'); | |
135 | |
136 // make sure they valid transports | |
137 matches[0].split('.')[0].split('+').slice(1).forEach(function (transport) { | |
138 if (!!~available.indexOf(transport)) { | |
139 transports.push(transport); | |
140 } | |
141 }); | |
142 | |
143 if (!transports.length) return callback('No valid transports'); | |
144 build(transports, callback); | |
145 }); | |
146 | |
147 // clear cache when transports change | |
148 this.manager.on('set:transports', function (key, value) { | |
149 delete self.cache['/socket.io.js']; | |
150 Object.keys(self.cache).forEach(function (key) { | |
151 if (bundle.test(key)) { | |
152 delete self.cache[key]; | |
153 } | |
154 }); | |
155 }); | |
156 }; | |
157 | |
158 /** | |
159 * Gzip compress buffers. | |
160 * | |
161 * @param {Buffer} data The buffer that needs gzip compression | |
162 * @param {Function} callback | |
163 * @api public | |
164 */ | |
165 | |
166 Static.prototype.gzip = function (data, callback) { | |
167 var gzip = cp.spawn('gzip', ['-9', '-c', '-f', '-n']) | |
168 , encoding = Buffer.isBuffer(data) ? 'binary' : 'utf8' | |
169 , buffer = [] | |
170 , err; | |
171 | |
172 gzip.stdout.on('data', function (data) { | |
173 buffer.push(data); | |
174 }); | |
175 | |
176 gzip.stderr.on('data', function (data) { | |
177 err = data +''; | |
178 buffer.length = 0; | |
179 }); | |
180 | |
181 gzip.on('close', function () { | |
182 if (err) return callback(err); | |
183 | |
184 var size = 0 | |
185 , index = 0 | |
186 , i = buffer.length | |
187 , content; | |
188 | |
189 while (i--) { | |
190 size += buffer[i].length; | |
191 } | |
192 | |
193 content = new Buffer(size); | |
194 i = buffer.length; | |
195 | |
196 buffer.forEach(function (buffer) { | |
197 var length = buffer.length; | |
198 | |
199 buffer.copy(content, index, 0, length); | |
200 index += length; | |
201 }); | |
202 | |
203 buffer.length = 0; | |
204 callback(null, content); | |
205 }); | |
206 | |
207 gzip.stdin.end(data, encoding); | |
208 }; | |
209 | |
210 /** | |
211 * Is the path a static file? | |
212 * | |
213 * @param {String} path The path that needs to be checked | |
214 * @api public | |
215 */ | |
216 | |
217 Static.prototype.has = function (path) { | |
218 // fast case | |
219 if (this.paths[path]) return this.paths[path]; | |
220 | |
221 var keys = Object.keys(this.paths) | |
222 , i = keys.length; | |
223 | |
224 while (i--) { | |
225 if (-~path.indexOf(keys[i])) return this.paths[keys[i]]; | |
226 } | |
227 | |
228 return false; | |
229 }; | |
230 | |
231 /** | |
232 * Add new paths new paths that can be served using the static provider. | |
233 * | |
234 * @param {String} path The path to respond to | |
235 * @param {Options} options Options for writing out the response | |
236 * @param {Function} [callback] Optional callback if no options.file is | |
237 * supplied this would be called instead. | |
238 * @api public | |
239 */ | |
240 | |
241 Static.prototype.add = function (path, options, callback) { | |
242 var extension = /(?:\.(\w{1,4}))$/.exec(path); | |
243 | |
244 if (!callback && typeof options == 'function') { | |
245 callback = options; | |
246 options = {}; | |
247 } | |
248 | |
249 options.mime = options.mime || (extension ? mime[extension[1]] : false); | |
250 | |
251 if (callback) options.callback = callback; | |
252 if (!(options.file || options.callback) || !options.mime) return false; | |
253 | |
254 this.paths[path] = options; | |
255 | |
256 return true; | |
257 }; | |
258 | |
259 /** | |
260 * Writes a static response. | |
261 * | |
262 * @param {String} path The path for the static content | |
263 * @param {HTTPRequest} req The request object | |
264 * @param {HTTPResponse} res The response object | |
265 * @api public | |
266 */ | |
267 | |
268 Static.prototype.write = function (path, req, res) { | |
269 /** | |
270 * Write a response without throwing errors because can throw error if the | |
271 * response is no longer writable etc. | |
272 * | |
273 * @api private | |
274 */ | |
275 | |
276 function write (status, headers, content, encoding) { | |
277 try { | |
278 res.writeHead(status, headers || undefined); | |
279 | |
280 // only write content if it's not a HEAD request and we actually have | |
281 // some content to write (304's doesn't have content). | |
282 res.end( | |
283 req.method !== 'HEAD' && content ? content : '' | |
284 , encoding || undefined | |
285 ); | |
286 } catch (e) {} | |
287 } | |
288 | |
289 /** | |
290 * Answers requests depending on the request properties and the reply object. | |
291 * | |
292 * @param {Object} reply The details and content to reply the response with | |
293 * @api private | |
294 */ | |
295 | |
296 function answer (reply) { | |
297 var cached = req.headers['if-none-match'] === reply.etag; | |
298 if (cached && self.manager.enabled('browser client etag')) { | |
299 return write(304); | |
300 } | |
301 | |
302 var accept = req.headers['accept-encoding'] || '' | |
303 , gzip = !!~accept.toLowerCase().indexOf('gzip') | |
304 , mime = reply.mime | |
305 , versioned = reply.versioned | |
306 , headers = { | |
307 'Content-Type': mime.type | |
308 }; | |
309 | |
310 // check if we can add a etag | |
311 if (self.manager.enabled('browser client etag') && reply.etag && !versioned) { | |
312 headers['Etag'] = reply.etag; | |
313 } | |
314 | |
315 // see if we need to set Expire headers because the path is versioned | |
316 if (versioned) { | |
317 var expires = self.manager.get('browser client expires'); | |
318 headers['Cache-Control'] = 'private, x-gzip-ok="", max-age=' + expires; | |
319 headers['Date'] = new Date().toUTCString(); | |
320 headers['Expires'] = new Date(Date.now() + (expires * 1000)).toUTCString(); | |
321 } | |
322 | |
323 if (gzip && reply.gzip) { | |
324 headers['Content-Length'] = reply.gzip.length; | |
325 headers['Content-Encoding'] = 'gzip'; | |
326 headers['Vary'] = 'Accept-Encoding'; | |
327 write(200, headers, reply.gzip.content, mime.encoding); | |
328 } else { | |
329 headers['Content-Length'] = reply.length; | |
330 write(200, headers, reply.content, mime.encoding); | |
331 } | |
332 | |
333 self.manager.log.debug('served static content ' + path); | |
334 } | |
335 | |
336 var self = this | |
337 , details; | |
338 | |
339 // most common case first | |
340 if (this.manager.enabled('browser client cache') && this.cache[path]) { | |
341 return answer(this.cache[path]); | |
342 } else if (this.manager.get('browser client handler')) { | |
343 return this.manager.get('browser client handler').call(this, req, res); | |
344 } else if ((details = this.has(path))) { | |
345 /** | |
346 * A small helper function that will let us deal with fs and dynamic files | |
347 * | |
348 * @param {Object} err Optional error | |
349 * @param {Buffer} content The data | |
350 * @api private | |
351 */ | |
352 | |
353 function ready (err, content, etag) { | |
354 if (err) { | |
355 self.manager.log.warn('Unable to serve file. ' + (err.message || err)); | |
356 return write(500, null, 'Error serving static ' + path); | |
357 } | |
358 | |
359 // store the result in the cache | |
360 var reply = self.cache[path] = { | |
361 content: content | |
362 , length: content.length | |
363 , mime: details.mime | |
364 , etag: etag || client.version | |
365 , versioned: versioning.test(path) | |
366 }; | |
367 | |
368 // check if gzip is enabled | |
369 if (details.mime.gzip && self.manager.enabled('browser client gzip')) { | |
370 self.gzip(content, function (err, content) { | |
371 if (!err) { | |
372 reply.gzip = { | |
373 content: content | |
374 , length: content.length | |
375 } | |
376 } | |
377 | |
378 answer(reply); | |
379 }); | |
380 } else { | |
381 answer(reply); | |
382 } | |
383 } | |
384 | |
385 if (details.file) { | |
386 fs.readFile(details.file, ready); | |
387 } else if(details.callback) { | |
388 details.callback.call(this, path, ready); | |
389 } else { | |
390 write(404, null, 'File handle not found'); | |
391 } | |
392 } else { | |
393 write(404, null, 'File not found'); | |
394 } | |
395 }; |