rack-proxy 0.7.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6cbdbb6efeff29ecbbd6dbebe7b67ac4c923fa0ab898d245a310fa872e6d9581
4
- data.tar.gz: e21085cf4be6ec258b67ed91685086a8c0c5c0a83252a92c87853c5f3b79701e
3
+ metadata.gz: 888784aa8d1d28ae0dc2a1352aa44ba8e639d5cd604043facbb31da3fa1dc759
4
+ data.tar.gz: 9ba49effcffcacb930ab08fe2f6a9fd08040b60800b8aa8e5ccc274053f36c4e
5
5
  SHA512:
6
- metadata.gz: 5eed533bedea80e3a485b9a61e6f5e55ab9480146eed5793c0aeb520cb348e86bdfa4a514467300b626f0b016760ce8ecb1a3318272d7cdcc5649c24131d7134
7
- data.tar.gz: 4aea216293ead6071d44a1460b7c0f3f865e53d5332c994c441cbe956db6f57d67aa7b9b80a17bbd593676b2f651860f11f284c698200dbe4acfb0385ac00a15
6
+ metadata.gz: 606ed720fb5b8c67cd1fc3058b9644e88fb2e7768d4fce4606ba0332fac24cadca11a36ab50d97cb7ff5767664864b1c1a2cf5108cd58a66fecfb3b93de37517
7
+ data.tar.gz: a91cc8541d7af6c390fe1c0faa3c923942a14cce746eebc3d170b95b45aafc5871a04ad1ec9fee6f0c07500534755c794f76d0c14bccdcf5fdaad06e239aeb07
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: [ncr]
data/Gemfile.lock CHANGED
@@ -1,22 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rack-proxy (0.7.2)
4
+ rack-proxy (0.7.7)
5
5
  rack
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- power_assert (0.2.6)
11
- rack (2.2.3)
12
- rack-test (0.5.6)
13
- rack (>= 1.0)
10
+ power_assert (2.0.3)
11
+ rack (3.0.8)
12
+ rack-test (2.1.0)
13
+ rack (>= 1.3)
14
14
  rake (13.0.6)
15
- test-unit (3.1.5)
15
+ test-unit (3.6.1)
16
16
  power_assert
17
17
 
18
18
  PLATFORMS
19
- ruby
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
- 1.17.2
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.7.2'
9
+ gem 'rack-proxy', '~> 0.7.7'
10
10
  ```
11
11
 
12
12
  Or install:
@@ -136,7 +136,7 @@ Test with `require 'rack_proxy_examples/example_service_proxy'`
136
136
  # 1. rails new test_app
137
137
  # 2. cd test_app
138
138
  # 3. install Rack-Proxy in `Gemfile`
139
- # a. `gem 'rack-proxy', '~> 0.7.2'`
139
+ # a. `gem 'rack-proxy', '~> 0.7.7'`
140
140
  # 4. install gem: `bundle install`
141
141
  # 5. create `config/initializers/proxy.rb` adding this line `require 'rack_proxy_examples/example_service_proxy'`
142
142
  # 6. run: `SERVICE_URL=http://guides.rubyonrails.org rails server`
@@ -297,6 +297,35 @@ Add some domain name like `debug.your_app.com` into your local `/etc/hosts` file
297
297
 
