rack-reproxy 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []