rack-proxy 0.6.5 → 0.7.7
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.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +3 -0
- data/.travis.yml +0 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +10 -10
- data/README.md +109 -14
- data/lib/net_http_hacked.rb +1 -1
- data/lib/rack/http_streaming_response.rb +37 -32
- data/lib/rack/proxy.rb +59 -23
- data/lib/rack_proxy_examples/example_service_proxy.rb +4 -4
- data/rack-proxy.gemspec +2 -2
- data/test/http_streaming_response_test.rb +11 -10
- data/test/rack_proxy_test.rb +10 -7
- metadata +10 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 888784aa8d1d28ae0dc2a1352aa44ba8e639d5cd604043facbb31da3fa1dc759
|
4
|
+
data.tar.gz: 9ba49effcffcacb930ab08fe2f6a9fd08040b60800b8aa8e5ccc274053f36c4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 606ed720fb5b8c67cd1fc3058b9644e88fb2e7768d4fce4606ba0332fac24cadca11a36ab50d97cb7ff5767664864b1c1a2cf5108cd58a66fecfb3b93de37517
|
7
|
+
data.tar.gz: a91cc8541d7af6c390fe1c0faa3c923942a14cce746eebc3d170b95b45aafc5871a04ad1ec9fee6f0c07500534755c794f76d0c14bccdcf5fdaad06e239aeb07
|
data/.github/FUNDING.yml
ADDED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rack-proxy (0.
|
4
|
+
rack-proxy (0.7.7)
|
5
5
|
rack
|
6
6
|
|
7
7
|
GEM
|
8
|
-
remote:
|
8
|
+
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
power_assert (0.
|
11
|
-
rack (
|
12
|
-
rack-test (
|
13
|
-
rack (>= 1.
|
14
|
-
rake (0.
|
15
|
-
test-unit (3.1
|
10
|
+
power_assert (2.0.3)
|
11
|
+
rack (3.0.8)
|
12
|
+
rack-test (2.1.0)
|
13
|
+
rack (>= 1.3)
|
14
|
+
rake (13.0.6)
|
15
|
+
test-unit (3.6.1)
|
16
16
|
power_assert
|
17
17
|
|
18
18
|
PLATFORMS
|
19
|
-
|
19
|
+
arm64-darwin-22
|
20
20
|
|
21
21
|
DEPENDENCIES
|
22
22
|
rack-proxy!
|
@@ -25,4 +25,4 @@ DEPENDENCIES
|
|
25
25
|
test-unit
|
26
26
|
|
27
27
|
BUNDLED WITH
|
28
|
-
|
28
|
+
2.4.17
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Installation
|
|
6
6
|
Add the following to your `Gemfile`:
|
7
7
|
|
8
8
|
```
|
9
|
-
gem 'rack-proxy', '~> 0.
|
9
|
+
gem 'rack-proxy', '~> 0.7.7'
|
10
10
|
```
|
11
11
|
|
12
12
|
Or install:
|
@@ -17,7 +17,8 @@ gem install rack-proxy
|
|
17
17
|
|
18
18
|
Use Cases
|
19
19
|
----
|
20
|
-
|
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.
|
21
22
|
|
22
23
|
* Allowing one app to act as central trust authority
|
23
24
|
* handle accepting self-sign certificates for internal apps
|
@@ -25,7 +26,7 @@ Below are some examples of real world use cases for Rack-Proxy, done something i
|
|
25
26
|
* avoiding CORs complications by proxying from same domain to another backend
|
26
27
|
* subdomain based pass-through to multiple apps
|
27
28
|
* Complex redirect rules
|
28
|
-
* redirect pages with different extensions (ex: `.php`) to another app
|
29
|
+
* redirect pages with different extensions (ex: `.php`) to another app
|
29
30
|
* useful for handling awkward redirection rules for moved pages
|
30
31
|
* fan Parallel Requests: turning a single API request to [multiple concurrent backend requests](https://github.com/typhoeus/typhoeus#making-parallel-requests) & merging results.
|
31
32
|
* inserting or stripping headers required or problematic for certain clients
|
@@ -76,7 +77,7 @@ class ForwardHost < Rack::Proxy
|
|
76
77
|
|
77
78
|
# example of inserting an additional header
|
78
79
|
headers["X-Foo"] = "Bar"
|
79
|
-
|
80
|
+
|
80
81
|
# if you rewrite env, it appears that content-length isn't calculated correctly
|
81
82
|
# resulting in only partial responses being sent to users
|
82
83
|
# you can remove it or recalculate it here
|
@@ -98,14 +99,14 @@ class TrustingProxy < Rack::Proxy
|
|
98
99
|
def rewrite_env(env)
|
99
100
|
env["HTTP_HOST"] = "self-signed.badssl.com"
|
100
101
|
|
101
|
-
# We are going to trust the self-signed SSL
|
102
|
+
# We are going to trust the self-signed SSL
|
102
103
|
env["rack.ssl_verify_none"] = true
|
103
104
|
env
|
104
105
|
end
|
105
106
|
|
106
107
|
def rewrite_response(triplet)
|
107
108
|
status, headers, body = triplet
|
108
|
-
|
109
|
+
|
109
110
|
# if you rewrite env, it appears that content-length isn't calculated correctly
|
110
111
|
# resulting in only partial responses being sent to users
|
111
112
|
# you can remove it or recalculate it here
|
@@ -130,12 +131,12 @@ Test with `require 'rack_proxy_examples/example_service_proxy'`
|
|
130
131
|
```ruby
|
131
132
|
###
|
132
133
|
# This is an example of how to use Rack-Proxy in a Rails application.
|
133
|
-
#
|
134
|
+
#
|
134
135
|
# Setup:
|
135
|
-
# 1. rails new test_app
|
136
|
+
# 1. rails new test_app
|
136
137
|
# 2. cd test_app
|
137
138
|
# 3. install Rack-Proxy in `Gemfile`
|
138
|
-
# a. `gem 'rack-proxy', '~> 0.
|
139
|
+
# a. `gem 'rack-proxy', '~> 0.7.7'`
|
139
140
|
# 4. install gem: `bundle install`
|
140
141
|
# 5. create `config/initializers/proxy.rb` adding this line `require 'rack_proxy_examples/example_service_proxy'`
|
141
142
|
# 6. run: `SERVICE_URL=http://guides.rubyonrails.org rails server`
|
@@ -157,7 +158,7 @@ class ExampleServiceProxy < Rack::Proxy
|
|
157
158
|
|
158
159
|
# This is the only path that needs to be set currently on Rails 5 & greater
|
159
160
|
env['PATH_INFO'] = ENV['SERVICE_PATH'] || '/configuring.html'
|
160
|
-
|
161
|
+
|
161
162
|
# don't send your sites cookies to target service, unless it is a trusted internal service that can parse all your cookies
|
162
163
|
env['HTTP_COOKIE'] = ''
|
163
164
|
super(env)
|
@@ -185,14 +186,14 @@ class RackPhpProxy < Rack::Proxy
|
|
185
186
|
if request.path =~ %r{\.php}
|
186
187
|
env["HTTP_HOST"] = ENV["HTTP_HOST"] ? URI(ENV["HTTP_HOST"]).host : "localhost"
|
187
188
|
ENV["PHP_PATH"] ||= '/manual/en/tutorial.firstpage.php'
|
188
|
-
|
189
|
+
|
189
190
|
# Rails 3 & 4
|
190
191
|
env["REQUEST_PATH"] = ENV["PHP_PATH"] || "/php/#{request.fullpath}"
|
191
192
|
# Rails 5 and above
|
192
193
|
env['PATH_INFO'] = ENV["PHP_PATH"] || "/php/#{request.fullpath}"
|
193
194
|
|
194
195
|
env['content-length'] = nil
|
195
|
-
|
196
|
+
|
196
197
|
super(env)
|
197
198
|
else
|
198
199
|
@app.call(env)
|
@@ -201,7 +202,7 @@ class RackPhpProxy < Rack::Proxy
|
|
201
202
|
|
202
203
|
def rewrite_response(triplet)
|
203
204
|
status, headers, body = triplet
|
204
|
-
|
205
|
+
|
205
206
|
# if you proxy depending on the backend, it appears that content-length isn't calculated correctly
|
206
207
|
# resulting in only partial responses being sent to users
|
207
208
|
# you can remove it or recalculate it here
|
@@ -232,6 +233,100 @@ This will allow to run the other requests through the application and only proxy
|
|
232
233
|
|
233
234
|
See tests for more examples.
|
234
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
|
+
### Using SSL/TLS certificates with HTTP connection
|
301
|
+
This may be helpful, when third-party API has authentication by client TLS certificates and you need to proxy your requests and sign them with certificate.
|
302
|
+
|
303
|
+
Just specify Rack::Proxy SSL options and your request will use TLS HTTP connection:
|
304
|
+
```ruby
|
305
|
+
# config.ru
|
306
|
+
. . .
|
307
|
+
|
308
|
+
cert_raw = File.read('./certs/rootCA.crt')
|
309
|
+
key_raw = File.read('./certs/key.pem')
|
310
|
+
|
311
|
+
cert = OpenSSL::X509::Certificate.new(cert_raw)
|
312
|
+
key = OpenSSL::PKey.read(key_raw)
|
313
|
+
|
314
|
+
use TLSProxy, cert: cert, key: key, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_PEER, ssl_version: 'TLSv1_2'
|
315
|
+
```
|
316
|
+
|
317
|
+
And rewrite host for example:
|
318
|
+
```ruby
|
319
|
+
# tls_proxy.rb
|
320
|
+
class TLSProxy < Rack::Proxy
|
321
|
+
attr_accessor :original_request, :query_params
|
322
|
+
|
323
|
+
def rewrite_env(env)
|
324
|
+
env["HTTP_HOST"] = "client-tls-auth-api.com:443"
|
325
|
+
env
|
326
|
+
end
|
327
|
+
end
|
328
|
+
```
|
329
|
+
|
235
330
|
WARNING
|
236
331
|
----
|
237
332
|
|
@@ -242,4 +337,4 @@ Todos
|
|
242
337
|
|
243
338
|
* 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.
|
244
339
|
* Improve and validate requirements for Host and Path rewrite rules
|
245
|
-
* Ability to inject logger and set log level
|
340
|
+
* Ability to inject logger and set log level
|
data/lib/net_http_hacked.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
require "net_http_hacked"
|
2
|
+
require "stringio"
|
2
3
|
|
3
4
|
module Rack
|
4
|
-
|
5
5
|
# Wraps the hacked net/http in a Rack way.
|
6
6
|
class HttpStreamingResponse
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
STATUSES_WITH_NO_ENTITY_BODY = {
|
8
|
+
204 => true,
|
9
|
+
205 => true,
|
10
|
+
304 => true
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
attr_accessor :use_ssl, :verify_mode, :read_timeout, :ssl_version, :cert, :key
|
11
14
|
|
12
15
|
def initialize(request, host, port = nil)
|
13
16
|
@request, @host, @port = request, host, port
|
@@ -18,60 +21,62 @@ module Rack
|
|
18
21
|
end
|
19
22
|
|
20
23
|
def code
|
21
|
-
response.code.to_i
|
24
|
+
response.code.to_i.tap do |response_code|
|
25
|
+
STATUSES_WITH_NO_ENTITY_BODY[response_code] && close_connection
|
26
|
+
end
|
22
27
|
end
|
23
28
|
# #status is deprecated
|
24
29
|
alias_method :status, :code
|
25
30
|
|
26
31
|
def headers
|
27
|
-
|
28
|
-
|
29
|
-
response.to_hash.each do |k, v|
|
30
|
-
h[k] = v
|
31
|
-
end
|
32
|
-
|
33
|
-
h
|
32
|
+
Rack::Proxy.build_header_hash(response.to_hash)
|
34
33
|
end
|
35
34
|
|
36
35
|
# Can be called only once!
|
37
36
|
def each(&block)
|
37
|
+
return if connection_closed
|
38
|
+
|
38
39
|
response.read_body(&block)
|
39
40
|
ensure
|
40
|
-
|
41
|
-
session.finish
|
41
|
+
close_connection
|
42
42
|
end
|
43
43
|
|
44
44
|
def to_s
|
45
|
-
@
|
46
|
-
lines = []
|
47
|
-
|
48
|
-
each do |line|
|
49
|
-
lines << line
|
50
|
-
end
|
51
|
-
|
52
|
-
lines.join
|
53
|
-
end
|
45
|
+
@to_s ||= StringIO.new.tap { |io| each { |line| io << line } }.string
|
54
46
|
end
|
55
47
|
|
56
48
|
protected
|
57
49
|
|
58
50
|
# Net::HTTPResponse
|
59
51
|
def response
|
60
|
-
@response ||= session.begin_request_hacked(
|
52
|
+
@response ||= session.begin_request_hacked(request)
|
61
53
|
end
|
62
54
|
|
63
55
|
# Net::HTTP
|
64
56
|
def session
|
65
|
-
@session ||=
|
66
|
-
http =
|
67
|
-
http.
|
68
|
-
http.
|
69
|
-
http.
|
70
|
-
http.
|
57
|
+
@session ||= Net::HTTP.new(host, port).tap do |http|
|
58
|
+
http.use_ssl = use_ssl
|
59
|
+
http.verify_mode = verify_mode
|
60
|
+
http.read_timeout = read_timeout
|
61
|
+
http.ssl_version = ssl_version if ssl_version
|
62
|
+
http.cert = cert if cert
|
63
|
+
http.key = key if key
|
71
64
|
http.start
|
72
65
|
end
|
73
66
|
end
|
74
67
|
|
75
|
-
|
68
|
+
private
|
69
|
+
|
70
|
+
attr_reader :request, :host, :port
|
71
|
+
|
72
|
+
attr_accessor :connection_closed
|
76
73
|
|
74
|
+
def close_connection
|
75
|
+
return if connection_closed
|
76
|
+
|
77
|
+
session.end_request_hacked
|
78
|
+
session.finish
|
79
|
+
self.connection_closed = true
|
80
|
+
end
|
81
|
+
end
|
77
82
|
end
|
data/lib/rack/proxy.rb
CHANGED
@@ -5,36 +5,57 @@ 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.7".freeze
|
9
|
+
|
10
|
+
HOP_BY_HOP_HEADERS = {
|
11
|
+
'connection' => true,
|
12
|
+
'keep-alive' => true,
|
13
|
+
'proxy-authenticate' => true,
|
14
|
+
'proxy-authorization' => true,
|
15
|
+
'te' => true,
|
16
|
+
'trailer' => true,
|
17
|
+
'transfer-encoding' => true,
|
18
|
+
'upgrade' => true
|
19
|
+
}.freeze
|
9
20
|
|
10
21
|
class << self
|
11
22
|
def extract_http_request_headers(env)
|
12
23
|
headers = env.reject do |k, v|
|
13
|
-
!(/^HTTP_[A-Z0-9_]+$/ === k) || v.nil?
|
24
|
+
!(/^HTTP_[A-Z0-9_\.]+$/ === k) || v.nil?
|
14
25
|
end.map do |k, v|
|
15
26
|
[reconstruct_header_name(k), v]
|
16
|
-
end.
|
17
|
-
k, v = k_v
|
18
|
-
hash[k] = v
|
19
|
-
hash
|
20
|
-
end
|
27
|
+
end.then { |pairs| build_header_hash(pairs) }
|
21
28
|
|
22
|
-
x_forwarded_for = (headers[
|
29
|
+
x_forwarded_for = (headers['X-Forwarded-For'].to_s.split(/, +/) << env['REMOTE_ADDR']).join(', ')
|
23
30
|
|
24
|
-
headers.merge!(
|
31
|
+
headers.merge!('X-Forwarded-For' => x_forwarded_for)
|
25
32
|
end
|
26
33
|
|
27
34
|
def normalize_headers(headers)
|
28
35
|
mapped = headers.map do |k, v|
|
29
|
-
[k, if v.is_a? Array then v.join("\n") else v end]
|
36
|
+
[titleize(k), if v.is_a? Array then v.join("\n") else v end]
|
37
|
+
end
|
38
|
+
build_header_hash Hash[mapped]
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_header_hash(pairs)
|
42
|
+
if Rack.const_defined?(:Headers)
|
43
|
+
# Rack::Headers is only available from Rack 3 onward
|
44
|
+
Headers.new.tap { |headers| pairs.each { |k, v| headers[k] = v } }
|
45
|
+
else
|
46
|
+
# Rack::Utils::HeaderHash is deprecated from Rack 3 onward and is to be removed in 3.1
|
47
|
+
Utils::HeaderHash.new(pairs)
|
30
48
|
end
|
31
|
-
Utils::HeaderHash.new Hash[mapped]
|
32
49
|
end
|
33
50
|
|
34
51
|
protected
|
35
52
|
|
36
53
|
def reconstruct_header_name(name)
|
37
|
-
name.sub(/^HTTP_/, "").gsub("_", "-")
|
54
|
+
titleize(name.sub(/^HTTP_/, "").gsub("_", "-"))
|
55
|
+
end
|
56
|
+
|
57
|
+
def titleize(str)
|
58
|
+
str.split("-").map(&:capitalize).join("-")
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
@@ -46,11 +67,20 @@ module Rack
|
|
46
67
|
else
|
47
68
|
@app = app
|
48
69
|
end
|
70
|
+
|
49
71
|
@streaming = opts.fetch(:streaming, true)
|
50
72
|
@ssl_verify_none = opts.fetch(:ssl_verify_none, false)
|
51
|
-
@backend =
|
73
|
+
@backend = opts[:backend] ? URI(opts[:backend]) : nil
|
52
74
|
@read_timeout = opts.fetch(:read_timeout, 60)
|
53
|
-
@ssl_version = opts[:ssl_version]
|
75
|
+
@ssl_version = opts[:ssl_version]
|
76
|
+
@cert = opts[:cert]
|
77
|
+
@key = opts[:key]
|
78
|
+
@verify_mode = opts[:verify_mode]
|
79
|
+
|
80
|
+
@username = opts[:username]
|
81
|
+
@password = opts[:password]
|
82
|
+
|
83
|
+
@opts = opts
|
54
84
|
end
|
55
85
|
|
56
86
|
def call(env)
|
@@ -79,7 +109,7 @@ module Rack
|
|
79
109
|
full_path = source_request.fullpath
|
80
110
|
end
|
81
111
|
|
82
|
-
target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(full_path)
|
112
|
+
target_request = Net::HTTP.const_get(source_request.request_method.capitalize, false).new(full_path)
|
83
113
|
|
84
114
|
# Setup headers
|
85
115
|
target_request.initialize_http_header(self.class.extract_http_request_headers(source_request.env))
|
@@ -92,9 +122,11 @@ module Rack
|
|
92
122
|
target_request.body_stream.rewind
|
93
123
|
end
|
94
124
|
|
125
|
+
# Use basic auth if we have to
|
126
|
+
target_request.basic_auth(@username, @password) if @username && @password
|
127
|
+
|
95
128
|
backend = env.delete('rack.backend') || @backend || source_request
|
96
|
-
use_ssl = backend.scheme == "https"
|
97
|
-
ssl_verify_none = (env.delete('rack.ssl_verify_none') || @ssl_verify_none) == true
|
129
|
+
use_ssl = backend.scheme == "https" || @cert
|
98
130
|
read_timeout = env.delete('http.read_timeout') || @read_timeout
|
99
131
|
|
100
132
|
# Create the response
|
@@ -103,30 +135,34 @@ module Rack
|
|
103
135
|
target_response = HttpStreamingResponse.new(target_request, backend.host, backend.port)
|
104
136
|
target_response.use_ssl = use_ssl
|
105
137
|
target_response.read_timeout = read_timeout
|
106
|
-
target_response.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl && ssl_verify_none
|
107
138
|
target_response.ssl_version = @ssl_version if @ssl_version
|
139
|
+
target_response.verify_mode = (@verify_mode || OpenSSL::SSL::VERIFY_NONE) if use_ssl
|
140
|
+
target_response.cert = @cert if @cert
|
141
|
+
target_response.key = @key if @key
|
108
142
|
else
|
109
143
|
http = Net::HTTP.new(backend.host, backend.port)
|
110
144
|
http.use_ssl = use_ssl if use_ssl
|
111
145
|
http.read_timeout = read_timeout
|
112
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl && ssl_verify_none
|
113
146
|
http.ssl_version = @ssl_version if @ssl_version
|
147
|
+
http.verify_mode = (@verify_mode || OpenSSL::SSL::VERIFY_NONE if use_ssl) if use_ssl
|
148
|
+
http.cert = @cert if @cert
|
149
|
+
http.key = @key if @key
|
114
150
|
|
115
151
|
target_response = http.start do
|
116
152
|
http.request(target_request)
|
117
153
|
end
|
118
154
|
end
|
119
155
|
|
156
|
+
code = target_response.code
|
120
157
|
headers = self.class.normalize_headers(target_response.respond_to?(:headers) ? target_response.headers : target_response.to_hash)
|
121
158
|
body = target_response.body || [""]
|
122
159
|
body = [body] unless body.respond_to?(:each)
|
123
160
|
|
124
161
|
# According to https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3.1Acc
|
125
162
|
# should remove hop-by-hop header fields
|
126
|
-
headers.reject! { |k| [
|
127
|
-
[target_response.code, headers, body]
|
128
|
-
end
|
163
|
+
headers.reject! { |k| HOP_BY_HOP_HEADERS[k.downcase] }
|
129
164
|
|
165
|
+
[code, headers, body]
|
166
|
+
end
|
130
167
|
end
|
131
|
-
|
132
168
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
###
|
2
2
|
# This is an example of how to use Rack-Proxy in a Rails application.
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# Setup:
|
5
|
-
# 1. rails new test_app
|
5
|
+
# 1. rails new test_app
|
6
6
|
# 2. cd test_app
|
7
7
|
# 3. install Rack-Proxy in `Gemfile`
|
8
|
-
# a. `gem 'rack-proxy', '~> 0.
|
8
|
+
# a. `gem 'rack-proxy', '~> 0.7.7'`
|
9
9
|
# 4. install gem: `bundle install`
|
10
10
|
# 5. create `config/initializers/proxy.rb` adding this line `require 'rack_proxy_examples/example_service_proxy'`
|
11
11
|
# 6. run: `SERVICE_URL=http://guides.rubyonrails.org rails server`
|
@@ -27,7 +27,7 @@ class ExampleServiceProxy < Rack::Proxy
|
|
27
27
|
|
28
28
|
# This is the only path that needs to be set currently on Rails 5 & greater
|
29
29
|
env['PATH_INFO'] = ENV['SERVICE_PATH'] || '/configuring.html'
|
30
|
-
|
30
|
+
|
31
31
|
# don't send your sites cookies to target service, unless it is a trusted internal service that can parse all your cookies
|
32
32
|
env['HTTP_COOKIE'] = ''
|
33
33
|
super(env)
|
data/rack-proxy.gemspec
CHANGED
@@ -6,13 +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
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
|
-
s.rubyforge_project = "rack-proxy"
|
15
|
+
s.required_ruby_version = '>= 2.6'
|
16
16
|
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -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 = "
|
8
|
-
@response = Rack::HttpStreamingResponse.new(req, host)
|
7
|
+
host, req = "example.com", 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
|
-
|
14
|
-
|
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
|
20
|
+
assert headers.size.positive?
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
assert headers["content-length"].first.to_i
|
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
|
32
|
+
assert chunks.size.positive?
|
32
33
|
chunks.each do |chunk|
|
33
34
|
assert chunk.is_a?(String)
|
34
35
|
end
|
@@ -36,7 +37,7 @@ class HttpStreamingResponseTest < Test::Unit::TestCase
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def test_to_s
|
39
|
-
assert_equal @response.headers["Content-Length"].first.to_i, @response.body.to_s.
|
40
|
+
assert_equal @response.headers["Content-Length"].first.to_i, @response.body.to_s.bytesize
|
40
41
|
end
|
41
42
|
|
42
43
|
def test_to_s_called_twice
|
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
|
@@ -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 == "
|
81
|
+
assert header == "Abc"
|
81
82
|
|
82
83
|
header = proxy_class.send(:reconstruct_header_name, "HTTP_ABC_D")
|
83
|
-
assert header == "
|
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
|
@@ -117,7 +120,7 @@ class RackProxyTest < Test::Unit::TestCase
|
|
117
120
|
end
|
118
121
|
|
119
122
|
def test_response_header_included_Hop_by_hop
|
120
|
-
app({:streaming => true}).host = '
|
123
|
+
app({:streaming => true}).host = 'mockapi.io'
|
121
124
|
get 'https://example.com/oauth2/token/info?access_token=123'
|
122
125
|
assert !last_response.headers.key?('transfer-encoding')
|
123
126
|
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.7
|
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: 2023-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -60,6 +60,7 @@ executables: []
|
|
60
60
|
extensions: []
|
61
61
|
extra_rdoc_files: []
|
62
62
|
files:
|
63
|
+
- ".github/FUNDING.yml"
|
63
64
|
- ".gitignore"
|
64
65
|
- ".travis.yml"
|
65
66
|
- Gemfile
|
@@ -81,9 +82,10 @@ files:
|
|
81
82
|
- test/rack_proxy_test.rb
|
82
83
|
- test/test_helper.rb
|
83
84
|
homepage: https://github.com/ncr/rack-proxy
|
84
|
-
licenses:
|
85
|
+
licenses:
|
86
|
+
- MIT
|
85
87
|
metadata: {}
|
86
|
-
post_install_message:
|
88
|
+
post_install_message:
|
87
89
|
rdoc_options: []
|
88
90
|
require_paths:
|
89
91
|
- lib
|
@@ -91,16 +93,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
93
|
requirements:
|
92
94
|
- - ">="
|
93
95
|
- !ruby/object:Gem::Version
|
94
|
-
version: '
|
96
|
+
version: '2.6'
|
95
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
98
|
requirements:
|
97
99
|
- - ">="
|
98
100
|
- !ruby/object:Gem::Version
|
99
101
|
version: '0'
|
100
102
|
requirements: []
|
101
|
-
|
102
|
-
|
103
|
-
signing_key:
|
103
|
+
rubygems_version: 3.2.3
|
104
|
+
signing_key:
|
104
105
|
specification_version: 4
|
105
106
|
summary: A request/response rewriting HTTP proxy. A Rack app.
|
106
107
|
test_files:
|