Skip to content

Commit fd9e1c7

Browse files
committed
Use express-layer matching for batch routing,
- Extract request logger for later use
1 parent ab3b4ad commit fd9e1c7

File tree

2 files changed

+90
-87
lines changed

2 files changed

+90
-87
lines changed

src/PromiseRouter.js

Lines changed: 35 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,26 @@ import express from 'express';
1010
import url from 'url';
1111
import log from './logger';
1212
import {inspect} from 'util';
13+
import {
14+
logRequest,
15+
logResponse
16+
} from './SensitiveLogger';
17+
18+
const Layer = require('express/lib/router/layer');
19+
20+
function validateParameter(key, value) {
21+
if (key == 'className') {
22+
if (value.match(/_?[A-Za-z][A-Za-z_0-9]*/)) {
23+
return value;
24+
}
25+
}else if (key == 'objectId') {
26+
if (value.match(/[A-Za-z0-9]+/)) {
27+
return value;
28+
}
29+
} else {
30+
return value;
31+
}
32+
}
1333

1434
export default class PromiseRouter {
1535
// Each entry should be an object with:
@@ -65,7 +85,8 @@ export default class PromiseRouter {
6585
this.routes.push({
6686
path: path,
6787
method: method,
68-
handler: handler
88+
handler: handler,
89+
layer: new Layer(path, null, handler)
6990
});
7091
};
7192

@@ -78,30 +99,16 @@ export default class PromiseRouter {
7899
if (route.method != method) {
79100
continue;
80101
}
81-
// NOTE: we can only route the specific wildcards :className and
82-
// :objectId, and in that order.
83-
// This is pretty hacky but I don't want to rebuild the entire
84-
// express route matcher. Maybe there's a way to reuse its logic.
85-
var pattern = '^' + route.path + '$';
86-
87-
pattern = pattern.replace(':className',
88-
'(_?[A-Za-z][A-Za-z_0-9]*)');
89-
pattern = pattern.replace(':objectId',
90-
'([A-Za-z0-9]+)');
91-
var re = new RegExp(pattern);
92-
var m = path.match(re);
93-
if (!m) {
94-
continue;
95-
}
96-
var params = {};
97-
if (m[1]) {
98-
params.className = m[1];
99-
}
100-
if (m[2]) {
101-
params.objectId = m[2];
102-
}
103102

104-
return {params: params, handler: route.handler};
103+
let layer = route.layer || new Layer(route.path, null, route.handler);
104+
let match = layer.match(path);
105+
if (match) {
106+
let params = layer.params;
107+
Object.keys(params).forEach((key) => {
108+
params[key] = validateParameter(key, params[key]);
109+
});
110+
return {params: params, handler: route.handler};
111+
}
105112
}
106113
};
107114

