httparty 0.15.0 → 0.21.0
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/.editorconfig +18 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +1 -1
- data/{History.md → Changelog.md} +137 -0
- data/Gemfile +8 -1
- data/Guardfile +3 -2
- data/README.md +6 -6
- data/docs/README.md +120 -38
- data/examples/README.md +28 -11
- data/examples/aaws.rb +6 -2
- data/examples/body_stream.rb +14 -0
- data/examples/custom_parsers.rb +4 -0
- data/examples/headers_and_user_agents.rb +7 -3
- data/examples/idn.rb +10 -0
- data/examples/logging.rb +3 -3
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/stream_download.rb +8 -2
- data/httparty.gemspec +7 -4
- data/lib/httparty/connection_adapter.rb +59 -16
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +102 -0
- data/lib/httparty/exceptions.rb +4 -1
- data/lib/httparty/hash_conversions.rb +28 -12
- data/lib/httparty/headers_processor.rb +32 -0
- data/lib/httparty/logger/apache_formatter.rb +31 -6
- data/lib/httparty/logger/curl_formatter.rb +9 -7
- data/lib/httparty/logger/logger.rb +5 -1
- data/lib/httparty/logger/logstash_formatter.rb +61 -0
- data/lib/httparty/module_inheritable_attributes.rb +6 -4
- data/lib/httparty/net_digest_auth.rb +15 -15
- data/lib/httparty/parser.rb +25 -16
- data/lib/httparty/request/body.rb +105 -0
- data/lib/httparty/request/multipart_boundary.rb +13 -0
- data/lib/httparty/request.rb +96 -105
- data/lib/httparty/response/headers.rb +6 -2
- data/lib/httparty/response.rb +59 -8
- data/lib/httparty/response_fragment.rb +21 -0
- data/lib/httparty/text_encoder.rb +72 -0
- data/lib/httparty/utils.rb +13 -0
- data/lib/httparty/version.rb +3 -1
- data/lib/httparty.rb +70 -25
- data/website/css/common.css +1 -1
- metadata +38 -106
- data/.simplecov +0 -1
- data/.travis.yml +0 -8
- data/features/basic_authentication.feature +0 -20
- data/features/command_line.feature +0 -95
- data/features/deals_with_http_error_codes.feature +0 -26
- data/features/digest_authentication.feature +0 -30
- data/features/handles_compressed_responses.feature +0 -27
- data/features/handles_multiple_formats.feature +0 -57
- data/features/steps/env.rb +0 -27
- data/features/steps/httparty_response_steps.rb +0 -56
- data/features/steps/httparty_steps.rb +0 -43
- data/features/steps/mongrel_helper.rb +0 -127
- data/features/steps/remote_service_steps.rb +0 -92
- data/features/supports_read_timeout_option.feature +0 -13
- data/features/supports_redirection.feature +0 -22
- data/features/supports_timeout_option.feature +0 -13
- data/spec/fixtures/delicious.xml +0 -23
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +0 -3
- data/spec/fixtures/ssl/generate.sh +0 -29
- data/spec/fixtures/ssl/generated/1fe462c2.0 +0 -1
- data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
- data/spec/fixtures/ssl/generated/ca.crt +0 -16
- data/spec/fixtures/ssl/generated/ca.key +0 -15
- data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
- data/spec/fixtures/ssl/generated/server.crt +0 -13
- data/spec/fixtures/ssl/generated/server.key +0 -15
- data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
- data/spec/fixtures/twitter.csv +0 -2
- data/spec/fixtures/twitter.json +0 -1
- data/spec/fixtures/twitter.xml +0 -403
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
- data/spec/httparty/connection_adapter_spec.rb +0 -495
- data/spec/httparty/cookie_hash_spec.rb +0 -100
- data/spec/httparty/exception_spec.rb +0 -45
- data/spec/httparty/hash_conversions_spec.rb +0 -49
- data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
- data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
- data/spec/httparty/logger/logger_spec.rb +0 -38
- data/spec/httparty/net_digest_auth_spec.rb +0 -268
- data/spec/httparty/parser_spec.rb +0 -178
- data/spec/httparty/request_spec.rb +0 -1244
- data/spec/httparty/response_spec.rb +0 -347
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -877
- data/spec/spec_helper.rb +0 -59
- data/spec/support/ssl_test_helper.rb +0 -47
- data/spec/support/ssl_test_server.rb +0 -80
- data/spec/support/stub_response.rb +0 -49
data/examples/logging.rb
CHANGED
@@ -3,7 +3,7 @@ require File.join(dir, 'httparty')
|
|
3
3
|
require 'logger'
|
4
4
|
require 'pp'
|
5
5
|
|
6
|
-
my_logger = Logger.new
|
6
|
+
my_logger = Logger.new STDOUT
|
7
7
|
|
8
8
|
my_logger.info "Logging can be used on the main HTTParty class. It logs redirects too."
|
9
9
|
HTTParty.get "http://google.com", logger: my_logger
|
@@ -14,7 +14,7 @@ my_logger.info "It can be used also on a custom class."
|
|
14
14
|
|
15
15
|
class Google
|
16
16
|
include HTTParty
|
17
|
-
logger ::Logger.new
|
17
|
+
logger ::Logger.new STDOUT
|
18
18
|
end
|
19
19
|
|
20
20
|
Google.get "http://google.com"
|
@@ -30,7 +30,7 @@ my_logger.info '*' * 70
|
|
30
30
|
my_logger.info "These configs are also available on custom classes."
|
31
31
|
class Google
|
32
32
|
include HTTParty
|
33
|
-
logger ::Logger.new(
|
33
|
+
logger ::Logger.new(STDOUT), :debug, :curl
|
34
34
|
end
|
35
35
|
|
36
36
|
Google.get "http://google.com"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
class MicrosoftGraph
|
4
|
+
MS_BASE_URL = "https://login.microsoftonline.com".freeze
|
5
|
+
TOKEN_REQUEST_PATH = "oauth2/v2.0/token".freeze
|
6
|
+
|
7
|
+
def initialize(tenant_id)
|
8
|
+
@tenant_id = tenant_id
|
9
|
+
end
|
10
|
+
|
11
|
+
# Make a request to the Microsoft Graph API, for instance https://graph.microsoft.com/v1.0/users
|
12
|
+
def request(url)
|
13
|
+
return false unless (token = bearer_token)
|
14
|
+
|
15
|
+
response = HTTParty.get(
|
16
|
+
url,
|
17
|
+
headers: {
|
18
|
+
Authorization: "Bearer #{token}"
|
19
|
+
}
|
20
|
+
)
|
21
|
+
|
22
|
+
return false unless response.code == 200
|
23
|
+
|
24
|
+
return JSON.parse(response.body)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# A post to the Microsoft Graph to get a bearer token for the specified tenant. In this example
|
30
|
+
# our Rails application has already been given permission to request these tokens by the admin of
|
31
|
+
# the specified tenant_id.
|
32
|
+
#
|
33
|
+
# See here for more information https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service
|
34
|
+
#
|
35
|
+
# This request also makes use of the multipart/form-data post body.
|
36
|
+
def bearer_token
|
37
|
+
response = HTTParty.post(
|
38
|
+
"#{MS_BASE_URL}/#{@tenant_id}/#{TOKEN_REQUEST_PATH}",
|
39
|
+
multipart: true,
|
40
|
+
body: {
|
41
|
+
client_id: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_id],
|
42
|
+
client_secret: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_secret],
|
43
|
+
scope: 'https://graph.microsoft.com/.default',
|
44
|
+
grant_type: 'client_credentials'
|
45
|
+
}
|
46
|
+
)
|
47
|
+
|
48
|
+
return false unless response.code == 200
|
49
|
+
|
50
|
+
JSON.parse(response.body)['access_token']
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# If you are uploading file in params, multipart will used as content-type automatically
|
2
|
+
|
3
|
+
HTTParty.post(
|
4
|
+
'http://localhost:3000/user',
|
5
|
+
body: {
|
6
|
+
name: 'Foo Bar',
|
7
|
+
email: 'example@email.com',
|
8
|
+
avatar: File.open('/full/path/to/avatar.jpg')
|
9
|
+
}
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
# However, you can force it yourself
|
14
|
+
|
15
|
+
HTTParty.post(
|
16
|
+
'http://localhost:3000/user',
|
17
|
+
multipart: true,
|
18
|
+
body: {
|
19
|
+
name: 'Foo Bar',
|
20
|
+
email: 'example@email.com'
|
21
|
+
}
|
22
|
+
)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
|
4
|
+
peer_cert = nil
|
5
|
+
HTTParty.get("https://www.example.com") do |fragment|
|
6
|
+
peer_cert ||= fragment.connection.peer_cert
|
7
|
+
end
|
8
|
+
|
9
|
+
puts "The server's certificate expires #{peer_cert.not_after}"
|
data/examples/stream_download.rb
CHANGED
@@ -9,8 +9,14 @@ url = "https://cdn.kernel.org/pub/linux/kernel/v4.x/#{filename}"
|
|
9
9
|
|
10
10
|
File.open(filename, "w") do |file|
|
11
11
|
response = HTTParty.get(url, stream_body: true) do |fragment|
|
12
|
-
|
13
|
-
|
12
|
+
if [301, 302].include?(fragment.code)
|
13
|
+
print "skip writing for redirect"
|
14
|
+
elsif fragment.code == 200
|
15
|
+
print "."
|
16
|
+
file.write(fragment)
|
17
|
+
else
|
18
|
+
raise StandardError, "Non-success status code while streaming #{fragment.code}"
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
16
22
|
puts
|
data/httparty.gemspec
CHANGED
@@ -9,19 +9,22 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.licenses = ['MIT']
|
10
10
|
s.authors = ["John Nunemaker", "Sandro Turriate"]
|
11
11
|
s.email = ["nunemaker@gmail.com"]
|
12
|
-
s.homepage = "
|
12
|
+
s.homepage = "https://github.com/jnunemaker/httparty"
|
13
13
|
s.summary = 'Makes http fun! Also, makes consuming restful web services dead easy.'
|
14
14
|
s.description = 'Makes http fun! Also, makes consuming restful web services dead easy.'
|
15
15
|
|
16
|
-
s.required_ruby_version = '>= 2.
|
16
|
+
s.required_ruby_version = '>= 2.3.0'
|
17
17
|
|
18
18
|
s.add_dependency 'multi_xml', ">= 0.5.2"
|
19
|
+
s.add_dependency 'mini_mime', ">= 1.0.0"
|
19
20
|
|
20
21
|
# If this line is removed, all hard partying will cease.
|
21
22
|
s.post_install_message = "When you HTTParty, you must party hard!"
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
all_files = `git ls-files`.split("\n")
|
25
|
+
test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
|
+
|
27
|
+
s.files = all_files - test_files
|
25
28
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
26
29
|
s.require_paths = ["lib"]
|
27
30
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
# Default connection adapter that returns a new Net::HTTP each time
|
3
5
|
#
|
4
6
|
# == Custom Connection Factories
|
5
7
|
#
|
6
8
|
# If you like to implement your own connection adapter, subclassing
|
7
|
-
#
|
9
|
+
# HTTParty::ConnectionAdapter will make it easier. Just override
|
8
10
|
# the #connection method. The uri and options attributes will have
|
9
11
|
# all the info you need to construct your http connection. Whatever
|
10
12
|
# you return from your connection method needs to adhere to the
|
@@ -38,12 +40,13 @@ module HTTParty
|
|
38
40
|
# in the #options attribute. It is up to you to interpret them within your
|
39
41
|
# connection adapter. Take a look at the implementation of
|
40
42
|
# HTTParty::ConnectionAdapter#connection for examples of how they are used.
|
41
|
-
# The keys used in options are
|
43
|
+
# The keys used in options are
|
42
44
|
# * :+timeout+: timeout in seconds
|
43
45
|
# * :+open_timeout+: http connection open_timeout in seconds, overrides timeout if set
|
44
46
|
# * :+read_timeout+: http connection read_timeout in seconds, overrides timeout if set
|
47
|
+
# * :+write_timeout+: http connection write_timeout in seconds, overrides timeout if set (Ruby >= 2.6.0 required)
|
45
48
|
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
|
46
|
-
# * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates'
|
49
|
+
# * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates'
|
47
50
|
# * :+pem+: contains pem client certificate data. see method 'attach_ssl_certificates'
|
48
51
|
# * :+p12+: contains PKCS12 client client certificate data. see method 'attach_ssl_certificates'
|
49
52
|
# * :+verify+: verify the server’s certificate against the ca certificate.
|
@@ -77,6 +80,12 @@ module HTTParty
|
|
77
80
|
new(uri, options).connection
|
78
81
|
end
|
79
82
|
|
83
|
+
def self.default_cert_store
|
84
|
+
@default_cert_store ||= OpenSSL::X509::Store.new.tap do |cert_store|
|
85
|
+
cert_store.set_default_paths
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
80
89
|
attr_reader :uri, :options
|
81
90
|
|
82
91
|
def initialize(uri, options = {})
|
@@ -91,7 +100,14 @@ module HTTParty
|
|
91
100
|
host = clean_host(uri.host)
|
92
101
|
port = uri.port || (uri.scheme == 'https' ? 443 : 80)
|
93
102
|
if options.key?(:http_proxyaddr)
|
94
|
-
http = Net::HTTP.new(
|
103
|
+
http = Net::HTTP.new(
|
104
|
+
host,
|
105
|
+
port,
|
106
|
+
options[:http_proxyaddr],
|
107
|
+
options[:http_proxyport],
|
108
|
+
options[:http_proxyuser],
|
109
|
+
options[:http_proxypass]
|
110
|
+
)
|
95
111
|
else
|
96
112
|
http = Net::HTTP.new(host, port)
|
97
113
|
end
|
@@ -100,19 +116,35 @@ module HTTParty
|
|
100
116
|
|
101
117
|
attach_ssl_certificates(http, options)
|
102
118
|
|
103
|
-
if
|
119
|
+
if add_timeout?(options[:timeout])
|
104
120
|
http.open_timeout = options[:timeout]
|
105
121
|
http.read_timeout = options[:timeout]
|
122
|
+
|
123
|
+
from_ruby_version('2.6.0', option: :write_timeout, warn: false) do
|
124
|
+
http.write_timeout = options[:timeout]
|
125
|
+
end
|
106
126
|
end
|
107
127
|
|
108
|
-
if
|
128
|
+
if add_timeout?(options[:read_timeout])
|
109
129
|
http.read_timeout = options[:read_timeout]
|
110
130
|
end
|
111
131
|
|
112
|
-
if
|
132
|
+
if add_timeout?(options[:open_timeout])
|
113
133
|
http.open_timeout = options[:open_timeout]
|
114
134
|
end
|
115
135
|
|
136
|
+
if add_timeout?(options[:write_timeout])
|
137
|
+
from_ruby_version('2.6.0', option: :write_timeout) do
|
138
|
+
http.write_timeout = options[:write_timeout]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if add_max_retries?(options[:max_retries])
|
143
|
+
from_ruby_version('2.5.0', option: :max_retries) do
|
144
|
+
http.max_retries = options[:max_retries]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
116
148
|
if options[:debug_output]
|
117
149
|
http.set_debug_output(options[:debug_output])
|
118
150
|
end
|
@@ -125,18 +157,14 @@ module HTTParty
|
|
125
157
|
#
|
126
158
|
# @see https://bugs.ruby-lang.org/issues/6617
|
127
159
|
if options[:local_host]
|
128
|
-
|
160
|
+
from_ruby_version('2.0.0', option: :local_host) do
|
129
161
|
http.local_host = options[:local_host]
|
130
|
-
else
|
131
|
-
Kernel.warn("Warning: option :local_host requires Ruby version 2.0 or later")
|
132
162
|
end
|
133
163
|
end
|
134
164
|
|
135
165
|
if options[:local_port]
|
136
|
-
|
166
|
+
from_ruby_version('2.0.0', option: :local_port) do
|
137
167
|
http.local_port = options[:local_port]
|
138
|
-
else
|
139
|
-
Kernel.warn("Warning: option :local_port requires Ruby version 2.0 or later")
|
140
168
|
end
|
141
169
|
end
|
142
170
|
|
@@ -145,6 +173,22 @@ module HTTParty
|
|
145
173
|
|
146
174
|
private
|
147
175
|
|
176
|
+
def from_ruby_version(ruby_version, option: nil, warn: true)
|
177
|
+
if RUBY_VERSION >= ruby_version
|
178
|
+
yield
|
179
|
+
elsif warn
|
180
|
+
Kernel.warn("Warning: option #{ option } requires Ruby version #{ ruby_version } or later")
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_timeout?(timeout)
|
185
|
+
timeout && (timeout.is_a?(Integer) || timeout.is_a?(Float))
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_max_retries?(max_retries)
|
189
|
+
max_retries && max_retries.is_a?(Integer) && max_retries >= 0
|
190
|
+
end
|
191
|
+
|
148
192
|
def clean_host(host)
|
149
193
|
strip_ipv6_brackets(host)
|
150
194
|
end
|
@@ -169,8 +213,7 @@ module HTTParty
|
|
169
213
|
http.cert_store = options[:cert_store]
|
170
214
|
else
|
171
215
|
# Use the default cert store by default, i.e. system ca certs
|
172
|
-
http.cert_store =
|
173
|
-
http.cert_store.set_default_paths
|
216
|
+
http.cert_store = self.class.default_cert_store
|
174
217
|
end
|
175
218
|
else
|
176
219
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
@@ -180,7 +223,7 @@ module HTTParty
|
|
180
223
|
# Note: options[:pem] must contain the content of a PEM file having the private key appended
|
181
224
|
if options[:pem]
|
182
225
|
http.cert = OpenSSL::X509::Certificate.new(options[:pem])
|
183
|
-
http.key = OpenSSL::PKey
|
226
|
+
http.key = OpenSSL::PKey.read(options[:pem], options[:pem_password])
|
184
227
|
http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
185
228
|
end
|
186
229
|
|
data/lib/httparty/cookie_hash.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class HTTParty::CookieHash < Hash #:nodoc:
|
2
|
-
CLIENT_COOKIES = %w(path expires domain path secure httponly)
|
4
|
+
CLIENT_COOKIES = %w(path expires domain path secure httponly samesite)
|
3
5
|
|
4
|
-
def add_cookies(
|
5
|
-
case
|
6
|
+
def add_cookies(data)
|
7
|
+
case data
|
6
8
|
when Hash
|
7
|
-
merge!(
|
9
|
+
merge!(data)
|
8
10
|
when String
|
9
|
-
|
10
|
-
|
11
|
-
self[
|
11
|
+
data.split('; ').each do |cookie|
|
12
|
+
key, value = cookie.split('=', 2)
|
13
|
+
self[key.to_sym] = value if key
|
12
14
|
end
|
13
15
|
else
|
14
16
|
raise "add_cookies only takes a Hash or a String"
|
@@ -16,6 +18,6 @@ class HTTParty::CookieHash < Hash #:nodoc:
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def to_cookie_string
|
19
|
-
|
21
|
+
select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join('; ')
|
20
22
|
end
|
21
23
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
# Decompresses the response body based on the Content-Encoding header.
|
5
|
+
#
|
6
|
+
# Net::HTTP automatically decompresses Content-Encoding values "gzip" and "deflate".
|
7
|
+
# This class will handle "br" (Brotli) and "compress" (LZW) if the requisite
|
8
|
+
# gems are installed. Otherwise, it returns nil if the body data cannot be
|
9
|
+
# decompressed.
|
10
|
+
#
|
11
|
+
# @abstract Read the HTTP Compression section for more information.
|
12
|
+
class Decompressor
|
13
|
+
|
14
|
+
# "gzip" and "deflate" are handled by Net::HTTP
|
15
|
+
# hence they do not need to be handled by HTTParty
|
16
|
+
SupportedEncodings = {
|
17
|
+
'none' => :none,
|
18
|
+
'identity' => :none,
|
19
|
+
'br' => :brotli,
|
20
|
+
'compress' => :lzw,
|
21
|
+
'zstd' => :zstd
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
# The response body of the request
|
25
|
+
# @return [String]
|
26
|
+
attr_reader :body
|
27
|
+
|
28
|
+
# The Content-Encoding algorithm used to encode the body
|
29
|
+
# @return [Symbol] e.g. :gzip
|
30
|
+
attr_reader :encoding
|
31
|
+
|
32
|
+
# @param [String] body - the response body of the request
|
33
|
+
# @param [Symbol] encoding - the Content-Encoding algorithm used to encode the body
|
34
|
+
def initialize(body, encoding)
|
35
|
+
@body = body
|
36
|
+
@encoding = encoding
|
37
|
+
end
|
38
|
+
|
39
|
+
# Perform decompression on the response body
|
40
|
+
# @return [String] the decompressed body
|
41
|
+
# @return [nil] when the response body is nil or cannot decompressed
|
42
|
+
def decompress
|
43
|
+
return nil if body.nil?
|
44
|
+
return body if encoding.nil? || encoding.strip.empty?
|
45
|
+
|
46
|
+
if supports_encoding?
|
47
|
+
decompress_supported_encoding
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def supports_encoding?
|
56
|
+
SupportedEncodings.keys.include?(encoding)
|
57
|
+
end
|
58
|
+
|
59
|
+
def decompress_supported_encoding
|
60
|
+
method = SupportedEncodings[encoding]
|
61
|
+
if respond_to?(method, true)
|
62
|
+
send(method)
|
63
|
+
else
|
64
|
+
raise NotImplementedError, "#{self.class.name} has not implemented a decompression method for #{encoding.inspect} encoding."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def none
|
69
|
+
body
|
70
|
+
end
|
71
|
+
|
72
|
+
def brotli
|
73
|
+
return nil unless defined?(::Brotli)
|
74
|
+
begin
|
75
|
+
::Brotli.inflate(body)
|
76
|
+
rescue StandardError
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def lzw
|
82
|
+
begin
|
83
|
+
if defined?(::LZWS::String)
|
84
|
+
::LZWS::String.decompress(body)
|
85
|
+
elsif defined?(::LZW::Simple)
|
86
|
+
::LZW::Simple.new.decompress(body)
|
87
|
+
end
|
88
|
+
rescue StandardError
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def zstd
|
94
|
+
return nil unless defined?(::Zstd)
|
95
|
+
begin
|
96
|
+
::Zstd.decompress(body)
|
97
|
+
rescue StandardError
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/httparty/exceptions.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
|
-
# @
|
4
|
+
# @abstract Exceptions raised by HTTParty inherit from Error
|
3
5
|
class Error < StandardError; end
|
4
6
|
|
5
7
|
# Exception raised when you attempt to set a non-existent format
|
@@ -20,6 +22,7 @@ module HTTParty
|
|
20
22
|
# @param [Net::HTTPResponse]
|
21
23
|
def initialize(response)
|
22
24
|
@response = response
|
25
|
+
super(response)
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
|
3
5
|
module HTTParty
|
@@ -24,32 +26,46 @@ module HTTParty
|
|
24
26
|
#
|
25
27
|
# @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
|
26
28
|
def self.normalize_param(key, value)
|
27
|
-
|
29
|
+
normalized_keys = normalize_keys(key, value)
|
30
|
+
|
31
|
+
normalized_keys.flatten.each_slice(2).inject(''.dup) do |string, (k, v)|
|
32
|
+
string << "#{ERB::Util.url_encode(k)}=#{ERB::Util.url_encode(v.to_s)}&"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.normalize_keys(key, value)
|
28
37
|
stack = []
|
38
|
+
normalized_keys = []
|
29
39
|
|
30
40
|
if value.respond_to?(:to_ary)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
41
|
+
if value.empty?
|
42
|
+
normalized_keys << ["#{key}[]", '']
|
43
|
+
else
|
44
|
+
normalized_keys = value.to_ary.flat_map do |element|
|
45
|
+
normalize_keys("#{key}[]", element)
|
46
|
+
end
|
47
|
+
end
|
36
48
|
elsif value.respond_to?(:to_hash)
|
37
49
|
stack << [key, value.to_hash]
|
38
50
|
else
|
39
|
-
|
51
|
+
normalized_keys << [key.to_s, value]
|
40
52
|
end
|
41
53
|
|
42
54
|
stack.each do |parent, hash|
|
43
|
-
hash.each do |
|
44
|
-
if
|
45
|
-
stack << ["#{parent}[#{
|
55
|
+
hash.each do |child_key, child_value|
|
56
|
+
if child_value.respond_to?(:to_hash)
|
57
|
+
stack << ["#{parent}[#{child_key}]", child_value.to_hash]
|
58
|
+
elsif child_value.respond_to?(:to_ary)
|
59
|
+
child_value.to_ary.each do |v|
|
60
|
+
normalized_keys << normalize_keys("#{parent}[#{child_key}][]", v).flatten
|
61
|
+
end
|
46
62
|
else
|
47
|
-
|
63
|
+
normalized_keys << normalize_keys("#{parent}[#{child_key}]", child_value).flatten
|
48
64
|
end
|
49
65
|
end
|
50
66
|
end
|
51
67
|
|
52
|
-
|
68
|
+
normalized_keys
|
53
69
|
end
|
54
70
|
end
|
55
71
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
class HeadersProcessor
|
5
|
+
attr_reader :headers, :options
|
6
|
+
|
7
|
+
def initialize(headers, options)
|
8
|
+
@headers = headers
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
return unless options[:headers]
|
14
|
+
|
15
|
+
options[:headers] = headers.merge(options[:headers]) if headers.any?
|
16
|
+
options[:headers] = Utils.stringify_keys(process_dynamic_headers)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def process_dynamic_headers
|
22
|
+
options[:headers].each_with_object({}) do |header, processed_headers|
|
23
|
+
key, value = header
|
24
|
+
processed_headers[key] = if value.respond_to?(:call)
|
25
|
+
value.arity == 0 ? value.call : value.call(options)
|
26
|
+
else
|
27
|
+
value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
module Logger
|
3
5
|
class ApacheFormatter #:nodoc:
|
4
6
|
TAG_NAME = HTTParty.name
|
5
7
|
|
6
|
-
attr_accessor :level, :logger
|
8
|
+
attr_accessor :level, :logger
|
7
9
|
|
8
10
|
def initialize(logger, level)
|
9
11
|
@logger = logger
|
@@ -11,11 +13,34 @@ module HTTParty
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def format(request, response)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
@request = request
|
17
|
+
@response = response
|
18
|
+
|
19
|
+
logger.public_send level, message
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :request, :response
|
25
|
+
|
26
|
+
def message
|
27
|
+
"[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_time
|
31
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
|
32
|
+
end
|
33
|
+
|
34
|
+
def http_method
|
35
|
+
request.http_method.name.split('::').last.upcase
|
36
|
+
end
|
37
|
+
|
38
|
+
def path
|
39
|
+
request.path.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def content_length
|
43
|
+
response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
|
19
44
|
end
|
20
45
|
end
|
21
46
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
module Logger
|
3
5
|
class CurlFormatter #:nodoc:
|
4
6
|
TAG_NAME = HTTParty.name
|
5
|
-
OUT = '>'
|
6
|
-
IN = '<'
|
7
|
+
OUT = '>'
|
8
|
+
IN = '<'
|
7
9
|
|
8
10
|
attr_accessor :level, :logger
|
9
11
|
|
@@ -20,7 +22,7 @@ module HTTParty
|
|
20
22
|
log_request
|
21
23
|
log_response
|
22
24
|
|
23
|
-
logger.
|
25
|
+
logger.public_send level, messages.join("\n")
|
24
26
|
end
|
25
27
|
|
26
28
|
private
|
@@ -44,7 +46,7 @@ module HTTParty
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def log_url
|
47
|
-
http_method = request.http_method.name.split(
|
49
|
+
http_method = request.http_method.name.split('::').last.upcase
|
48
50
|
uri = if request.options[:base_uri]
|
49
51
|
request.options[:base_uri] + request.path.path
|
50
52
|
else
|
@@ -80,11 +82,11 @@ module HTTParty
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def log(direction, line = '')
|
83
|
-
messages << "[#{TAG_NAME}] [#{
|
85
|
+
messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}"
|
84
86
|
end
|
85
87
|
|
86
|
-
def
|
87
|
-
|
88
|
+
def current_time
|
89
|
+
Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|