rack-proxy 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +1 -1
- data/Gemfile.lock +5 -5
- data/README.md +230 -15
- data/lib/net_http_hacked.rb +7 -7
- data/lib/rack/proxy.rb +15 -3
- data/lib/rack_proxy_examples/example_service_proxy.rb +40 -0
- data/lib/rack_proxy_examples/forward_host.rb +24 -0
- data/lib/rack_proxy_examples/rack_php_proxy.rb +37 -0
- data/lib/rack_proxy_examples/trusting_proxy.rb +24 -0
- data/rack-proxy.gemspec +2 -3
- data/test/rack_proxy_test.rb +13 -4
- metadata +13 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 228f3bf6e52dcd2bdcebf9c52bf1db47dfdc7179ed52cd3664e14cfd8df80db9
|
4
|
+
data.tar.gz: 6613d7f13087046d9ad6ee22e5afa56db6910145df427e42101f5aa491d3fe26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 151b04e286d1305d116cf743f00c6b811ed10fad0f24cf806b685a4e038a4dcf5c3b8f7e0dd0cf1e8aa95dd8c46763b0f8b114d36c668fdaabda541d48d5722a
|
7
|
+
data.tar.gz: 234ec48678c973b203cf00f256039c6e88f63c67c5e279e6f0ec17fc51de07022f7a3099dfe0efdaacf46808a0512cd9e16444823b6d616fdee08ae5572efbb2
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rack-proxy (0.
|
4
|
+
rack-proxy (0.7.0)
|
5
5
|
rack
|
6
6
|
|
7
7
|
GEM
|
8
|
-
remote:
|
8
|
+
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
power_assert (0.2.6)
|
11
|
-
rack (
|
11
|
+
rack (2.0.3)
|
12
12
|
rack-test (0.5.6)
|
13
13
|
rack (>= 1.0)
|
14
|
-
rake (0.
|
14
|
+
rake (13.0.1)
|
15
15
|
test-unit (3.1.5)
|
16
16
|
power_assert
|
17
17
|
|
@@ -25,4 +25,4 @@ DEPENDENCIES
|
|
25
25
|
test-unit
|
26
26
|
|
27
27
|
BUNDLED WITH
|
28
|
-
1.
|
28
|
+
1.17.2
|
data/README.md
CHANGED
@@ -1,22 +1,88 @@
|
|
1
1
|
A request/response rewriting HTTP proxy. A Rack app. Subclass `Rack::Proxy` and provide your `rewrite_env` and `rewrite_response` methods.
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
Installation
|
4
|
+
----
|
5
|
+
|
6
|
+
Add the following to your `Gemfile`:
|
7
|
+
|
8
|
+
```
|
9
|
+
gem 'rack-proxy', '~> 0.7.0'
|
10
|
+
```
|
11
|
+
|
12
|
+
Or install:
|
13
|
+
|
14
|
+
```
|
15
|
+
gem install rack-proxy
|
16
|
+
```
|
17
|
+
|
18
|
+
Use Cases
|
19
|
+
----
|
20
|
+
|
21
|
+
Below are some examples of real world use cases for Rack-Proxy. If you have done something interesting, add it to the list below and send a PR.
|
22
|
+
|
23
|
+
* Allowing one app to act as central trust authority
|
24
|
+
* handle accepting self-sign certificates for internal apps
|
25
|
+
* authentication / authorization prior to proxying requests to a blindly trusting backend
|
26
|
+
* avoiding CORs complications by proxying from same domain to another backend
|
27
|
+
* subdomain based pass-through to multiple apps
|
28
|
+
* Complex redirect rules
|
29
|
+
* redirect pages with different extensions (ex: `.php`) to another app
|
30
|
+
* useful for handling awkward redirection rules for moved pages
|
31
|
+
* fan Parallel Requests: turning a single API request to [multiple concurrent backend requests](https://github.com/typhoeus/typhoeus#making-parallel-requests) & merging results.
|
32
|
+
* inserting or stripping headers required or problematic for certain clients
|
33
|
+
|
34
|
+
Options
|
35
|
+
----
|
36
|
+
|
37
|
+
Options can be set when initializing the middleware or overriding a method.
|
38
|
+
|
39
|
+
|
40
|
+
* `:streaming` - defaults to `true`, but does not work on all Ruby versions, recommend to set to `false`
|
41
|
+
* `:ssl_verify_none` - tell `Net::HTTP` to not validate certs
|
42
|
+
* `:ssl_version` - tell `Net::HTTP` to set a specific `ssl_version`
|
43
|
+
* `:backend` - the URI parseable format of host and port of the target proxy backend. If not set it will assume the backend target is the same as the source.
|
44
|
+
* `:read_timeout` - set proxy timeout it defaults to 60 seconds
|
45
|
+
|
46
|
+
To pass in options, when you configure your middleware you can pass them in as an optional hash.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Rails.application.config.middleware.use ExampleServiceProxy, backend: 'http://guides.rubyonrails.org', streaming: false
|
50
|
+
```
|
51
|
+
|
52
|
+
Examples
|
53
|
+
----
|
54
|
+
|
55
|
+
See and run the examples below from `lib/rack_proxy_examples/`. To mount any example into an existing Rails app:
|
56
|
+
|
57
|
+
1. create `config/initializers/proxy.rb`
|
58
|
+
2. modify the file to require the example file
|
59
|
+
```ruby
|
60
|
+
require 'rack_proxy_examples/forward_host'
|
61
|
+
```
|
62
|
+
|
63
|
+
### Forward request to Host and Insert Header
|
64
|
+
|
65
|
+
Test with `require 'rack_proxy_examples/forward_host'`
|
5
66
|
|
6
67
|
```ruby
|
7
|
-
class
|
68
|
+
class ForwardHost < Rack::Proxy
|
8
69
|
|
9
70
|
def rewrite_env(env)
|
10
71
|
env["HTTP_HOST"] = "example.com"
|
11
|
-
|
12
72
|
env
|
13
73
|
end
|
14
74
|
|
15
75
|
def rewrite_response(triplet)
|
16
76
|
status, headers, body = triplet
|
17
77
|
|
78
|
+
# example of inserting an additional header
|
18
79
|
headers["X-Foo"] = "Bar"
|
19
80
|
|
81
|
+
# if you rewrite env, it appears that content-length isn't calculated correctly
|
82
|
+
# resulting in only partial responses being sent to users
|
83
|
+
# you can remove it or recalculate it here
|
84
|
+
headers["content-length"] = nil
|
85
|
+
|
20
86
|
triplet
|
21
87
|
end
|
22
88
|
|
@@ -25,15 +91,30 @@ end
|
|
25
91
|
|
26
92
|
### Disable SSL session verification when proxying a server with e.g. self-signed SSL certs
|
27
93
|
|
94
|
+
Test with `require 'rack_proxy_examples/trusting_proxy'`
|
95
|
+
|
28
96
|
```ruby
|
29
97
|
class TrustingProxy < Rack::Proxy
|
30
98
|
|
31
99
|
def rewrite_env(env)
|
32
|
-
env["
|
100
|
+
env["HTTP_HOST"] = "self-signed.badssl.com"
|
33
101
|
|
102
|
+
# We are going to trust the self-signed SSL
|
103
|
+
env["rack.ssl_verify_none"] = true
|
34
104
|
env
|
35
105
|
end
|
36
106
|
|
107
|
+
def rewrite_response(triplet)
|
108
|
+
status, headers, body = triplet
|
109
|
+
|
110
|
+
# if you rewrite env, it appears that content-length isn't calculated correctly
|
111
|
+
# resulting in only partial responses being sent to users
|
112
|
+
# you can remove it or recalculate it here
|
113
|
+
headers["content-length"] = nil
|
114
|
+
|
115
|
+
triplet
|
116
|
+
end
|
117
|
+
|
37
118
|
end
|
38
119
|
```
|
39
120
|
|
@@ -43,31 +124,98 @@ The same can be achieved for *all* requests going through the `Rack::Proxy` inst
|
|
43
124
|
Rack::Proxy.new(ssl_verify_none: true)
|
44
125
|
```
|
45
126
|
|
46
|
-
|
47
|
-
|
127
|
+
### Rails middleware example
|
128
|
+
|
129
|
+
Test with `require 'rack_proxy_examples/example_service_proxy'`
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
###
|
133
|
+
# This is an example of how to use Rack-Proxy in a Rails application.
|
134
|
+
#
|
135
|
+
# Setup:
|
136
|
+
# 1. rails new test_app
|
137
|
+
# 2. cd test_app
|
138
|
+
# 3. install Rack-Proxy in `Gemfile`
|
139
|
+
# a. `gem 'rack-proxy', '~> 0.7.0'`
|
140
|
+
# 4. install gem: `bundle install`
|
141
|
+
# 5. create `config/initializers/proxy.rb` adding this line `require 'rack_proxy_examples/example_service_proxy'`
|
142
|
+
# 6. run: `SERVICE_URL=http://guides.rubyonrails.org rails server`
|
143
|
+
# 7. open in browser: `http://localhost:3000/example_service`
|
144
|
+
#
|
145
|
+
###
|
146
|
+
ENV['SERVICE_URL'] ||= 'http://guides.rubyonrails.org'
|
147
|
+
|
148
|
+
class ExampleServiceProxy < Rack::Proxy
|
149
|
+
def perform_request(env)
|
150
|
+
request = Rack::Request.new(env)
|
151
|
+
|
152
|
+
# use rack proxy for anything hitting our host app at /example_service
|
153
|
+
if request.path =~ %r{^/example_service}
|
154
|
+
backend = URI(ENV['SERVICE_URL'])
|
155
|
+
# most backends required host set properly, but rack-proxy doesn't set this for you automatically
|
156
|
+
# even when a backend host is passed in via the options
|
157
|
+
env["HTTP_HOST"] = backend.host
|
158
|
+
|
159
|
+
# This is the only path that needs to be set currently on Rails 5 & greater
|
160
|
+
env['PATH_INFO'] = ENV['SERVICE_PATH'] || '/configuring.html'
|
161
|
+
|
162
|
+
# don't send your sites cookies to target service, unless it is a trusted internal service that can parse all your cookies
|
163
|
+
env['HTTP_COOKIE'] = ''
|
164
|
+
super(env)
|
165
|
+
else
|
166
|
+
@app.call(env)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
### Using as middleware to forward only some extensions to another Application
|
173
|
+
|
174
|
+
Test with `require 'rack_proxy_examples/rack_php_proxy'`
|
48
175
|
|
49
176
|
Example: Proxying only requests that end with ".php" could be done like this:
|
50
177
|
|
51
178
|
```ruby
|
52
|
-
|
179
|
+
###
|
180
|
+
# Open http://localhost:3000/test.php to trigger proxy
|
181
|
+
###
|
53
182
|
class RackPhpProxy < Rack::Proxy
|
54
183
|
|
55
184
|
def perform_request(env)
|
56
185
|
request = Rack::Request.new(env)
|
57
186
|
if request.path =~ %r{\.php}
|
58
|
-
env["HTTP_HOST"] = "localhost"
|
59
|
-
|
187
|
+
env["HTTP_HOST"] = ENV["HTTP_HOST"] ? URI(ENV["HTTP_HOST"]).host : "localhost"
|
188
|
+
ENV["PHP_PATH"] ||= '/manual/en/tutorial.firstpage.php'
|
189
|
+
|
190
|
+
# Rails 3 & 4
|
191
|
+
env["REQUEST_PATH"] = ENV["PHP_PATH"] || "/php/#{request.fullpath}"
|
192
|
+
# Rails 5 and above
|
193
|
+
env['PATH_INFO'] = ENV["PHP_PATH"] || "/php/#{request.fullpath}"
|
194
|
+
|
195
|
+
env['content-length'] = nil
|
196
|
+
|
60
197
|
super(env)
|
61
198
|
else
|
62
199
|
@app.call(env)
|
63
200
|
end
|
64
201
|
end
|
202
|
+
|
203
|
+
def rewrite_response(triplet)
|
204
|
+
status, headers, body = triplet
|
205
|
+
|
206
|
+
# if you proxy depending on the backend, it appears that content-length isn't calculated correctly
|
207
|
+
# resulting in only partial responses being sent to users
|
208
|
+
# you can remove it or recalculate it here
|
209
|
+
headers["content-length"] = nil
|
210
|
+
|
211
|
+
triplet
|
212
|
+
end
|
65
213
|
end
|
66
214
|
```
|
67
215
|
|
68
216
|
To use the middleware, please consider the following:
|
69
217
|
|
70
|
-
1) For Rails we could add a configuration in config/application.rb
|
218
|
+
1) For Rails we could add a configuration in `config/application.rb`
|
71
219
|
|
72
220
|
```ruby
|
73
221
|
config.middleware.use RackPhpProxy, {ssl_verify_none: true}
|
@@ -85,12 +233,79 @@ This will allow to run the other requests through the application and only proxy
|
|
85
233
|
|
86
234
|
See tests for more examples.
|
87
235
|
|
236
|
+
### SSL proxy for SpringBoot applications debugging
|
237
|
+
|
238
|
+
Whenever you need to debug communication with external services with HTTPS protocol (like OAuth based) you have to be able to access to your local web app through HTTPS protocol too. Typical way is to use nginx or Apache httpd as a reverse proxy but it might be inconvinuent for development environment. Simple proxy server is a better way in this case. The only what we need is to unpack incoming SSL queries and proxy them to a backend. We can prepare minimal set of files to create autonomous proxy server.
|
239
|
+
|
240
|
+
Create `config.ru` file:
|
241
|
+
```ruby
|
242
|
+
#
|
243
|
+
# config.ru
|
244
|
+
#
|
245
|
+
require 'rack'
|
246
|
+
require 'rack-proxy'
|
247
|
+
|
248
|
+
class ForwardHost < Rack::Proxy
|
249
|
+
def rewrite_env(env)
|
250
|
+
env['HTTP_X_FORWARDED_HOST'] = env['SERVER_NAME']
|
251
|
+
env['HTTP_X_FORWARDED_PROTO'] = env['rack.url_scheme']
|
252
|
+
env
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
run ForwardHost.new(backend: 'http://localhost:8080')
|
257
|
+
```
|
258
|
+
|
259
|
+
Create `Gemfile` file:
|
260
|
+
```ruby
|
261
|
+
source "https://rubygems.org"
|
262
|
+
|
263
|
+
gem 'thin'
|
264
|
+
gem 'rake'
|
265
|
+
gem 'rack-proxy'
|
266
|
+
```
|
267
|
+
|
268
|
+
Create `config.yml` file with configuration of web server `thin`:
|
269
|
+
```yml
|
270
|
+
---
|
271
|
+
ssl: true
|
272
|
+
ssl-key-file: keys/domain.key
|
273
|
+
ssl-cert-file: keys/domain.crt
|
274
|
+
ssl-disable-verify: false
|
275
|
+
```
|
276
|
+
|
277
|
+
Create 'keys' directory and generate SSL key and certificates files `domain.key` and `domain.crt`
|
278
|
+
|
279
|
+
Run `bundle exec thin start` for running it with `thin`'s default port.
|
280
|
+
|
281
|
+
Or use `sudo -E thin start -C config.yml -p 443` for running with default for `https://` port.
|
282
|
+
|
283
|
+
Don't forget to enable processing of `X-Forwarded-...` headers on your application side. Just add following strings to your `resources/application.yml` file.
|
284
|
+
```yml
|
285
|
+
---
|
286
|
+
server:
|
287
|
+
tomcat:
|
288
|
+
remote-ip-header: x-forwarded-for
|
289
|
+
protocol-header: x-forwarded-proto
|
290
|
+
use-forward-headers: true
|
291
|
+
```
|
292
|
+
|
293
|
+
Add some domain name like `debug.your_app.com` into your local `/etc/hosts` file like
|
294
|
+
```
|
295
|
+
127.0.0.1 debug.your_app.com
|
296
|
+
```
|
297
|
+
|
298
|
+
Next start the proxy and your app. And now you can access to your Spring application through SSL connection via `https://debug.your_app.com` URI in a browser.
|
299
|
+
|
300
|
+
|
88
301
|
WARNING
|
89
|
-
|
302
|
+
----
|
90
303
|
|
91
|
-
Doesn't work with fakeweb
|
304
|
+
Doesn't work with `fakeweb`/`webmock`. Both libraries monkey-patch net/http code.
|
92
305
|
|
93
306
|
Todos
|
94
|
-
|
307
|
+
----
|
95
308
|
|
96
|
-
|
309
|
+
* Make the docs up to date with the current use case for this code: everything except streaming which involved a rather ugly monkey patch and only worked in 1.8, but does not work now.
|
310
|
+
* Improve and validate requirements for Host and Path rewrite rules
|
311
|
+
* Ability to inject logger and set log level
|
data/lib/net_http_hacked.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# We are hacking net/http to change semantics of streaming handling
|
2
|
-
# from "block" semantics to regular "return"
|
2
|
+
# from "block" semantics to regular "return" semantics.
|
3
3
|
# We need it to construct a streamable rack triplet:
|
4
4
|
#
|
5
5
|
# [status, headers, streamable_body]
|
6
6
|
#
|
7
|
-
# See http://github.com/
|
7
|
+
# See http://github.com/zerowidth/rack-streaming-proxy
|
8
8
|
# for alternative that uses additional process.
|
9
9
|
#
|
10
10
|
# BTW I don't like monkey patching either
|
11
|
-
# but this is not real monkey patching.
|
11
|
+
# but this is not real monkey patching.
|
12
12
|
# I just added some methods and named them very uniquely
|
13
13
|
# to avoid eventual conflicts. You're safe. Trust me.
|
14
14
|
#
|
@@ -45,7 +45,7 @@ class Net::HTTP
|
|
45
45
|
#
|
46
46
|
# res
|
47
47
|
# end
|
48
|
-
|
48
|
+
|
49
49
|
def begin_request_hacked(req)
|
50
50
|
begin_transport req
|
51
51
|
req.exec @socket, @curr_http_version, edit_path(req.path)
|
@@ -56,7 +56,7 @@ class Net::HTTP
|
|
56
56
|
@req_hacked, @res_hacked = req, res
|
57
57
|
@res_hacked
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
def end_request_hacked
|
61
61
|
@res_hacked.end_reading_body_hacked
|
62
62
|
end_transport @req_hacked, @res_hacked
|
@@ -77,12 +77,12 @@ class Net::HTTPResponse
|
|
77
77
|
# @socket = nil
|
78
78
|
# end
|
79
79
|
# end
|
80
|
-
|
80
|
+
|
81
81
|
def begin_reading_body_hacked(sock, reqmethodallowbody)
|
82
82
|
@socket = sock
|
83
83
|
@body_exist = reqmethodallowbody && self.class.body_permitted?
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
def end_reading_body_hacked
|
87
87
|
self.body
|
88
88
|
@socket = nil
|
data/lib/rack/proxy.rb
CHANGED
@@ -5,12 +5,12 @@ module Rack
|
|
5
5
|
|
6
6
|
# Subclass and bring your own #rewrite_request and #rewrite_response
|
7
7
|
class Proxy
|
8
|
-
VERSION = "0.
|
8
|
+
VERSION = "0.7.0"
|
9
9
|
|
10
10
|
class << self
|
11
11
|
def extract_http_request_headers(env)
|
12
12
|
headers = env.reject do |k, v|
|
13
|
-
!(/^HTTP_[A-Z0-9_]+$/ === k) || v.nil?
|
13
|
+
!(/^HTTP_[A-Z0-9_\.]+$/ === k) || v.nil?
|
14
14
|
end.map do |k, v|
|
15
15
|
[reconstruct_header_name(k), v]
|
16
16
|
end.inject(Utils::HeaderHash.new) do |hash, k_v|
|
@@ -46,11 +46,17 @@ module Rack
|
|
46
46
|
else
|
47
47
|
@app = app
|
48
48
|
end
|
49
|
+
|
49
50
|
@streaming = opts.fetch(:streaming, true)
|
50
51
|
@ssl_verify_none = opts.fetch(:ssl_verify_none, false)
|
51
52
|
@backend = URI(opts[:backend]) if opts[:backend]
|
52
53
|
@read_timeout = opts.fetch(:read_timeout, 60)
|
53
54
|
@ssl_version = opts[:ssl_version] if opts[:ssl_version]
|
55
|
+
|
56
|
+
@username = opts[:username] if opts[:username]
|
57
|
+
@password = opts[:password] if opts[:password]
|
58
|
+
|
59
|
+
@opts = opts
|
54
60
|
end
|
55
61
|
|
56
62
|
def call(env)
|
@@ -92,6 +98,9 @@ module Rack
|
|
92
98
|
target_request.body_stream.rewind
|
93
99
|
end
|
94
100
|
|
101
|
+
# Use basic auth if we have to
|
102
|
+
target_request.basic_auth(@username, @password) if @username && @password
|
103
|
+
|
95
104
|
backend = env.delete('rack.backend') || @backend || source_request
|
96
105
|
use_ssl = backend.scheme == "https"
|
97
106
|
ssl_verify_none = (env.delete('rack.ssl_verify_none') || @ssl_verify_none) == true
|
@@ -117,10 +126,13 @@ module Rack
|
|
117
126
|
end
|
118
127
|
end
|
119
128
|
|
120
|
-
headers = (target_response.respond_to?(:headers)
|
129
|
+
headers = self.class.normalize_headers(target_response.respond_to?(:headers) ? target_response.headers : target_response.to_hash)
|
121
130
|
body = target_response.body || [""]
|
122
131
|
body = [body] unless body.respond_to?(:each)
|
123
132
|
|
133
|
+
# According to https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3.1Acc
|
134
|
+
# should remove hop-by-hop header fields
|
135
|
+
headers.reject! { |k| ['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'transfer-encoding', 'upgrade'].include? k.downcase }
|
124
136
|
[target_response.code, headers, body]
|
125
137
|
end
|
126
138
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
###
|
2
|
+
# This is an example of how to use Rack-Proxy in a Rails application.
|
3
|
+
#
|
4
|
+
# Setup:
|
5
|
+
# 1. rails new test_app
|
6
|
+
# 2. cd test_app
|
7
|
+
# 3. install Rack-Proxy in `Gemfile`
|
8
|
+
# a. `gem 'rack-proxy', '~> 0.7.0'`
|
9
|
+
# 4. install gem: `bundle install`
|
10
|
+
# 5. create `config/initializers/proxy.rb` adding this line `require 'rack_proxy_examples/example_service_proxy'`
|
11
|
+
# 6. run: `SERVICE_URL=http://guides.rubyonrails.org rails server`
|
12
|
+
# 7. open in browser: `http://localhost:3000/example_service`
|
13
|
+
#
|
14
|
+
###
|
15
|
+
ENV['SERVICE_URL'] ||= 'http://guides.rubyonrails.org'
|
16
|
+
|
17
|
+
class ExampleServiceProxy < Rack::Proxy
|
18
|
+
def perform_request(env)
|
19
|
+
request = Rack::Request.new(env)
|
20
|
+
|
21
|
+
# use rack proxy for anything hitting our host app at /example_service
|
22
|
+
if request.path =~ %r{^/example_service}
|
23
|
+
backend = URI(ENV['SERVICE_URL'])
|
24
|
+
# most backends required host set properly, but rack-proxy doesn't set this for you automatically
|
25
|
+
# even when a backend host is passed in via the options
|
26
|
+
env["HTTP_HOST"] = backend.host
|
27
|
+
|
28
|
+
# This is the only path that needs to be set currently on Rails 5 & greater
|
29
|
+
env['PATH_INFO'] = ENV['SERVICE_PATH'] || '/configuring.html'
|
30
|
+
|
31
|
+
# don't send your sites cookies to target service, unless it is a trusted internal service that can parse all your cookies
|
32
|
+
env['HTTP_COOKIE'] = ''
|
33
|
+
super(env)
|
34
|
+
else
|
35
|
+
@app.call(env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Rails.application.config.middleware.use ExampleServiceProxy, backend: ENV['SERVICE_URL'], streaming: false
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class ForwardHost < Rack::Proxy
|
2
|
+
|
3
|
+
def rewrite_env(env)
|
4
|
+
env["HTTP_HOST"] = "example.com"
|
5
|
+
env
|
6
|
+
end
|
7
|
+
|
8
|
+
def rewrite_response(triplet)
|
9
|
+
status, headers, body = triplet
|
10
|
+
|
11
|
+
# example of inserting an additional header
|
12
|
+
headers["X-Foo"] = "Bar"
|
13
|
+
|
14
|
+
# if you rewrite env, it appears that content-length isn't calculated correctly
|
15
|
+
# resulting in only partial responses being sent to users
|
16
|
+
# you can remove it or recalculate it here
|
17
|
+
headers["content-length"] = nil
|
18
|
+
|
19
|
+
triplet
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
Rails.application.config.middleware.use ForwardHost, backend: 'http://example.com', streaming: false
|
@@ -0,0 +1,37 @@
|
|
1
|
+
###
|
2
|
+
# Open http://localhost:3000/test.php to trigger proxy
|
3
|
+
###
|
4
|
+
class RackPhpProxy < Rack::Proxy
|
5
|
+
|
6
|
+
def perform_request(env)
|
7
|
+
request = Rack::Request.new(env)
|
8
|
+
if request.path =~ %r{\.php}
|
9
|
+
env["HTTP_HOST"] = ENV["HTTP_HOST"] ? URI(ENV["HTTP_HOST"]).host : "localhost"
|
10
|
+
ENV["PHP_PATH"] ||= '/manual/en/tutorial.firstpage.php'
|
11
|
+
|
12
|
+
# Rails 3 & 4
|
13
|
+
env["REQUEST_PATH"] = ENV["PHP_PATH"] || "/php/#{request.fullpath}"
|
14
|
+
# Rails 5 and above
|
15
|
+
env['PATH_INFO'] = ENV["PHP_PATH"] || "/php/#{request.fullpath}"
|
16
|
+
|
17
|
+
env['content-length'] = nil
|
18
|
+
|
19
|
+
super(env)
|
20
|
+
else
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def rewrite_response(triplet)
|
26
|
+
status, headers, body = triplet
|
27
|
+
|
28
|
+
# if you proxy depending on the backend, it appears that content-length isn't calculated correctly
|
29
|
+
# resulting in only partial responses being sent to users
|
30
|
+
# you can remove it or recalculate it here
|
31
|
+
headers["content-length"] = nil
|
32
|
+
|
33
|
+
triplet
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Rails.application.config.middleware.use RackPhpProxy, backend: ENV["HTTP_HOST"]='http://php.net', streaming: false
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class TrustingProxy < Rack::Proxy
|
2
|
+
|
3
|
+
def rewrite_env(env)
|
4
|
+
env["HTTP_HOST"] = "self-signed.badssl.com"
|
5
|
+
|
6
|
+
# We are going to trust the self-signed SSL
|
7
|
+
env["rack.ssl_verify_none"] = true
|
8
|
+
env
|
9
|
+
end
|
10
|
+
|
11
|
+
def rewrite_response(triplet)
|
12
|
+
status, headers, body = triplet
|
13
|
+
|
14
|
+
# if you rewrite env, it appears that content-length isn't calculated correctly
|
15
|
+
# resulting in only partial responses being sent to users
|
16
|
+
# you can remove it or recalculate it here
|
17
|
+
headers["content-length"] = nil
|
18
|
+
|
19
|
+
triplet
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
Rails.application.config.middleware.use TrustingProxy, backend: 'https://self-signed.badssl.com', streaming: false
|
data/rack-proxy.gemspec
CHANGED
@@ -6,14 +6,13 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "rack-proxy"
|
7
7
|
s.version = Rack::Proxy::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
+
s.license = 'MIT'
|
9
10
|
s.authors = ["Jacek Becela"]
|
10
11
|
s.email = ["jacek.becela@gmail.com"]
|
11
|
-
s.homepage = "
|
12
|
+
s.homepage = "https://github.com/ncr/rack-proxy"
|
12
13
|
s.summary = %q{A request/response rewriting HTTP proxy. A Rack app.}
|
13
14
|
s.description = %q{A Rack app that provides request/response rewriting proxy capabilities with streaming.}
|
14
15
|
|
15
|
-
s.rubyforge_project = "rack-proxy"
|
16
|
-
|
17
16
|
s.files = `git ls-files`.split("\n")
|
18
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
data/test/rack_proxy_test.rb
CHANGED
@@ -6,7 +6,7 @@ class RackProxyTest < Test::Unit::TestCase
|
|
6
6
|
attr_accessor :host
|
7
7
|
|
8
8
|
def rewrite_env(env)
|
9
|
-
env["HTTP_HOST"] = self.host || '
|
9
|
+
env["HTTP_HOST"] = self.host || 'example.com'
|
10
10
|
env
|
11
11
|
end
|
12
12
|
end
|
@@ -18,14 +18,15 @@ class RackProxyTest < Test::Unit::TestCase
|
|
18
18
|
def test_http_streaming
|
19
19
|
get "/"
|
20
20
|
assert last_response.ok?
|
21
|
-
|
21
|
+
|
22
|
+
assert_match(/Example Domain/, last_response.body)
|
22
23
|
end
|
23
24
|
|
24
25
|
def test_http_full_request
|
25
26
|
app(:streaming => false)
|
26
27
|
get "/"
|
27
28
|
assert last_response.ok?
|
28
|
-
assert_match(/
|
29
|
+
assert_match(/Example Domain/, last_response.body)
|
29
30
|
end
|
30
31
|
|
31
32
|
def test_http_full_request_headers
|
@@ -89,12 +90,14 @@ class RackProxyTest < Test::Unit::TestCase
|
|
89
90
|
'NOT-HTTP-HEADER' => 'test-value',
|
90
91
|
'HTTP_ACCEPT' => 'text/html',
|
91
92
|
'HTTP_CONNECTION' => nil,
|
92
|
-
'HTTP_CONTENT_MD5' => 'deadbeef'
|
93
|
+
'HTTP_CONTENT_MD5' => 'deadbeef',
|
94
|
+
'HTTP_HEADER.WITH.PERIODS' => 'stillmooing'
|
93
95
|
}
|
94
96
|
|
95
97
|
headers = proxy_class.extract_http_request_headers(env)
|
96
98
|
assert headers.key?('ACCEPT')
|
97
99
|
assert headers.key?('CONTENT-MD5')
|
100
|
+
assert headers.key?('HEADER.WITH.PERIODS')
|
98
101
|
assert !headers.key?('CONNECTION')
|
99
102
|
assert !headers.key?('NOT-HTTP-HEADER')
|
100
103
|
end
|
@@ -115,4 +118,10 @@ class RackProxyTest < Test::Unit::TestCase
|
|
115
118
|
post "/", nil, "CONTENT_LENGTH" => nil
|
116
119
|
end
|
117
120
|
end
|
121
|
+
|
122
|
+
def test_response_header_included_Hop_by_hop
|
123
|
+
app({:streaming => true}).host = 'auth.goeasyship.com'
|
124
|
+
get 'https://example.com/oauth2/token/info?access_token=123'
|
125
|
+
assert !last_response.headers.key?('transfer-encoding')
|
126
|
+
end
|
118
127
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-proxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacek Becela
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -71,15 +71,20 @@ files:
|
|
71
71
|
- lib/rack-proxy.rb
|
72
72
|
- lib/rack/http_streaming_response.rb
|
73
73
|
- lib/rack/proxy.rb
|
74
|
+
- lib/rack_proxy_examples/example_service_proxy.rb
|
75
|
+
- lib/rack_proxy_examples/forward_host.rb
|
76
|
+
- lib/rack_proxy_examples/rack_php_proxy.rb
|
77
|
+
- lib/rack_proxy_examples/trusting_proxy.rb
|
74
78
|
- rack-proxy.gemspec
|
75
79
|
- test/http_streaming_response_test.rb
|
76
80
|
- test/net_http_hacked_test.rb
|
77
81
|
- test/rack_proxy_test.rb
|
78
82
|
- test/test_helper.rb
|
79
|
-
homepage:
|
80
|
-
licenses:
|
83
|
+
homepage: https://github.com/ncr/rack-proxy
|
84
|
+
licenses:
|
85
|
+
- MIT
|
81
86
|
metadata: {}
|
82
|
-
post_install_message:
|
87
|
+
post_install_message:
|
83
88
|
rdoc_options: []
|
84
89
|
require_paths:
|
85
90
|
- lib
|
@@ -94,9 +99,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
99
|
- !ruby/object:Gem::Version
|
95
100
|
version: '0'
|
96
101
|
requirements: []
|
97
|
-
|
98
|
-
|
99
|
-
signing_key:
|
102
|
+
rubygems_version: 3.0.3
|
103
|
+
signing_key:
|
100
104
|
specification_version: 4
|
101
105
|
summary: A request/response rewriting HTTP proxy. A Rack app.
|
102
106
|
test_files:
|