1 /*
  2  * Geddy JavaScript Web development framework
  3  * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
  4  *
  5  * Licensed under the Apache License, Version 2.0 (the "License");
  6  * you may not use this file except in compliance with the License.
  7  * You may obtain a copy of the License at
  8  *
  9  *         http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  *
 17 */
 18 
 19 var fs = require('fs')
 20   , errors = require('./errors');
 21 
 22 var response = new function () {
 23 
 24   this.formats = {
 25     txt: 'text/plain',
 26     html: 'text/html',
 27     json: 'application/json|text/json',
 28     js: 'application/javascript|text/javascript',
 29     xml: 'application/xml|text/xml'
 30   };
 31 
 32   this.formatsReverseMap = {};
 33   this.formatPatterns = {};
 34   this.formatsPreferred = {};
 35   var formatTypes;
 36   for (var p in this.formats) {
 37     formatTypes = this.formats[p].split('|');
 38     this.formatsPreferred[p] = formatTypes[0];
 39     for (var i = 0; i < formatTypes.length; i++) {
 40       this.formatsReverseMap[formatTypes[i]] = p;
 41     }
 42     this.formatPatterns[p] = new RegExp(
 43         this.formats[p].replace(/(\/)/g, "\\$1"));
 44   }
 45 
 46   // From Paperboy, http://github.com/felixge/node-paperboy
 47   this.contentTypes = JSON.parse(fs.readFileSync(__dirname +
 48       '/content_types.json').toString());
 49 
 50   this.charsets = {
 51     'text/javascript': 'UTF-8',
 52     'text/html': 'UTF-8'
 53   };
 54 
 55 }();
 56 
 57 var Response = function (resp) {
 58   this.resp = resp;
 59 };
 60 
 61 Response.prototype = new function () {
 62   this.send = function (content, statusCode, headers) {
 63     var success = !errors.errorTypes[statusCode];
 64     var s = statusCode || 200;
 65     var h = headers || {};
 66     this.setHeaders(s, h);
 67     this.finalize(content);
 68   };
 69 
 70   this.finalize = function (content) {
 71     this.writeBody(content);
 72     this.finish();
 73   };
 74 
 75   this.sendFile = function (filepath) {
 76     var _this = this;
 77     var ext = geddy.uri.getFileExtension(filepath);
 78     var contentType = response.contentTypes[ext] || 'application/octet-stream';
 79     var encoding = 'binary';
 80 
 81     this.setHeaders(200, {'Content-Type': contentType});
 82 
 83     // From Paperboy, http://github.com/felixge/node-paperboy
 84     fs.open(filepath, 'r', 0666, function (err, fd) {
 85       var pos = 0;
 86       var streamChunk = function () {
 87         fs.read(fd, 16 * 1024, pos, encoding,
 88             function (err, chunk, bytesRead) {
 89           if (!chunk) {
 90             fs.close(fd);
 91             _this.resp.end();
 92             /*
 93             log.debug('FILE: sent static file ' + filepath + '\nFinished handling request in ' +
 94                 ((new Date().getTime()) - _this.resp.startTime) + ' ms').flush();
 95             */
 96             return;
 97           }
 98 
 99           _this.resp.write(chunk, encoding);
100           pos += bytesRead;
101 
102           streamChunk();
103         });
104       }
105       streamChunk();
106     });
107 
108   };
109 
110   this.setHeaders = function (statusCode, headers) {
111     var contentType = headers['Content-Type'];
112     var charset = response.charsets[contentType];
113     if (charset) {
114       contentType += '; charset: ' + charset;
115       headers['Content-Type'] = contentType;
116     }
117     this.resp.statusCode = statusCode;
118     for (var p in headers) {
119       this.resp.setHeader(p, headers[p]);
120     }
121   };
122 
123   this.writeBody = function (content) {
124     this.resp.write(content);
125   };
126 
127   this.finish = function (success) {
128     this.resp.end();
129     if (success) {
130       /*
131       log.debug('Finished handling request in ' +
132           ((new Date().getTime()) - this.resp.startTime) + ' ms').flush();
133       */
134     }
135   };
136 
137 }();
138 
139 for (var p in response) { exports[p] = response[p]; }
140 exports.Response = Response;
141 
142