GithubHelp home page GithubHelp logo

rack-streaming-proxy's Introduction

Rack::StreamingProxy

A transparent streaming proxy to be used as rack middleware.

  • Streams the response from the downstream server to minimize memory usage
  • Handles chunked encoding if used
  • Proxies GET/PUT/POST/DELETE, XHR, and cookies

Now updated to be compatible with Rails 3 and 4, and fixes major concurrency issues that were present in 1.0.

Use Rack::StreamingProxy when you need to have the response streamed back to the client, for example when handling large file requests that could be proxied directly but need to be authenticated against the rest of your middleware stack.

Note that this will not work well with EventMachine. EM buffers the entire rack response before sending it to the client. When testing, try Unicorn or Passenger rather than the EM-based Thin (See discussion).

A simple streamer app has been included for testing and development.

Usage

To use inside a Rails app, add a config/initializers/streaming_proxy.rb initialization file, and place in it:

require 'rack/streaming_proxy'

YourRailsApp::Application.configure do
  config.streaming_proxy.logger             = Rails.logger                          # stdout by default
  config.streaming_proxy.log_verbosity      = Rails.env.production? ? :low : :high  # :low or :high, :low by default
  config.streaming_proxy.num_retries_on_5xx = 5                                     # 0 by default
  config.streaming_proxy.raise_on_5xx       = true                                  # false by default

  # Will be inserted at the end of the middleware stack by default.
  config.middleware.use Rack::StreamingProxy::Proxy do |request|

    # Inside the request block, return the full URI to redirect the request to,
    # or nil/false if the request should continue on down the middleware stack.
    if request.path.start_with?('/search')
      "http://www.some-other-service.com/search?#{request.query}"
    end
  end
end

To use as a Rack app:

require 'rack/streaming_proxy'

use Rack::StreamingProxy::Proxy do |request|
  if request.path.start_with?('/proxy')
    # You probably want to get rid of the '/proxy' in the path, when requesting from the destination.
    proxy_path = request.path.sub %r{^/proxy}, ''
    "http://www.another-server.com#{proxy_path}"
  end
end

Installation

Add this line to your application's Gemfile:

gem 'rack-streaming-proxy'

And then execute:

$ bundle

Or install it yourself as:

$ gem install rack-streaming-proxy

Requirements

  • Ruby = 1.9.3
  • rack >= 1.4
  • servolux ~> 0.10

These requirements (other than Ruby) will be automatically installed via Bundler.

This gem has not been tested with versions lower than those indicated.

