UNPKG

5.61 kBJavaScriptView Raw
1
2'use strict';
3
4// Middlewares
5module.exports = function (config, renderer) {
6 var pkg = require('../package.json');
7 var httpProxy = require('http-proxy');
8 var rewriteRedirect = config.argv['rewrite-redirect'] || false;
9 var protocolRewrite = config.argv['rewrite-protocol'] || null;
10 var proxy = httpProxy.createProxyServer({
11 autoRewrite: rewriteRedirect,
12 protocolRewrite: protocolRewrite
13 });
14 var deployTimestamp = require('../timestamp').value;
15 var dispatch = require('./dispatch')(config);
16 var statsd = require('./statsd')(config);
17 var router = require('./router')(config);
18
19 var proxyErr = false;
20 proxy.on('proxyRes', function (proxyRes) {
21 if (proxyRes.statusCode >= 400) {
22 proxyErr = new Error(proxyRes.statusMessage);
23 proxyErr.status = proxyRes.statusCode;
24 } else {
25 proxyErr = false;
26 }
27 });
28
29 var parseJson = function (data) {
30 var json = null;
31 var err = null;
32
33 try {
34 json = JSON.parse(data);
35 } catch (e) {
36 err = e;
37 }
38
39 return {
40 error: err,
41 data: json
42 };
43 };
44
45 var processor = {
46 timestamp: function (req, res, next) {
47 req.headers['X-Shunter-Deploy-Timestamp'] = deployTimestamp;
48 next();
49 },
50
51 shunterVersion: function (req, res, next) {
52 req.headers['X-Shunter'] = pkg.version;
53 next();
54 },
55
56 intercept: function (req, res, next) {
57 var data = [];
58 var status = null;
59
60 var shouldIntercept = function () {
61 var type = res.getHeader('Content-type');
62 var method = req.method ? req.method.toUpperCase() : 'GET';
63 var acceptedType = type && (type.indexOf('x-shunter+json') !== -1);
64 var acceptedMethod = method === 'GET' || method === 'POST';
65
66 return acceptedType && acceptedMethod;
67 };
68
69 var write = function (chunk) {
70 data.push(chunk);
71 };
72
73 var endProxyError = function () {
74 res.writeHead = res.__originalWriteHead;
75 res.write = res.__originalWrite;
76 res.end = res.__originalEnd;
77 return dispatch.send(proxyErr, '', req, res);
78 };
79
80 var endIntercept = function () {
81 var timer = config.timer();
82
83 if (req.__proxyTimingFunctionBodyReceived) {
84 statsd.classifiedTiming(req.url, 'proxy_body_received', req.__proxyTimingFunctionBodyReceived('Received body ' + req.url));
85 }
86 var rawJson = Buffer.concat(data).toString('utf8');
87 var json = parseJson(rawJson);
88
89 statsd.classifiedGauge(req.url, 'json_size', Buffer.byteLength(rawJson));
90 statsd.classifiedTiming(req.url, 'parsing', timer('Parsing JSON ' + req.url));
91
92 res.writeHead = res.__originalWriteHead;
93 res.write = res.__originalWrite;
94 res.end = res.__originalEnd;
95
96 if (json.error) {
97 dispatch.send(json.error, '', req, res);
98 } else {
99 if (config.jsonViewParameter && req.query[config.jsonViewParameter]) {
100 req.isJson = true;
101 var jsonOutput = JSON.stringify(json.data, null, '\t');
102 return dispatch.send(null, jsonOutput, req, res, status);
103 }
104
105 timer = config.timer();
106 renderer.render(req, res, json.data, function (err, out) {
107 statsd.classifiedTiming(req.url, 'rendering', timer('Rendering ' + req.url));
108 dispatch.send(err, out, req, res, status);
109 });
110 }
111 };
112
113 var writeHead = function (code) {
114 statsd.increment('total_requests');
115
116 if (shouldIntercept()) {
117 res.write = write;
118 res.end = endIntercept;
119 status = code;
120 if (req.__proxyTimingFunctionHeadersReceived) {
121 statsd.classifiedTiming(req.url, 'proxy_headers_received', req.__proxyTimingFunctionHeadersReceived('Received headers ' + req.url));
122 }
123 } else if (proxyErr) {
124 res.write = write;
125 res.end = endProxyError;
126 status = code;
127 } else {
128 res.__originalWriteHead.apply(res, [].slice.call(arguments, 0));
129 }
130 };
131
132 res.__originalWriteHead = res.writeHead;
133 res.__originalEnd = res.end;
134 res.__originalWrite = res.write;
135
136 res.writeHead = writeHead;
137
138 next();
139 },
140
141 ping: function (req, res) {
142 res.writeHead(200);
143 res.end('pong');
144 },
145
146 api: function (req, res) {
147 var name = req.url.replace(/^\/+/, '').replace(/\?.*/, '').replace(/\/+/g, '__');
148 var body = req.body;
149 var err = null;
150
151 if (!name && body.layout && body.layout.template) {
152 name = body.layout.template;
153 }
154
155 if (name) {
156 renderer.renderPartial(name, req, res, body, function (err, out) {
157 dispatch.send(err, out, req, res);
158 });
159 } else {
160 err = new Error('Template not found');
161 err.status = 404;
162 dispatch.send(err, null, req, res);
163 }
164 },
165
166 proxy: function (req, res) {
167 req.headers = req.headers || {};
168
169 var host = req.headers.host || '';
170 var route = router.map(host, req.url);
171 var err = null;
172
173 var rewriteRequestHeaders = function () {
174 if (route.changeOrigin) {
175 req.headers['X-Orig-Host'] = host;
176 }
177 };
178
179 if (route) {
180 if (route.host) {
181 route.target = 'http://' + route.host + (route.port ? ':' + route.port : '');
182
183 config.log.info('Proxying request for ' + host + req.url + ' to ' + route.target + req.url);
184 req.__proxyTimingFunctionHeadersReceived = config.timer();
185 req.__proxyTimingFunctionBodyReceived = config.timer();
186
187 rewriteRequestHeaders();
188 proxy.web(req, res, route, function (err) {
189 err.status = (err.code === 'ECONNREFUSED') ? 502 : 500;
190 dispatch.send(err, null, req, res);
191 });
192 } else {
193 err = new Error('Route does not define a host');
194 err.status = 500;
195 dispatch.send(err, null, req, res);
196 }
197 } else {
198 err = new Error('Request did not match any route');
199 err.status = 404;
200 dispatch.send(err, null, req, res);
201 }
202 }
203 };
204
205 return processor;
206};