@@ -129,24 +136,7 @@ export default class PromiseRouter {
129136

130137
expressApp() {
131138
var expressApp = express();
132-
for (var route of this.routes) {
133-
switch(route.method) {
134-
case 'POST':
135-
expressApp.post(route.path, makeExpressHandler(this.appId, route.handler));
136-
break;
137-
case 'GET':
138-
expressApp.get(route.path, makeExpressHandler(this.appId, route.handler));
139-
break;
140-
case 'PUT':
141-
expressApp.put(route.path, makeExpressHandler(this.appId, route.handler));
142-
break;
143-
case 'DELETE':
144-
expressApp.delete(route.path, makeExpressHandler(this.appId, route.handler));
145-
break;
146-
default:
147-
throw 'unexpected code branch';
148-
}
149-
}
139+
this.mountOnto(expressApp);
150140
return expressApp;
151141
}
152142
}
@@ -159,26 +149,15 @@ function makeExpressHandler(appId, promiseHandler) {
159149
let config = AppCache.get(appId);
160150
return function(req, res, next) {
161151
try {
162-
let url = maskSensitiveUrl(req);
163-
let body = maskSensitiveBody(req);
164-
let stringifiedBody = JSON.stringify(body, null, 2);
165-
log.verbose(`REQUEST for [${req.method}] ${url}: ${stringifiedBody}`, {
166-
method: req.method,
167-
url: url,
168-
headers: req.headers,
169-
body: body
170-
});
152+
logRequest(req.originalUrl, req.method, req.body, req.headers);
153+
171154
promiseHandler(req).then((result) => {
172155
if (!result.response && !result.location && !result.text) {
173156
log.error('the handler did not include a "response" or a "location" field');
174157
throw 'control should not get here';
175158
}
176159

177-
let stringifiedResponse = JSON.stringify(result, null, 2);
178-
log.verbose(
179-
`RESPONSE from [${req.method}] ${url}: ${stringifiedResponse}`,
180-
{result: result}
181-
);
160+
logResponse(req.originalUrl, req.method, result);
182161

183162
var status = result.status || 200;
184163
res.status(status);
@@ -214,34 +193,3 @@ function makeExpressHandler(appId, promiseHandler) {
214193
}
215194
}
216195
}
217-
218-
function maskSensitiveBody(req) {
219-
let maskBody = Object.assign({}, req.body);
220-
let shouldMaskBody = (req.method === 'POST' && req.originalUrl.endsWith('/users')
221-
&& !req.originalUrl.includes('classes')) ||
222-
(req.method === 'PUT' && /users\/\w+$/.test(req.originalUrl)
223-
&& !req.originalUrl.includes('classes')) ||
224-
(req.originalUrl.includes('classes/_User'));
225-
if (shouldMaskBody) {
226-
for (let key of Object.keys(maskBody)) {
227-
if (key == 'password') {
228-
maskBody[key] = '********';
229-
break;
230-
}
231-
}
232-
}
233-
return maskBody;
234-
}
235-
236-
function maskSensitiveUrl(req) {
237-
let maskUrl = req.originalUrl.toString();
238-
let shouldMaskUrl = req.method === 'GET' && req.originalUrl.includes('/login')
239-
&& !req.originalUrl.includes('classes');
240-
if (shouldMaskUrl) {
241-
let password = url.parse(req.originalUrl, true).query.password;
242-
if (password) {
243-
maskUrl = maskUrl.replace('password=' + password, 'password=********')
244-
}
245-
}
246-
return maskUrl;
247-
}

src/SensitiveLogger.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import log from './logger';
2+
const URL = require('url');
3+
4+
function maskSensitiveBody(url, method, body) {
5+
let maskBody = Object.assign({}, body);
6+
let shouldMaskBody = (method === 'POST' && url.endsWith('/users')
7+
&& !url.includes('classes')) ||
8+
(method === 'PUT' && /users\/\w+$/.test(url)
9+
&& !url.includes('classes')) ||
10+
(url.includes('classes/_User'));
11+
if (shouldMaskBody) {
12+
for (let key of Object.keys(maskBody)) {
13+
if (key == 'password') {
14+
maskBody[key] = '********';
15+
break;
16+
}
17+
}
18+
}
19+
return maskBody;
20+
}
21+
22+
function maskSensitiveUrl(url, method, body) {
23+
let maskUrl = url.toString();
24+
let shouldMaskUrl = method === 'GET' && url.includes('/login')
25+
&& !url.includes('classes');
26+
if (shouldMaskUrl) {
27+
let password = URL.parse(url, true).query.password;
28+
if (password) {
29+
maskUrl = maskUrl.replace('password=' + password, 'password=********')
30+
}
31+
}
32+
return maskUrl;
33+
}
34+
35+
export function logRequest(url, method, body, headers) {
36+
url = maskSensitiveUrl(url, method, body);
37+
body = maskSensitiveBody(url, method, body);
38+
let stringifiedBody = JSON.stringify(body, null, 2);
39+
log.verbose(`REQUEST for [${method}] ${url}: ${stringifiedBody}`, {
40+
method: method,
41+
url: url,
42+
headers: headers,
43+
body: body
44+
});
45+
}
46+
47+
export function logResponse(url, method, body) {
48+
url = maskSensitiveUrl(url, method, body);
49+
body = maskSensitiveBody(url, method, body);
50+
let stringifiedResponse = JSON.stringify(body, null, 2);
51+
log.verbose(
52+
`RESPONSE from [${method}] ${url}: ${stringifiedResponse}`,
53+
{result: body}
54+
);
55+
}

0 commit comments

Comments
 (0)