298
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
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
+ ```
300
329
 
301
330
  WARNING
302
331
  ----
@@ -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
- attr_accessor :use_ssl
8
- attr_accessor :verify_mode
9
- attr_accessor :read_timeout
10
- attr_accessor :ssl_version
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
- h = Utils::HeaderHash.new
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
- session.end_request_hacked
41
- session.finish
41
+ close_connection
42
42
  end
43
43
 
44
44
  def to_s
45
- @body ||= begin
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(@request)
52
+ @response ||= session.begin_request_hacked(request)
61
53
  end
62
54
 
63
55
  # Net::HTTP
64
56
  def session
65
- @session ||= begin
66
- http = Net::HTTP.new @host, @port
67
- http.use_ssl = self.use_ssl
68
- http.verify_mode = self.verify_mode
69
- http.read_timeout = self.read_timeout
70
- http.ssl_version = self.ssl_version if self.use_ssl
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
- end
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,7 +5,18 @@ module Rack
5
5
 
6
6
  # Subclass and bring your own #rewrite_request and #rewrite_response
7
7
  class Proxy
8
- VERSION = "0.7.2"
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)
@@ -13,22 +24,28 @@ module Rack
13
24
  !(/^HTTP_[A-Z0-9_\.]+$/ === k) || v.nil?
14
25
  end.map do |k, v|
15
26
  [reconstruct_header_name(k), v]
16
- end.inject(Utils::HeaderHash.new) do |hash, k_v|
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["X-Forwarded-For"].to_s.split(/, +/) << env["REMOTE_ADDR"]).join(", ")
29
+ x_forwarded_for = (headers['X-Forwarded-For'].to_s.split(/, +/) << env['REMOTE_ADDR']).join(', ')
23
30
 
24
- headers.merge!("X-Forwarded-For" => x_forwarded_for)
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
36
  [titleize(k), if v.is_a? Array then v.join("\n") else v end]
30
37
  end
31
- Utils::HeaderHash.new Hash[mapped]
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)
48
+ end
32
49
  end
33
50
 
34
51
  protected
@@ -56,6 +73,9 @@ module Rack
56
73
  @backend = opts[:backend] ? URI(opts[:backend]) : nil
57
74
  @read_timeout = opts.fetch(:read_timeout, 60)
58
75
  @ssl_version = opts[:ssl_version]
76
+ @cert = opts[:cert]
77
+ @key = opts[:key]
78
+ @verify_mode = opts[:verify_mode]
59
79
 
60
80
  @username = opts[:username]
61
81
  @password = opts[:password]
@@ -106,8 +126,7 @@ module Rack
106
126
  target_request.basic_auth(@username, @password) if @username && @password
107
127
 
108
128
  backend = env.delete('rack.backend') || @backend || source_request
109
- use_ssl = backend.scheme == "https"
110
- ssl_verify_none = (env.delete('rack.ssl_verify_none') || @ssl_verify_none) == true
129
+ use_ssl = backend.scheme == "https" || @cert
111
130
  read_timeout = env.delete('http.read_timeout') || @read_timeout
112
131
 
113
132
  # Create the response
@@ -116,30 +135,34 @@ module Rack
116
135
  target_response = HttpStreamingResponse.new(target_request, backend.host, backend.port)
117
136
  target_response.use_ssl = use_ssl
118
137
  target_response.read_timeout = read_timeout
119
- target_response.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl && ssl_verify_none
120
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
121
142
  else
122
143
  http = Net::HTTP.new(backend.host, backend.port)
123
144
  http.use_ssl = use_ssl if use_ssl
124
145
  http.read_timeout = read_timeout
125
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl && ssl_verify_none
126
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
127
150
 
128
151
  target_response = http.start do
129
152
  http.request(target_request)
130
153
  end
131
154
  end
132
155
 
156
+ code = target_response.code
133
157
  headers = self.class.normalize_headers(target_response.respond_to?(:headers) ? target_response.headers : target_response.to_hash)
134
158
  body = target_response.body || [""]
135
159
  body = [body] unless body.respond_to?(:each)
136
160
 
137
161
  # According to https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3.1Acc
138
162
  # 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 }
140
- [target_response.code, headers, body]
141
- end
163
+ headers.reject! { |k| HOP_BY_HOP_HEADERS[k.downcase] }
142
164
 
165
+ [code, headers, body]
166
+ end
143
167
  end
144
-
145
168
  end
@@ -5,7 +5,7 @@
5
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.7.2'`
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`
data/rack-proxy.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.homepage = "https://github.com/ncr/rack-proxy"
13
13
  s.summary = %q{A request/response rewriting HTTP proxy. A Rack app.}
14
14
  s.description = %q{A Rack app that provides request/response rewriting proxy capabilities with streaming.}
15
+ s.required_ruby_version = '>= 2.6'
15
16
 
16
17
  s.files = `git ls-files`.split("\n")
17
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -4,7 +4,7 @@ require "rack/http_streaming_response"
4
4
  class HttpStreamingResponseTest < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
- host, req = "mockapi.io", Net::HTTP::Get.new("/")
7
+ host, req = "example.com", Net::HTTP::Get.new("/")
8
8
  @response = Rack::HttpStreamingResponse.new(req, host, 443)
9
9
  @response.use_ssl = true
10
10
  end
@@ -37,7 +37,7 @@ class HttpStreamingResponseTest < Test::Unit::TestCase
37
37
  end
38
38
 
39
39
  def test_to_s
40
- assert_equal @response.headers["Content-Length"].first.to_i, @response.body.to_s.size
40
+ assert_equal @response.headers["Content-Length"].first.to_i, @response.body.to_s.bytesize
41
41
  end
42
42
 
43
43
  def test_to_s_called_twice
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.7.2
4
+ version: 0.7.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacek Becela
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-05 00:00:00.000000000 Z
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
@@ -92,14 +93,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
93
  requirements:
93
94
  - - ">="
94
95
  - !ruby/object:Gem::Version
95
- version: '0'
96
+ version: '2.6'
96
97
  required_rubygems_version: !ruby/object:Gem::Requirement
97
98
  requirements:
98
99
  - - ">="
99
100
  - !ruby/object:Gem::Version
100
101
  version: '0'
101
102
  requirements: []
102
- rubygems_version: 3.0.3
103
+ rubygems_version: 3.2.3
103
104
  signing_key:
104
105
  specification_version: 4
105
106
  summary: A request/response rewriting HTTP proxy. A Rack app.