1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const router_1 = require("./router");
|
4 | exports.Router = router_1.WorkerRouter;
|
5 | const akala = require("@akala/core");
|
6 | const stream = require("stream");
|
7 | const send = require("send");
|
8 | const onFinished = require("on-finished");
|
9 | const log = akala.log('akala:worker');
|
10 | function createClient(namespace) {
|
11 | return akala.resolve('$agent.' + namespace);
|
12 | }
|
13 | exports.createClient = createClient;
|
14 | const masterPrefixes = ['header', 'route', 'query', 'body'];
|
15 | class WorkerInjectorImpl extends akala.Injector {
|
16 | constructor(request) {
|
17 | super();
|
18 | this.request = request;
|
19 | }
|
20 | resolve(name) {
|
21 | let indexOfDot = name.indexOf('.');
|
22 | if (~indexOfDot) {
|
23 | let master = name.substr(0, indexOfDot);
|
24 | if (~masterPrefixes.indexOf(master)) {
|
25 | log(`resolving ${name}`);
|
26 | switch (master) {
|
27 | case 'header':
|
28 | return this.request.headers[name.substr(indexOfDot + 1)];
|
29 | case 'query':
|
30 | return this.request.query && this.request.query[name.substr(indexOfDot + 1)];
|
31 | case 'route':
|
32 | log(this.request.params);
|
33 | return this.request.params && this.request.params[name.substr(indexOfDot + 1)];
|
34 | case 'body':
|
35 | return this.request.body && this.request.body[name.substr(indexOfDot + 1)];
|
36 | }
|
37 | }
|
38 | }
|
39 | return super.resolve(name);
|
40 | }
|
41 | }
|
42 | exports.WorkerInjectorImpl = WorkerInjectorImpl;
|
43 | function expressWrap(handler) {
|
44 | return function (req, next) {
|
45 | var callback = req.injector.resolve('$callback');
|
46 | var headers = {};
|
47 | var response = buildResponse(req, callback, next);
|
48 | handler(req, response, next);
|
49 | };
|
50 | }
|
51 | exports.expressWrap = expressWrap;
|
52 | function buildResponse(req, callback, next) {
|
53 | return new MyResponse(req, callback, next);
|
54 | }
|
55 | class MyResponse extends stream.Transform {
|
56 | constructor(req, callback, next) {
|
57 | super({
|
58 | transform: (chunk, _encoding, callback) => {
|
59 | callback(null, chunk);
|
60 | }, decodeStrings: true
|
61 | });
|
62 | this.req = req;
|
63 | this.isStream = false;
|
64 | this.headers = {};
|
65 | this.links = undefined;
|
66 | this.jsonp = undefined;
|
67 | this.sendfile = undefined;
|
68 | this.download = undefined;
|
69 | this.type = undefined;
|
70 | this.format = undefined;
|
71 | this.attachment = undefined;
|
72 | this.headersSent = false;
|
73 | this.get = undefined;
|
74 | this.clearCookie = undefined;
|
75 | this.cookie = undefined;
|
76 | |
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | this.render = undefined;
|
87 | |
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | this.vary = undefined;
|
95 | this.app = undefined;
|
96 | this.setTimeout = undefined;
|
97 | this.addTrailers = undefined;
|
98 | this.writeContinue = undefined;
|
99 | this.finished = false;
|
100 | this.writable = true;
|
101 | this.headers = {};
|
102 | this.sendStatus = callback;
|
103 | this.status = callback;
|
104 | this.send = callback;
|
105 | this.json = callback;
|
106 | this.on('pipe', () => {
|
107 | this.isStream = true;
|
108 | });
|
109 | this.on('end', () => { this.send(this); });
|
110 | }
|
111 | sendFile(path, options, callback) {
|
112 | var encodedPath = encodeURI(path);
|
113 | var done = callback;
|
114 | var req = this.req;
|
115 | var res = this;
|
116 | var next = function (err) {
|
117 | if (err)
|
118 | res.send(500, err);
|
119 | else
|
120 | res.send(200);
|
121 | };
|
122 | var opts = options || {};
|
123 | if (!path) {
|
124 | throw new TypeError('path argument is required to res.sendFile');
|
125 | }
|
126 |
|
127 | if (typeof options === 'function') {
|
128 | done = options;
|
129 | opts = {};
|
130 | }
|
131 |
|
132 | var pathname = encodeURI(path);
|
133 | var file = send(req, pathname, opts);
|
134 |
|
135 | sendfile(res, file, opts, function (err) {
|
136 | if (done)
|
137 | return done(err);
|
138 | if (err && err.code === 'EISDIR')
|
139 | return next();
|
140 |
|
141 | if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
|
142 | next(err);
|
143 | }
|
144 | });
|
145 | }
|
146 | contentType(type) { this.setHeader('contentType', type); return this; }
|
147 | set(field, value) {
|
148 | return this.header(field, value);
|
149 | }
|
150 | header(field, value) {
|
151 | if (typeof field == 'string') {
|
152 | if (typeof (value) == 'undefined')
|
153 | return this.headers[field];
|
154 | this.setHeader(field, value);
|
155 | return this;
|
156 | }
|
157 | else {
|
158 | var self = this;
|
159 | if (field)
|
160 | akala.each(field, function (value, key) {
|
161 | self.setHeader(key, value);
|
162 | });
|
163 | }
|
164 | }
|
165 | location(location) { this.setHeader('location', location); return this; }
|
166 | setHeader(field, value) {
|
167 | this.headers[field] = value;
|
168 | }
|
169 | redirect(url, status) {
|
170 | if (typeof (status) == 'undefined')
|
171 | status = 302;
|
172 | if (typeof (url) == 'number' && typeof (status) == 'string') {
|
173 | var swap = url;
|
174 | url = status;
|
175 | status = swap;
|
176 | }
|
177 | this.setHeader('location', url);
|
178 | this.send(status);
|
179 | }
|
180 | writeHead(statusCode, reasonPhrase, headers) {
|
181 | log('writeHead');
|
182 | this.statusCode = statusCode;
|
183 | if (typeof reasonPhrase != 'string') {
|
184 | headers = reasonPhrase;
|
185 | reasonPhrase = null;
|
186 | }
|
187 | if (reasonPhrase)
|
188 | this.statusMessage = reasonPhrase;
|
189 | this.header(headers);
|
190 | this.send(this);
|
191 | this.headersSent = true;
|
192 | }
|
193 | getHeader(name) {
|
194 | return this.headers[name];
|
195 | }
|
196 | ;
|
197 | removeHeader(name) {
|
198 | delete this.headers[name];
|
199 | }
|
200 | _write(chunk, encoding, callback) {
|
201 | if (!this.headersSent)
|
202 | this.writeHead(this.statusCode);
|
203 | super['_write'](chunk, encoding, callback);
|
204 | }
|
205 | }
|
206 | function handle(app, root) {
|
207 | return function handle(request, next) {
|
208 | return new Promise((resolve, reject) => {
|
209 | var callback = function callback(status, data) {
|
210 | var response;
|
211 | if (arguments.length == 0)
|
212 | return reject();
|
213 | if (arguments.length == 1 && status === 'route')
|
214 | return reject(status);
|
215 | if (isNaN(Number(status)) || Array.isArray(status)) {
|
216 | response = status;
|
217 | if (typeof (data) == 'undefined') {
|
218 | if (typeof (status) == 'undefined')
|
219 | response = { statusCode: 404, data: 'Not found' };
|
220 | else if (isNaN(Number(response.statusCode)) && !(response instanceof stream.Readable)) {
|
221 | data = response;
|
222 | response = { statusCode: 200, data: data };
|
223 | status = null;
|
224 | }
|
225 | else
|
226 | data = undefined;
|
227 | status = null;
|
228 | }
|
229 | }
|
230 | else
|
231 | response = { statusCode: status, data: 'No data' };
|
232 | response.statusCode = response.statusCode || 200;
|
233 | if (!(data instanceof stream.Readable) && !Buffer.isBuffer(data) && typeof (data) !== 'string' && typeof data != 'number' && typeof (data) != 'undefined') {
|
234 | if (!response.headers)
|
235 | response.headers = {};
|
236 | if (data instanceof Error) {
|
237 | if (!response.headers['Content-Type'])
|
238 | response.headers['Content-Type'] = 'text/text';
|
239 | data = data.stack;
|
240 | }
|
241 | else {
|
242 | if (!response.headers['Content-Type'])
|
243 | response.headers['Content-Type'] = 'application/json';
|
244 | data = JSON.stringify(data);
|
245 | }
|
246 | }
|
247 | if (typeof (data) != 'undefined')
|
248 | response.data = data;
|
249 | resolve(response);
|
250 | };
|
251 | callback.redirect = function (url) {
|
252 | return callback({ status: 302, headers: { location: url } });
|
253 | };
|
254 | var requestInjector = new WorkerInjectorImpl(request);
|
255 | requestInjector.register('$request', request);
|
256 | requestInjector.register('$callback', callback);
|
257 |
|
258 | Object.defineProperty(request, 'injector', { value: requestInjector, enumerable: false, configurable: false, writable: false });
|
259 | log(request.url);
|
260 | app.handle(request, callback, function (err, _req, _callback) {
|
261 | reject(err);
|
262 | });
|
263 | });
|
264 | };
|
265 | }
|
266 | exports.handle = handle;
|
267 | function sendfile(res, file, options, callback) {
|
268 | var done = false;
|
269 | var streaming;
|
270 |
|
271 | function onaborted() {
|
272 | if (done)
|
273 | return;
|
274 | done = true;
|
275 | var err = new Error('Request aborted');
|
276 | err['code'] = 'ECONNABORTED';
|
277 | callback(err);
|
278 | }
|
279 |
|
280 | function ondirectory() {
|
281 | if (done)
|
282 | return;
|
283 | done = true;
|
284 | var err = new Error('EISDIR, read');
|
285 | err['code'] = 'EISDIR';
|
286 | callback(err);
|
287 | }
|
288 |
|
289 | function onerror(err) {
|
290 | if (done)
|
291 | return;
|
292 | done = true;
|
293 | callback(err);
|
294 | }
|
295 |
|
296 | function onend() {
|
297 | if (done)
|
298 | return;
|
299 | done = true;
|
300 | callback();
|
301 | }
|
302 |
|
303 | function onfile() {
|
304 | streaming = false;
|
305 | }
|
306 |
|
307 | function onfinish(err) {
|
308 | if (err && err.code === 'ECONNRESET')
|
309 | return onaborted();
|
310 | if (err)
|
311 | return onerror(err);
|
312 | if (done)
|
313 | return;
|
314 | setImmediate(function () {
|
315 | if (streaming !== false && !done) {
|
316 | onaborted();
|
317 | return;
|
318 | }
|
319 | if (done)
|
320 | return;
|
321 | done = true;
|
322 | callback();
|
323 | });
|
324 | }
|
325 |
|
326 | function onstream() {
|
327 | streaming = true;
|
328 | }
|
329 | file.on('directory', ondirectory);
|
330 | file.on('end', onend);
|
331 | file.on('error', onerror);
|
332 | file.on('file', onfile);
|
333 | file.on('stream', onstream);
|
334 | onFinished(res, onfinish);
|
335 | if (options.headers) {
|
336 |
|
337 | file.on('headers', function headers(res) {
|
338 | var obj = options.headers;
|
339 | var keys = Object.keys(obj);
|
340 | for (var i = 0; i < keys.length; i++) {
|
341 | var k = keys[i];
|
342 | res.setHeader(k, obj[k]);
|
343 | }
|
344 | });
|
345 | }
|
346 |
|
347 | file.pipe(res);
|
348 | }
|
349 |
|
\ | No newline at end of file |