express-http-proxy icon indicating copy to clipboard operation
express-http-proxy copied to clipboard

proxy multipart request

Open msefidi opened this issue 8 years ago • 22 comments

Hi there, I have used your lib to create a proxy server. I use body-parser and multer middleware for parsing request bodies. everything is going fine except for the multipart requests. is there anyway to proxy these requests?

msefidi avatar Aug 30 '16 09:08 msefidi

Multipart requests are not explicitly supported right now, although I'd like to get this in soon. If you happen to have a sharable code snippet I could use for a test, I'd be happy to have it.

monkpow avatar Aug 30 '16 21:08 monkpow

Actually I figured it out. Here is what I did:

  • Main problem sovled when I ignored body-parser(json, urlencoded) for multipart requests. Before this igonring bodyContent of proxyReq was empty. I did it like bellow:
const isMultipartRequest = function (req) {
  let contentTypeHeader = req.headers['content-type'];
  return contentTypeHeader && contentTypeHeader.indexOf('multipart') > -1;
};

const bodyParserJsonMiddleware = function () {
  return function (req, res, next) {
    if (isMultipartRequest(req)) {
      return next();
    }
    return bodyParser.json()(req, res, next);
  };
};

app.use(bodyParserJsonMiddleware());
  • I removed using of multer because it's was useless in my case
  • I added bellow code to dynamically change options for multipart requests:
const proxyMiddleware = function () {
  return function (req, res, next) {
    let reqAsBuffer = false;
    let reqBodyEncoding = true;
    let contentTypeHeader = req.headers['content-type'];
    if (isMultipartRequest(req)) {
      reqAsBuffer = true;
      reqBodyEncoding = null;
    }
    return proxy(apiUrl, {
      reqAsBuffer,
      reqBodyEncoding,
      timeout: timeout,
      filter: function (req, res) {
        //filter logic
      },
      decorateRequest: function (proxyReq, originalReq) {
        //decorate logic
      }
    })(req, res, next);
  };
};

app.use(apiUri, proxyMiddleware());

msefidi avatar Aug 31 '16 07:08 msefidi

I still have issue with large file request forwarding. I added the limit: '4mb', but it still doesn't work with error "Request Entity Too Large". What is the work around for multipart file uploading? thanks!

yannpeng avatar Oct 24 '16 06:10 yannpeng

@yannpeng can you add a failing test with a large file? I'd be happy to try to work this out.

monkpow avatar Nov 02 '16 15:11 monkpow

Hello,

I am just checking in because the last traffic on this issue was several months ago. Does express-http-proxy support multipart form data and file uploads? Is anyone working on it? Im interested in using this with the multer package.

nbroeking avatar Mar 13 '17 21:03 nbroeking

@nbroeking There is no explicit multipart support. I plan to address this in detail in priority order, but this is a little unattractive because I don't have much to start with. Are you interested in contributing a failing test or failing test case? This would accelerate action on this ticket.

Thanks,

monkpow avatar Mar 22 '17 15:03 monkpow

@monkpow could you update readme to explicitly tell people that form upload just does not work and it is better to try, for example, http-proxy-middleware ?

AndreyKozlov1984 avatar May 17 '17 13:05 AndreyKozlov1984

thanks so much for your solutions @mahyarmobilab. I got yours to work with one important addition, setting parseReqBody: false on the proxy config when multipart. So seeing it with your example as such:

const proxyMiddleware = function () {
  return function (req, res, next) {
    let reqAsBuffer = false;
    let reqBodyEncoding = true;
    let parseReqBody = true;
    let contentTypeHeader = req.headers['content-type'];
    if (isMultipartRequest(req)) {
      reqAsBuffer = true;
      reqBodyEncoding = null;
      parseReqBody = false;
    }
    return proxy(apiUrl, {
      reqAsBuffer,
      reqBodyEncoding,
      parseReqBody,
      timeout: timeout,
      filter: function (req, res) {
        //filter logic
      },
      decorateRequest: function (proxyReq, originalReq) {
        //decorate logic
      }
    })(req, res, next);
  };
};

app.use(apiUri, proxyMiddleware());

At the time of my post, v1.x of this project is out, so readers make appropriate changes for API-breaking v1 changes.

@monkpow Thanks for this project. This is a pretty important feature to make smooth. @yannpeng -- this parseReqBody: false could be your issue too.

[Note: I corrected example code above replacing parseBodyReq with parseReqBody as pointed out by @mitchellirvin below.]

btmurrell avatar Sep 28 '17 18:09 btmurrell

