views:

163

answers:

1

Hello,

I'm building some kind of proxy.
When I call some url in a rack application, I forward that request to an other url.

The request I forward is a POST with a file and some parameters.
I want to add more parameters.
But the file can be quite big. So I send it with Net::HTTP#body_stream instead of Net::HTTP#body.

I get my request as a Rack::Request object and I create my Net::HTTP object with that.

req = Net::HTTP::Post.new(request.path_info)
req.body_stream = request.body
req.content_type = request.content_type
req.content_length = request.content_length

http = Net::HTTP.new(@host, @port)
res = http.request(req)

I've tried several ways to add the proxy's parameters. But it seems nothing in Net::HTTP allows to add parameters to a body_stream request, only to a body one.

Is there a simpler way to proxy a rack request like that ? Or a clean way to add my parameters to my request ?

+1  A: 

Well.. as i see it, this is a normal behaviour. I'll explain why. If you only have access to a Rack::Request,(i guess that) your middleware does not parse the response (you do not include something like ActionController::ParamsParser), so you don't have access to a hash of parameters, but to a StringIo. This StringIO corresponds to a stream like:

Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x 
Content-Disposition: form-data; name="param1" 
value1
--AaB03x 
Content-Disposition: form-data; name="files"; filename="file1.txt" 
Content-Type: text/plain 
... contents of file1.txt ...
--AaB03x--

What you are trying to do with the Net::HTTP class is to: (1). parse the request into a hash of parameters; (2). merge the parameters hash with your own parameters; (3). recreate the request. The problem is that Net::HTTP library can't do (1), since it is a client library, not a server one.

Therefore, you can not escape parsing some how your request before adding the new parameters.

Possible solutions:

  1. Insert ActionController::ParamsParser before your middleware. After that, you may use the excellent rest-client lib to do something like:

    RestClient.post ('http://your_server' + request.path_info), :params => params.merge(your_params)

  2. You can attempt to make a wrapper on the StringIO object, and add, at the end of stream,your own parameters. However, this is not trivial nor advisable.

Vlad Zloteanu
Yeah I put away the second one. I'll try the first. Thanks :)
Damien MATHIEU
You're welcome :) However, by parsing the request this way, you must wait for the whole content to be uploaded to the proxy server. Therefore, the total_time > time_to_upload_to_proxy + time_to_upload_to_real_server.
Vlad Zloteanu