rack-proxy 0.6.0 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 93dce27bea8526a5e14b0ebd3265682a657b119a
4
- data.tar.gz: 727bccbcece1514a3b447f5b8cca2522695129db
2
+ SHA256:
3
+ metadata.gz: 6cbdbb6efeff29ecbbd6dbebe7b67ac4c923fa0ab898d245a310fa872e6d9581
4
+ data.tar.gz: e21085cf4be6ec258b67ed91685086a8c0c5c0a83252a92c87853c5f3b79701e
5
5
  SHA512:
6
- metadata.gz: b20e8d141a3d369f2bf32cf93a24d336a850c41c5dd960a300c8dd293f1d867eed389f44345da101675b1750ccf510f6e1d06f6d5bc6dd5ae8e7eccc1e206600
7
- data.tar.gz: bf2664fb58d99a8a4fbe0f4f3aaee1dc6fd9726a97ec5ff9c23678f0dc65038a5f0867006ffa2c931cd83dfe2b27ccced449e1d612f3a67203ab60b633fad3d1
6
+ metadata.gz: 5eed533bedea80e3a485b9a61e6f5e55ab9480146eed5793c0aeb520cb348e86bdfa4a514467300b626f0b016760ce8ecb1a3318272d7cdcc5649c24131d7134
7
+ data.tar.gz: 4aea216293ead6071d44a1460b7c0f3f865e53d5332c994c441cbe956db6f57d67aa7b9b80a17bbd593676b2f651860f11f284c698200dbe4acfb0385ac00a15
data/.travis.yml CHANGED
@@ -1,4 +1,3 @@
1
- sudo: false
2
1
  cache: bundler
3
2
  language: ruby
4
3
  before_install:
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  gem 'rake'
4
4
 
data/Gemfile.lock CHANGED
@@ -1,17 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-proxy (0.5.18)
4
+ rack-proxy (0.7.2)
5
5
  rack
6
6
 
7
7
  GEM
8
- remote: http://rubygems.org/
8
+ remote: https://rubygems.org/
9
9
  specs:
10
10
  power_assert (0.2.6)
11
- rack (1.2.1)
11
+ rack (2.2.3)
12
12
  rack-test (0.5.6)
13
13
  rack (>= 1.0)
14
- rake (0.9.2.2)
14
+ rake (13.0.6)
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.12.4
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
- Example
4
- -------
3
+ Installation
4
+ ----
5
+
6
+ Add the following to your `Gemfile`:
7
+
8
+ ```
9
+ gem 'rack-proxy', '~> 0.7.2'
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 Foo < Rack::Proxy
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["rack.ssl_verify_none"] = true
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
- Using it as a middleware:
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.2'`
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
- require 'rack/proxy'
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
- env["REQUEST_PATH"] = "/php/#{request.fullpath}"
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/webmock. Both libraries monkey-patch net/http code.
304
+ Doesn't work with `fakeweb`/`webmock`. Both libraries monkey-patch net/http code.
92
305
 
93
306
  Todos
94
- -----
307
+ ----
95
308
 
96
- - 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.
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
@@ -1,14 +1,14 @@
1
1
  # We are hacking net/http to change semantics of streaming handling
2
- # from "block" semantics to regular "return" semnatics.
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/aniero/rack-streaming-proxy
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
@@ -38,6 +38,7 @@ module Rack
38
38
  response.read_body(&block)
39
39
  ensure
40
40
  session.end_request_hacked
41
+ session.finish
41
42
  end
42
43
 
43
44
  def to_s
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.6.0"
8
+ VERSION = "0.7.2"
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|
@@ -26,7 +26,7 @@ module Rack
26
26
 
27
27
  def normalize_headers(headers)
28
28
  mapped = headers.map do |k, v|
29
- [k, if v.is_a? Array then v.join("\n") else v end]
29
+ [titleize(k), if v.is_a? Array then v.join("\n") else v end]
30
30
  end
31
31
  Utils::HeaderHash.new Hash[mapped]
32
32
  end
@@ -34,7 +34,11 @@ module Rack
34
34
  protected
35
35
 
36
36
  def reconstruct_header_name(name)
37
- name.sub(/^HTTP_/, "").gsub("_", "-")
37
+ titleize(name.sub(/^HTTP_/, "").gsub("_", "-"))
38
+ end
39
+
40
+ def titleize(str)
41
+ str.split("-").map(&:capitalize).join("-")
38
42
  end
39
43
  end
40
44
 
@@ -46,11 +50,17 @@ module Rack
46
50
  else
47
51
  @app = app
48
52
  end
53
+
49
54
  @streaming = opts.fetch(:streaming, true)
50
55
  @ssl_verify_none = opts.fetch(:ssl_verify_none, false)
51
- @backend = URI(opts[:backend]) if opts[:backend]
56
+ @backend = opts[:backend] ? URI(opts[:backend]) : nil
52
57
  @read_timeout = opts.fetch(:read_timeout, 60)