@btmurrell I'm not seeing "parseBodyReq" in the options for express-http-proxy, but I'm seeing "parseReqBody", did it get changed? Also, it notes that: "Note that setting this(parseReqBody) to false overrides reqAsBuffer and reqBodyEncoding below", and I've noticed that we're still setting values for those two options even though they get overridden (when it's a multipart req). I'm working with the code above to see what will run successfully in my app.

@monkpow is there a verified solution for multipart requests? even if it's not a part of the official package

mitchellirvin avatar Dec 01 '17 15:12 mitchellirvin

sorry @mitchellirvin, i just noticed your comment. you were correct, and i updated above. as for reqAsBuffer and reqBodyEncoding, found the need to set them as above when multipart.

btmurrell avatar Jan 04 '18 18:01 btmurrell

@btmurrell I noticed you do let contentTypeHeader = req.headers['content-type'];, but then don't pass it into the proxy() call. Did you mean to use it?

andrewsosa avatar Jan 24 '18 18:01 andrewsosa

+1 for this request. is there any way we can proxy multipart requests using express-http-proxy? if not, any other suggestion for multipart file upload proxy may be using multer and express-http-proxy.

MiteshSharma avatar Mar 01 '18 17:03 MiteshSharma

I ended up using this proxy instead. It handles multipart files swimmingly and was syntactically simpler.

var proxy = require('http-proxy-middleware');
/*
  Service to proxy http requests: '/api' -> '/someproxydestination'
*/
app.use('/api', proxy({
  target: '/proxydestination',
  changeOrigin: true         // needed for virtual hosted sites
}));

mitchellirvin avatar Mar 01 '18 20:03 mitchellirvin

Hi mitchellirvin, do you have an example code for this upload? Thanks

algaly avatar Mar 25 '18 13:03 algaly

@mitchellirvin I also tried http-proxy-middleware but we needed proxy for prod. So I tried @btmurrell's solution and it works. Thanks.

canbakis avatar Apr 18 '18 16:04 canbakis

@algaly I'll get back to you with the boilerplate for the file upload that I ended up using.

@canbakis Is there a reason http-proxy-middleware couldn't be used for prod? We've been using it to proxy service requests in a production environment for several months now. If it's not prod quality, let me know so we can fix it haha

mitchellirvin avatar Apr 18 '18 20:04 mitchellirvin

@algaly sorry this is a little sloppy, short on time. this setup allows for a more elegant file input solution in angularjs

<input custom-onchange="handleFileUpload" class="ng-hide" id="input-file-id" type="file" name="file"/>
<input disabled placeholder="Choose a file" ng-model="ctrl.fileName" type="text"/>
<label for="input-file-id" class="button md-ink-ripple" ng-class="{'disabled': ctrl.uploading}">
   <i class="icon_file_upload"></i>
</label>

// helper directive
angular.module('app')
  .directive('customOnchange', customOnchange);

function customOnchange() {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var onChangeHandler = scope.$eval(attrs.customOnchange);
      element.on('change', onChangeHandler);
    }
  };
} 

// in the controller
function handleFileUpload(ev) {
    vm.file = ev.target.files[0];
}

mitchellirvin avatar Apr 25 '18 15:04 mitchellirvin

Hey i had the same issue, and what solved the error for me was moving the body parser middleware declaration under the proxy declaration

so if you had

app.use(bodyParser(...))
app.use(URL, proxy() {...})

change it to

app.use(URL, proxy() {...})
app.use(bodyParser(...))

20matan avatar Sep 04 '18 15:09 20matan

@mahyarmobilab Thanks, wrapping proxy and calculating dynamically parseReqBody works

pawlowskim avatar Mar 20 '19 11:03 pawlowskim

I took @btmurrell's code which was a modified version of @mahyarmobilab's and added further changes that I find simpler, although all three of these solutions should be effective. I am running express-http-proxy v1.5.1 and this works for me.

  1. I changed proxyMiddleware so that is no longer a thunk. (A thunk is a function that takes no input and just returns a value for the purpose of delaying its execution-- however in the code above it gets executed immediately right below, so I don't think it added anything in this case.)
  2. I removed reqBodyEncoding and reqAsBuffer since, per the documentation of parseReqBody, those values are ignored when parseReqBody is set to false.
const isMultipartRequest = (req) => {
  const contentTypeHeader = req.headers["content-type"];
  return contentTypeHeader && contentTypeHeader.indexOf("multipart") > -1;
};

const proxyMiddleware = (req, res, next) => {
  return proxy(targetHost, {
    parseReqBody: !isMultipartRequest(req),
    // your other fields here...
  })(req, res, next);
};

app.use(path, proxyMiddleware);

elijahcarrel avatar May 13 '19 21:05 elijahcarrel

Thanks @mahyarmblb, your solution is working splendidly four years down the line. Also keeps the code clean :)

Nikhilnair48 avatar Jul 27 '20 09:07 Nikhilnair48

@20matan 's solution worked for me, if you are just proxying the requests just move down the express.json() / bodyParser.json()

maxutov0 avatar Jun 07 '23 03:06 maxutov0