http-party / node-http-proxy

A full-featured http proxy for node.js
https://github.com/http-party/node-http-proxy
Other
13.95k stars 1.98k forks source link

Cannot forward Post and PUT request using proxy.web method #1142

Open tamilvjm opened 7 years ago

tamilvjm commented 7 years ago

Cannot forward Post and PUT request using proxy.web method. can you help me how to fix this issue

tamilvjm commented 7 years ago

It's not responding while forwarding put request

zanaca commented 7 years ago

The problem seems like a stream "issue". Are you using something before sending the proxy request?

For example:

http.createServer((req, res) => {
    anyBody(req, res, { limit: process.env.MAX_BODY_SIZE || '2mb' }, (err, body) => {
        req.body = body;
        proxy.web(req, res, { secure: false, proxyTimeout: process.env.TIMEOUT || 8000, xfwd: true, toProxy: true, changeOrigin: true});
    });
}).listen(1234);

That works like a charm for NON BODY requests, like GET, HEAD or DELETE. But POST, for example, has lost its data stream before sending to proxy.web. The upstream server will get the data, but no response will be received.

UPDATE

worked after:

proxy.on('proxyReq', (proxyReq, req) => {
        if (req.body) {
            const bodyData = JSON.stringify(req.body);
            // incase if content-type is application/x-www-form-urlencoded -> we need to change to application/json
            proxyReq.setHeader('Content-Type','application/json');
            proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
            // stream the content
            proxyReq.write(bodyData);
        }
    });
taikongfeizhu commented 7 years ago

when I use proxyReq.write(bodyData), it call like this: throw new Error('Can\'t set headers after they are sent.'); How can I fix it?

oldrich-s commented 7 years ago

I hit the same bug. @zanaca solution seems to work ;)

danbell commented 6 years ago

I have hit the same bug. However, I had to modify @zanaca's solution to get it to work in all the situations that I was encountering:

  proxy.on('proxyReq', (proxyReq, req) => {
    if (req.body && req.complete) {
      const bodyData = JSON.stringify(req.body);
      proxyReq.setHeader('Content-Type', 'application/json');
      proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
      proxyReq.write(bodyData);
    }
  });

Just a head's up if you hit this problem.

pek77 commented 6 years ago

My target server is Tomcat Server for Java. The page post the JSON data through ajax and I use request.getParameter("data") to get my posted data in Java. And then I try @zanaca ‘s method, but it doesn't work. On Java Server, I tried to output the request inputstream for this post action without http-proxy. I saw the correct output was

data=%7B%22%E8%B4%A7%E5%B8%81%E5%9E%8B%22%3A%7B%22wealth%22%3A10000%2C%22url

And I tried to output the request inputstream for this post action with @zanaca ‘s method. I saw the wrong output was

{"data":"{\"璐у竵鍨媆":{\"wealth\":10000,\"url\":\"r

I can't modify my Java code, so if I want to use the request.getParameter("data") to get the data in Java, I must joint the String for inputstream to match Java's need. So I rewrote the method. Maybe someone need this code for Java back-end server:

proxy.on('proxyReq', function (proxyReq, req, res, options) {
    if (req.body) {
        // var bodyData = encodeURIComponent(JSON.stringify(req.body))
        var postStr = "";
        for(var key in req.body){
            postStr = postStr + encodeURIComponent(key) + "=" + encodeURIComponent(req.body[key]) + "&";
        }
        postStr = postStr.substr(0, postStr.length - 1);
        for(var key in req.headers){
            proxyReq.setHeader(key, req.headers[key])
        }
        // incase if content-type is application/x-www-form-urlencoded -> we need to change to application/json
        // proxyReq.setHeader('Content-Type', 'application/json');
        proxyReq.setHeader('Content-Length', Buffer.byteLength(postStr))
        // stream the content
        proxyReq.write(postStr)
    }
})

Because there are non-English language in my post data, So I should use encodeURIComponent() to encode the data.

StewartPan commented 6 years ago

It seems that I hit the same bug. I used to http-proxy to build a mock server. and I want to conditional proxy the request based on the req body. It works for the GET method, but it just hangs for the PUT method. My code looks like the following:

let server = http.createServer(function(req, res){
    let body = [];
    req.on('error', (err) => {
      console.error(err);
    }).on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(body).toString();
      body = queryString.parse(body);
      if(bodyMeetRequirement(body)){
        console.log('return MockData');
        returnMockData(mockData, res);
      }else{
        proxy.on('error', function (err, req, res){
          console.log('Both Remote Server and MockData are not available');
          res.writeHead(500, {
            'Content-Type': 'text/plain'
          });
          res.end('Both Remote Server and MockData are not available');
        });
      }
      proxy.web(req, res, {target: server_url});
    });
}).listen(server_port);

Can somebody help me, I have been struggling in the problem for like 3 days and could not figure out why.

StewartPan commented 6 years ago

667 Solved my issue.

RakeshChouhan commented 1 year ago

The problem seems like a stream "issue". Are you using something before sending the proxy request?

For example:

http.createServer((req, res) => {
    anyBody(req, res, { limit: process.env.MAX_BODY_SIZE || '2mb' }, (err, body) => {
        req.body = body;
        proxy.web(req, res, { secure: false, proxyTimeout: process.env.TIMEOUT || 8000, xfwd: true, toProxy: true, changeOrigin: true});
    });
}).listen(1234);

That works like a charm for NON BODY requests, like GET, HEAD or DELETE. But POST, for example, has lost its data stream before sending to proxy.web. The upstream server will get the data, but no response will be received.

UPDATE

worked after:

proxy.on('proxyReq', (proxyReq, req) => {
        if (req.body) {
            const bodyData = JSON.stringify(req.body);
            // incase if content-type is application/x-www-form-urlencoded -> we need to change to application/json
            proxyReq.setHeader('Content-Type','application/json');
            proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
            // stream the content
            proxyReq.write(bodyData);
        }
    });

Thanks @zanaca ! This solution worked for me.