I'm using node-http-proxy as middleware. When handling GET
requests it's ok. For POST
requests with JSON body, I cannot send the application/json
header and the body json encoded.
This is my middleware. I have added the reastream option as suggested, but this seems not to solve the issue:
//restream parsed body before proxying
proxy.on('proxyReq', function (proxyReq, req, res, options) {
if (!req.body || !Object.keys(req.body).length) {
return;
}
var contentType = proxyReq.getHeader('Content-Type');
var bodyData;
if (contentType === 'application/json') {
bodyData = JSON.stringify(req.body);
}
if (contentType === 'application/x-www-form-urlencoded') {
bodyData = queryString.stringify(req.body);
}
if (bodyData) {
console.log("------", bodyData);
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
}
});
proxy.on('proxyRes', function (proxyRes, req, res) {
modifyResponse(res, proxyRes, function (response) {
if (!MXMUtil.empty(response) && !MXMUtil.empty(response.message) && !MXMUtil.empty(response.message.body)) { // valid response
if (!MXMUtil.empty(response.message.body.error)) { // error response
var message = self.processor.createErrorMessage(500, '', response.message.body.error);
response = message;
} else { // valid response
var message = self.processor.prepareResponse(req_start, response.message.body);
response = message;
}
}
return response; // return value can be a promise
});
});
proxy.on('error', function (error, req, res) {
var errorMessage = self.processor.createErrorMessage(500, '', error.message);
res.send(errorMessage);
res.end();
});
proxy.web(req, res, {
// url string to be parsed with the url module
target: target_uri,
// true/false, Default: false - changes the origin of the host header to the target URL
changeOrigin: true,
// true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request
ignorePath: true
});
On the backend side I get this error:
Malformed HTTP message from 172.17.0.1: Malformed HTTP headers: '\r\n0\r\n\r\n'
Also the second request will fail with a Can't set headers after they are sent.
error on the middleware:
[Tue Sep 25 2018 18:16:29 GMT+0200 (CEST)] trapped error code: message:Can't set headers after they are sent. stack:Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ClientRequest.setHeader (_http_outgoing.js:498:3)
at ProxyServer.<anonymous> (/webservice/lib/api.js:616:34)
at ProxyServer.emit (/webservice/node_modules/eventemitter3/index.js:210:27)
at ClientRequest.<anonymous> (/webservice/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js:132:27)
at emitOne (events.js:121:20)
at ClientRequest.emit (events.js:211:7)
at tickOnSocket (_http_client.js:652:7)
at onSocketNT (_http_client.js:668:5)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickDomainCallback (internal/process/next_tick.js:218:9):
I'm using the body parser before the middleware initialization:
// express app
this.app = express();
// development || production
this.app.set('env', env);
// custom logging: only error codes
this.app.use(morgan('combined', {
skip: function (req, res) {
return res.statusCode < 400;
}
}));
// LP: handle Error: request entity too large
this.app.use(bodyParser.json({limit: '50mb'}));
this.app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
this.app.use(cookieParser());
this.app.use(compression()); // gzip compression
NOTE. This could potentially be a bug and I have opened an issue here, but there should be an alternative approach. Another linked issue is here.
The request raised with curl -d {...}
to the tornado
backend will work correctly.
[UPDATE]
I have fixed the Can't set headers after they are sent.
issue closing the request after write the data:
proxyReq.write(bodyData);
proxyReq.end();
I have tried the suggestion solution to put bodyParser
after the proxy use, but I cannot do this in my architecture, this means that in my case the order is like:
httpProxy = require('http-proxy'),
proxy = httpProxy.createProxyServer({});
// custom logging: only error codes
this.app.use(morgan('combined', {
skip: function (req, res) {
return res.statusCode < 400;
}
}));
// LP: handle Error: request entity too large
this.app.use(bodyParser.json({ limit: '50mb' }));
this.app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
this.app.use(cookieParser());
this.app.use(compression()); // gzip compression
//...api routes...
this.app.post(self._options.baseUrl.uri, function (req, res) {
var req_start = new Date().getTime();
var target_uri = self._options.proxy.uri;
//restream parsed body before proxying
proxy.on('proxyReq', function (proxyReq, req, res, options) {
if (!req.body || !Object.keys(req.body).length) {
return;
}
var bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.write(bodyData);
proxyReq.end();
});
proxy.on('proxyRes', function (proxyRes, req, res) {
modifyResponse(res, proxyRes, function (response) {
console.log("--response--->", response)
if (!MXMUtil.empty(response) && !MXMUtil.empty(response.message) && !MXMUtil.empty(response.message.body)) { // valid response
if (!MXMUtil.empty(response.message.body.error)) { // error response
var message = self.processor.createErrorMessage(500, '', response.message.body.error);
response = message;
} else { // valid response
var message = self.processor.prepareResponse(req_start, response.message.body);
response = message;
}
}
return response; // return value can be a promise
});
});
proxy.on('error', function (error, req, res) {
var errorMessage = self.processor.createErrorMessage(500, '', error.message);
res.send(errorMessage);
res.end();
});
proxy.web(req, res, {
// url string to be parsed with the url module
target: target_uri,
// true/false, Default: false - changes the origin of the host header to the target URL
changeOrigin: true,
// true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request
ignorePath: true
});
});
As noticed in the comments below, it happens the proxied request is sent twice as for the code above, so I can see twice logging in the proxyReq
and proxyRes
events.