alchemymvc 1.2.5 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/lib/app/assets/scripts/.gitkeep +0 -0
- package/lib/app/assets/stylesheets/alchemy-info.less +0 -0
- package/lib/app/behaviour/publishable_behaviour.js +0 -0
- package/lib/app/behaviour/revision_behaviour.js +0 -0
- package/lib/app/behaviour/sluggable_behaviour.js +0 -0
- package/lib/app/component/.gitkeep +0 -0
- package/lib/app/conduit/electron_conduit.js +0 -0
- package/lib/app/conduit/http_conduit.js +173 -173
- package/lib/app/conduit/socket_conduit.js +620 -620
- package/lib/app/controller/alchemy_info_controller.js +0 -0
- package/lib/app/datasource/mongo_datasource.js +0 -0
- package/lib/app/helper/client_collection.js +0 -0
- package/lib/app/helper/pagination_helper.js +0 -0
- package/lib/app/helper/router_helper.js +0 -0
- package/lib/app/helper/socket_helper.js +613 -613
- package/lib/app/helper_component/paginate_component.js +0 -0
- package/lib/app/helper_controller/component.js +0 -0
- package/lib/app/helper_controller/conduit.js +0 -0
- package/lib/app/helper_controller/controller.js +0 -0
- package/lib/app/helper_datasource/00-nosql_datasource.js +0 -0
- package/lib/app/helper_datasource/05-fallback_datasource.js +0 -0
- package/lib/app/helper_datasource/idb_datasource.js +0 -0
- package/lib/app/helper_datasource/indexed_db.js +0 -0
- package/lib/app/helper_field/00-objectid_field.js +0 -0
- package/lib/app/helper_field/06-text_field.js +0 -0
- package/lib/app/helper_field/10-number_field.js +0 -0
- package/lib/app/helper_field/boolean_field.js +0 -0
- package/lib/app/helper_field/date_field.js +0 -0
- package/lib/app/helper_field/datetime_field.js +0 -0
- package/lib/app/helper_field/enum_field.js +0 -0
- package/lib/app/helper_field/geopoint_field.js +0 -0
- package/lib/app/helper_field/habtm_field.js +0 -0
- package/lib/app/helper_field/hasoneparent_field.js +0 -0
- package/lib/app/helper_field/html_field.js +0 -0
- package/lib/app/helper_field/integer_field.js +0 -0
- package/lib/app/helper_field/object_field.js +0 -0
- package/lib/app/helper_field/regexp_field.js +0 -0
- package/lib/app/helper_field/schema_field.js +23 -2
- package/lib/app/helper_field/time_field.js +0 -0
- package/lib/app/helper_field/url_field.js +0 -0
- package/lib/app/helper_model/criteria.js +0 -0
- package/lib/app/helper_model/db_query.js +0 -0
- package/lib/app/helper_model/document_list.js +0 -0
- package/lib/app/model/alchemy_task_model.js +0 -0
- package/lib/app/routes.js +0 -0
- package/lib/app/view/alchemy/info.ejs +0 -0
- package/lib/app/view/error/unknown.ejs +0 -0
- package/lib/app/view/paginate/navlist.ejs +0 -0
- package/lib/bootstrap.js +0 -0
- package/lib/class/behaviour.js +0 -0
- package/lib/class/component.js +0 -0
- package/lib/class/conduit.js +2555 -2552
- package/lib/class/controller.js +4 -1
- package/lib/class/document_list.js +0 -0
- package/lib/class/helper.js +0 -0
- package/lib/class/inode.js +0 -0
- package/lib/class/inode_dir.js +0 -0
- package/lib/class/inode_file.js +112 -112
- package/lib/class/inode_list.js +0 -0
- package/lib/class/model.js +1772 -1769
- package/lib/class/path_definition.js +0 -0
- package/lib/class/route.js +0 -0
- package/lib/class/session.js +0 -0
- package/lib/class/task.js +0 -0
- package/lib/core/base.js +50 -9
- package/lib/core/discovery.js +0 -0
- package/lib/core/routing.js +0 -0
- package/lib/core/socket.js +159 -159
- package/lib/init/alchemy.js +1823 -1823
- package/lib/init/constants.js +0 -0
- package/lib/init/functions.js +8 -4
- package/lib/init/load_functions.js +0 -0
- package/lib/init/requirements.js +101 -101
- package/package.json +74 -74
package/lib/class/conduit.js
CHANGED
|
@@ -1,2552 +1,2555 @@
|
|
|
1
|
-
var fileCache = alchemy.shared('files.fileCache'),
|
|
2
|
-
libstream = alchemy.use('stream'),
|
|
3
|
-
libpath = alchemy.use('path'),
|
|
4
|
-
libmime = alchemy.use('mime'),
|
|
5
|
-
libua = alchemy.use('useragent'),
|
|
6
|
-
zlib = alchemy.use('zlib'),
|
|
7
|
-
BODY = Symbol('body'),
|
|
8
|
-
TESTED_ROUTES = Symbol('tested_routes'),
|
|
9
|
-
magic,
|
|
10
|
-
fs = alchemy.use('fs'),
|
|
11
|
-
prefixes = alchemy.shared('Routing.prefixes');
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The Conduit Class
|
|
15
|
-
*
|
|
16
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
17
|
-
* @since 0.2.0
|
|
18
|
-
* @version 1.2.0
|
|
19
|
-
*
|
|
20
|
-
* @param {IncomingMessage} req
|
|
21
|
-
* @param {ServerResponse} res
|
|
22
|
-
* @param {Router} router
|
|
23
|
-
*/
|
|
24
|
-
var Conduit = Function.inherits('Alchemy.Base', 'Alchemy.Conduit', function Conduit(req, res, router) {
|
|
25
|
-
|
|
26
|
-
// Store the starting time
|
|
27
|
-
this.start = new Date();
|
|
28
|
-
|
|
29
|
-
// Create a reference to ourselves
|
|
30
|
-
this.conduit = this;
|
|
31
|
-
|
|
32
|
-
// Debug messages for this request
|
|
33
|
-
this.debuglog = [];
|
|
34
|
-
|
|
35
|
-
this._debugObject = this.debug({label: 'Initialize Conduit'});
|
|
36
|
-
this._debugConduitInitialize = this._debugObject;
|
|
37
|
-
|
|
38
|
-
// Allow use of the log in the views
|
|
39
|
-
if (alchemy.settings.debug) {
|
|
40
|
-
this.internal('debuglog', {_placeholder_: 'debuglog'});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Cookies to send to the client
|
|
44
|
-
this.new_cookies = {};
|
|
45
|
-
this.new_cookie_header = [];
|
|
46
|
-
|
|
47
|
-
// The headers to send
|
|
48
|
-
this.response_headers = {};
|
|
49
|
-
|
|
50
|
-
// Where the body will go
|
|
51
|
-
this.body = {};
|
|
52
|
-
|
|
53
|
-
// Where the files will go
|
|
54
|
-
this.files = {};
|
|
55
|
-
|
|
56
|
-
this.initValues();
|
|
57
|
-
this.setReqRes(req, res);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Deprecated property names
|
|
62
|
-
*
|
|
63
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
64
|
-
* @since 1.0.0
|
|
65
|
-
* @version 1.1.0
|
|
66
|
-
*/
|
|
67
|
-
Conduit.setDeprecatedProperty('originalPath', 'original_path');
|
|
68
|
-
Conduit.setDeprecatedProperty('newCookies', 'new_cookies');
|
|
69
|
-
Conduit.setDeprecatedProperty('newCookieHeader', 'new_cookie_header');
|
|
70
|
-
Conduit.setDeprecatedProperty('viewRender', 'renderer');
|
|
71
|
-
Conduit.setDeprecatedProperty('view_render', 'renderer');
|
|
72
|
-
Conduit.setDeprecatedProperty('sceneId', 'scene_id');
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Return the cookies
|
|
76
|
-
*
|
|
77
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
78
|
-
* @since 0.2.0
|
|
79
|
-
* @version 0.2.0
|
|
80
|
-
*/
|
|
81
|
-
Conduit.prepareProperty(function cookies() {
|
|
82
|
-
return String.decodeCookies(this.headers.cookie);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Return the parsed useragent string
|
|
87
|
-
*
|
|
88
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
89
|
-
* @since 0.2.0
|
|
90
|
-
* @version 0.5.0
|
|
91
|
-
*/
|
|
92
|
-
Conduit.prepareProperty(function useragent() {
|
|
93
|
-
return libua.lookup(this.headers['user-agent']);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Create a Hawkejs Renderer
|
|
98
|
-
*
|
|
99
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
100
|
-
* @since 0.2.0
|
|
101
|
-
* @version 1.1.5
|
|
102
|
-
*/
|
|
103
|
-
Conduit.prepareProperty(function renderer() {
|
|
104
|
-
|
|
105
|
-
let result;
|
|
106
|
-
|
|
107
|
-
if (this.parent && this.parent != this && this.parent.renderer) {
|
|
108
|
-
result = this.parent.renderer.createSubRenderer();
|
|
109
|
-
} else {
|
|
110
|
-
result = alchemy.hawkejs.createRenderer();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return result;
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Enforce the scene_id
|
|
118
|
-
*
|
|
119
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
120
|
-
* @since 1.1.0
|
|
121
|
-
* @version 1.1.7
|
|
122
|
-
*/
|
|
123
|
-
Conduit.enforceProperty(function scene_id(new_value, old_value) {
|
|
124
|
-
|
|
125
|
-
if (new_value) {
|
|
126
|
-
return new_value;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (this.headers['x-scene-id']) {
|
|
130
|
-
return this.headers['x-scene-id'];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// If there also was no old value, create a new scene
|
|
134
|
-
if (old_value == null) {
|
|
135
|
-
// Generate the scene_id
|
|
136
|
-
new_value = Crypto.randomHex(8) || Crypto.pseudoHex(8);
|
|
137
|
-
|
|
138
|
-
// Tell the session this scene can be expected
|
|
139
|
-
this.getSession().expectScene(new_value);
|
|
140
|
-
|
|
141
|
-
let path;
|
|
142
|
-
|
|
143
|
-
if (this.url) {
|
|
144
|
-
path = this.url.path;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Set the sceneid cookie
|
|
148
|
-
this.cookie('scene_start_' + ~~(Math.random()*1000), {
|
|
149
|
-
|
|
150
|
-
// The time this scene has started
|
|
151
|
-
start: Date.now(),
|
|
152
|
-
|
|
153
|
-
// The id of the scene
|
|
154
|
-
id: new_value
|
|
155
|
-
}, {
|
|
156
|
-
// Cookie should only be visible on this path
|
|
157
|
-
path: path,
|
|
158
|
-
|
|
159
|
-
// Cookie should not live for more than 15 seconds
|
|
160
|
-
maxAge: 1000 * 15
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
return new_value;
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Enforce the active_prefix
|
|
169
|
-
*
|
|
170
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
171
|
-
* @since 1.1.0
|
|
172
|
-
* @version 1.1.5
|
|
173
|
-
*/
|
|
174
|
-
Conduit.enforceProperty(function active_prefix(new_value, old_value) {
|
|
175
|
-
|
|
176
|
-
if (!new_value) {
|
|
177
|
-
this.renderer.language = null;
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (new_value == old_value) {
|
|
182
|
-
return new_value;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Set the active prefix
|
|
186
|
-
this.internal('active_prefix', new_value);
|
|
187
|
-
this.expose('active_prefix', new_value);
|
|
188
|
-
|
|
189
|
-
if (this.locales[0] != new_value) {
|
|
190
|
-
this.locales.unshift(new_value);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Set the translate options for use in hawkejs
|
|
194
|
-
this.internal('locales', this.locales);
|
|
195
|
-
this.expose('locales', this.locales);
|
|
196
|
-
|
|
197
|
-
let config = Prefix.get(new_value);
|
|
198
|
-
|
|
199
|
-
if (config) {
|
|
200
|
-
this.renderer.language = config.locale;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return new_value;
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Get a session object by id
|
|
208
|
-
*
|
|
209
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
210
|
-
* @since 0.2.0
|
|
211
|
-
* @version 0.2.0
|
|
212
|
-
*/
|
|
213
|
-
Conduit.setStatic(function getSessionById(id) {
|
|
214
|
-
return alchemy.sessions.get(id);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* See if this is a secure connection
|
|
219
|
-
*
|
|
220
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
221
|
-
* @since 0.4.2
|
|
222
|
-
* @version 1.0.2
|
|
223
|
-
*/
|
|
224
|
-
Conduit.setProperty(function is_secure() {
|
|
225
|
-
|
|
226
|
-
var protocol;
|
|
227
|
-
|
|
228
|
-
if (alchemy.settings.assume_https) {
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (this.headers && this.headers['x-forwarded-proto'] == 'https') {
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (this.url && this.url.protocol == 'https:') {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (this.protocol && this.protocol.startsWith('https')) {
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (this.encrypted == true) {
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return false;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Set the request body
|
|
253
|
-
*
|
|
254
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
255
|
-
* @since 1.1.0
|
|
256
|
-
* @version 1.1.0
|
|
257
|
-
*
|
|
258
|
-
* @param {Object}
|
|
259
|
-
*/
|
|
260
|
-
Conduit.setMethod(function setRequestBody(body) {
|
|
261
|
-
|
|
262
|
-
if (!body) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
Object.assign(this.body, body);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Has the given route been tested yet?
|
|
271
|
-
*
|
|
272
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
273
|
-
* @since 1.2.5
|
|
274
|
-
* @version 1.2.5
|
|
275
|
-
*
|
|
276
|
-
* @param {Route}
|
|
277
|
-
*/
|
|
278
|
-
Conduit.setMethod(function hasRouteBeenTested(route) {
|
|
279
|
-
|
|
280
|
-
if (!route || !this[TESTED_ROUTES]) {
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return this[TESTED_ROUTES].has(route);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Mark this route as having been tested
|
|
289
|
-
*
|
|
290
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
291
|
-
* @since 1.2.5
|
|
292
|
-
* @version 1.2.5
|
|
293
|
-
*
|
|
294
|
-
* @param {Route}
|
|
295
|
-
*/
|
|
296
|
-
Conduit.setMethod(function markRouteAsTested(route) {
|
|
297
|
-
|
|
298
|
-
if (!this[TESTED_ROUTES]) {
|
|
299
|
-
this[TESTED_ROUTES] = new Set();
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
this[TESTED_ROUTES].add(route);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Rewrite a certain URL parameter
|
|
307
|
-
* (Causing some kind of redirect)
|
|
308
|
-
*
|
|
309
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
310
|
-
* @since 1.2.5
|
|
311
|
-
* @version 1.2.5
|
|
312
|
-
*
|
|
313
|
-
* @param {String} route_param
|
|
314
|
-
* @param {*} new_value
|
|
315
|
-
*/
|
|
316
|
-
Conduit.setMethod(function rewriteRequestRouteParam(route_param, new_value) {
|
|
317
|
-
|
|
318
|
-
if (!this.rewritten_request_route_param) {
|
|
319
|
-
this.rewritten_request_route_param = {};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
this.rewritten_request_route_param[route_param] = new_value;
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Set the request files
|
|
327
|
-
*
|
|
328
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
329
|
-
* @since 1.1.0
|
|
330
|
-
* @version 1.1.0
|
|
331
|
-
*
|
|
332
|
-
* @param {Object}
|
|
333
|
-
*/
|
|
334
|
-
Conduit.setMethod(function setRequestFiles(files) {
|
|
335
|
-
|
|
336
|
-
if (!files) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
_setRequestFiles(this, files, this.files);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Set the request files
|
|
345
|
-
*
|
|
346
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
347
|
-
* @since 1.2.0
|
|
348
|
-
* @version 1.2.0
|
|
349
|
-
*
|
|
350
|
-
* @param {Conduit} conduit
|
|
351
|
-
* @param {Array} files
|
|
352
|
-
* @param {Object} target
|
|
353
|
-
*/
|
|
354
|
-
function _setRequestFiles(conduit, files, target) {
|
|
355
|
-
|
|
356
|
-
let context,
|
|
357
|
-
upload,
|
|
358
|
-
entry,
|
|
359
|
-
key;
|
|
360
|
-
|
|
361
|
-
for (key in files) {
|
|
362
|
-
entry = files[key];
|
|
363
|
-
|
|
364
|
-
if (Array.isArray(entry)) {
|
|
365
|
-
context = target[key];
|
|
366
|
-
|
|
367
|
-
if (!context) {
|
|
368
|
-
context = target[key] = {};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
_setRequestFiles(conduit, entry, context);
|
|
372
|
-
} else {
|
|
373
|
-
target[key] = Classes.Alchemy.Inode.File.from(entry);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Don't convert a conduit to any special json data
|
|
380
|
-
*
|
|
381
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
382
|
-
* @since 0.2.0
|
|
383
|
-
* @version 0.2.0
|
|
384
|
-
*/
|
|
385
|
-
Conduit.setMethod(function toJSON() {
|
|
386
|
-
return null;
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Set the request & response objects
|
|
391
|
-
*
|
|
392
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
393
|
-
* @since 1.2.0
|
|
394
|
-
* @version 1.2.0
|
|
395
|
-
*/
|
|
396
|
-
Conduit.setMethod(function setReqRes(req, res) {
|
|
397
|
-
|
|
398
|
-
if (req != null) {
|
|
399
|
-
// Make conduit available in req
|
|
400
|
-
req.conduit = this;
|
|
401
|
-
|
|
402
|
-
// Basic HTTP objects
|
|
403
|
-
this.request = req;
|
|
404
|
-
|
|
405
|
-
// The HTTP request headers
|
|
406
|
-
this.headers = req.headers;
|
|
407
|
-
|
|
408
|
-
// Parse the original URL without host
|
|
409
|
-
this.original_url = new RURL(req.url);
|
|
410
|
-
|
|
411
|
-
// Is this an AJAX request?
|
|
412
|
-
this.ajax = null;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (res != null) {
|
|
416
|
-
this.response = res;
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Init values
|
|
422
|
-
*
|
|
423
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
424
|
-
* @since 0.3.3
|
|
425
|
-
* @version 1.1.0
|
|
426
|
-
*/
|
|
427
|
-
Conduit.setMethod(function initValues() {
|
|
428
|
-
|
|
429
|
-
// Use passed-along router, or default router instance
|
|
430
|
-
this.router = this.router || Router;
|
|
431
|
-
|
|
432
|
-
// The path without any prefix, including section mounts
|
|
433
|
-
this.path = null;
|
|
434
|
-
|
|
435
|
-
// The path without prefix or section mount
|
|
436
|
-
this.sectionPath = null;
|
|
437
|
-
|
|
438
|
-
// The accepted languages
|
|
439
|
-
this.languages = null;
|
|
440
|
-
|
|
441
|
-
// URL paths can be prefixed with certain locales,
|
|
442
|
-
// these locales should then get preference over the user's browser locale
|
|
443
|
-
this.prefix = null;
|
|
444
|
-
|
|
445
|
-
// All the locales the user's browser accepts
|
|
446
|
-
this.locales = null;
|
|
447
|
-
|
|
448
|
-
// The matching Route instance
|
|
449
|
-
this.route = null;
|
|
450
|
-
|
|
451
|
-
// The named parameters inside the path
|
|
452
|
-
this.params = null;
|
|
453
|
-
|
|
454
|
-
// The original string parameters
|
|
455
|
-
this.route_string_parameters = null;
|
|
456
|
-
|
|
457
|
-
// The section vhost domain
|
|
458
|
-
this.sectionDomain = null;
|
|
459
|
-
|
|
460
|
-
// The section of the used route
|
|
461
|
-
this.section = null;
|
|
462
|
-
|
|
463
|
-
// The parsed path (including querystring)
|
|
464
|
-
this.url = null
|
|
465
|
-
|
|
466
|
-
// The current active theme
|
|
467
|
-
this.theme = null;
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Get the time since the conduit was made
|
|
472
|
-
*
|
|
473
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
474
|
-
* @since 0.2.0
|
|
475
|
-
* @version 1.1.0
|
|
476
|
-
*/
|
|
477
|
-
Conduit.setMethod(function time() {
|
|
478
|
-
return Date.now() - this.start;
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Parse the request, get information from the url
|
|
483
|
-
*
|
|
484
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
485
|
-
* @since 0.2.0
|
|
486
|
-
* @version 1.2.5
|
|
487
|
-
*
|
|
488
|
-
* @param {IncomingMessage} req
|
|
489
|
-
* @param {ServerResponse} res
|
|
490
|
-
*/
|
|
491
|
-
Conduit.setMethod(async function parseRequest() {
|
|
492
|
-
|
|
493
|
-
var protocol,
|
|
494
|
-
section;
|
|
495
|
-
|
|
496
|
-
if (this.method == null && this.request && this.request.method) {
|
|
497
|
-
this.method = this.request.method.toLowerCase();
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
this.parseShortcuts();
|
|
501
|
-
this.parseLanguages();
|
|
502
|
-
this.parsePrefix();
|
|
503
|
-
this.parseSection();
|
|
504
|
-
|
|
505
|
-
// Try getting the route
|
|
506
|
-
await this.parseRoute();
|
|
507
|
-
|
|
508
|
-
if (this.halt_request) {
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Is this encrypted?
|
|
513
|
-
if (this.encrypted == null) {
|
|
514
|
-
this.encrypted = this.request.connection.encrypted;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (this.rewritten_request_route_param) {
|
|
518
|
-
let params = Object.assign({}, this.route_string_parameters, this.rewritten_request_route_param);
|
|
519
|
-
let new_url = this.route.generateUrl(params, this);
|
|
520
|
-
this.overrideResponseUrl(new_url);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// If the url has already been parsed, return early
|
|
524
|
-
if (this.url) {
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (alchemy.settings.assume_https) {
|
|
529
|
-
protocol = 'https://';
|
|
530
|
-
} else if (this.headers['x-forwarded-proto']) {
|
|
531
|
-
protocol = this.headers['x-forwarded-proto'];
|
|
532
|
-
} else if (this.protocol) {
|
|
533
|
-
protocol = this.protocol;
|
|
534
|
-
} else if (this.encrypted) {
|
|
535
|
-
protocol = 'https://';
|
|
536
|
-
} else {
|
|
537
|
-
protocol = 'http://';
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Create a new RURL instance
|
|
541
|
-
this.url = new RURL();
|
|
542
|
-
|
|
543
|
-
// Set the protocol
|
|
544
|
-
this.url.protocol = protocol;
|
|
545
|
-
|
|
546
|
-
// Set the host
|
|
547
|
-
this.url.hostname = this.headers.host;
|
|
548
|
-
|
|
549
|
-
let path = this.path;
|
|
550
|
-
|
|
551
|
-
if (this.prefix) {
|
|
552
|
-
path = '/' + this.prefix + '/' + path;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
this.url.path = path;
|
|
556
|
-
|
|
557
|
-
return true;
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Parse the headers for shortcuts
|
|
562
|
-
*
|
|
563
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
564
|
-
* @since 0.2.0
|
|
565
|
-
* @version 0.2.0
|
|
566
|
-
*/
|
|
567
|
-
Conduit.setMethod(function parseShortcuts() {
|
|
568
|
-
|
|
569
|
-
var headers = this.headers;
|
|
570
|
-
|
|
571
|
-
// A request can just tell us what route to use
|
|
572
|
-
if (headers['x-alchemy-route-name']) {
|
|
573
|
-
this.route = this.router.getRouteByName(headers['x-alchemy-route-name']);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// And which prefix (this is a forced prefix)
|
|
577
|
-
if (headers['x-alchemy-prefix'] && prefixes[headers['x-alchemy-prefix']]) {
|
|
578
|
-
this.prefix = headers['x-alchemy-prefix'];
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Section domains can only be requested through headers
|
|
582
|
-
if (headers['x-alchemy-section-domain']) {
|
|
583
|
-
this.sectionDomain = headers['x-alchemy-section-domain'];
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Only get ajax on the first parse
|
|
587
|
-
if (this.ajax == null) {
|
|
588
|
-
this.ajax = headers['x-requested-with'] === 'XMLHttpRequest';
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Sort the parsed accept-language header array
|
|
595
|
-
*
|
|
596
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
597
|
-
* @since 0.0.1
|
|
598
|
-
* @version 0.0.1
|
|
599
|
-
*
|
|
600
|
-
* @param {Object} a
|
|
601
|
-
* @param {Object} b
|
|
602
|
-
*/
|
|
603
|
-
function qualityCmp(a, b) {
|
|
604
|
-
if (a.quality === b.quality) {
|
|
605
|
-
return 0;
|
|
606
|
-
} else if (a.quality < b.quality) {
|
|
607
|
-
return 1;
|
|
608
|
-
} else {
|
|
609
|
-
return -1;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Parses the HTTP accept-language header
|
|
615
|
-
*
|
|
616
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
617
|
-
* @since 0.0.1
|
|
618
|
-
* @version 0.2.0
|
|
619
|
-
*/
|
|
620
|
-
Conduit.setMethod(function parseLanguages() {
|
|
621
|
-
|
|
622
|
-
var rawLangs,
|
|
623
|
-
rawLang,
|
|
624
|
-
locales,
|
|
625
|
-
parts,
|
|
626
|
-
langs,
|
|
627
|
-
qval,
|
|
628
|
-
temp,
|
|
629
|
-
i,
|
|
630
|
-
q;
|
|
631
|
-
|
|
632
|
-
langs = [];
|
|
633
|
-
locales = [];
|
|
634
|
-
|
|
635
|
-
if (this.headers['accept-language']) {
|
|
636
|
-
|
|
637
|
-
rawLangs = this.headers['accept-language'].split(',');
|
|
638
|
-
|
|
639
|
-
for (i = 0; i < rawLangs.length; i++) {
|
|
640
|
-
rawLang = rawLangs[i];
|
|
641
|
-
|
|
642
|
-
parts = rawLang.split(';');
|
|
643
|
-
qval = null;
|
|
644
|
-
q = 1;
|
|
645
|
-
|
|
646
|
-
if (parts.length > 1 && parts[1].indexOf('q=') === 0) {
|
|
647
|
-
qval = parseFloat(parts[1].split('=')[1]);
|
|
648
|
-
|
|
649
|
-
if (isNaN(qval) === false) {
|
|
650
|
-
q = qval;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Get the lang-loc code
|
|
655
|
-
temp = parts[0].trim().toLowerCase().split('-');
|
|
656
|
-
|
|
657
|
-
langs.push({lang: temp[0], loc: temp[1], quality: q});
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
langs.sort(qualityCmp);
|
|
661
|
-
};
|
|
662
|
-
|
|
663
|
-
temp = {};
|
|
664
|
-
|
|
665
|
-
for (i = 0; i < langs.length; i++) {
|
|
666
|
-
if (!temp[langs[i].lang]) {
|
|
667
|
-
locales.push(langs[i].lang);
|
|
668
|
-
temp[langs[i].lang] = true;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
this.languages = langs;
|
|
673
|
-
this.locales = locales;
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Parses accept-encoding strings
|
|
678
|
-
*
|
|
679
|
-
* @author jshttp
|
|
680
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
681
|
-
* @since 0.2.0
|
|
682
|
-
* @version 0.2.0
|
|
683
|
-
*/
|
|
684
|
-
function parseEncoding(s, i) {
|
|
685
|
-
var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/);
|
|
686
|
-
|
|
687
|
-
if (!match) return null;
|
|
688
|
-
|
|
689
|
-
var encoding = match[1];
|
|
690
|
-
var q = 1;
|
|
691
|
-
if (match[2]) {
|
|
692
|
-
var params = match[2].split(';');
|
|
693
|
-
for (var i = 0; i < params.length; i ++) {
|
|
694
|
-
var p = params[i].trim().split('=');
|
|
695
|
-
if (p[0] === 'q') {
|
|
696
|
-
q = parseFloat(p[1]);
|
|
697
|
-
break;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
return {
|
|
703
|
-
encoding: encoding,
|
|
704
|
-
q: q,
|
|
705
|
-
i: i
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
/**
|
|
710
|
-
* Parses accept-encoding strings
|
|
711
|
-
*
|
|
712
|
-
* @author jshttp
|
|
713
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
714
|
-
* @since 0.2.0
|
|
715
|
-
* @version 0.2.0
|
|
716
|
-
*/
|
|
717
|
-
function specify(encoding, spec, index) {
|
|
718
|
-
var s = 0;
|
|
719
|
-
if(spec.encoding.toLowerCase() === encoding.toLowerCase()){
|
|
720
|
-
s |= 1;
|
|
721
|
-
} else if (spec.encoding !== '*' ) {
|
|
722
|
-
return null
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
return {
|
|
726
|
-
i: index,
|
|
727
|
-
o: spec.i,
|
|
728
|
-
q: spec.q,
|
|
729
|
-
s: s
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Parses the HTTP accept-encoding header
|
|
735
|
-
*
|
|
736
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
737
|
-
* @since 0.2.0
|
|
738
|
-
* @version 0.2.0
|
|
739
|
-
*/
|
|
740
|
-
Conduit.setMethod(function parseAcceptEncoding() {
|
|
741
|
-
|
|
742
|
-
var hasIdentity,
|
|
743
|
-
minQuality,
|
|
744
|
-
encoding,
|
|
745
|
-
accepts,
|
|
746
|
-
i,
|
|
747
|
-
j;
|
|
748
|
-
|
|
749
|
-
// Make sure this only runs once
|
|
750
|
-
if (this.accepted_encodings != null) {
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (!this.headers['accept-encoding']) {
|
|
755
|
-
this.accepted_encodings = false;
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
accepts = this.headers['accept-encoding'].split(',');
|
|
760
|
-
minQuality = 1;
|
|
761
|
-
|
|
762
|
-
for (i = 0, j = 0; i < accepts.length; i++) {
|
|
763
|
-
encoding = parseEncoding(accepts[i].trim(), i);
|
|
764
|
-
|
|
765
|
-
if (encoding) {
|
|
766
|
-
accepts[j++] = encoding;
|
|
767
|
-
hasIdentity = hasIdentity || specify('identity', encoding);
|
|
768
|
-
minQuality = Math.min(minQuality, encoding.q || 1);
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
if (!hasIdentity) {
|
|
773
|
-
/*
|
|
774
|
-
* If identity doesn't explicitly appear in the accept-encoding header,
|
|
775
|
-
* it's added to the list of acceptable encoding with the lowest q
|
|
776
|
-
*/
|
|
777
|
-
accepts[j++] = {
|
|
778
|
-
encoding: 'identity',
|
|
779
|
-
q: minQuality,
|
|
780
|
-
i: i
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// trim accepts
|
|
785
|
-
accepts.length = j;
|
|
786
|
-
|
|
787
|
-
this.accepted_encodings = accepts;
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
/**
|
|
791
|
-
* See if the wanted encoding is accepted by the client
|
|
792
|
-
*
|
|
793
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
794
|
-
* @since 0.2.0
|
|
795
|
-
* @version 0.2.0
|
|
796
|
-
*/
|
|
797
|
-
Conduit.setMethod(function accepts(encoding) {
|
|
798
|
-
|
|
799
|
-
var i;
|
|
800
|
-
|
|
801
|
-
// Parse the encodings on the fly
|
|
802
|
-
this.parseAcceptEncoding();
|
|
803
|
-
|
|
804
|
-
if (!this.accepted_encodings) {
|
|
805
|
-
return false;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
for (i = 0; i < this.accepted_encodings.length; i++) {
|
|
809
|
-
if (this.accepted_encodings[i].encoding == encoding) {
|
|
810
|
-
return true;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
return false;
|
|
815
|
-
});
|
|
816
|
-
|
|
817
|
-
/**
|
|
818
|
-
* Create a loopback conduit
|
|
819
|
-
*
|
|
820
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
821
|
-
* @since 0.2.0
|
|
822
|
-
* @version 1.1.3
|
|
823
|
-
*
|
|
824
|
-
* @param {Object} args
|
|
825
|
-
* @param {Function} callback
|
|
826
|
-
*
|
|
827
|
-
* @return {Alchemy.LoopbackConduit}
|
|
828
|
-
*/
|
|
829
|
-
Conduit.setMethod(function loopback(args, callback) {
|
|
830
|
-
return Classes.Alchemy.Conduit.Loopback.create(this, args, callback);
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Parse the request, get information from the url
|
|
835
|
-
*
|
|
836
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
837
|
-
* @since 0.2.0
|
|
838
|
-
* @version 1.1.0
|
|
839
|
-
*/
|
|
840
|
-
Conduit.setMethod(function parsePrefix() {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
* @
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
* @
|
|
983
|
-
*
|
|
984
|
-
*
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
this.
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
if
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
this.
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
//
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
//
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
this.renderer.
|
|
1185
|
-
|
|
1186
|
-
//
|
|
1187
|
-
this.renderer.
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
*
|
|
1238
|
-
* @
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
//
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
//
|
|
1275
|
-
this.response.
|
|
1276
|
-
|
|
1277
|
-
//
|
|
1278
|
-
this.response =
|
|
1279
|
-
|
|
1280
|
-
//
|
|
1281
|
-
this.
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1291
|
-
* @
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
*
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
* @
|
|
1311
|
-
* @
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
this.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
} else {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
* @
|
|
1414
|
-
* @
|
|
1415
|
-
*
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
*
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1489
|
-
* @
|
|
1490
|
-
* @
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1516
|
-
* @
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
*
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
*
|
|
1543
|
-
*
|
|
1544
|
-
*
|
|
1545
|
-
* @
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
//
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
*
|
|
1573
|
-
*
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
*
|
|
1585
|
-
*
|
|
1586
|
-
*
|
|
1587
|
-
* @
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
json_type = 'json
|
|
1614
|
-
json_fnc = JSON.
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
//
|
|
1646
|
-
this.setHeader('
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
// Set the
|
|
1665
|
-
this.setHeader('content-
|
|
1666
|
-
|
|
1667
|
-
//
|
|
1668
|
-
this.setHeader('
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
*
|
|
1685
|
-
*
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
*
|
|
1742
|
-
*
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
*
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
*
|
|
1765
|
-
*
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
//
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
*
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1848
|
-
* @
|
|
1849
|
-
*
|
|
1850
|
-
*
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
*
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
1872
|
-
* @
|
|
1873
|
-
* @
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
//
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
//
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
that.setHeader('cache-control', '
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
outStream.
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
that.response.on('
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
*
|
|
2153
|
-
*
|
|
2154
|
-
*
|
|
2155
|
-
* @
|
|
2156
|
-
*
|
|
2157
|
-
*
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
//
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
*
|
|
2226
|
-
*
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2257
|
-
* @
|
|
2258
|
-
* @
|
|
2259
|
-
*
|
|
2260
|
-
* @
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2283
|
-
*
|
|
2284
|
-
* @
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
*
|
|
2295
|
-
*
|
|
2296
|
-
*
|
|
2297
|
-
* @
|
|
2298
|
-
* @
|
|
2299
|
-
*
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
if (
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
// If
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
//
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
*
|
|
2353
|
-
*
|
|
2354
|
-
*
|
|
2355
|
-
* @
|
|
2356
|
-
* @
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
}
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
*
|
|
2376
|
-
*
|
|
2377
|
-
*
|
|
2378
|
-
* @
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
*
|
|
2396
|
-
*
|
|
2397
|
-
*
|
|
2398
|
-
* @
|
|
2399
|
-
* @
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
//
|
|
2407
|
-
this.
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
*
|
|
2415
|
-
*
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
*
|
|
2444
|
-
*
|
|
2445
|
-
*
|
|
2446
|
-
* @
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
*
|
|
2458
|
-
*
|
|
2459
|
-
*
|
|
2460
|
-
* @
|
|
2461
|
-
*
|
|
2462
|
-
*
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
*
|
|
2499
|
-
*
|
|
2500
|
-
*
|
|
2501
|
-
* @
|
|
2502
|
-
* @
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
*
|
|
2529
|
-
*
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
magic =
|
|
2547
|
-
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
}
|
|
2551
|
-
|
|
2552
|
-
|
|
1
|
+
var fileCache = alchemy.shared('files.fileCache'),
|
|
2
|
+
libstream = alchemy.use('stream'),
|
|
3
|
+
libpath = alchemy.use('path'),
|
|
4
|
+
libmime = alchemy.use('mime'),
|
|
5
|
+
libua = alchemy.use('useragent'),
|
|
6
|
+
zlib = alchemy.use('zlib'),
|
|
7
|
+
BODY = Symbol('body'),
|
|
8
|
+
TESTED_ROUTES = Symbol('tested_routes'),
|
|
9
|
+
magic,
|
|
10
|
+
fs = alchemy.use('fs'),
|
|
11
|
+
prefixes = alchemy.shared('Routing.prefixes');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The Conduit Class
|
|
15
|
+
*
|
|
16
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
17
|
+
* @since 0.2.0
|
|
18
|
+
* @version 1.2.0
|
|
19
|
+
*
|
|
20
|
+
* @param {IncomingMessage} req
|
|
21
|
+
* @param {ServerResponse} res
|
|
22
|
+
* @param {Router} router
|
|
23
|
+
*/
|
|
24
|
+
var Conduit = Function.inherits('Alchemy.Base', 'Alchemy.Conduit', function Conduit(req, res, router) {
|
|
25
|
+
|
|
26
|
+
// Store the starting time
|
|
27
|
+
this.start = new Date();
|
|
28
|
+
|
|
29
|
+
// Create a reference to ourselves
|
|
30
|
+
this.conduit = this;
|
|
31
|
+
|
|
32
|
+
// Debug messages for this request
|
|
33
|
+
this.debuglog = [];
|
|
34
|
+
|
|
35
|
+
this._debugObject = this.debug({label: 'Initialize Conduit'});
|
|
36
|
+
this._debugConduitInitialize = this._debugObject;
|
|
37
|
+
|
|
38
|
+
// Allow use of the log in the views
|
|
39
|
+
if (alchemy.settings.debug) {
|
|
40
|
+
this.internal('debuglog', {_placeholder_: 'debuglog'});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Cookies to send to the client
|
|
44
|
+
this.new_cookies = {};
|
|
45
|
+
this.new_cookie_header = [];
|
|
46
|
+
|
|
47
|
+
// The headers to send
|
|
48
|
+
this.response_headers = {};
|
|
49
|
+
|
|
50
|
+
// Where the body will go
|
|
51
|
+
this.body = {};
|
|
52
|
+
|
|
53
|
+
// Where the files will go
|
|
54
|
+
this.files = {};
|
|
55
|
+
|
|
56
|
+
this.initValues();
|
|
57
|
+
this.setReqRes(req, res);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Deprecated property names
|
|
62
|
+
*
|
|
63
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
64
|
+
* @since 1.0.0
|
|
65
|
+
* @version 1.1.0
|
|
66
|
+
*/
|
|
67
|
+
Conduit.setDeprecatedProperty('originalPath', 'original_path');
|
|
68
|
+
Conduit.setDeprecatedProperty('newCookies', 'new_cookies');
|
|
69
|
+
Conduit.setDeprecatedProperty('newCookieHeader', 'new_cookie_header');
|
|
70
|
+
Conduit.setDeprecatedProperty('viewRender', 'renderer');
|
|
71
|
+
Conduit.setDeprecatedProperty('view_render', 'renderer');
|
|
72
|
+
Conduit.setDeprecatedProperty('sceneId', 'scene_id');
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Return the cookies
|
|
76
|
+
*
|
|
77
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
78
|
+
* @since 0.2.0
|
|
79
|
+
* @version 0.2.0
|
|
80
|
+
*/
|
|
81
|
+
Conduit.prepareProperty(function cookies() {
|
|
82
|
+
return String.decodeCookies(this.headers.cookie);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Return the parsed useragent string
|
|
87
|
+
*
|
|
88
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
89
|
+
* @since 0.2.0
|
|
90
|
+
* @version 0.5.0
|
|
91
|
+
*/
|
|
92
|
+
Conduit.prepareProperty(function useragent() {
|
|
93
|
+
return libua.lookup(this.headers['user-agent']);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a Hawkejs Renderer
|
|
98
|
+
*
|
|
99
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
100
|
+
* @since 0.2.0
|
|
101
|
+
* @version 1.1.5
|
|
102
|
+
*/
|
|
103
|
+
Conduit.prepareProperty(function renderer() {
|
|
104
|
+
|
|
105
|
+
let result;
|
|
106
|
+
|
|
107
|
+
if (this.parent && this.parent != this && this.parent.renderer) {
|
|
108
|
+
result = this.parent.renderer.createSubRenderer();
|
|
109
|
+
} else {
|
|
110
|
+
result = alchemy.hawkejs.createRenderer();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Enforce the scene_id
|
|
118
|
+
*
|
|
119
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
120
|
+
* @since 1.1.0
|
|
121
|
+
* @version 1.1.7
|
|
122
|
+
*/
|
|
123
|
+
Conduit.enforceProperty(function scene_id(new_value, old_value) {
|
|
124
|
+
|
|
125
|
+
if (new_value) {
|
|
126
|
+
return new_value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (this.headers['x-scene-id']) {
|
|
130
|
+
return this.headers['x-scene-id'];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// If there also was no old value, create a new scene
|
|
134
|
+
if (old_value == null) {
|
|
135
|
+
// Generate the scene_id
|
|
136
|
+
new_value = Crypto.randomHex(8) || Crypto.pseudoHex(8);
|
|
137
|
+
|
|
138
|
+
// Tell the session this scene can be expected
|
|
139
|
+
this.getSession().expectScene(new_value);
|
|
140
|
+
|
|
141
|
+
let path;
|
|
142
|
+
|
|
143
|
+
if (this.url) {
|
|
144
|
+
path = this.url.path;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Set the sceneid cookie
|
|
148
|
+
this.cookie('scene_start_' + ~~(Math.random()*1000), {
|
|
149
|
+
|
|
150
|
+
// The time this scene has started
|
|
151
|
+
start: Date.now(),
|
|
152
|
+
|
|
153
|
+
// The id of the scene
|
|
154
|
+
id: new_value
|
|
155
|
+
}, {
|
|
156
|
+
// Cookie should only be visible on this path
|
|
157
|
+
path: path,
|
|
158
|
+
|
|
159
|
+
// Cookie should not live for more than 15 seconds
|
|
160
|
+
maxAge: 1000 * 15
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return new_value;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Enforce the active_prefix
|
|
169
|
+
*
|
|
170
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
171
|
+
* @since 1.1.0
|
|
172
|
+
* @version 1.1.5
|
|
173
|
+
*/
|
|
174
|
+
Conduit.enforceProperty(function active_prefix(new_value, old_value) {
|
|
175
|
+
|
|
176
|
+
if (!new_value) {
|
|
177
|
+
this.renderer.language = null;
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (new_value == old_value) {
|
|
182
|
+
return new_value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Set the active prefix
|
|
186
|
+
this.internal('active_prefix', new_value);
|
|
187
|
+
this.expose('active_prefix', new_value);
|
|
188
|
+
|
|
189
|
+
if (this.locales[0] != new_value) {
|
|
190
|
+
this.locales.unshift(new_value);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Set the translate options for use in hawkejs
|
|
194
|
+
this.internal('locales', this.locales);
|
|
195
|
+
this.expose('locales', this.locales);
|
|
196
|
+
|
|
197
|
+
let config = Prefix.get(new_value);
|
|
198
|
+
|
|
199
|
+
if (config) {
|
|
200
|
+
this.renderer.language = config.locale;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return new_value;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get a session object by id
|
|
208
|
+
*
|
|
209
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
210
|
+
* @since 0.2.0
|
|
211
|
+
* @version 0.2.0
|
|
212
|
+
*/
|
|
213
|
+
Conduit.setStatic(function getSessionById(id) {
|
|
214
|
+
return alchemy.sessions.get(id);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* See if this is a secure connection
|
|
219
|
+
*
|
|
220
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
221
|
+
* @since 0.4.2
|
|
222
|
+
* @version 1.0.2
|
|
223
|
+
*/
|
|
224
|
+
Conduit.setProperty(function is_secure() {
|
|
225
|
+
|
|
226
|
+
var protocol;
|
|
227
|
+
|
|
228
|
+
if (alchemy.settings.assume_https) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (this.headers && this.headers['x-forwarded-proto'] == 'https') {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (this.url && this.url.protocol == 'https:') {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (this.protocol && this.protocol.startsWith('https')) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (this.encrypted == true) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return false;
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Set the request body
|
|
253
|
+
*
|
|
254
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
255
|
+
* @since 1.1.0
|
|
256
|
+
* @version 1.1.0
|
|
257
|
+
*
|
|
258
|
+
* @param {Object}
|
|
259
|
+
*/
|
|
260
|
+
Conduit.setMethod(function setRequestBody(body) {
|
|
261
|
+
|
|
262
|
+
if (!body) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
Object.assign(this.body, body);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Has the given route been tested yet?
|
|
271
|
+
*
|
|
272
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
273
|
+
* @since 1.2.5
|
|
274
|
+
* @version 1.2.5
|
|
275
|
+
*
|
|
276
|
+
* @param {Route}
|
|
277
|
+
*/
|
|
278
|
+
Conduit.setMethod(function hasRouteBeenTested(route) {
|
|
279
|
+
|
|
280
|
+
if (!route || !this[TESTED_ROUTES]) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return this[TESTED_ROUTES].has(route);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Mark this route as having been tested
|
|
289
|
+
*
|
|
290
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
291
|
+
* @since 1.2.5
|
|
292
|
+
* @version 1.2.5
|
|
293
|
+
*
|
|
294
|
+
* @param {Route}
|
|
295
|
+
*/
|
|
296
|
+
Conduit.setMethod(function markRouteAsTested(route) {
|
|
297
|
+
|
|
298
|
+
if (!this[TESTED_ROUTES]) {
|
|
299
|
+
this[TESTED_ROUTES] = new Set();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this[TESTED_ROUTES].add(route);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Rewrite a certain URL parameter
|
|
307
|
+
* (Causing some kind of redirect)
|
|
308
|
+
*
|
|
309
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
310
|
+
* @since 1.2.5
|
|
311
|
+
* @version 1.2.5
|
|
312
|
+
*
|
|
313
|
+
* @param {String} route_param
|
|
314
|
+
* @param {*} new_value
|
|
315
|
+
*/
|
|
316
|
+
Conduit.setMethod(function rewriteRequestRouteParam(route_param, new_value) {
|
|
317
|
+
|
|
318
|
+
if (!this.rewritten_request_route_param) {
|
|
319
|
+
this.rewritten_request_route_param = {};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this.rewritten_request_route_param[route_param] = new_value;
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Set the request files
|
|
327
|
+
*
|
|
328
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
329
|
+
* @since 1.1.0
|
|
330
|
+
* @version 1.1.0
|
|
331
|
+
*
|
|
332
|
+
* @param {Object}
|
|
333
|
+
*/
|
|
334
|
+
Conduit.setMethod(function setRequestFiles(files) {
|
|
335
|
+
|
|
336
|
+
if (!files) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
_setRequestFiles(this, files, this.files);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Set the request files
|
|
345
|
+
*
|
|
346
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
347
|
+
* @since 1.2.0
|
|
348
|
+
* @version 1.2.0
|
|
349
|
+
*
|
|
350
|
+
* @param {Conduit} conduit
|
|
351
|
+
* @param {Array} files
|
|
352
|
+
* @param {Object} target
|
|
353
|
+
*/
|
|
354
|
+
function _setRequestFiles(conduit, files, target) {
|
|
355
|
+
|
|
356
|
+
let context,
|
|
357
|
+
upload,
|
|
358
|
+
entry,
|
|
359
|
+
key;
|
|
360
|
+
|
|
361
|
+
for (key in files) {
|
|
362
|
+
entry = files[key];
|
|
363
|
+
|
|
364
|
+
if (Array.isArray(entry)) {
|
|
365
|
+
context = target[key];
|
|
366
|
+
|
|
367
|
+
if (!context) {
|
|
368
|
+
context = target[key] = {};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
_setRequestFiles(conduit, entry, context);
|
|
372
|
+
} else {
|
|
373
|
+
target[key] = Classes.Alchemy.Inode.File.from(entry);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Don't convert a conduit to any special json data
|
|
380
|
+
*
|
|
381
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
382
|
+
* @since 0.2.0
|
|
383
|
+
* @version 0.2.0
|
|
384
|
+
*/
|
|
385
|
+
Conduit.setMethod(function toJSON() {
|
|
386
|
+
return null;
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Set the request & response objects
|
|
391
|
+
*
|
|
392
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
393
|
+
* @since 1.2.0
|
|
394
|
+
* @version 1.2.0
|
|
395
|
+
*/
|
|
396
|
+
Conduit.setMethod(function setReqRes(req, res) {
|
|
397
|
+
|
|
398
|
+
if (req != null) {
|
|
399
|
+
// Make conduit available in req
|
|
400
|
+
req.conduit = this;
|
|
401
|
+
|
|
402
|
+
// Basic HTTP objects
|
|
403
|
+
this.request = req;
|
|
404
|
+
|
|
405
|
+
// The HTTP request headers
|
|
406
|
+
this.headers = req.headers;
|
|
407
|
+
|
|
408
|
+
// Parse the original URL without host
|
|
409
|
+
this.original_url = new RURL(req.url);
|
|
410
|
+
|
|
411
|
+
// Is this an AJAX request?
|
|
412
|
+
this.ajax = null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (res != null) {
|
|
416
|
+
this.response = res;
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Init values
|
|
422
|
+
*
|
|
423
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
424
|
+
* @since 0.3.3
|
|
425
|
+
* @version 1.1.0
|
|
426
|
+
*/
|
|
427
|
+
Conduit.setMethod(function initValues() {
|
|
428
|
+
|
|
429
|
+
// Use passed-along router, or default router instance
|
|
430
|
+
this.router = this.router || Router;
|
|
431
|
+
|
|
432
|
+
// The path without any prefix, including section mounts
|
|
433
|
+
this.path = null;
|
|
434
|
+
|
|
435
|
+
// The path without prefix or section mount
|
|
436
|
+
this.sectionPath = null;
|
|
437
|
+
|
|
438
|
+
// The accepted languages
|
|
439
|
+
this.languages = null;
|
|
440
|
+
|
|
441
|
+
// URL paths can be prefixed with certain locales,
|
|
442
|
+
// these locales should then get preference over the user's browser locale
|
|
443
|
+
this.prefix = null;
|
|
444
|
+
|
|
445
|
+
// All the locales the user's browser accepts
|
|
446
|
+
this.locales = null;
|
|
447
|
+
|
|
448
|
+
// The matching Route instance
|
|
449
|
+
this.route = null;
|
|
450
|
+
|
|
451
|
+
// The named parameters inside the path
|
|
452
|
+
this.params = null;
|
|
453
|
+
|
|
454
|
+
// The original string parameters
|
|
455
|
+
this.route_string_parameters = null;
|
|
456
|
+
|
|
457
|
+
// The section vhost domain
|
|
458
|
+
this.sectionDomain = null;
|
|
459
|
+
|
|
460
|
+
// The section of the used route
|
|
461
|
+
this.section = null;
|
|
462
|
+
|
|
463
|
+
// The parsed path (including querystring)
|
|
464
|
+
this.url = null
|
|
465
|
+
|
|
466
|
+
// The current active theme
|
|
467
|
+
this.theme = null;
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Get the time since the conduit was made
|
|
472
|
+
*
|
|
473
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
474
|
+
* @since 0.2.0
|
|
475
|
+
* @version 1.1.0
|
|
476
|
+
*/
|
|
477
|
+
Conduit.setMethod(function time() {
|
|
478
|
+
return Date.now() - this.start;
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Parse the request, get information from the url
|
|
483
|
+
*
|
|
484
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
485
|
+
* @since 0.2.0
|
|
486
|
+
* @version 1.2.5
|
|
487
|
+
*
|
|
488
|
+
* @param {IncomingMessage} req
|
|
489
|
+
* @param {ServerResponse} res
|
|
490
|
+
*/
|
|
491
|
+
Conduit.setMethod(async function parseRequest() {
|
|
492
|
+
|
|
493
|
+
var protocol,
|
|
494
|
+
section;
|
|
495
|
+
|
|
496
|
+
if (this.method == null && this.request && this.request.method) {
|
|
497
|
+
this.method = this.request.method.toLowerCase();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
this.parseShortcuts();
|
|
501
|
+
this.parseLanguages();
|
|
502
|
+
this.parsePrefix();
|
|
503
|
+
this.parseSection();
|
|
504
|
+
|
|
505
|
+
// Try getting the route
|
|
506
|
+
await this.parseRoute();
|
|
507
|
+
|
|
508
|
+
if (this.halt_request) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Is this encrypted?
|
|
513
|
+
if (this.encrypted == null) {
|
|
514
|
+
this.encrypted = this.request.connection.encrypted;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (this.rewritten_request_route_param) {
|
|
518
|
+
let params = Object.assign({}, this.route_string_parameters, this.rewritten_request_route_param);
|
|
519
|
+
let new_url = this.route.generateUrl(params, this);
|
|
520
|
+
this.overrideResponseUrl(new_url);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// If the url has already been parsed, return early
|
|
524
|
+
if (this.url) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (alchemy.settings.assume_https) {
|
|
529
|
+
protocol = 'https://';
|
|
530
|
+
} else if (this.headers['x-forwarded-proto']) {
|
|
531
|
+
protocol = this.headers['x-forwarded-proto'];
|
|
532
|
+
} else if (this.protocol) {
|
|
533
|
+
protocol = this.protocol;
|
|
534
|
+
} else if (this.encrypted) {
|
|
535
|
+
protocol = 'https://';
|
|
536
|
+
} else {
|
|
537
|
+
protocol = 'http://';
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Create a new RURL instance
|
|
541
|
+
this.url = new RURL();
|
|
542
|
+
|
|
543
|
+
// Set the protocol
|
|
544
|
+
this.url.protocol = protocol;
|
|
545
|
+
|
|
546
|
+
// Set the host
|
|
547
|
+
this.url.hostname = this.headers.host;
|
|
548
|
+
|
|
549
|
+
let path = this.path;
|
|
550
|
+
|
|
551
|
+
if (this.prefix) {
|
|
552
|
+
path = '/' + this.prefix + '/' + path;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
this.url.path = path;
|
|
556
|
+
|
|
557
|
+
return true;
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Parse the headers for shortcuts
|
|
562
|
+
*
|
|
563
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
564
|
+
* @since 0.2.0
|
|
565
|
+
* @version 0.2.0
|
|
566
|
+
*/
|
|
567
|
+
Conduit.setMethod(function parseShortcuts() {
|
|
568
|
+
|
|
569
|
+
var headers = this.headers;
|
|
570
|
+
|
|
571
|
+
// A request can just tell us what route to use
|
|
572
|
+
if (headers['x-alchemy-route-name']) {
|
|
573
|
+
this.route = this.router.getRouteByName(headers['x-alchemy-route-name']);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// And which prefix (this is a forced prefix)
|
|
577
|
+
if (headers['x-alchemy-prefix'] && prefixes[headers['x-alchemy-prefix']]) {
|
|
578
|
+
this.prefix = headers['x-alchemy-prefix'];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Section domains can only be requested through headers
|
|
582
|
+
if (headers['x-alchemy-section-domain']) {
|
|
583
|
+
this.sectionDomain = headers['x-alchemy-section-domain'];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Only get ajax on the first parse
|
|
587
|
+
if (this.ajax == null) {
|
|
588
|
+
this.ajax = headers['x-requested-with'] === 'XMLHttpRequest';
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Sort the parsed accept-language header array
|
|
595
|
+
*
|
|
596
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
597
|
+
* @since 0.0.1
|
|
598
|
+
* @version 0.0.1
|
|
599
|
+
*
|
|
600
|
+
* @param {Object} a
|
|
601
|
+
* @param {Object} b
|
|
602
|
+
*/
|
|
603
|
+
function qualityCmp(a, b) {
|
|
604
|
+
if (a.quality === b.quality) {
|
|
605
|
+
return 0;
|
|
606
|
+
} else if (a.quality < b.quality) {
|
|
607
|
+
return 1;
|
|
608
|
+
} else {
|
|
609
|
+
return -1;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Parses the HTTP accept-language header
|
|
615
|
+
*
|
|
616
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
617
|
+
* @since 0.0.1
|
|
618
|
+
* @version 0.2.0
|
|
619
|
+
*/
|
|
620
|
+
Conduit.setMethod(function parseLanguages() {
|
|
621
|
+
|
|
622
|
+
var rawLangs,
|
|
623
|
+
rawLang,
|
|
624
|
+
locales,
|
|
625
|
+
parts,
|
|
626
|
+
langs,
|
|
627
|
+
qval,
|
|
628
|
+
temp,
|
|
629
|
+
i,
|
|
630
|
+
q;
|
|
631
|
+
|
|
632
|
+
langs = [];
|
|
633
|
+
locales = [];
|
|
634
|
+
|
|
635
|
+
if (this.headers['accept-language']) {
|
|
636
|
+
|
|
637
|
+
rawLangs = this.headers['accept-language'].split(',');
|
|
638
|
+
|
|
639
|
+
for (i = 0; i < rawLangs.length; i++) {
|
|
640
|
+
rawLang = rawLangs[i];
|
|
641
|
+
|
|
642
|
+
parts = rawLang.split(';');
|
|
643
|
+
qval = null;
|
|
644
|
+
q = 1;
|
|
645
|
+
|
|
646
|
+
if (parts.length > 1 && parts[1].indexOf('q=') === 0) {
|
|
647
|
+
qval = parseFloat(parts[1].split('=')[1]);
|
|
648
|
+
|
|
649
|
+
if (isNaN(qval) === false) {
|
|
650
|
+
q = qval;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Get the lang-loc code
|
|
655
|
+
temp = parts[0].trim().toLowerCase().split('-');
|
|
656
|
+
|
|
657
|
+
langs.push({lang: temp[0], loc: temp[1], quality: q});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
langs.sort(qualityCmp);
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
temp = {};
|
|
664
|
+
|
|
665
|
+
for (i = 0; i < langs.length; i++) {
|
|
666
|
+
if (!temp[langs[i].lang]) {
|
|
667
|
+
locales.push(langs[i].lang);
|
|
668
|
+
temp[langs[i].lang] = true;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
this.languages = langs;
|
|
673
|
+
this.locales = locales;
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Parses accept-encoding strings
|
|
678
|
+
*
|
|
679
|
+
* @author jshttp
|
|
680
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
681
|
+
* @since 0.2.0
|
|
682
|
+
* @version 0.2.0
|
|
683
|
+
*/
|
|
684
|
+
function parseEncoding(s, i) {
|
|
685
|
+
var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/);
|
|
686
|
+
|
|
687
|
+
if (!match) return null;
|
|
688
|
+
|
|
689
|
+
var encoding = match[1];
|
|
690
|
+
var q = 1;
|
|
691
|
+
if (match[2]) {
|
|
692
|
+
var params = match[2].split(';');
|
|
693
|
+
for (var i = 0; i < params.length; i ++) {
|
|
694
|
+
var p = params[i].trim().split('=');
|
|
695
|
+
if (p[0] === 'q') {
|
|
696
|
+
q = parseFloat(p[1]);
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return {
|
|
703
|
+
encoding: encoding,
|
|
704
|
+
q: q,
|
|
705
|
+
i: i
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Parses accept-encoding strings
|
|
711
|
+
*
|
|
712
|
+
* @author jshttp
|
|
713
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
714
|
+
* @since 0.2.0
|
|
715
|
+
* @version 0.2.0
|
|
716
|
+
*/
|
|
717
|
+
function specify(encoding, spec, index) {
|
|
718
|
+
var s = 0;
|
|
719
|
+
if(spec.encoding.toLowerCase() === encoding.toLowerCase()){
|
|
720
|
+
s |= 1;
|
|
721
|
+
} else if (spec.encoding !== '*' ) {
|
|
722
|
+
return null
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return {
|
|
726
|
+
i: index,
|
|
727
|
+
o: spec.i,
|
|
728
|
+
q: spec.q,
|
|
729
|
+
s: s
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Parses the HTTP accept-encoding header
|
|
735
|
+
*
|
|
736
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
737
|
+
* @since 0.2.0
|
|
738
|
+
* @version 0.2.0
|
|
739
|
+
*/
|
|
740
|
+
Conduit.setMethod(function parseAcceptEncoding() {
|
|
741
|
+
|
|
742
|
+
var hasIdentity,
|
|
743
|
+
minQuality,
|
|
744
|
+
encoding,
|
|
745
|
+
accepts,
|
|
746
|
+
i,
|
|
747
|
+
j;
|
|
748
|
+
|
|
749
|
+
// Make sure this only runs once
|
|
750
|
+
if (this.accepted_encodings != null) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (!this.headers['accept-encoding']) {
|
|
755
|
+
this.accepted_encodings = false;
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
accepts = this.headers['accept-encoding'].split(',');
|
|
760
|
+
minQuality = 1;
|
|
761
|
+
|
|
762
|
+
for (i = 0, j = 0; i < accepts.length; i++) {
|
|
763
|
+
encoding = parseEncoding(accepts[i].trim(), i);
|
|
764
|
+
|
|
765
|
+
if (encoding) {
|
|
766
|
+
accepts[j++] = encoding;
|
|
767
|
+
hasIdentity = hasIdentity || specify('identity', encoding);
|
|
768
|
+
minQuality = Math.min(minQuality, encoding.q || 1);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (!hasIdentity) {
|
|
773
|
+
/*
|
|
774
|
+
* If identity doesn't explicitly appear in the accept-encoding header,
|
|
775
|
+
* it's added to the list of acceptable encoding with the lowest q
|
|
776
|
+
*/
|
|
777
|
+
accepts[j++] = {
|
|
778
|
+
encoding: 'identity',
|
|
779
|
+
q: minQuality,
|
|
780
|
+
i: i
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// trim accepts
|
|
785
|
+
accepts.length = j;
|
|
786
|
+
|
|
787
|
+
this.accepted_encodings = accepts;
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* See if the wanted encoding is accepted by the client
|
|
792
|
+
*
|
|
793
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
794
|
+
* @since 0.2.0
|
|
795
|
+
* @version 0.2.0
|
|
796
|
+
*/
|
|
797
|
+
Conduit.setMethod(function accepts(encoding) {
|
|
798
|
+
|
|
799
|
+
var i;
|
|
800
|
+
|
|
801
|
+
// Parse the encodings on the fly
|
|
802
|
+
this.parseAcceptEncoding();
|
|
803
|
+
|
|
804
|
+
if (!this.accepted_encodings) {
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
for (i = 0; i < this.accepted_encodings.length; i++) {
|
|
809
|
+
if (this.accepted_encodings[i].encoding == encoding) {
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return false;
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Create a loopback conduit
|
|
819
|
+
*
|
|
820
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
821
|
+
* @since 0.2.0
|
|
822
|
+
* @version 1.1.3
|
|
823
|
+
*
|
|
824
|
+
* @param {Object} args
|
|
825
|
+
* @param {Function} callback
|
|
826
|
+
*
|
|
827
|
+
* @return {Alchemy.LoopbackConduit}
|
|
828
|
+
*/
|
|
829
|
+
Conduit.setMethod(function loopback(args, callback) {
|
|
830
|
+
return Classes.Alchemy.Conduit.Loopback.create(this, args, callback);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Parse the request, get information from the url
|
|
835
|
+
*
|
|
836
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
837
|
+
* @since 0.2.0
|
|
838
|
+
* @version 1.1.0
|
|
839
|
+
*/
|
|
840
|
+
Conduit.setMethod(function parsePrefix() {
|
|
841
|
+
|
|
842
|
+
let path = this.original_path;
|
|
843
|
+
|
|
844
|
+
if (!path) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
let active_prefix,
|
|
849
|
+
prefix,
|
|
850
|
+
begin;
|
|
851
|
+
|
|
852
|
+
// Look for the prefix at the beginning of the url path
|
|
853
|
+
if (!this.prefix) {
|
|
854
|
+
for (prefix in prefixes) {
|
|
855
|
+
begin = '/' + prefix + '/';
|
|
856
|
+
|
|
857
|
+
if (path.indexOf(begin) === 0) {
|
|
858
|
+
this.prefix = prefix;
|
|
859
|
+
break;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Handle urls with ONLY the prefix and no ending slash
|
|
865
|
+
if (!this.prefix) {
|
|
866
|
+
for (prefix in prefixes) {
|
|
867
|
+
begin = '/' + prefix;
|
|
868
|
+
|
|
869
|
+
if (this.original_pathname == begin) {
|
|
870
|
+
this.prefix = prefix;
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (this.prefix && path.endsWith('/' + this.prefix)) {
|
|
876
|
+
this.path = '/';
|
|
877
|
+
} else if (this.prefix && path.startsWith('/' + this.prefix + '?')) {
|
|
878
|
+
this.path = '/?' + path.after('?');
|
|
879
|
+
} else {
|
|
880
|
+
this.path = path;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
} else if (this.prefix && path.indexOf('/' + this.prefix + '/') === 0) {
|
|
884
|
+
// Remove the prefix from the path if one is given
|
|
885
|
+
this.path = path.slice(this.prefix.length+1);
|
|
886
|
+
} else {
|
|
887
|
+
this.path = path;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// Add this prefix to the top of the locales
|
|
891
|
+
if (this.prefix) {
|
|
892
|
+
active_prefix = this.prefix;
|
|
893
|
+
this.locales.unshift(this.prefix);
|
|
894
|
+
|
|
895
|
+
// Remember this prefix in the session
|
|
896
|
+
this.session('last_forced_prefix', this.prefix);
|
|
897
|
+
|
|
898
|
+
// Let the client know this prefix should be used
|
|
899
|
+
this.expose('forced_prefix', this.prefix);
|
|
900
|
+
} else {
|
|
901
|
+
|
|
902
|
+
let last_forced_prefix = this.session('last_forced_prefix');
|
|
903
|
+
|
|
904
|
+
if (last_forced_prefix) {
|
|
905
|
+
active_prefix = last_forced_prefix;
|
|
906
|
+
} else if (this.active_prefix) {
|
|
907
|
+
// There already is an active prefix, so just keep on using that
|
|
908
|
+
// (Is the case in redirects)
|
|
909
|
+
return;
|
|
910
|
+
} else {
|
|
911
|
+
|
|
912
|
+
// If no prefix has been found yet, look for the default prefix
|
|
913
|
+
// This will override the browser locale
|
|
914
|
+
if (this.headers['x-alchemy-default-prefix']) {
|
|
915
|
+
if (prefixes[this.headers['x-alchemy-default-prefix']]) {
|
|
916
|
+
active_prefix = this.headers['x-alchemy-default-prefix'];
|
|
917
|
+
|
|
918
|
+
if (this.locales[0] != active_prefix) {
|
|
919
|
+
this.locales.unshift(active_prefix);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (!active_prefix) {
|
|
925
|
+
active_prefix = this.locales[0];
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
this.active_prefix = active_prefix;
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Get the section
|
|
935
|
+
*
|
|
936
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
937
|
+
* @since 0.2.0
|
|
938
|
+
* @version 0.2.1
|
|
939
|
+
*/
|
|
940
|
+
Conduit.setMethod(function parseSection() {
|
|
941
|
+
|
|
942
|
+
// Get the section this path is using
|
|
943
|
+
this.section = this.router.getPathSection(this.path);
|
|
944
|
+
|
|
945
|
+
if (!this.section) {
|
|
946
|
+
log.warn('No section found for path "' + this.path + '"');
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// If the section has a parent it's not the root
|
|
950
|
+
if (this.section && this.section.parent) {
|
|
951
|
+
this.sectionPath = this.path.slice(this.section.mount.length) || '/';
|
|
952
|
+
} else {
|
|
953
|
+
this.sectionPath = this.path;
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Get a route by its name
|
|
959
|
+
*
|
|
960
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
961
|
+
* @since 0.2.0
|
|
962
|
+
* @version 0.2.0
|
|
963
|
+
*
|
|
964
|
+
* @param {String|Object} name The name of the route
|
|
965
|
+
*/
|
|
966
|
+
Conduit.setMethod(function getRouteByName(name) {
|
|
967
|
+
|
|
968
|
+
// See if the name is an object, which means it's for sockets
|
|
969
|
+
if (name && typeof name == 'object') {
|
|
970
|
+
this.route = name;
|
|
971
|
+
} else {
|
|
972
|
+
this.route = this.router.getRouteByName(name);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return this.route;
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Get the Route instance & named parameters
|
|
980
|
+
*
|
|
981
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
982
|
+
* @since 0.2.0
|
|
983
|
+
* @version 1.1.7
|
|
984
|
+
*
|
|
985
|
+
* @param {Route} after_route Only check routes after this one
|
|
986
|
+
*
|
|
987
|
+
* @return {Boolean} Continue processing this request or not?
|
|
988
|
+
*/
|
|
989
|
+
Conduit.setMethod(async function parseRoute(after_route) {
|
|
990
|
+
|
|
991
|
+
var temp;
|
|
992
|
+
|
|
993
|
+
this.section = this.router.getPathSection(this.path);
|
|
994
|
+
|
|
995
|
+
// Remove the current found route
|
|
996
|
+
if (after_route) {
|
|
997
|
+
this.route_rematch = true;
|
|
998
|
+
this.route = null;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// If the route hasn't been found in the header shortcuts yet, look for it
|
|
1002
|
+
if (!this.route) {
|
|
1003
|
+
|
|
1004
|
+
temp = this.router.getRouteBySectionPath(this, this.method, this.section, this.sectionPath, this.prefix, after_route);
|
|
1005
|
+
|
|
1006
|
+
if (temp && temp.then) {
|
|
1007
|
+
temp = await temp;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
if (temp) {
|
|
1011
|
+
this.route = temp.route;
|
|
1012
|
+
this.params = temp.parameters;
|
|
1013
|
+
this.route_string_parameters = temp.original_parameters;
|
|
1014
|
+
this.path_definition = temp.definition;
|
|
1015
|
+
} else {
|
|
1016
|
+
// Is this a HEAD request? Then we need to check if a GET exists
|
|
1017
|
+
if (this.method == 'head') {
|
|
1018
|
+
let get_route = this.router.getRouteBySectionPath(this, 'get', this.section, this.sectionPath, this.prefix, after_route);
|
|
1019
|
+
|
|
1020
|
+
if (get_route && get_route.then) {
|
|
1021
|
+
get_route = await get_route;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// A GET route was found, so we just need to end this request
|
|
1025
|
+
if (get_route) {
|
|
1026
|
+
this.end();
|
|
1027
|
+
this.halt_request = true;
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
} else {
|
|
1031
|
+
// See if the path matches another method
|
|
1032
|
+
temp = await this.router.getRouteBySectionPath(this, ['get', 'post', 'put'], this.section, this.sectionPath, this.prefix, after_route);
|
|
1033
|
+
|
|
1034
|
+
if (temp) {
|
|
1035
|
+
this.route_mismatch = temp.route;
|
|
1036
|
+
|
|
1037
|
+
temp = null;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
this.route_not_found = true;
|
|
1042
|
+
}
|
|
1043
|
+
} else {
|
|
1044
|
+
temp = this.route.match(this, this.method, this.sectionPath);
|
|
1045
|
+
|
|
1046
|
+
if (temp && temp.then) {
|
|
1047
|
+
temp = await temp;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (temp) {
|
|
1051
|
+
this.params = temp.parameters || {};
|
|
1052
|
+
this.route_string_parameters = temp.original_parameters || {};
|
|
1053
|
+
this.path_definition = temp.definition;
|
|
1054
|
+
} else {
|
|
1055
|
+
this.params = {};
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Run the middleware
|
|
1062
|
+
*
|
|
1063
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1064
|
+
* @since 0.2.0
|
|
1065
|
+
* @version 1.1.5
|
|
1066
|
+
*/
|
|
1067
|
+
Conduit.setMethod(async function callMiddleware() {
|
|
1068
|
+
|
|
1069
|
+
if (!this.section) {
|
|
1070
|
+
return this.callHandler();
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
let that = this,
|
|
1074
|
+
middlewares = await this.section.getMiddleware(this, this.section, this.path, this.prefix),
|
|
1075
|
+
debugObject = this._debugObject,
|
|
1076
|
+
middleDebug = this.debug({label: 'middleware', data: {title: 'Doing middleware'}}),
|
|
1077
|
+
routeDebug,
|
|
1078
|
+
theme;
|
|
1079
|
+
|
|
1080
|
+
if (middleDebug) {
|
|
1081
|
+
this._debugObject = middleDebug;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
middlewares = new Iterator(middlewares);
|
|
1085
|
+
|
|
1086
|
+
Function.while(function test() {
|
|
1087
|
+
return middlewares.hasNext();
|
|
1088
|
+
}, function middlewareTask(next) {
|
|
1089
|
+
|
|
1090
|
+
var route = middlewares.next().value,
|
|
1091
|
+
middlePath,
|
|
1092
|
+
req;
|
|
1093
|
+
|
|
1094
|
+
// Skip middleware that does not listen to the request method
|
|
1095
|
+
if (route.methods.indexOf(that.method) === -1) {
|
|
1096
|
+
return next();
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// Augment the request object
|
|
1100
|
+
req = Object.create(that.request);
|
|
1101
|
+
|
|
1102
|
+
// Get the path without the middleware mount path
|
|
1103
|
+
middlePath = req.conduit.sectionPath.replace(route.paths[''].source, '');
|
|
1104
|
+
|
|
1105
|
+
// Strip any query parameters
|
|
1106
|
+
if (middlePath.indexOf('?') > -1) {
|
|
1107
|
+
middlePath = middlePath.before('?');
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
if (middlePath[0] !== '/') {
|
|
1111
|
+
middlePath = '/' + middlePath;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// Look for theme settings
|
|
1115
|
+
if (req.conduit.url) {
|
|
1116
|
+
theme = req.conduit.url.query.theme;
|
|
1117
|
+
|
|
1118
|
+
if (theme) {
|
|
1119
|
+
middlePath = ['/' + theme + middlePath, middlePath];
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
req.middlePath = middlePath;
|
|
1124
|
+
req.original = that.request;
|
|
1125
|
+
|
|
1126
|
+
if (routeDebug) {
|
|
1127
|
+
routeDebug.stop();
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
if (middleDebug) {
|
|
1131
|
+
routeDebug = middleDebug.debug('route', {title: 'Doing "' + route.name + '"'});
|
|
1132
|
+
that._debugObject = routeDebug;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
route.fnc(req, that.response, next);
|
|
1136
|
+
}, function done(err) {
|
|
1137
|
+
|
|
1138
|
+
if (err) {
|
|
1139
|
+
return that.emit('error', err);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
if (routeDebug) {
|
|
1143
|
+
routeDebug.stop();
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// Don't do this for websockets
|
|
1147
|
+
if (that.websocket) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (middleDebug) {
|
|
1152
|
+
middleDebug.mark('Preparing viewrender');
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
that.prepareViewRender();
|
|
1156
|
+
|
|
1157
|
+
if (middleDebug) {
|
|
1158
|
+
middleDebug.mark(false);
|
|
1159
|
+
middleDebug.stop();
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
if (that._debugConduitInitialize) {
|
|
1163
|
+
that._debugConduitInitialize.stop();
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// Return the original debug object
|
|
1167
|
+
that._debugObject = debugObject;
|
|
1168
|
+
|
|
1169
|
+
that.callHandler();
|
|
1170
|
+
});
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* Create a new Hawkejs' ViewRender instance
|
|
1175
|
+
*
|
|
1176
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1177
|
+
* @since 0.2.0
|
|
1178
|
+
* @version 1.1.1
|
|
1179
|
+
*/
|
|
1180
|
+
Conduit.setMethod(function prepareViewRender() {
|
|
1181
|
+
|
|
1182
|
+
// Add a link to this conduit
|
|
1183
|
+
this.renderer.conduit = this;
|
|
1184
|
+
this.renderer.server_var('conduit', this);
|
|
1185
|
+
|
|
1186
|
+
// Let the ViewRender get some request info
|
|
1187
|
+
this.renderer.prepare(this.request, this);
|
|
1188
|
+
|
|
1189
|
+
// Pass url parameters to the client
|
|
1190
|
+
this.renderer.internal('urlparams', this.route_string_parameters);
|
|
1191
|
+
this.renderer.internal('url', this.url);
|
|
1192
|
+
|
|
1193
|
+
if (this.route) {
|
|
1194
|
+
this.renderer.internal('route', this.route.name);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
this.renderer.is_for_client_side = this.ajax;
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Call the handler of this route when parsing is finished
|
|
1202
|
+
*
|
|
1203
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1204
|
+
* @since 0.2.0
|
|
1205
|
+
* @version 1.1.7
|
|
1206
|
+
*/
|
|
1207
|
+
Conduit.setMethod(function callHandler() {
|
|
1208
|
+
|
|
1209
|
+
if (!this.route) {
|
|
1210
|
+
|
|
1211
|
+
if (this.route_mismatch) {
|
|
1212
|
+
|
|
1213
|
+
if (alchemy.settings.debug) {
|
|
1214
|
+
console.log('Route method not allowed:', this);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
this.error(405, 'Method Not Allowed', false);
|
|
1218
|
+
|
|
1219
|
+
} else {
|
|
1220
|
+
if (alchemy.settings.debug) {
|
|
1221
|
+
console.log('Route not found:', this);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
this.notFound('Route was not found');
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
this.route.callHandler(this);
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* End the current request with a 202 status
|
|
1235
|
+
* and tell the client to look at another url later
|
|
1236
|
+
*
|
|
1237
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1238
|
+
* @since 1.1.0
|
|
1239
|
+
* @version 1.2.5
|
|
1240
|
+
*
|
|
1241
|
+
* @param {String|Object} options Options or url
|
|
1242
|
+
*/
|
|
1243
|
+
Conduit.setMethod(function postpone(options) {
|
|
1244
|
+
|
|
1245
|
+
let session = this.getSession();
|
|
1246
|
+
|
|
1247
|
+
if (typeof options == 'number') {
|
|
1248
|
+
options = {
|
|
1249
|
+
expected_duration: options
|
|
1250
|
+
};
|
|
1251
|
+
} else if (!options) {
|
|
1252
|
+
options = {};
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// Make sure the scene id exists
|
|
1256
|
+
this.createScene();
|
|
1257
|
+
|
|
1258
|
+
// Already set the cookies
|
|
1259
|
+
if (this.new_cookie_header.length) {
|
|
1260
|
+
this.response.setHeader('set-cookie', this.new_cookie_header);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
let postponed_id = session.postpone(this),
|
|
1264
|
+
url = '/alchemy/postponed/' + postponed_id;
|
|
1265
|
+
|
|
1266
|
+
// Set the location header where the client should look at later
|
|
1267
|
+
this.response.setHeader('Location', url);
|
|
1268
|
+
this.response.setHeader('Content-Type', 'text/html');
|
|
1269
|
+
|
|
1270
|
+
if (options.expected_duration) {
|
|
1271
|
+
this.response.setHeader('Expected-Duration', Number(options.expected_duration / 1000).toFixed(2));
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// Write the headers & 202 status
|
|
1275
|
+
this.response.writeHead(202);
|
|
1276
|
+
|
|
1277
|
+
// End the response
|
|
1278
|
+
this.response.end('The response has been postponed, you can find it at <a href="' + url + '">' + url + '</a>');
|
|
1279
|
+
|
|
1280
|
+
// Nullify the response
|
|
1281
|
+
this.response = null;
|
|
1282
|
+
|
|
1283
|
+
// Set the original url
|
|
1284
|
+
this.overrideResponseUrl(this.url)
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* Set the response url
|
|
1289
|
+
*
|
|
1290
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1291
|
+
* @since 1.2.5
|
|
1292
|
+
* @version 1.2.5
|
|
1293
|
+
*
|
|
1294
|
+
* @param {String|RURL} url
|
|
1295
|
+
*/
|
|
1296
|
+
Conduit.setMethod(function overrideResponseUrl(url) {
|
|
1297
|
+
|
|
1298
|
+
if (typeof url != 'string') {
|
|
1299
|
+
url = String(url);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
this.setHeader('x-history-url', url);
|
|
1303
|
+
this.expose('redirected_to', url);
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Redirect to another url
|
|
1308
|
+
*
|
|
1309
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1310
|
+
* @since 0.2.0
|
|
1311
|
+
* @version 1.2.5
|
|
1312
|
+
*
|
|
1313
|
+
* @param {Number} status 3xx redirection codes. 302 (temporary redirect) by default
|
|
1314
|
+
* @param {String|Object} options Options or url
|
|
1315
|
+
*/
|
|
1316
|
+
Conduit.setMethod(function redirect(status, options) {
|
|
1317
|
+
|
|
1318
|
+
var hard_refresh = false,
|
|
1319
|
+
url;
|
|
1320
|
+
|
|
1321
|
+
if (typeof status != 'number') {
|
|
1322
|
+
|
|
1323
|
+
if (typeof options == 'object') {
|
|
1324
|
+
options.url = status;
|
|
1325
|
+
} else {
|
|
1326
|
+
options = status;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
status = 302;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if (typeof options == 'object' && options) {
|
|
1333
|
+
|
|
1334
|
+
if (options.href || options.path) {
|
|
1335
|
+
url = options.href || options.path;
|
|
1336
|
+
} else {
|
|
1337
|
+
|
|
1338
|
+
if (options.body) {
|
|
1339
|
+
Object.defineProperty(this, 'body', {
|
|
1340
|
+
value : options.body,
|
|
1341
|
+
configurable : true
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
if (options.method) {
|
|
1346
|
+
this.method = options.method;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// When headers are given, the redirect is internal
|
|
1350
|
+
if (options.headers) {
|
|
1351
|
+
this.headers = options.headers;
|
|
1352
|
+
|
|
1353
|
+
this.oldoriginal_path = this.original_path;
|
|
1354
|
+
|
|
1355
|
+
if (typeof options.url == 'string') {
|
|
1356
|
+
let temp = options.url;
|
|
1357
|
+
temp = RURL.parse(temp);
|
|
1358
|
+
url = temp.path;
|
|
1359
|
+
} else {
|
|
1360
|
+
url = options.url.path;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if (url == null) {
|
|
1364
|
+
throw new Error('Conduit#redirect can not redirect to null path');
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Register the new url as the one to use for the history
|
|
1368
|
+
this.overrideResponseUrl(url);
|
|
1369
|
+
|
|
1370
|
+
this.original_path = url;
|
|
1371
|
+
|
|
1372
|
+
// Reinitialize the conduit
|
|
1373
|
+
this.initValues();
|
|
1374
|
+
this.initHttp();
|
|
1375
|
+
|
|
1376
|
+
return;
|
|
1377
|
+
} else {
|
|
1378
|
+
url = options.url;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
if (options.hard_refresh) {
|
|
1383
|
+
hard_refresh = options.hard_refresh;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
} else if (typeof options == 'string') {
|
|
1387
|
+
url = options;
|
|
1388
|
+
options = null;
|
|
1389
|
+
} else {
|
|
1390
|
+
throw new Error('Conduit#redirect requires a valid url or options object');
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
this.status = status;
|
|
1394
|
+
|
|
1395
|
+
if (hard_refresh && this.headers['x-hawkejs-request']) {
|
|
1396
|
+
this.setHeader('x-hawkejs-navigate', url);
|
|
1397
|
+
|
|
1398
|
+
if (options && options.popup) {
|
|
1399
|
+
this.setHeader('x-hawkejs-popup', options.popup);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
} else {
|
|
1403
|
+
this.setHeader('Location', url);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
this._end();
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Respond with an error
|
|
1411
|
+
*
|
|
1412
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1413
|
+
* @since 0.2.0
|
|
1414
|
+
* @version 1.1.0
|
|
1415
|
+
*
|
|
1416
|
+
* @param {Nulber} status Response statuscode
|
|
1417
|
+
* @param {Error} message Optional error to send
|
|
1418
|
+
* @param {Boolean} printError Print the error, defaults to true
|
|
1419
|
+
*/
|
|
1420
|
+
Conduit.setMethod(function error(status, message, printError) {
|
|
1421
|
+
|
|
1422
|
+
if (status instanceof Classes.Alchemy.Error.HTTP) {
|
|
1423
|
+
message = status;
|
|
1424
|
+
status = message.status;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
if (typeof status !== 'number') {
|
|
1428
|
+
message = status;
|
|
1429
|
+
status = 500;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
if (!message) {
|
|
1433
|
+
message = 'Unknown server error';
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
if (typeof message == 'string') {
|
|
1437
|
+
let error = new Classes.Alchemy.Error.HTTP(status, message);
|
|
1438
|
+
error[Symbol.for('extra_skip_levels')] = 1;
|
|
1439
|
+
message = error;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
let subject = 'Error found on ' + this.original_path + '';
|
|
1443
|
+
|
|
1444
|
+
if (printError === false) {
|
|
1445
|
+
log.error(subject + ':\n' + message);
|
|
1446
|
+
} else if (message instanceof Error) {
|
|
1447
|
+
alchemy.printLog('error', [subject, String(message), message], {err: message, level: -2});
|
|
1448
|
+
} else {
|
|
1449
|
+
log.error(subject + ':\n' + message);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// Make sure the client doesn't expect compression
|
|
1453
|
+
this.setHeader('content-encoding', '');
|
|
1454
|
+
|
|
1455
|
+
this.status = status;
|
|
1456
|
+
|
|
1457
|
+
// Only render an expensive "Error" template when the client directly
|
|
1458
|
+
// browses to an HTML page.
|
|
1459
|
+
// Don't render a template for AJAX or asset requests
|
|
1460
|
+
if (this.renderer && (this.ajax || (this.headers.accept && this.headers.accept.indexOf('html') > -1))) {
|
|
1461
|
+
|
|
1462
|
+
// Hawkejs will have the option to throw the error OR render the error
|
|
1463
|
+
if (this.ajax) {
|
|
1464
|
+
this.end({
|
|
1465
|
+
error : true,
|
|
1466
|
+
status : status,
|
|
1467
|
+
message : message,
|
|
1468
|
+
error_templates : ['error/' + status, 'error/unknown'],
|
|
1469
|
+
});
|
|
1470
|
+
} else {
|
|
1471
|
+
this.set('status', status);
|
|
1472
|
+
this.set('message', message);
|
|
1473
|
+
this.render(['error/' + status, 'error/unknown']);
|
|
1474
|
+
}
|
|
1475
|
+
} else {
|
|
1476
|
+
// Requests for images or scripts just get a non-expensive string response
|
|
1477
|
+
this.setHeader('content-type', 'text/plain');
|
|
1478
|
+
this._end(status + ':\n' + message + '\n');
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// Emit this as a conduit error
|
|
1482
|
+
alchemy.emit('conduit_error', this, status, message);
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Deny access
|
|
1487
|
+
*
|
|
1488
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1489
|
+
* @since 0.2.0
|
|
1490
|
+
* @version 1.1.0
|
|
1491
|
+
*
|
|
1492
|
+
* @param {Number} status
|
|
1493
|
+
* @param {Error} message optional error to send
|
|
1494
|
+
*/
|
|
1495
|
+
Conduit.setMethod(function deny(status, message) {
|
|
1496
|
+
|
|
1497
|
+
if (typeof status == 'string') {
|
|
1498
|
+
message = status;
|
|
1499
|
+
status = 403;
|
|
1500
|
+
} else if (status instanceof Classes.Alchemy.Error.HTTP) {
|
|
1501
|
+
return this.error(status);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
if (message == null) {
|
|
1505
|
+
message = 'Forbidden';
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
this.error(status, message);
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* The current user is not authorized and needs to log in
|
|
1513
|
+
* (Default implementation, is overriden by the acl plugin)
|
|
1514
|
+
*
|
|
1515
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1516
|
+
* @since 1.0.7
|
|
1517
|
+
* @version 1.0.7
|
|
1518
|
+
*
|
|
1519
|
+
* @param {Boolean} tried_auth Indicate that this was an auth attempt
|
|
1520
|
+
*/
|
|
1521
|
+
Conduit.setMethod(function notAuthorized(tried_auth) {
|
|
1522
|
+
return this.deny();
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* The current user is authenticated, but not allowed
|
|
1527
|
+
* (Default implementation, is overriden by the acl plugin)
|
|
1528
|
+
*
|
|
1529
|
+
* @author Jelle De Loecker <jelle@kipdola.be>
|
|
1530
|
+
* @since 1.0.7
|
|
1531
|
+
* @version 1.1.0
|
|
1532
|
+
*/
|
|
1533
|
+
Conduit.setMethod(function forbidden() {
|
|
1534
|
+
|
|
1535
|
+
let error = new Classes.Alchemy.Error.HTTP(403, 'Forbidden');
|
|
1536
|
+
error.skipTraceLines(1);
|
|
1537
|
+
|
|
1538
|
+
return this.deny(error);
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Respond with a not found error status
|
|
1543
|
+
*
|
|
1544
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1545
|
+
* @since 0.2.0
|
|
1546
|
+
* @version 1.1.0
|
|
1547
|
+
*
|
|
1548
|
+
* @param {Error} message optional error to send
|
|
1549
|
+
*/
|
|
1550
|
+
Conduit.setMethod(async function notFound(message) {
|
|
1551
|
+
|
|
1552
|
+
// Look for other paths
|
|
1553
|
+
if (!this.route_not_found && this.route && !this.route_rematch) {
|
|
1554
|
+
|
|
1555
|
+
// Try matching against paths after the ones we currently matched
|
|
1556
|
+
await this.parseRoute(this.route);
|
|
1557
|
+
|
|
1558
|
+
// Call the handler of that route if it has been found
|
|
1559
|
+
if (this.route) {
|
|
1560
|
+
return this.route.callHandler(this);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
if (message == null) {
|
|
1565
|
+
message = 'Not found';
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
this.error(404, message, false);
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
/**
|
|
1572
|
+
* Respond with a "Not Modified" 304 status
|
|
1573
|
+
*
|
|
1574
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1575
|
+
* @since 0.2.0
|
|
1576
|
+
* @version 0.4.0
|
|
1577
|
+
*/
|
|
1578
|
+
Conduit.setMethod(function notModified() {
|
|
1579
|
+
this.status = 304;
|
|
1580
|
+
this._end();
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
/**
|
|
1584
|
+
* Respond with text. Objects get JSON-dry encoded
|
|
1585
|
+
*
|
|
1586
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1587
|
+
* @since 0.2.0
|
|
1588
|
+
* @version 1.1.0
|
|
1589
|
+
*
|
|
1590
|
+
* @param {String|Object} message
|
|
1591
|
+
*/
|
|
1592
|
+
Conduit.setMethod(function end(message) {
|
|
1593
|
+
|
|
1594
|
+
var that = this,
|
|
1595
|
+
json_type,
|
|
1596
|
+
json_fnc,
|
|
1597
|
+
cache,
|
|
1598
|
+
etag,
|
|
1599
|
+
temp;
|
|
1600
|
+
|
|
1601
|
+
if (this.websocket) {
|
|
1602
|
+
throw new Error('You can not end a websocket, use the callback instead');
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
if (this.method == 'head') {
|
|
1606
|
+
return this._end();
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
if (typeof message !== 'string') {
|
|
1610
|
+
|
|
1611
|
+
// Use regular JSON if DRY has been disabled in settings
|
|
1612
|
+
if (alchemy.settings.json_dry_response === false || this.json_dry === false) {
|
|
1613
|
+
json_type = 'json';
|
|
1614
|
+
json_fnc = JSON.stringify;
|
|
1615
|
+
} else {
|
|
1616
|
+
json_type = 'json-dry';
|
|
1617
|
+
json_fnc = JSON.dry;
|
|
1618
|
+
|
|
1619
|
+
// Clone the object
|
|
1620
|
+
message = JSON.clone(message, 'toHawkejs');
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// Only send the mimetype if it hasn't been set yet
|
|
1624
|
+
if (this.setHeader('content-type') == null) {
|
|
1625
|
+
this.setHeader('content-type', "application/" + json_type + ";charset=utf-8");
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
message = json_fnc(message) || 'null';
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
cache = this.headers['cache-control'] || this.headers['pragma'];
|
|
1632
|
+
|
|
1633
|
+
// Only generate etags when caching is enabled locally & on the browser
|
|
1634
|
+
if (alchemy.settings.cache !== false && (cache == null || cache != 'no-cache')) {
|
|
1635
|
+
|
|
1636
|
+
// Calculate the hash as etag
|
|
1637
|
+
etag = alchemy.checksum(message);
|
|
1638
|
+
|
|
1639
|
+
if (etag != null) {
|
|
1640
|
+
|
|
1641
|
+
if (this.headers['if-none-match'] == etag) {
|
|
1642
|
+
return this.notModified();
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// Responses through `end` should always be privately cached
|
|
1646
|
+
this.setHeader('cache-control', 'private');
|
|
1647
|
+
|
|
1648
|
+
// Send the hash as a response header
|
|
1649
|
+
this.setHeader('etag', etag);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// No need to replace anything if debugging is disabled or the log is empty
|
|
1654
|
+
if (alchemy.settings.debug && this.debuglog && this.debuglog.length && message.indexOf('_placeholder_') > -1) {
|
|
1655
|
+
temp = JSON.dry(this.debuglog);
|
|
1656
|
+
message = message.replace(/{\s*"_placeholder_":\s*"debuglog"\s*}/g, temp);
|
|
1657
|
+
message = message.replace(/{\s*\\"_placeholder_\\":\s*\\"debuglog\\"\s*}/g, JSON.stringify(temp).slice(1,-1));
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
// Compress the output if the client accepts it,
|
|
1661
|
+
// but only if the file is at least 150 bytes
|
|
1662
|
+
if (alchemy.settings.compression && message.length > 150 && this.accepts('gzip')) {
|
|
1663
|
+
|
|
1664
|
+
// Set the decompressed content-length for use in progress bars
|
|
1665
|
+
this.setHeader('x-decompressed-content-length', Buffer.byteLength(message));
|
|
1666
|
+
|
|
1667
|
+
// Set the gzip header
|
|
1668
|
+
this.setHeader('content-encoding', 'gzip');
|
|
1669
|
+
|
|
1670
|
+
// Make sure proxy servers only cache this under this content-encoding type
|
|
1671
|
+
this.setHeader('vary', 'accept-encoding');
|
|
1672
|
+
|
|
1673
|
+
zlib.gzip(message, function gotZipped(err, zipped) {
|
|
1674
|
+
that._end(zipped, 'utf-8');
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
this._end(message, 'utf-8');
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Call the actual end method
|
|
1685
|
+
*
|
|
1686
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1687
|
+
* @since 0.2.0
|
|
1688
|
+
* @version 1.1.0
|
|
1689
|
+
*/
|
|
1690
|
+
Conduit.setMethod(function _end(message, encoding = 'utf-8') {
|
|
1691
|
+
|
|
1692
|
+
this.ended = new Date();
|
|
1693
|
+
|
|
1694
|
+
if (!this.response) {
|
|
1695
|
+
let args = [];
|
|
1696
|
+
|
|
1697
|
+
if (arguments.length) {
|
|
1698
|
+
args.push(message);
|
|
1699
|
+
args.push(encoding);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
this._end_arguments = args;
|
|
1703
|
+
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
let headers = [],
|
|
1708
|
+
value,
|
|
1709
|
+
key;
|
|
1710
|
+
|
|
1711
|
+
if (this.status) {
|
|
1712
|
+
this.response.statusCode = this.status;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Set the content-length if it hasn't been set yet
|
|
1716
|
+
if (arguments.length > 0 && !this.response_headers['content-length']) {
|
|
1717
|
+
this.response_headers['content-length'] = Buffer.byteLength(message);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
for (key in this.response_headers) {
|
|
1721
|
+
value = this.response_headers[key];
|
|
1722
|
+
this.response.setHeader(key, value);
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
if (this.new_cookie_header.length) {
|
|
1726
|
+
this.response.setHeader('set-cookie', this.new_cookie_header);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// Write the actual headers
|
|
1730
|
+
this.response.writeHead(this.status);
|
|
1731
|
+
|
|
1732
|
+
if (arguments.length === 0) {
|
|
1733
|
+
return this.response.end();
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// End the response
|
|
1737
|
+
return this.response.end(message, encoding);
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* Send a response to the client
|
|
1742
|
+
*
|
|
1743
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1744
|
+
* @since 0.2.0
|
|
1745
|
+
* @version 0.2.0
|
|
1746
|
+
*/
|
|
1747
|
+
Conduit.setMethod(function send(content) {
|
|
1748
|
+
return this.end(content);
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Create the scene id (if it doesn't exist already)
|
|
1753
|
+
* We do this using cookies, so the HTML response can be cached
|
|
1754
|
+
*
|
|
1755
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1756
|
+
* @since 0.2.0
|
|
1757
|
+
* @version 1.1.0
|
|
1758
|
+
*/
|
|
1759
|
+
Conduit.setMethod(function createScene() {
|
|
1760
|
+
return this.scene_id;
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* Render a view and send it to the client
|
|
1765
|
+
*
|
|
1766
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1767
|
+
* @since 0.2.0
|
|
1768
|
+
* @version 1.1.0
|
|
1769
|
+
*/
|
|
1770
|
+
Conduit.setMethod(function render(template_name, options, callback) {
|
|
1771
|
+
|
|
1772
|
+
var that = this,
|
|
1773
|
+
templates;
|
|
1774
|
+
|
|
1775
|
+
if (typeof options == 'function') {
|
|
1776
|
+
callback = options;
|
|
1777
|
+
options = {};
|
|
1778
|
+
} else if (options == null) {
|
|
1779
|
+
options = {};
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
if (template_name) {
|
|
1783
|
+
templates = [template_name];
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
if (templates) {
|
|
1787
|
+
templates.push('error/404');
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// Expose the useragent info to the hawkejs renderer
|
|
1791
|
+
this.internal('useragent', this.useragent);
|
|
1792
|
+
|
|
1793
|
+
this.createScene();
|
|
1794
|
+
|
|
1795
|
+
// Pass along the clientRender property,
|
|
1796
|
+
// can be used to force rendering HTML
|
|
1797
|
+
if (options.clientRender != null) {
|
|
1798
|
+
this.renderer.clientRender = options.clientRender;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
if (this.layout) {
|
|
1802
|
+
this.renderer.layout = this.layout;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
this.renderer.renderHTML(templates).done(function afterRender(err, html) {
|
|
1806
|
+
|
|
1807
|
+
var mimetype;
|
|
1808
|
+
|
|
1809
|
+
if (err != null) {
|
|
1810
|
+
|
|
1811
|
+
if (callback) {
|
|
1812
|
+
return callback(err);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
throw err;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
that.registerBindings();
|
|
1819
|
+
|
|
1820
|
+
if (typeof html !== 'string') {
|
|
1821
|
+
|
|
1822
|
+
// Stringify using json-dry
|
|
1823
|
+
html = JSON.dry(html);
|
|
1824
|
+
|
|
1825
|
+
// Tell the client to expect a json-dry response
|
|
1826
|
+
mimetype = 'application/json-dry';
|
|
1827
|
+
} else {
|
|
1828
|
+
mimetype = 'text/html';
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// Only send the mimetype if it hasn't been set yet
|
|
1832
|
+
if (that.setHeader('content-type') == null) {
|
|
1833
|
+
that.setHeader('content-type', mimetype + ";charset=utf-8");
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
if (callback) {
|
|
1837
|
+
return callback(null, html);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
that.end(html);
|
|
1841
|
+
});
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
/**
|
|
1845
|
+
* Convert a buffer into a stream
|
|
1846
|
+
*
|
|
1847
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1848
|
+
* @since 1.1.0
|
|
1849
|
+
* @version 1.1.0
|
|
1850
|
+
*
|
|
1851
|
+
* @param {Buffer} buffer
|
|
1852
|
+
*
|
|
1853
|
+
* @return {Readable}
|
|
1854
|
+
*/
|
|
1855
|
+
function bufferToStream(buffer) {
|
|
1856
|
+
|
|
1857
|
+
const readable_stream = new libstream.Readable({
|
|
1858
|
+
read() {
|
|
1859
|
+
this.push(buffer);
|
|
1860
|
+
this.push(null);
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1864
|
+
return readable_stream;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
/**
|
|
1868
|
+
* Send a file to the browser.
|
|
1869
|
+
* Uses cache-control by default.
|
|
1870
|
+
*
|
|
1871
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1872
|
+
* @since 0.2.0
|
|
1873
|
+
* @version 1.1.0
|
|
1874
|
+
*
|
|
1875
|
+
* @param {String} path The path on the server to send to the browser
|
|
1876
|
+
* @param {Object} options Options, including headers
|
|
1877
|
+
*/
|
|
1878
|
+
Conduit.setMethod(function serveFile(path, options) {
|
|
1879
|
+
|
|
1880
|
+
var that = this,
|
|
1881
|
+
tasks = [],
|
|
1882
|
+
stats,
|
|
1883
|
+
isStream;
|
|
1884
|
+
|
|
1885
|
+
// Create an options object if it doesn't exist yet
|
|
1886
|
+
if (options == null) {
|
|
1887
|
+
options = {};
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// Error handling function
|
|
1891
|
+
if (!options.onError) {
|
|
1892
|
+
options.onError = function onError(err) {
|
|
1893
|
+
that.notFound(err);
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// See if we have a stats object
|
|
1898
|
+
if (Object.isObject(path)) {
|
|
1899
|
+
|
|
1900
|
+
if (Buffer.isBuffer(path)) {
|
|
1901
|
+
path = bufferToStream(path);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
if (path.readable) {
|
|
1905
|
+
isStream = true;
|
|
1906
|
+
stats = {
|
|
1907
|
+
mimetype: 'application/octet-stream'
|
|
1908
|
+
};
|
|
1909
|
+
} else {
|
|
1910
|
+
stats = path;
|
|
1911
|
+
}
|
|
1912
|
+
} else {
|
|
1913
|
+
stats = fileCache[path];
|
|
1914
|
+
|
|
1915
|
+
if (stats == null) {
|
|
1916
|
+
stats = {
|
|
1917
|
+
path: path
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// Don't check for file information when it's a stream
|
|
1923
|
+
if (!isStream) {
|
|
1924
|
+
|
|
1925
|
+
if (!stats.path) {
|
|
1926
|
+
return options.onError(new Error('No file to serve'));
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// Make sure the stats object is in the cache
|
|
1930
|
+
if (fileCache[stats.path] == null) {
|
|
1931
|
+
fileCache[stats.path] = stats;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
// Get file stats if it isn't available yet
|
|
1935
|
+
if (stats.mtime == null) {
|
|
1936
|
+
tasks.push(function getFileStats(next) {
|
|
1937
|
+
|
|
1938
|
+
fs.stat(stats.path, function gotStats(err, fileStats) {
|
|
1939
|
+
|
|
1940
|
+
if (err) {
|
|
1941
|
+
stats.err = err;
|
|
1942
|
+
stats.mtime = new Date();
|
|
1943
|
+
} else {
|
|
1944
|
+
Object.assign(stats, fileStats);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
next();
|
|
1948
|
+
});
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// Get the mimetype if it isn't available yet
|
|
1953
|
+
if (!options.mimetype && stats.mimetype == null) {
|
|
1954
|
+
tasks.push(function getMimetype(next) {
|
|
1955
|
+
|
|
1956
|
+
// Don't use libmime if it isn't loaded,
|
|
1957
|
+
// that could be the case on NW.js
|
|
1958
|
+
if (!libmime) {
|
|
1959
|
+
return next();
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// Lookup the mimetype by the extension alone
|
|
1963
|
+
stats.mimetype = libmime.getType(stats.path);
|
|
1964
|
+
|
|
1965
|
+
// Return the result if a valid mimetype was found
|
|
1966
|
+
if (stats.mimetype !== 'application/octet-stream') {
|
|
1967
|
+
return next();
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// If no mimetype was found,
|
|
1971
|
+
// see if we can find it using the original path (for resized images)
|
|
1972
|
+
if (options.original_path) {
|
|
1973
|
+
stats.mimetype = libmime.getType(options.original_path);
|
|
1974
|
+
|
|
1975
|
+
if (stats.mimetype !== 'application/octet-stream') {
|
|
1976
|
+
return next();
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// "magic" currently doesn't work in nw.js
|
|
1981
|
+
if (Blast.isNW) {
|
|
1982
|
+
return next();
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
// Don't try to use magic if it's not loaded
|
|
1986
|
+
if (!getMagic()) {
|
|
1987
|
+
return next();
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// Look inside the data (using "magic") for a better mimetype
|
|
1991
|
+
magic.detectFile(stats.path, function detectedMimetype(err, result) {
|
|
1992
|
+
|
|
1993
|
+
if (!err) {
|
|
1994
|
+
stats.mimetype = result;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
next();
|
|
1998
|
+
});
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
Function.parallel(tasks, function gotFileInfo(err) {
|
|
2004
|
+
|
|
2005
|
+
var disposition,
|
|
2006
|
+
outStream,
|
|
2007
|
+
mimetype,
|
|
2008
|
+
headers,
|
|
2009
|
+
isText,
|
|
2010
|
+
since,
|
|
2011
|
+
key;
|
|
2012
|
+
|
|
2013
|
+
if (err) {
|
|
2014
|
+
return that.error(err);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
if (stats.err) {
|
|
2018
|
+
return options.onError(stats.err);
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
if (!isStream && !stats.path) {
|
|
2022
|
+
return options.onError(new Error('File not found'));
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
// Check the if-modified-since header if it's supplied
|
|
2026
|
+
if (alchemy.settings.cache !== false && that.headers['if-modified-since'] != null) {
|
|
2027
|
+
|
|
2028
|
+
// Turn the string into a date
|
|
2029
|
+
since = new Date(that.headers['if-modified-since']);
|
|
2030
|
+
|
|
2031
|
+
// If the file's modifytime is smaller or equal to the since time,
|
|
2032
|
+
// don't serve the contents!
|
|
2033
|
+
if (stats.mtime <= since) {
|
|
2034
|
+
return that.notModified();
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
mimetype = stats.mimetype;
|
|
2039
|
+
|
|
2040
|
+
// If we get a general mimetype, and an alternative is provided, use that one
|
|
2041
|
+
if (!mimetype || mimetype === 'application/octet-stream') {
|
|
2042
|
+
if (options.mimetype != null) {
|
|
2043
|
+
mimetype = options.mimetype;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
isText = /svg|xml|javascript|text/.test(mimetype);
|
|
2048
|
+
|
|
2049
|
+
// Serve text files as utf-8
|
|
2050
|
+
if (isText) {
|
|
2051
|
+
mimetype += '; charset=utf-8';
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
that.setHeader('content-type', mimetype);
|
|
2055
|
+
|
|
2056
|
+
// Setting the disposition makes the browser download the file
|
|
2057
|
+
// This is on by default, but can be disabled
|
|
2058
|
+
if (options.disposition == 'inline') {
|
|
2059
|
+
disposition = 'inline';
|
|
2060
|
+
|
|
2061
|
+
if (options.filename) {
|
|
2062
|
+
disposition += '; filename=' + JSON.stringify(options.filename)
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
that.setHeader('content-disposition', disposition);
|
|
2066
|
+
} else if (options.disposition !== false) {
|
|
2067
|
+
if (options.filename) {
|
|
2068
|
+
disposition = 'attachment; filename=' + JSON.stringify(options.filename);
|
|
2069
|
+
} else {
|
|
2070
|
+
disposition = 'attachment';
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
that.setHeader('content-disposition', disposition);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
if (stats.mtime && alchemy.settings.cache) {
|
|
2077
|
+
// Allow the browser to cache this for 60 minutes,
|
|
2078
|
+
// after which it has to revalidate the content
|
|
2079
|
+
// by seeing if it has been modified
|
|
2080
|
+
that.setHeader('cache-control', 'public, max-age=3600, must-revalidate');
|
|
2081
|
+
that.setHeader('last-modified', stats.mtime.toGMTString());
|
|
2082
|
+
} else if (!alchemy.settings.cache) {
|
|
2083
|
+
that.setHeader('cache-control', 'no-cache');
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
for (key in options.headers) {
|
|
2087
|
+
that.setHeader(key, options.headers[key]);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// End now if it's just a HEAD request
|
|
2091
|
+
if (that.method == 'head') {
|
|
2092
|
+
return that.end();
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
if (isStream) {
|
|
2096
|
+
outStream = path;
|
|
2097
|
+
} else {
|
|
2098
|
+
outStream = fs.createReadStream(path, {bufferSize: 64*1024});
|
|
2099
|
+
|
|
2100
|
+
// Listen for file errors
|
|
2101
|
+
outStream.on('error', options.onError);
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// Compress text responses
|
|
2105
|
+
if (isText && alchemy.settings.compression && that.accepts('gzip')) {
|
|
2106
|
+
|
|
2107
|
+
// Set the gzip header
|
|
2108
|
+
that.setHeader('content-encoding', 'gzip');
|
|
2109
|
+
that.setHeader('vary', 'accept-encoding');
|
|
2110
|
+
|
|
2111
|
+
// Create the gzip stream
|
|
2112
|
+
outStream = outStream.pipe(zlib.createGzip());
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// If we received a stream as parameter...
|
|
2116
|
+
if (isStream) {
|
|
2117
|
+
that.response.on('end', cleanup);
|
|
2118
|
+
that.response.on('finish', cleanup);
|
|
2119
|
+
that.response.on('error', cleanup);
|
|
2120
|
+
that.response.on('close', cleanup);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function cleanup() {
|
|
2124
|
+
|
|
2125
|
+
// Remove all pipes
|
|
2126
|
+
outStream.unpipe();
|
|
2127
|
+
|
|
2128
|
+
if (outStream.destroy) {
|
|
2129
|
+
outStream.destroy();
|
|
2130
|
+
} else if (outStream.end) {
|
|
2131
|
+
outStream.end();
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// Send the headers
|
|
2136
|
+
for (key in that.response_headers) {
|
|
2137
|
+
that.response.setHeader(key, that.response_headers[key]);
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
if (that.new_cookie_header.length) {
|
|
2141
|
+
that.response.setHeader('set-cookie', that.new_cookie_header);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
that.response.statusCode = 200;
|
|
2145
|
+
|
|
2146
|
+
// Stream the file to the client
|
|
2147
|
+
outStream.pipe(that.response);
|
|
2148
|
+
});
|
|
2149
|
+
});
|
|
2150
|
+
|
|
2151
|
+
/**
|
|
2152
|
+
* Create a session
|
|
2153
|
+
*
|
|
2154
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2155
|
+
* @since 0.2.0
|
|
2156
|
+
* @version 1.1.0
|
|
2157
|
+
*
|
|
2158
|
+
* @param {Boolean} create Create a session if none exist
|
|
2159
|
+
*
|
|
2160
|
+
* @return {UserSession}
|
|
2161
|
+
*/
|
|
2162
|
+
Conduit.setMethod(function getSession(allow_create = true) {
|
|
2163
|
+
|
|
2164
|
+
var cookie_name,
|
|
2165
|
+
fingerprint,
|
|
2166
|
+
session_id,
|
|
2167
|
+
session;
|
|
2168
|
+
|
|
2169
|
+
// Only do this once per request
|
|
2170
|
+
if (this.sessionData != null) {
|
|
2171
|
+
return this.sessionData;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// Set the name of the cookie (could change in the future)
|
|
2175
|
+
cookie_name = alchemy.settings.session_key || 'alchemy_sid';
|
|
2176
|
+
|
|
2177
|
+
// Get the ID of the session
|
|
2178
|
+
session_id = this.cookie(cookie_name);
|
|
2179
|
+
|
|
2180
|
+
if (session_id) {
|
|
2181
|
+
// Get the session
|
|
2182
|
+
session = alchemy.sessions.get(session_id);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// If no session is found, see if we can find one
|
|
2186
|
+
// based on the browser fingerprint
|
|
2187
|
+
if (!session && this.ip) {
|
|
2188
|
+
fingerprint = this.fingerprint;
|
|
2189
|
+
|
|
2190
|
+
if (fingerprint) {
|
|
2191
|
+
session = alchemy.fingerprints.get(fingerprint);
|
|
2192
|
+
|
|
2193
|
+
if (session && session.id) {
|
|
2194
|
+
session_id = session.id;
|
|
2195
|
+
this.cookie(cookie_name, session_id, {httpOnly: true});
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
// If no valid session exists, create a new one
|
|
2201
|
+
if (!session && allow_create) {
|
|
2202
|
+
session = new Classes.Alchemy.ClientSession(this);
|
|
2203
|
+
session_id = session.id;
|
|
2204
|
+
|
|
2205
|
+
if (fingerprint) {
|
|
2206
|
+
alchemy.fingerprints.set(fingerprint, session);
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
this.cookie(cookie_name, session_id, {httpOnly: true});
|
|
2210
|
+
|
|
2211
|
+
alchemy.sessions.set(session_id, session);
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
if (session) {
|
|
2215
|
+
this.sessionData = session;
|
|
2216
|
+
session.request_count++;
|
|
2217
|
+
} else {
|
|
2218
|
+
return false;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
return session;
|
|
2222
|
+
});
|
|
2223
|
+
|
|
2224
|
+
/**
|
|
2225
|
+
* Register live data bindings via websockets
|
|
2226
|
+
*
|
|
2227
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2228
|
+
* @since 0.2.0
|
|
2229
|
+
* @version 0.4.0
|
|
2230
|
+
*/
|
|
2231
|
+
Conduit.setMethod(function registerBindings(arr) {
|
|
2232
|
+
|
|
2233
|
+
var data_ids;
|
|
2234
|
+
|
|
2235
|
+
// Don't do anything is websockets aren't enabled
|
|
2236
|
+
if (!alchemy.settings.websockets) {
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
if (arr) {
|
|
2241
|
+
data_ids = arr;
|
|
2242
|
+
} else {
|
|
2243
|
+
data_ids = this.renderer.live_bindings;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
if (Object.isEmpty(data_ids)) {
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
this.getSession().registerBindings(data_ids, this.sceneId);
|
|
2251
|
+
});
|
|
2252
|
+
|
|
2253
|
+
/**
|
|
2254
|
+
* Get a a value from the session object
|
|
2255
|
+
*
|
|
2256
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2257
|
+
* @since 0.2.0
|
|
2258
|
+
* @version 0.4.0
|
|
2259
|
+
*
|
|
2260
|
+
* @param {String} name
|
|
2261
|
+
* @param {Mixed} value
|
|
2262
|
+
*
|
|
2263
|
+
* @return {Mixed}
|
|
2264
|
+
*/
|
|
2265
|
+
Conduit.setMethod(function session(name, value) {
|
|
2266
|
+
|
|
2267
|
+
this.getSession();
|
|
2268
|
+
|
|
2269
|
+
if (arguments.length === 0) {
|
|
2270
|
+
return this.sessionData;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
if (arguments.length === 1) {
|
|
2274
|
+
return this.sessionData[name];
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
this.sessionData[name] = value;
|
|
2278
|
+
});
|
|
2279
|
+
|
|
2280
|
+
/**
|
|
2281
|
+
* Get a parameter from the route
|
|
2282
|
+
*
|
|
2283
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2284
|
+
* @since 0.2.0
|
|
2285
|
+
* @version 0.2.0
|
|
2286
|
+
*
|
|
2287
|
+
* @param {String} name
|
|
2288
|
+
*/
|
|
2289
|
+
Conduit.setMethod(function routeParam(name) {
|
|
2290
|
+
return this.params[name];
|
|
2291
|
+
});
|
|
2292
|
+
|
|
2293
|
+
/**
|
|
2294
|
+
* Get/set a cookie
|
|
2295
|
+
*
|
|
2296
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2297
|
+
* @since 0.2.0
|
|
2298
|
+
* @version 0.4.2
|
|
2299
|
+
*
|
|
2300
|
+
* @param {String} name
|
|
2301
|
+
* @param {Mixed} value
|
|
2302
|
+
* @param {Object} options
|
|
2303
|
+
*/
|
|
2304
|
+
Conduit.setMethod(function cookie(name, value, options) {
|
|
2305
|
+
|
|
2306
|
+
var header,
|
|
2307
|
+
arr,
|
|
2308
|
+
key;
|
|
2309
|
+
|
|
2310
|
+
// Return if cookies are disabled
|
|
2311
|
+
if (!alchemy.settings.cookies) {
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
if (arguments.length == 1) {
|
|
2316
|
+
return this.new_cookies[name] || this.cookies[name];
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
if (options == null) options = {};
|
|
2320
|
+
|
|
2321
|
+
// If the value is null or undefined, the cookie should be removed
|
|
2322
|
+
if (value == null) {
|
|
2323
|
+
options.expires = new Date(0);
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
// If no path is given, default to the root path
|
|
2327
|
+
if (options.path == null) options.path = '/';
|
|
2328
|
+
|
|
2329
|
+
// If the `secure` flag is not set,
|
|
2330
|
+
// see if this connection is secure
|
|
2331
|
+
if (options.secure == null) {
|
|
2332
|
+
if (this.is_secure) {
|
|
2333
|
+
options.secure = true;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// Store it in the new_cookies object, for quick access
|
|
2338
|
+
this.new_cookies[name] = value;
|
|
2339
|
+
|
|
2340
|
+
if (this.websocket) {
|
|
2341
|
+
return this.socket.emit('alchemy-set-cookie', {name: name, value: value, options: options});
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
// Create the basic header string
|
|
2345
|
+
header = String.encodeCookie(name, value, options);
|
|
2346
|
+
|
|
2347
|
+
// Add this to the cookieheader array
|
|
2348
|
+
this.new_cookie_header.push(header);
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* Set a response header
|
|
2353
|
+
*
|
|
2354
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2355
|
+
* @since 0.2.0
|
|
2356
|
+
* @version 1.1.0
|
|
2357
|
+
*
|
|
2358
|
+
* @param {String} name
|
|
2359
|
+
* @param {Mixed} value
|
|
2360
|
+
*/
|
|
2361
|
+
Conduit.setMethod(function setHeader(name, value) {
|
|
2362
|
+
|
|
2363
|
+
if (arguments.length == 1) {
|
|
2364
|
+
return this.getHeader(name);
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
if (this.websocket) {
|
|
2368
|
+
throw new Error("Can't set a header on a websocket connection");
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
this.response_headers[name] = value;
|
|
2372
|
+
});
|
|
2373
|
+
|
|
2374
|
+
/**
|
|
2375
|
+
* Get a response header
|
|
2376
|
+
*
|
|
2377
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2378
|
+
* @since 1.1.0
|
|
2379
|
+
* @version 1.1.0
|
|
2380
|
+
*
|
|
2381
|
+
* @param {String} name
|
|
2382
|
+
*/
|
|
2383
|
+
Conduit.setMethod(function getHeader(name) {
|
|
2384
|
+
|
|
2385
|
+
if (this.response_headers[name] != null) {
|
|
2386
|
+
return this.response_headers[name];
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
if (this.response) {
|
|
2390
|
+
return this.response.getHeader(name);
|
|
2391
|
+
}
|
|
2392
|
+
});
|
|
2393
|
+
|
|
2394
|
+
/**
|
|
2395
|
+
* Update data to this scene only
|
|
2396
|
+
*
|
|
2397
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2398
|
+
* @since 0.2.0
|
|
2399
|
+
* @version 0.4.0
|
|
2400
|
+
*
|
|
2401
|
+
* @param {String} name
|
|
2402
|
+
* @param {Mixed} value
|
|
2403
|
+
*/
|
|
2404
|
+
Conduit.setMethod(function update(name, value) {
|
|
2405
|
+
|
|
2406
|
+
// Make sure a scene id is created
|
|
2407
|
+
this.createScene();
|
|
2408
|
+
|
|
2409
|
+
// Send this update to this scene only
|
|
2410
|
+
this.getSession().sendDataUpdate(name, value, this.sceneId);
|
|
2411
|
+
});
|
|
2412
|
+
|
|
2413
|
+
/**
|
|
2414
|
+
* Push a flash message to the client
|
|
2415
|
+
*
|
|
2416
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2417
|
+
* @since 0.2.0
|
|
2418
|
+
* @version 0.2.0
|
|
2419
|
+
*/
|
|
2420
|
+
Conduit.setMethod(function flash(message, options) {
|
|
2421
|
+
|
|
2422
|
+
var newFlashes;
|
|
2423
|
+
|
|
2424
|
+
if (options == null) {
|
|
2425
|
+
options = {};
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
newFlashes = this.internal('newFlashes');
|
|
2429
|
+
|
|
2430
|
+
if (newFlashes == null) {
|
|
2431
|
+
newFlashes = {};
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
newFlashes[Date.now() + '-' + Number.random(100)] = {
|
|
2435
|
+
message: message,
|
|
2436
|
+
options: options
|
|
2437
|
+
};
|
|
2438
|
+
|
|
2439
|
+
this.internal('newFlashes', newFlashes);
|
|
2440
|
+
});
|
|
2441
|
+
|
|
2442
|
+
/**
|
|
2443
|
+
* Set a theme to use
|
|
2444
|
+
*
|
|
2445
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2446
|
+
* @since 0.2.0
|
|
2447
|
+
* @version 0.2.0
|
|
2448
|
+
*
|
|
2449
|
+
* @param {String} name
|
|
2450
|
+
*/
|
|
2451
|
+
Conduit.setMethod(function setTheme(name) {
|
|
2452
|
+
this.theme = name;
|
|
2453
|
+
this.renderer.setTheme(name);
|
|
2454
|
+
});
|
|
2455
|
+
|
|
2456
|
+
/**
|
|
2457
|
+
* Does this user support a certain feature?
|
|
2458
|
+
*
|
|
2459
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2460
|
+
* @since 1.0.4
|
|
2461
|
+
* @version 1.0.4
|
|
2462
|
+
*
|
|
2463
|
+
* @param {String} feature
|
|
2464
|
+
*
|
|
2465
|
+
* @return {Boolean}
|
|
2466
|
+
*/
|
|
2467
|
+
Conduit.setMethod(function supports(feature) {
|
|
2468
|
+
|
|
2469
|
+
if (this.useragent && (feature == 'async' || feature == 'await')) {
|
|
2470
|
+
let agent = this.useragent;
|
|
2471
|
+
|
|
2472
|
+
if (agent.family == 'IE') {
|
|
2473
|
+
return false;
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
if (agent.family == 'Edge' && agent.major < 15) {
|
|
2477
|
+
return false;
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
// Its actually supported on 10.1, but oh well
|
|
2481
|
+
if (agent.family == 'Safari' && agent.major < 11) {
|
|
2482
|
+
return false;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
if (agent.family == 'Samsung Internet' && agent.major < 6) {
|
|
2486
|
+
return false;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
if (agent.family == 'Opera Mini') {
|
|
2490
|
+
return false;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
return null;
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2497
|
+
/**
|
|
2498
|
+
* Broadcast data to every connected user
|
|
2499
|
+
*
|
|
2500
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2501
|
+
* @since 0.3.0
|
|
2502
|
+
* @version 0.4.0
|
|
2503
|
+
*
|
|
2504
|
+
* @param {String} type
|
|
2505
|
+
* @param {Object} data
|
|
2506
|
+
*/
|
|
2507
|
+
Alchemy.setMethod(function broadcast(type, data) {
|
|
2508
|
+
|
|
2509
|
+
alchemy.sessions.forEach(function eachSession(session, key) {
|
|
2510
|
+
|
|
2511
|
+
// Go over every listening scene and submit the data
|
|
2512
|
+
Object.each(session.connections, function eachScene(scene, scene_id) {
|
|
2513
|
+
|
|
2514
|
+
if (!scene) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
if (alchemy.settings.debug) {
|
|
2519
|
+
log.debug('Broadcasting', type, {data, scene});
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
scene.submit(type, data);
|
|
2523
|
+
});
|
|
2524
|
+
});
|
|
2525
|
+
});
|
|
2526
|
+
|
|
2527
|
+
/**
|
|
2528
|
+
* Get the magic mimetype function
|
|
2529
|
+
*
|
|
2530
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
2531
|
+
* @since 0.3.0
|
|
2532
|
+
* @version 0.3.0
|
|
2533
|
+
*/
|
|
2534
|
+
function getMagic() {
|
|
2535
|
+
|
|
2536
|
+
var mmmagic;
|
|
2537
|
+
|
|
2538
|
+
if (magic || magic === false) {
|
|
2539
|
+
return magic;
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
// Get mmmagic module
|
|
2543
|
+
mmmagic = alchemy.use('mmmagic')
|
|
2544
|
+
|
|
2545
|
+
if (mmmagic) {
|
|
2546
|
+
magic = new mmmagic.Magic(mmmagic.MAGIC_MIME_TYPE);
|
|
2547
|
+
} else {
|
|
2548
|
+
log.error('Could not load mmmagic module');
|
|
2549
|
+
magic = false;
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
return magic;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
global.Conduit = Conduit;
|