ssrf_filter 1.0.7 → 1.1.2
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 +4 -4
- data/lib/ssrf_filter/patch/ssl_socket.rb +43 -26
- data/lib/ssrf_filter/ssrf_filter.rb +35 -24
- data/lib/ssrf_filter/version.rb +1 -1
- data/lib/ssrf_filter.rb +0 -1
- metadata +80 -24
- data/lib/ssrf_filter/patch/http_generic_request.rb +0 -83
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 315ab29498751c0bb60964f06932464d8663ec66e50ed403b04c2e19c9fce5e6
|
4
|
+
data.tar.gz: 8b3475337b149bf8e04dd11e379e9c0698f74d05528524fea847ed996aff405c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 299b91d225b906faa91a1ebd4a8ad32ce46889deaeffa89d5a7488b871310e68ed7f73332c9ee25fd0ff3d01a25dd949907a390e8ce44926910657e2fa054f3e
|
7
|
+
data.tar.gz: 89b6051cb2a8f232336e1e25e95a5898c71ba4439f582224452effbcc41b3a68af13d1e16d855f8ebb7634c013fefd3301c661d5d6bf72f3d98894feb867f47f
|
@@ -1,45 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class SsrfFilter
|
2
4
|
module Patch
|
3
5
|
module SSLSocket
|
4
|
-
# When fetching a url we'd like to have the following workflow:
|
5
|
-
# 1) resolve the hostname www.example.com, and choose a public ip address to connect to
|
6
|
-
# 2) connect to that specific ip address, to prevent things like DNS TOCTTOU bugs or other trickery
|
7
|
-
#
|
8
|
-
# Ideally this would happen by the ruby http library giving us control over DNS resolution,
|
9
|
-
# but it doesn't. Instead, when making the request we set the uri.hostname to the chosen ip address,
|
10
|
-
# and send a 'Host' header of the original hostname, i.e. connect to 'http://93.184.216.34/' and send
|
11
|
-
# a 'Host: www.example.com' header.
|
12
|
-
#
|
13
|
-
# This works for the http case, http://www.example.com. For the https case, this causes certificate
|
14
|
-
# validation failures, since the server certificate for https://www.example.com will not have a
|
15
|
-
# Subject Alternate Name for 93.184.216.34.
|
16
|
-
#
|
17
|
-
# Thus we perform the monkey-patch below, modifying SSLSocket's `post_connection_check(hostname)`
|
18
|
-
# and `hostname=(hostname)` methods:
|
19
|
-
# If our fiber local variable is set, use that for the hostname instead, otherwise behave as usual.
|
20
|
-
# The only time the variable will be set is if you are executing inside a `with_forced_hostname` block,
|
21
|
-
# which is used in ssrf_filter.rb.
|
22
|
-
#
|
23
|
-
# An alternative approach could be to pass in our own OpenSSL::X509::Store with a custom
|
24
|
-
# `verify_callback` to the ::Net::HTTP.start call, but this would require reimplementing certification
|
25
|
-
# validation, which is dangerous. This way we can piggyback on the existing validation and simply pretend
|
26
|
-
# that we connected to the desired hostname.
|
27
|
-
|
28
6
|
def self.apply!
|
29
7
|
return if instance_variable_defined?(:@patched_ssl_socket)
|
30
8
|
|
31
9
|
@patched_ssl_socket = true
|
32
10
|
|
33
11
|
::OpenSSL::SSL::SSLSocket.class_eval do
|
12
|
+
# When fetching a url we'd like to have the following workflow:
|
13
|
+
# 1) resolve the hostname www.example.com, and choose a public ip address to connect to
|
14
|
+
# 2) connect to that specific ip address, to prevent things like DNS TOCTTOU bugs or other trickery
|
15
|
+
#
|
16
|
+
# Ideally this would happen by the ruby http library giving us control over DNS resolution,
|
17
|
+
# but it doesn't. Instead, when making the request we set the uri.hostname to the chosen ip address,
|
18
|
+
# and send a 'Host' header of the original hostname, i.e. connect to 'http://93.184.216.34/' and send
|
19
|
+
# a 'Host: www.example.com' header.
|
20
|
+
#
|
21
|
+
# This works for the http case, http://www.example.com. For the https case, this causes certificate
|
22
|
+
# validation failures, since the server certificate for https://www.example.com will not have a
|
23
|
+
# Subject Alternate Name for 93.184.216.34.
|
24
|
+
#
|
25
|
+
# Thus we perform the monkey-patch below, modifying SSLSocket's `post_connection_check(hostname)`
|
26
|
+
# and `hostname=(hostname)` methods:
|
27
|
+
# If our fiber local variable is set, use that for the hostname instead, otherwise behave as usual.
|
28
|
+
# The only time the variable will be set is if you are executing inside a `with_forced_hostname` block,
|
29
|
+
# which is used in ssrf_filter.rb.
|
30
|
+
#
|
31
|
+
# An alternative approach could be to pass in our own OpenSSL::X509::Store with a custom
|
32
|
+
# `verify_callback` to the ::Net::HTTP.start call, but this would require reimplementing certification
|
33
|
+
# validation, which is dangerous. This way we can piggyback on the existing validation and simply pretend
|
34
|
+
# that we connected to the desired hostname.
|
35
|
+
|
34
36
|
original_post_connection_check = instance_method(:post_connection_check)
|
35
37
|
define_method(:post_connection_check) do |hostname|
|
36
|
-
original_post_connection_check.bind(self).call(::Thread.current[::SsrfFilter::
|
38
|
+
original_post_connection_check.bind(self).call(::Thread.current[::SsrfFilter::FIBER_HOSTNAME_KEY] ||
|
39
|
+
hostname)
|
37
40
|
end
|
38
41
|
|
39
42
|
if method_defined?(:hostname=)
|
40
43
|
original_hostname = instance_method(:hostname=)
|
41
44
|
define_method(:hostname=) do |hostname|
|
42
|
-
original_hostname.bind(self).call(::Thread.current[::SsrfFilter::
|
45
|
+
original_hostname.bind(self).call(::Thread.current[::SsrfFilter::FIBER_HOSTNAME_KEY] || hostname)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# This patch is the successor to https://github.com/arkadiyt/ssrf_filter/pull/54
|
50
|
+
# Due to some changes in Ruby's net/http library (namely https://github.com/ruby/net-http/pull/36),
|
51
|
+
# the SSLSocket in the request was no longer getting `s.hostname` set.
|
52
|
+
# The original fix tried to monkey-patch the Regexp class to cause the original code path to execute,
|
53
|
+
# but this caused other problems (like https://github.com/arkadiyt/ssrf_filter/issues/61)
|
54
|
+
# This fix attempts a different approach to set the hostname on the socket
|
55
|
+
original_initialize = instance_method(:initialize)
|
56
|
+
define_method(:initialize) do |*args|
|
57
|
+
original_initialize.bind(self).call(*args)
|
58
|
+
if ::Thread.current.key?(::SsrfFilter::FIBER_HOSTNAME_KEY)
|
59
|
+
self.hostname = ::Thread.current[::SsrfFilter::FIBER_HOSTNAME_KEY]
|
43
60
|
end
|
44
61
|
end
|
45
62
|
end
|
@@ -10,7 +10,9 @@ class SsrfFilter
|
|
10
10
|
mask_addr = ipaddr.instance_variable_get('@mask_addr')
|
11
11
|
raise ArgumentError, 'Invalid mask' if mask_addr.zero?
|
12
12
|
|
13
|
-
|
13
|
+
while (mask_addr & 0x1).zero?
|
14
|
+
mask_addr >>= 1
|
15
|
+
end
|
14
16
|
|
15
17
|
length = 0
|
16
18
|
while mask_addr & 0x1 == 0x1
|
@@ -70,16 +72,19 @@ class SsrfFilter
|
|
70
72
|
::Resolv.getaddresses(hostname).map { |ip| ::IPAddr.new(ip) }
|
71
73
|
end
|
72
74
|
|
75
|
+
DEFAULT_ALLOW_UNFOLLOWED_REDIRECTS = false
|
73
76
|
DEFAULT_MAX_REDIRECTS = 10
|
74
77
|
|
75
78
|
VERB_MAP = {
|
76
79
|
get: ::Net::HTTP::Get,
|
77
80
|
put: ::Net::HTTP::Put,
|
78
81
|
post: ::Net::HTTP::Post,
|
79
|
-
delete: ::Net::HTTP::Delete
|
82
|
+
delete: ::Net::HTTP::Delete,
|
83
|
+
head: ::Net::HTTP::Head,
|
84
|
+
patch: ::Net::HTTP::Patch
|
80
85
|
}.freeze
|
81
86
|
|
82
|
-
|
87
|
+
FIBER_HOSTNAME_KEY = :__ssrf_filter_hostname
|
83
88
|
|
84
89
|
class Error < ::StandardError
|
85
90
|
end
|
@@ -99,17 +104,18 @@ class SsrfFilter
|
|
99
104
|
class CRLFInjection < Error
|
100
105
|
end
|
101
106
|
|
102
|
-
%i[get put post delete].each do |method|
|
107
|
+
%i[get put post delete head patch].each do |method|
|
103
108
|
define_singleton_method(method) do |url, options = {}, &block|
|
104
109
|
::SsrfFilter::Patch::SSLSocket.apply!
|
105
|
-
::SsrfFilter::Patch::HTTPGenericRequest.apply!
|
106
110
|
|
107
111
|
original_url = url
|
108
|
-
scheme_whitelist = options
|
109
|
-
resolver = options
|
110
|
-
|
112
|
+
scheme_whitelist = options.fetch(:scheme_whitelist, DEFAULT_SCHEME_WHITELIST)
|
113
|
+
resolver = options.fetch(:resolver, DEFAULT_RESOLVER)
|
114
|
+
allow_unfollowed_redirects = options.fetch(:allow_unfollowed_redirects, DEFAULT_ALLOW_UNFOLLOWED_REDIRECTS)
|
115
|
+
max_redirects = options.fetch(:max_redirects, DEFAULT_MAX_REDIRECTS)
|
111
116
|
url = url.to_s
|
112
117
|
|
118
|
+
response = nil
|
113
119
|
(max_redirects + 1).times do
|
114
120
|
uri = URI(url)
|
115
121
|
|
@@ -124,18 +130,12 @@ class SsrfFilter
|
|
124
130
|
public_addresses = ip_addresses.reject(&method(:unsafe_ip_address?))
|
125
131
|
raise PrivateIPAddress, "Hostname '#{hostname}' has no public ip addresses" if public_addresses.empty?
|
126
132
|
|
127
|
-
response = fetch_once(uri, public_addresses.sample.to_s, method, options, &block)
|
128
|
-
|
129
|
-
case response
|
130
|
-
when ::Net::HTTPRedirection then
|
131
|
-
url = response['location']
|
132
|
-
# Handle relative redirects
|
133
|
-
url = "#{uri.scheme}://#{hostname}:#{uri.port}#{url}" if url.start_with?('/')
|
134
|
-
else
|
135
|
-
return response
|
136
|
-
end
|
133
|
+
response, url = fetch_once(uri, public_addresses.sample.to_s, method, options, &block)
|
134
|
+
return response if url.nil?
|
137
135
|
end
|
138
136
|
|
137
|
+
return response if allow_unfollowed_redirects
|
138
|
+
|
139
139
|
raise TooManyRedirects, "Got #{max_redirects} redirects fetching #{original_url}"
|
140
140
|
end
|
141
141
|
end
|
@@ -169,7 +169,7 @@ class SsrfFilter
|
|
169
169
|
|
170
170
|
def self.fetch_once(uri, ip, verb, options, &block)
|
171
171
|
if options[:params]
|
172
|
-
params = uri.query ? ::
|
172
|
+
params = uri.query ? ::URI.decode_www_form(uri.query).to_h : {}
|
173
173
|
params.merge!(options[:params])
|
174
174
|
uri.query = ::URI.encode_www_form(params)
|
175
175
|
end
|
@@ -186,15 +186,26 @@ class SsrfFilter
|
|
186
186
|
|
187
187
|
request.body = options[:body] if options[:body]
|
188
188
|
|
189
|
-
|
189
|
+
options[:request_proc].call(request) if options[:request_proc].respond_to?(:call)
|
190
190
|
validate_request(request)
|
191
191
|
|
192
192
|
http_options = options[:http_options] || {}
|
193
193
|
http_options[:use_ssl] = (uri.scheme == 'https')
|
194
194
|
|
195
195
|
with_forced_hostname(hostname) do
|
196
|
-
::Net::HTTP.start(uri.hostname, uri.port, http_options) do |http|
|
197
|
-
http.request(request)
|
196
|
+
::Net::HTTP.start(uri.hostname, uri.port, **http_options) do |http|
|
197
|
+
response = http.request(request) do |res|
|
198
|
+
block&.call(res)
|
199
|
+
end
|
200
|
+
case response
|
201
|
+
when ::Net::HTTPRedirection
|
202
|
+
url = response['location']
|
203
|
+
# Handle relative redirects
|
204
|
+
url = "#{uri.scheme}://#{hostname}:#{uri.port}#{url}" if url.start_with?('/')
|
205
|
+
else
|
206
|
+
url = nil
|
207
|
+
end
|
208
|
+
return response, url
|
198
209
|
end
|
199
210
|
end
|
200
211
|
end
|
@@ -214,10 +225,10 @@ class SsrfFilter
|
|
214
225
|
private_class_method :validate_request
|
215
226
|
|
216
227
|
def self.with_forced_hostname(hostname, &_block)
|
217
|
-
::Thread.current[
|
228
|
+
::Thread.current[FIBER_HOSTNAME_KEY] = hostname
|
218
229
|
yield
|
219
230
|
ensure
|
220
|
-
::Thread.current[
|
231
|
+
::Thread.current[FIBER_HOSTNAME_KEY] = nil
|
221
232
|
end
|
222
233
|
private_class_method :with_forced_hostname
|
223
234
|
end
|
data/lib/ssrf_filter/version.rb
CHANGED
data/lib/ssrf_filter.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ssrf_filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arkadiy Tetelman
|
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: bundler-audit
|
@@ -16,87 +16,143 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.9.1
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.9.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pry-byebug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
47
|
+
version: 3.12.0
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
54
|
+
version: 3.12.0
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
61
|
+
version: 1.35.0
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
68
|
+
version: 1.35.0
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: rubocop-rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: 2.12.1
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
82
|
+
version: 2.12.1
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.22.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.22.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov-lcov
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - "~>"
|
74
102
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
103
|
+
version: 0.8.0
|
76
104
|
type: :development
|
77
105
|
prerelease: false
|
78
106
|
version_requirements: !ruby/object:Gem::Requirement
|
79
107
|
requirements:
|
80
108
|
- - "~>"
|
81
109
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
110
|
+
version: 0.8.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.18.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 3.18.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webrick
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
83
139
|
description: A gem that makes it easy to prevent server side request forgery (SSRF)
|
84
140
|
attacks
|
85
|
-
email:
|
141
|
+
email:
|
86
142
|
executables: []
|
87
143
|
extensions: []
|
88
144
|
extra_rdoc_files: []
|
89
145
|
files:
|
90
146
|
- lib/ssrf_filter.rb
|
91
|
-
- lib/ssrf_filter/patch/http_generic_request.rb
|
92
147
|
- lib/ssrf_filter/patch/ssl_socket.rb
|
93
148
|
- lib/ssrf_filter/ssrf_filter.rb
|
94
149
|
- lib/ssrf_filter/version.rb
|
95
150
|
homepage: https://github.com/arkadiyt/ssrf_filter
|
96
151
|
licenses:
|
97
152
|
- MIT
|
98
|
-
metadata:
|
99
|
-
|
153
|
+
metadata:
|
154
|
+
rubygems_mfa_required: 'true'
|
155
|
+
post_install_message:
|
100
156
|
rdoc_options: []
|
101
157
|
require_paths:
|
102
158
|
- lib
|
@@ -104,15 +160,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
160
|
requirements:
|
105
161
|
- - ">="
|
106
162
|
- !ruby/object:Gem::Version
|
107
|
-
version: 2.
|
163
|
+
version: 2.6.0
|
108
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
165
|
requirements:
|
110
166
|
- - ">="
|
111
167
|
- !ruby/object:Gem::Version
|
112
168
|
version: '0'
|
113
169
|
requirements: []
|
114
|
-
rubygems_version: 3.
|
115
|
-
signing_key:
|
170
|
+
rubygems_version: 3.3.7
|
171
|
+
signing_key:
|
116
172
|
specification_version: 4
|
117
173
|
summary: A gem that makes it easy to prevent server side request forgery (SSRF) attacks
|
118
174
|
test_files: []
|
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
|
3
|
-
class SsrfFilter
|
4
|
-
module Patch
|
5
|
-
module HTTPGenericRequest
|
6
|
-
# Ruby had a bug in its Http library where if you set a custom `Host` header on a request it would get
|
7
|
-
# overwritten. This was tracked in:
|
8
|
-
# https://bugs.ruby-lang.org/issues/10054
|
9
|
-
# and resolved with the commit:
|
10
|
-
# https://github.com/ruby/ruby/commit/70a2eb63999265ff7e8d46d1f5b410c8ee3d30d7#diff-5c08b4ae27d2294a8294a27ff9af4a85
|
11
|
-
# Versions of Ruby that don't have this fix applied will fail to connect to certain hosts via SsrfFilter. The
|
12
|
-
# patch below backports the fix from the linked commit.
|
13
|
-
|
14
|
-
def self.should_apply?
|
15
|
-
# Check if the patch needs to be applied:
|
16
|
-
# The Ruby bug was that HTTPGenericRequest#exec overwrote the Host header, so this snippet checks
|
17
|
-
# if we can reproduce that behavior. It does not actually open any network connections.
|
18
|
-
|
19
|
-
uri = URI('https://www.example.com')
|
20
|
-
request = ::Net::HTTPGenericRequest.new('HEAD', false, false, uri)
|
21
|
-
request['host'] = '127.0.0.1'
|
22
|
-
request.exec(StringIO.new, '1.1', '/')
|
23
|
-
request['host'] == uri.hostname
|
24
|
-
end
|
25
|
-
|
26
|
-
# Apply the patch from the linked commit onto ::Net::HTTPGenericRequest. Since this is 3rd party code,
|
27
|
-
# disable code coverage and rubocop linting for this section.
|
28
|
-
# :nocov:
|
29
|
-
# rubocop:disable all
|
30
|
-
def self.apply!
|
31
|
-
return if instance_variable_defined?(:@checked_http_generic_request)
|
32
|
-
@checked_http_generic_request = true
|
33
|
-
return unless should_apply?
|
34
|
-
|
35
|
-
::Net::HTTPGenericRequest.class_eval do
|
36
|
-
def exec(sock, ver, path)
|
37
|
-
if @body
|
38
|
-
send_request_with_body sock, ver, path, @body
|
39
|
-
elsif @body_stream
|
40
|
-
send_request_with_body_stream sock, ver, path, @body_stream
|
41
|
-
elsif @body_data
|
42
|
-
send_request_with_body_data sock, ver, path, @body_data
|
43
|
-
else
|
44
|
-
write_header sock, ver, path
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def update_uri(addr, port, ssl)
|
49
|
-
# reflect the connection and @path to @uri
|
50
|
-
return unless @uri
|
51
|
-
|
52
|
-
if ssl
|
53
|
-
scheme = 'https'.freeze
|
54
|
-
klass = URI::HTTPS
|
55
|
-
else
|
56
|
-
scheme = 'http'.freeze
|
57
|
-
klass = URI::HTTP
|
58
|
-
end
|
59
|
-
|
60
|
-
if host = self['host']
|
61
|
-
host.sub!(/:.*/s, ''.freeze)
|
62
|
-
elsif host = @uri.host
|
63
|
-
else
|
64
|
-
host = addr
|
65
|
-
end
|
66
|
-
# convert the class of the URI
|
67
|
-
if @uri.is_a?(klass)
|
68
|
-
@uri.host = host
|
69
|
-
@uri.port = port
|
70
|
-
else
|
71
|
-
@uri = klass.new(
|
72
|
-
scheme, @uri.userinfo,
|
73
|
-
host, port, nil,
|
74
|
-
@uri.path, nil, @uri.query, nil)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
# rubocop:enable all
|
80
|
-
# :nocov:
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|