1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | var HttpNotFoundError = require('@themost/common/errors').HttpNotFoundError;
|
11 | var HttpServerError = require('@themost/common/errors').HttpServerError;
|
12 | var HttpForbiddenError = require('@themost/common/errors').HttpForbiddenError;
|
13 | var TraceUtils = require('@themost/common/utils').TraceUtils;
|
14 | var fs = require('fs');
|
15 | var url = require('url');
|
16 | var path = require("path");
|
17 | var crypto = require('crypto');
|
18 | var _ = require('lodash');
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | function StaticHandler(rootDir) {
|
30 | this.rootDir = rootDir;
|
31 | }
|
32 |
|
33 | StaticHandler.prototype.validateRequest = function(context, callback) {
|
34 | callback = callback || function() {};
|
35 | callback.call(context);
|
36 | };
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | StaticHandler.prototype.mapRequest = function(context, callback)
|
42 | {
|
43 | callback = callback || function() {};
|
44 | try {
|
45 | if (typeof this.rootDir !== 'string') {
|
46 | return callback();
|
47 | }
|
48 | if (_.isEmpty(this.rootDir)) {
|
49 | return callback();
|
50 | }
|
51 |
|
52 | var uri = url.parse(context.request.url);
|
53 | var filePath = path.join(this.rootDir ,uri.pathname);
|
54 | fs.exists(filePath, function(exists) {
|
55 | if (!exists) {
|
56 | callback(null);
|
57 | }
|
58 | else {
|
59 | fs.stat(filePath, function(err, stats) {
|
60 | if (err) {
|
61 | callback(err);
|
62 | }
|
63 | else {
|
64 |
|
65 | if (stats && stats.isFile()) {
|
66 |
|
67 | context.request.currentHandler = new StaticHandler();
|
68 |
|
69 | context.request.currentExecutionPath = filePath;
|
70 |
|
71 | context.request.currentExecutionFileStats = stats;
|
72 | }
|
73 | callback(null);
|
74 | }
|
75 | });
|
76 | }
|
77 | });
|
78 | } catch (e) {
|
79 | callback(e);
|
80 | }
|
81 | };
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | StaticHandler.prototype.unmodifiedRequest = function(context, executionPath, callback) {
|
90 | try {
|
91 | var requestETag = context.request.headers['if-none-match'];
|
92 | if (typeof requestETag === 'undefined' || requestETag === null) {
|
93 | callback(null, false);
|
94 | return;
|
95 | }
|
96 | fs.exists(executionPath, function(exists) {
|
97 | try {
|
98 | if (exists) {
|
99 | fs.stat(executionPath, function(err, stats) {
|
100 | if (err) {
|
101 | callback(err);
|
102 | }
|
103 | else {
|
104 | if (!stats.isFile()) {
|
105 | callback(null, false);
|
106 | }
|
107 | else {
|
108 |
|
109 | var md5 = crypto.createHash('md5');
|
110 | md5.update(stats.mtime.toString());
|
111 | var responseETag = md5.digest('base64');
|
112 | return callback(null, (requestETag===responseETag));
|
113 | }
|
114 | }
|
115 | });
|
116 | }
|
117 | else {
|
118 | callback(null, false);
|
119 | }
|
120 | }
|
121 | catch (err) {
|
122 | TraceUtils.error(err);
|
123 | callback(null, false);
|
124 | }
|
125 | });
|
126 | }
|
127 | catch (err) {
|
128 | TraceUtils.error(err);
|
129 | callback(null, false);
|
130 | }
|
131 | };
|
132 |
|
133 | StaticHandler.prototype.preflightRequest = function(context, callback) {
|
134 | try {
|
135 |
|
136 | if (context && (context.request.currentHandler instanceof StaticHandler)) {
|
137 | var headerNames = context.response["_headerNames"] || { };
|
138 | if (typeof headerNames["access-control-allow-origin"] === 'undefined') {
|
139 | if (context.request.headers.origin) {
|
140 | context.response.setHeader("Access-Control-Allow-Origin", context.request.headers.origin);
|
141 | }
|
142 | else {
|
143 | context.response.setHeader("Access-Control-Allow-Origin", "*");
|
144 | }
|
145 | }
|
146 | if (typeof headerNames["access-control-allow-headers"] === 'undefined')
|
147 | context.response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Content-Language, Accept, Accept-Language, Authorization");
|
148 | if (typeof headerNames["access-control-allow-credentials"] === 'undefined')
|
149 | context.response.setHeader("Access-Control-Allow-Credentials", "true");
|
150 | if (typeof headerNames["access-control-allow-methods"] === 'undefined')
|
151 | context.response.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
152 | }
|
153 | return callback();
|
154 | }
|
155 | catch(e) {
|
156 | callback(e);
|
157 | }
|
158 |
|
159 | };
|
160 |
|
161 | StaticHandler.prototype.postMapRequest = function(context, callback) {
|
162 | return StaticHandler.prototype.preflightRequest.call(this, context, callback);
|
163 | };
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 | StaticHandler.prototype.processRequest = function(context, callback)
|
170 | {
|
171 | callback = callback || function() {};
|
172 | try {
|
173 | if (context.is('OPTIONS')) {
|
174 |
|
175 | return callback();
|
176 | }
|
177 |
|
178 | var stats = context.request.currentExecutionFileStats;
|
179 | if (typeof stats === 'undefined' || stats === null) {
|
180 | callback(new HttpServerError('Invalid request handler.'));
|
181 | return;
|
182 | }
|
183 | if (!stats.isFile()) {
|
184 | return callback(new HttpNotFoundError());
|
185 | }
|
186 | else {
|
187 |
|
188 | var requestETag = context.request.headers['if-none-match'];
|
189 |
|
190 | var md5 = crypto.createHash('md5');
|
191 | md5.update(stats.mtime.toString());
|
192 | var responseETag = md5.digest('base64');
|
193 | if (requestETag)
|
194 | if (requestETag===responseETag) {
|
195 |
|
196 | context.response.writeHead(304, { });
|
197 | context.response.end();
|
198 | return callback.call(context);
|
199 | }
|
200 |
|
201 | var extensionName = path.extname(context.request.currentExecutionPath);
|
202 |
|
203 | var mimes = context.getApplication().getConfiguration().mimes;
|
204 | var contentType = null, contentEncoding=null;
|
205 |
|
206 | var mime =mimes.filter(function(x) { return x.extension===extensionName; })[0];
|
207 | if (mime) {
|
208 | contentType = mime.type;
|
209 | if (mime.encoding)
|
210 | contentEncoding = mime.encoding;
|
211 | }
|
212 |
|
213 | if (contentType===null) {
|
214 | callback(new HttpForbiddenError())
|
215 | }
|
216 | else {
|
217 |
|
218 | var source = fs.createReadStream(context.request.currentExecutionPath);
|
219 |
|
220 | context.response.writeHead(200, {'Content-Type': contentType + (contentEncoding ? ';charset=' + contentEncoding : ''), 'ETag' : responseETag});
|
221 |
|
222 | source.pipe(context.response);
|
223 |
|
224 | source.on('end', function() {
|
225 | callback();
|
226 | });
|
227 |
|
228 | source.on('error', function(err) {
|
229 | callback(err);
|
230 | });
|
231 | }
|
232 | }
|
233 | }
|
234 | catch (e) {
|
235 | callback.call(context, e);
|
236 | }
|
237 | };
|
238 |
|
239 |
|
240 | if (typeof exports !== 'undefined') {
|
241 | module.exports.StaticHandler = StaticHandler;
|
242 | module.exports.createInstance = function() {
|
243 | return new StaticHandler();
|
244 | };
|
245 | }
|
246 |
|
247 |
|