This gem works with Ubuntu 10.04. It has not been tested with later versions of Ubuntu or other Linuxes, but it should work just fine. It has not been tested with OS X but should work as well. However, I doubt it will work on any version of Windows, as it does process-based stuff. You are most welcome to try it and report back.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Implement your changes, and make sure to add tests!
  4. Commit your changes (git commit -am 'Add some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create new Pull Request

Thanks To

rack-streaming-proxy's People

Contributors

brauliomartinezlm avatar fredngo avatar gr2m avatar nikushi avatar sonots avatar thillerson avatar trliner avatar zerowidth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

rack-streaming-proxy's Issues

Need and example/ability to use subclass of Rack::StreamingProxy::Proxy

I want to subclass Proxy to be able to do something like:

    class ExternalProxy < Rack::StreamingProxy::Proxy
        def initialize(app)
            super app do | request |
                if request.path.start_with?('/foo')
                    "/bar"
                end
            end
        end
    end

and plug it like

use ExternalProxy

Just as concept, I will be happy if there is another, simpler way

POST with empty body results in error

I encountered an error when trying to proxy a POST without body, because in that case the Content-Length is not being set:

12:02:54 web.1 | [Rack::StreamingProxy] Parent process 62926 forked a child process 62927.
12:02:54 web.1 | [Rack::StreamingProxy] Child starting request to http://127.0.0.1:3600/messages/29/read.json
12:02:54 web.1 | [Rack::StreamingProxy] Child process 62927 passing on ArgumentError: Content-Length not given and Transfer-Encoding is not `chunked'
12:02:54 web.1 | [Rack::StreamingProxy] Child process 62927 closing connection.
12:02:54 web.1 | [Rack::StreamingProxy] Parent received unexpected nil status!
12:02:54 web.1 | [Rack::StreamingProxy] Child process 62927 exiting.
12:02:54 web.1 | [Rack::StreamingProxy] Parent process 62926 waiting for child process 62927 to exit.
12:02:54 web.1 | App 62909 stderr: Rack::StreamingProxy::UnknownError

Thank you for this middleware by the way. :)
Best regards,
Steven

Want to be a maintainer of rack-streaming-proxy

@zerowidth @fredngo Could you do me a favor?

We are using rack-streaming-proxy for our graph tool https://github.com/yohoushi/yohoushi.

We tested rack-streaming-proxy very well, and with deep understanding of codes, rack , and HTTP RFC, we fixed several issues such as fredngo#3, yohoushi@af0ef26, and yohoushi#2. We believe that we can be most active commiters of rack-streaming-proxy.

Could you do add me to owners of rack-streaming-proxy gem on rubygems so that we can maintain the gem? Like

gem owner rack-streaming-proxy -a [email protected]

undefined method bytesize

I have came across this issue while using this gem.

Realized this is an issue caused by a change in rack which removed Rack::Utils.bytesize(chunk) in favor of chunk.bytesize usage.

Proxy Response Headers 301 Dropbox URL Request

I am using proxy to load dropbox URL. My purpose is to load URL and display image on canvas. It was working fine but since last few days it returns me 301 status.

streaming_proxy.rb

if request.path.start_with?('/proxy')
  request.params['uri']
end

I am receiving,


[Rack::StreamingProxy] Parent process 11548 forked a child process 13396.
[Rack::StreamingProxy] Child starting request to https://www.dropbox.com/s/5l4vvb413squt2f/b4B86CN.jpg?dl=0&raw=1
[Rack::StreamingProxy] Child got response: Net::HTTPMovedPermanently
[Rack::StreamingProxy] Child process 13396 returning Status = 301.
[Rack::StreamingProxy] +-------------------------------------------------------------
[Rack::StreamingProxy] | Proxy Response Headers:
[Rack::StreamingProxy] Parent received: Status = 301.
[Rack::StreamingProxy] Parent received: Reponse has body? = true.

What wrong I am doing?

Proxying with puma seems to be unreliable even with non-chunked encoding

While creating a testcase for bug #6 I ended up finding an unreliable result with proxying non-chunked content. Here's the testcase:

https://github.com/pedrocr/camerasink/blob/b01aac64e8aaef96f8527f6e29768cc1f5f6199b/testcases/puma_unreliable_proxying.ru

Again this is runnable as "rackup puma_unreliable_proxying.ru".

Then asking for the same resource multiple times from the original server works fine:

$ curl http://localhost:9000
Hello!
pedrocr@wintermute:~$ curl http://localhost:9000
Hello!
pedrocr@wintermute:~$ curl http://localhost:9000
Hello!
pedrocr@wintermute:~$ curl http://localhost:9000
Hello!
pedrocr@wintermute:~$ curl http://localhost:9000
Hello!

But doing the same from the proxy server sometimes returns an empty result:

pedrocr@wintermute:~$ curl http://localhost:9292
Hello!
pedrocr@wintermute:~$ curl http://localhost:9292
pedrocr@wintermute:~$ curl http://localhost:9292
Hello!

zombie processes are spawned in some special use cases

We are using the rack-streaming-proxy with a unicorn to proxy to a (only locally reachable) apache location, that is serving svn repositories via webdav. In some very special use cases, zombie processes are being spawned by the proxy (its the only part of the system that does spawn any child processes).

This happens when trying to commit a large number of files (~2000 or more) with a filesize > 10-15 kb each. In such a use case, every file being committed consistently spawns one zombie process.

Chunked encoding gets doubly chunked when using puma corrupting the content

I am running a Rails app in puma and using rack-streaming-proxy to proxy a MJPEG HTTP stream. The stream is a multipart message in chunked encoding. The proxy seems to break the stream by not proxying it as chunked encoding. All the code is here:

https://github.com/pedrocr/camerasink

Testing with telnet the original stream returns:

HTTP/1.0 200 OK
Server: camerasave
Date: Fri, 18 Apr 2014 11:13:06 GMT
Transfer-Encoding: chunked
Content-Type: multipart/x-mixed-replace;boundary=SurelyJPEGDoesntIncludeThis

51
--SurelyJPEGDoesntIncludeThis
Content-Type: image/jpeg
Content-Length: 9437

(the image contents go here, the headers after "51" are repeated on every new image)

The same stream after being proxied with rack-streaming-proxy and puma returns:

HTTP/1.0 200 OK
server: camerasave
date: Fri, 18 Apr 2014 11:12:16 GMT
content-type: multipart/x-mixed-replace;boundary=SurelyJPEGDoesntIncludeThis
Cache-Control: no-cache
X-Request-Id: c68247f6-e4ef-4507-b34c-c37330062289
X-Runtime: 0.256272
Connection: close

--SurelyJPEGDoesntIncludeThis
Content-Type: image/jpeg
Content-Length: 9427

(the image contents go here and the headers get repeated as well)

The difference seems to be that the proxied request doesn't have chunked encoding. At least in firefox this breaks the MJPEG streaming.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.