53
- @ssl_version = opts[:ssl_version] if opts[:ssl_version]
58
+ @ssl_version = opts[:ssl_version]
59
+
60
+ @username = opts[:username]
61
+ @password = opts[:password]
62
+
63
+ @opts = opts
54
64
  end
55
65
 
56
66
  def call(env)
@@ -79,7 +89,7 @@ module Rack
79
89
  full_path = source_request.fullpath
80
90
  end
81
91
 
82
- target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(full_path)
92
+ target_request = Net::HTTP.const_get(source_request.request_method.capitalize, false).new(full_path)
83
93
 
84
94
  # Setup headers
85
95
  target_request.initialize_http_header(self.class.extract_http_request_headers(source_request.env))
@@ -92,6 +102,9 @@ module Rack
92
102
  target_request.body_stream.rewind
93
103
  end
94
104
 
105
+ # Use basic auth if we have to
106
+ target_request.basic_auth(@username, @password) if @username && @password
107
+
95
108
  backend = env.delete('rack.backend') || @backend || source_request
96
109
  use_ssl = backend.scheme == "https"
97
110
  ssl_verify_none = (env.delete('rack.ssl_verify_none') || @ssl_verify_none) == true
@@ -117,10 +130,13 @@ module Rack
117
130
  end
118
131
  end
119
132
 
120
- headers = (target_response.respond_to?(:headers) && target_response.headers) || self.class.normalize_headers(target_response.to_hash)
133
+ headers = self.class.normalize_headers(target_response.respond_to?(:headers) ? target_response.headers : target_response.to_hash)
121
134
  body = target_response.body || [""]
122
135
  body = [body] unless body.respond_to?(:each)
123
136
 
137
+ # According to https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3.1Acc
138
+ # should remove hop-by-hop header fields
139
+ headers.reject! { |k| ['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'transfer-encoding', 'upgrade'].include? k.downcase }
124
140
  [target_response.code, headers, body]
125
141
  end
126
142
 
@@ -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.2'`
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 = "http://rubygems.org/gems/rack-proxy"
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) }
@@ -4,23 +4,24 @@ require "rack/http_streaming_response"
4
4
  class HttpStreamingResponseTest < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
- host, req = "www.trix.pl", Net::HTTP::Get.new("/")
8
- @response = Rack::HttpStreamingResponse.new(req, host)
7
+ host, req = "mockapi.io", Net::HTTP::Get.new("/")
8
+ @response = Rack::HttpStreamingResponse.new(req, host, 443)
9
+ @response.use_ssl = true
9
10
  end
10
11
 
11
12
  def test_streaming
12
13
  # Response status
13
- assert @response.code == 200
14
- assert @response.status == 200
14
+ assert_equal 200, @response.status
15
+ assert_equal 200, @response.status
15
16
 
16
17
  # Headers
17
18
  headers = @response.headers
18
19
 
19
- assert headers.size > 0
20
+ assert headers.size.positive?
20
21
 
21
- assert headers["content-type"] == ["text/html;charset=utf-8"]
22
- assert headers["CoNtEnT-TyPe"] == headers["content-type"]
23
- assert headers["content-length"].first.to_i > 0
22
+ assert_match %r{text/html; ?charset=utf-8}, headers["content-type"].first.downcase
23
+ assert_equal headers["content-type"], headers["CoNtEnT-TyPe"]
24
+ assert headers["content-length"].first.to_i.positive?
24
25
 
25
26
  # Body
26
27
  chunks = []
@@ -28,7 +29,7 @@ class HttpStreamingResponseTest < Test::Unit::TestCase
28
29
  chunks << chunk
29
30
  end
30
31
 
31
- assert chunks.size > 0
32
+ assert chunks.size.positive?
32
33
  chunks.each do |chunk|
33
34
  assert chunk.is_a?(String)
34
35
  end
@@ -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 || 'www.trix.pl'
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
- assert_match(/Jacek Becela/, last_response.body)
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(/Jacek Becela/, last_response.body)
29
+ assert_match(/Example Domain/, last_response.body)
29
30
  end
30
31
 
31
32
  def test_http_full_request_headers
@@ -77,10 +78,10 @@ class RackProxyTest < Test::Unit::TestCase
77
78
  proxy_class = Rack::Proxy
78
79
 
79
80
  header = proxy_class.send(:reconstruct_header_name, "HTTP_ABC")
80
- assert header == "ABC"
81
+ assert header == "Abc"
81
82
 
82
83
  header = proxy_class.send(:reconstruct_header_name, "HTTP_ABC_D")
83
- assert header == "ABC-D"
84
+ assert header == "Abc-D"
84
85
  end
85
86
 
86
87
  def test_extract_http_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 = 'mockapi.io'
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.6.0
4
+ version: 0.7.2
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: 2016-05-19 00:00:00.000000000 Z
11
+ date: 2022-01-05 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: http://rubygems.org/gems/rack-proxy
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
- rubyforge_project: rack-proxy
98
- rubygems_version: 2.5.1
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: