rack-reproxy 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rack-reproxy.rb +1 -0
  3. data/lib/rack/reproxy.rb +172 -0
  4. metadata +88 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dc6068d5884538676a120ab16acf3048fbce97b9
4
+ data.tar.gz: ba82d90ae5890d41e15f2444c959ac9a0012d26e
5
+ SHA512:
6
+ metadata.gz: 9f230747d266b2407b772d593f22dbe8c0454a7b4cf6f204a62e98642d15c987d013d4d78c67ebf9f2408ef5a92d33cfdb33f950e65b7aa97f340c2cd405a586
7
+ data.tar.gz: 67ea7ea085e4d11de1c5a210a6c776d93870a5f517123020be16eccd8ee1ded654dc4a979593bc2046d5c656916846913188f877d9ee1d9cb1bc1b3792749c0d
@@ -0,0 +1 @@
1
+ require 'rack/reproxy'
@@ -0,0 +1,172 @@
1
+ module Rack
2
+ # = Reproxy
3
+ #
4
+ # Allow Rack responses to be proxied from a different URL. It's like
5
+ # Rack::Sendfile, but for any HTTP backend.
6
+ #
7
+ # Rack apps can return a URI as a response body (or an X-Reproxy-Url header)
8
+ # and we pass it upstream to Nginx/Apache/Lighttpd to serve.
9
+ #
10
+ # This is an approach pioneered by MogileFS using perlbal to reproxy file
11
+ # requests to an internal storage backend.
12
+ #
13
+ # === Proxing to an internal app: serving private files
14
+ #
15
+ # Rack::Sendfile can efficiently serve files from the local filesystem.
16
+ # But that means you have to have your files NFS-mounted on all your app
17
+ # servers, and you have to know their physical paths.
18
+ #
19
+ # Instead, you can expose your file server as a private HTTP service and
20
+ # reproxy requests to it. Get rid of fussy NFS mounts and just stream files
21
+ # back from your internal server.
22
+ #
23
+ # === Proxying to yourself
24
+ #
25
+ # You can reproxy requests back to your own app, too. This is useful when you
26
+ # you'd like to HTTP-cache private, authenticated content. You can't put a
27
+ # public HTTP cache in front of your app, but you can put it in the middle!
28
+ #
29
+ # Your app receives a request, authenticates, and proxies its own response
30
+ # via an internal HTTP cache that's backed by... your app.
31
+ #
32
+ # === Nginx
33
+ #
34
+ # # In config.ru
35
+ # use Rack::Reproxy::Nginx, location: '/reproxy'
36
+ #
37
+ # # Nginx config
38
+ # location /reproxy {
39
+ # internal;
40
+ # set $reproxy_url $upstream_http_x_reproxy_url;
41
+ # proxy_pass $reproxy_url;
42
+ # }
43
+ #
44
+ # === Apache with mod_reproxy
45
+ #
46
+ # # In config.ru
47
+ # use Rack::Reproxy::Apache
48
+ #
49
+ # # Apache config
50
+ # <Location />
51
+ # AllowReproxy on
52
+ # PreserveHeaders Content-Type Content-Disposition ETag Last-Modified
53
+ # </Location>
54
+ #
55
+ # === Lighttpd
56
+ #
57
+ # # In config.ru
58
+ # use Rack::Reproxy::Lighttpd
59
+ #
60
+ # # Lighttpd config
61
+ # proxy-core.allow-x-rewrite = "enable"
62
+ #
63
+ # === Rack
64
+ #
65
+ # Wait, what? Yeah, you can reproxy without doing an HTTP roundtrip by
66
+ # immediately redispatching back to your own app. This only becomes useful
67
+ # when you do something like reproxy through Rack::Cache.
68
+ #
69
+ # # In config.ru
70
+ # use Rack::Reproxy::Rack
71
+ #
72
+ # # To proxy to a different Rack app
73
+ # use Rack::Reproxy::Rack, app: SomeInternalApp.new
74
+ #
75
+ module Reproxy
76
+ class Middleware
77
+ def initialize(app, options = {})
78
+ @app = app
79
+ @header = options.fetch(:header, 'X-Reproxy-Url')
80
+ @scrub_reproxy_header = "HTTP_#{@header.gsub('-', '_').upcase}"
81
+ end
82
+
83
+ def call(env)
84
+ # Don't let clients ask us to reproxy URLs.
85
+ env.delete(@scrub_reproxy_header)
86
+
87
+ # In case the Rack app would like to know which header to set.
88
+ env['rack.reproxy.header'] ||= @header
89
+
90
+ status, headers, body = @app.call(env)
91
+
92
+ # Reproxy URI response bodies.
93
+ if body.is_a?(URI)
94
+ reproxy env, status, headers.merge(@header => body.to_s), body
95
+
96
+ # Reproxy explicit requests to respond with a different URL.
97
+ elsif headers.include?(@header)
98
+ reproxy env, status, headers, body
99
+
100
+ # Pass through the response, otherwise.
101
+ else
102
+ [status, headers, body]
103
+ end
104
+ end
105
+
106
+ private
107
+ def reproxy(env, status, headers, body)
108
+ [status, headers.merge('X-Reproxied' => '1'), []]
109
+ end
110
+ end
111
+
112
+ # Nginx relies on an upstream /reproxy location that proxies to
113
+ # X-Reproxy-Url. So we just return an X-Accel-Redirect: /reproxy header.
114
+ class Nginx < Middleware
115
+ def initialize(app, options = {})
116
+ super
117
+ @location = options.fetch(:location, '/reproxy')
118
+ end
119
+
120
+ private
121
+ def reproxy(env, status, headers, body)
122
+ super.tap do |response|
123
+ response[1]['X-Accel-Redirect'] = @location
124
+ end
125
+ end
126
+ end
127
+
128
+ # Apache with mod_reproxy uses X-Reproxy-Url directly.
129
+ class Apache < Middleware
130
+ end
131
+
132
+ # Lighttpd uses X-Rewrite-URI and X-Rewrite-Host response headers.
133
+ # Be sure to set proxy-core.allow-x-rewrite in your lighty config.
134
+ class Lighttpd < Middleware
135
+ private
136
+ def reproxy(env, status, headers, body)
137
+ super.tap do |response|
138
+ uri = URI(headers[@header])
139
+ response[1]['X-Rewrite-Host'] = uri.hostname
140
+ response[1]['X-Rewrite-URI'] = uri.request_uri
141
+ end
142
+ end
143
+ end
144
+
145
+ # Rack dispatches the request again and returns the proxied response
146
+ # with its headers merged onto the original response's.
147
+ class Rack < Middleware
148
+ def initialize(app, options = {})
149
+ super
150
+ @proxy_to = options.fetch(:app, self)
151
+ end
152
+
153
+ private
154
+ def reproxy(env, status, headers, body)
155
+ uri = URI(headers.delete(@header))
156
+
157
+ path_info = uri.path
158
+ if script_name = env['SCRIPT_NAME']
159
+ path_info.sub! /\A#{Regexp.escape(script_name)}/, ''
160
+ end
161
+
162
+ proxy_env = env.merge 'HTTP_X_REPROXIED' => '1',
163
+ 'HTTP_HOST' => uri.host,
164
+ 'PATH_INFO' => path_info,
165
+ 'QUERY_STRING' => uri.query
166
+
167
+ proxied_status, proxied_headers, proxied_body = @proxy_to.call(proxy_env)
168
+ [proxied_status, headers.merge(proxied_headers), proxied_body]
169
+ end
170
+ end
171
+ end
172
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-reproxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Kemper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.3'
55
+ description:
56
+ email: jeremykemper@gmail.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - "./lib/rack-reproxy.rb"
62
+ - "./lib/rack/reproxy.rb"
63
+ homepage:
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.2.2
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Redispatch your response via another URL. Like a transparent, internal HTTP
87
+ redirect.
88
+ test_files: []