Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onProxyReq error: trapped error code: message:Can't set headers after they are sent. stack:Error: Can't set headers after they are sent. #1292

Closed
loretoparisi opened this issue Sep 25, 2018 · 1 comment

Comments

@loretoparisi
Copy link

loretoparisi commented Sep 25, 2018

I'm using the new restream option because I have to POST a JSON body

                //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) {
                        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
                });

The first call does not send to the target the json encoded body, the second call gives me back the error on the express middleware side:

 [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):

by the backed side (tornado) I get a Malformed HTTP message from 172.17.0.1: Malformed HTTP headers: '\r\n0\r\n\r\n'.

The request raised with curl -d {...} to the tornado backend will work correctly.

I have also tried the approach suggested here #180

//restream parsed body before proxying
                proxy.on('proxyReq', function (proxyReq, req, res, options) {
                    if ( !req.body || !Object.keys( req.body ).length ) {
                        return;
                    }
            
                    let contentType = proxyReq.getHeader( 'Content-Type' );
                    let bodyData;
            
                    if ( contentType.includes( 'application/json' ) ) {
                        bodyData = JSON.stringify( req.body );
                    }
            
                    if ( contentType.includes( 'application/x-www-form-urlencoded' ) ) {
                        bodyData = queryString.stringify( req.body );
                    }
            
                    if ( bodyData ) {
                        proxyReq.setHeader( 'Content-Length', Buffer.byteLength( bodyData ) );
                        proxyReq.write( bodyData );
                    }
                });

I cannot use the solutions to put the bodyParser before the proxy middleware as suggested here linemanjs/lineman#46

My proxy configuration is the standalone proxy with custom server logic as depicted here and it's basically the following:

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
            });
        });

This issue could be related to #791 and to #1279

@loretoparisi
Copy link
Author

Solved using solution suggested in #1279 (comment)

proxy.once('proxyReq', function (proxyReq, req, res, options) { //restream
                    let bodyData;
                    if (!req.body || !Object.keys(req.body).length) {
                        return;
                    }
                    switch( proxyReq.getHeader('Content-Type') ){
                        case "application/json":
                        case "application/json; charset=UTF-8": 
                            bodyData = JSON.stringify(req.body);
                        break;
                    case "application/x-www-form-urlencoded":
                        bodyData = querystring.stringify(req.body);
                        break;
                    default: 
                    }
                    if (bodyData) {
                        console.debug("---[proxyReq]----", bodyData);
                        proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
                        proxyReq.write(bodyData);
                        proxyReq.end();
                    }
                });
                proxy.on('proxyRes', function (proxyRes, req, res) {
                    modifyResponse(res, proxyRes, function (response) {
                        console.log("----[proxyRes]---->", response)
                        // modify response eventually
                        return response; // return value can be a promise
                    });
                });
                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
                });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant