1 | "use strict";
|
2 |
|
3 | var http = require('http');
|
4 | var url = require('url');
|
5 | var util = require('util');
|
6 | var qs = require('qs');
|
7 |
|
8 | var co = require('co');
|
9 | var coBody = require('co-body');
|
10 | var bluebird = require('bluebird');
|
11 |
|
12 | var ActionError = require('skinny-bone-actions/lib/errors/actionError');
|
13 |
|
14 | var createError = require('create-error');
|
15 |
|
16 | function HttpTransport(skinny, options) {
|
17 | this.skinny = skinny;
|
18 | this.options = options;
|
19 |
|
20 | this.server = this._createServer();
|
21 |
|
22 | if (options.connectionTimeout) {
|
23 | this.server.timeout = options.connectionTimeout;
|
24 | }
|
25 | }
|
26 |
|
27 | var HttpTransportError = createError('HttpTransportError', { responseCode: 500 });
|
28 | var ServiceError = createError(HttpTransportError, 'ServiceError', { responseCode: 500 });
|
29 | var WrongSecretError = createError(HttpTransportError, 'WrongSecretError', { responseCode: 401 });
|
30 | var BadRequestError = createError(HttpTransportError, 'InvalidRequestError', { responseCode: 400 });
|
31 | var ActionNotPresent = createError(BadRequestError, 'ActionNotPresent', { responseCode: 404 });
|
32 |
|
33 | HttpTransport.prototype.ERRORS = {
|
34 | HttpTransportError: HttpTransportError,
|
35 | ServiceError: ServiceError,
|
36 | WrongSecretError: WrongSecretError,
|
37 | BadRequestError: BadRequestError,
|
38 | ActionNotPresent: ActionNotPresent
|
39 | };
|
40 |
|
41 | HttpTransport.prototype.listen = function listen() {
|
42 | return this.server.listenAsync(this.options.port, this.options.host);
|
43 | };
|
44 |
|
45 | HttpTransport.prototype.close = function close() {
|
46 | if (this.server.address()) {
|
47 | return this.server.closeAsync();
|
48 | } else {
|
49 | return Promise.resolve();
|
50 | }
|
51 | };
|
52 |
|
53 | HttpTransport.prototype._handleRequest = function _handleRequest(req, res) {
|
54 | return co(function *() {
|
55 | try {
|
56 |
|
57 | var token = (req.headers.authorization || '').split(/\s+/).pop() || '';
|
58 | var auth = new Buffer(token, 'base64').toString();
|
59 | var requestSecret = auth.split(/:/).shift() || '';
|
60 |
|
61 | if (this.options.secret !== undefined && requestSecret !== this.options.secret) {
|
62 | throw new WrongSecretError('Wrong secret!');
|
63 | }
|
64 |
|
65 |
|
66 | var parsedUrl = url.parse(req.url);
|
67 |
|
68 | var actionName = parsedUrl.pathname.substring(1);
|
69 |
|
70 | if (!this.skinny.actions[actionName]) {
|
71 | throw new ActionNotPresent('Action "' + actionName + '" not present!');
|
72 | }
|
73 |
|
74 | var params = qs.parse(parsedUrl.query);
|
75 |
|
76 | if (req.method == "POST") {
|
77 | var bodyParams = yield coBody.json(req);
|
78 | params = util._extend(bodyParams, params);
|
79 | }
|
80 |
|
81 |
|
82 | var actionSkinny = this.skinny.newSkinny();
|
83 |
|
84 | actionSkinny.httpRequest = req;
|
85 | actionSkinny.httpResponse = res;
|
86 |
|
87 | var actionResponse = yield this.skinny.actions[actionName](params, actionSkinny);
|
88 |
|
89 | if (!res.headersSent) {
|
90 | if (!res.getHeader('Content-Type')) {
|
91 | res.setHeader('Content-Type', 'application/json');
|
92 | }
|
93 |
|
94 | res.writeHead(res.statusCode || 200);
|
95 |
|
96 | var response = { status: 'success' };
|
97 | if (actionResponse !== undefined) {
|
98 | response.data = actionResponse;
|
99 | }
|
100 |
|
101 | res.write(JSON.stringify(response));
|
102 | }
|
103 |
|
104 | res.end();
|
105 | } catch (e) {
|
106 | this.skinny.emit('warning', e);
|
107 |
|
108 | if (e instanceof ActionError) {
|
109 | e = new BadRequestError(e.name + ': ' + e.message);
|
110 | } else if (!(e instanceof HttpTransportError)) {
|
111 | e = new ServiceError(e.name + ': ' + e.message);
|
112 | }
|
113 |
|
114 | var status = e.responseCode >= 400 && e.responseCode <= 499 ? 'error' : 'fail';
|
115 | var response = JSON.stringify({ status: status, errorCode: e.name, errorMessage: e.message });
|
116 |
|
117 | res.writeHead(e.responseCode, { 'Content-Type': 'application/json' });
|
118 | res.end(response);
|
119 | }
|
120 | }.bind(this)).catch(function(error) {
|
121 | this.skinny.emit('error', error);
|
122 |
|
123 | return error;
|
124 | }.bind(this));
|
125 | };
|
126 |
|
127 | HttpTransport.prototype._createServer = function _createServer() {
|
128 | var server = http.createServer(this._handleRequest.bind(this));
|
129 |
|
130 | bluebird.promisifyAll(server);
|
131 |
|
132 | return server;
|
133 | };
|
134 |
|
135 | module.exports = HttpTransport; |
\ | No newline at end of file |