-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbundle.js
202 lines (168 loc) · 5.81 KB
/
bundle.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
'use strict';
const textBody = require("body");
const jsonBody = require("body/json");
const formBody = require("body/form");
const anyBody = require("body/any");
const ContentTypes = {
TEXT_PLAIN: 'text/plain',
APPLICATION_FORM: 'application/x-www-form-urlencoded',
APPLICATION_JSON: 'application/json'
};
/**
*
* @param contentType {string} req.get('content-type')
* @param req {Express.Request}
* @param res {Express.Response}
* @param options { [key: number|string]: any }
* @returns Promise< string | { [key: number|string]: any }> the parsed body
*/
function parseRequestBody(contentType, req, res, options) {
let bodyParser;
switch (contentType) {
case ContentTypes.TEXT_PLAIN:
bodyParser = new Promise(r => textBody(req, (err, body) => r(body)));
break;
case ContentTypes.APPLICATION_FORM:
bodyParser = new Promise(r => formBody(req, {}, (err, body) => r(body)));
break;
case ContentTypes.APPLICATION_JSON:
bodyParser = new Promise(r => jsonBody(req, res, (err, body) => r(body)));
default:
bodyParser = new Promise(r => anyBody(req, res, {}, (err, body) => r(body)));
break;
}
return bodyParser;
}
/**
* @dependancy: called after parseRequestBody
* @param res Express.Response
* @param options { [key: number|string]: any }
* @return Promise<string | { [key: number|string]: any }> the parsed body
*/
function parseResponseBody(res, options, next) {
return new Promise((resolve, reject) => {
let write = res.write;
let end = res.end;
let chunks = [];
res.write = function newWrite(chunk) {
chunks.push(chunk);
write.apply(res, arguments);
};
res.end = function newEnd(chunk) {
if (chunk) {
// chunks.push(chunk);
}
end.apply(res, arguments);
};
res.once('finish', function () {
const isBuffer = input => input instanceof Buffer;
const responseBody = isBuffer(chunks[0]) ? Buffer.concat(chunks).toString('utf8') : chunks.toString();
console.log('response body', arguments);
// @TODO here is where you compute the latency
return resolve(responseBody);
});
return next();
});
}
/**
* @dependancy: called after parseResponseBody
* @param req {Express.Request}
* @returns Promise< string | { [key: number|string]: any }> the parsed body
*/
function parseUrl(req) {
const protocol = req.protocol || req.get('X-Forwarded-Protocol');
const host = req.hostname || req.get('host');
const path = req.originalUrl || req.url;
// console.log('url', `${protocol}://${host}${path}`);
return `${protocol}://${host}${path}`;
}
/**
* @dependancy: called after parseResponseBody
* @param req {Express.Request}
* @returns { method: string, headers: { [key: string]: string|number }, host: string, clientIp: string }
*/
function parseRequestMeta(req) {
const method = req.method;
// console.log('method', method);
const headers = req.headers;
// console.log('request headers', headers);
const host = req.get('host');
// console.log('request host', host);
const clientIp = req.headers['x-forwarded-for'] || req.ip;
// console.log('request clientIp', clientIp);
return { method, headers, host, clientIp };
}
/**
* @dependancy: called after parseResponseBody
* @param res {Express.Response}
* @returns { status: string, headers: { [key: string]: string|number } }
*/
function parseResponseMeta(res) {
const headers = res._headers;
// console.log('response headers', headers);
const status = res.statusCode;
// console.log('status', status);
return { headers, status };
}
/**
* the middleware entry
* @param {*} req
* @param {*} res
* @param {*} next
* @return Promise<{url: string, request: { method, headers, host, clientIp, payload }, respone: { headers, status, payload }, latency: number }>
*/
function capture(req, res, next) {
const start = Date.now();
const contentType = req.get('content-type');
let options = {};
let data = { url: '', request: {}, respone: {}, latency: 0 }; // output
return parseRequestBody(contentType, req, res, options)
.then(payload => { // => request body
data.request = Object.assign({}, data.request, { payload });
let options = {};
return parseResponseBody(res, options, next);
})
.catch(e => {
next();
// in here we should stop the execution
return Promise.reject(e);
})
.then(payload => { // => response body
data.respone = Object.assign({}, data.respone, { payload });
let url = parseUrl(req);
data = Object.assign({}, data, { url });
let { method, headers, host, clientIp } = parseRequestMeta(req);
data.request = Object.assign({}, data.request, { method, headers, host, clientIp });
return Promise.resolve(data);
})
.then(data => { // => response meta
let { headers, status } = parseResponseMeta(res);
data.respone = Object.assign({}, data.respone, { headers, status });
let latency = Date.now() - start;
data = Object.assign({}, data, { latency });
return Promise.resolve(data);
});
// let the index file catch whatever fails
}
function print(data, channel, options) {
console.log(data);
return Promise.resolve(true);
}
var printer = {
print
};
/*
* 1. https://stackoverflow.com/questions/18538537/time-requests-in-nodejs-express
* 2. https://github.com/expressjs/body-parser
* 3. https://github.com/Raynos/body
* 4. https://github.com/expressjs/express/issues/1816
* 5. https://stackoverflow.com/questions/19215042/express-logging-response-body
*/
var index = function (options) {
return function (req, res, next) {
return capture(req, res, next) // capture calls next!
.then(data => printer.print(data, options.channel, options))
.catch(e => console.error(`[${new Date()}]-[express-request-capture]`, e));
}
};
module.exports = index;