httparty 0.15.4 → 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of httparty might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.editorconfig +18 -0
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +2 -0
- data/.rubocop_todo.yml +1 -1
- data/Changelog.md +105 -0
- data/Gemfile +6 -1
- data/README.md +6 -6
- data/docs/README.md +100 -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 +3 -3
- data/lib/httparty/connection_adapter.rb +59 -16
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +92 -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 +22 -16
- data/lib/httparty/request/body.rb +98 -0
- data/lib/httparty/request/multipart_boundary.rb +13 -0
- data/lib/httparty/request.rb +82 -95
- data/lib/httparty/response/headers.rb +4 -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 -24
- data/website/css/common.css +1 -1
- metadata +35 -100
- 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/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 -185
- data/spec/httparty/request_spec.rb +0 -1251
- 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
@@ -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,13 +9,14 @@ 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('mime-types', "~> 3.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!"
|
@@ -24,7 +25,6 @@ Gem::Specification.new do |s|
|
|
24
25
|
test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
26
|
|
26
27
|
s.files = all_files - test_files
|
27
|
-
s.test_files = test_files
|
28
28
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
29
29
|
s.require_paths = ["lib"]
|
30
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,92 @@
|
|
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
|
+
}.freeze
|
22
|
+
|
23
|
+
# The response body of the request
|
24
|
+
# @return [String]
|
25
|
+
attr_reader :body
|
26
|
+
|
27
|
+
# The Content-Encoding algorithm used to encode the body
|
28
|
+
# @return [Symbol] e.g. :gzip
|
29
|
+
attr_reader :encoding
|
30
|
+
|
31
|
+
# @param [String] body - the response body of the request
|
32
|
+
# @param [Symbol] encoding - the Content-Encoding algorithm used to encode the body
|
33
|
+
def initialize(body, encoding)
|
34
|
+
@body = body
|
35
|
+
@encoding = encoding
|
36
|
+
end
|
37
|
+
|
38
|
+
# Perform decompression on the response body
|
39
|
+
# @return [String] the decompressed body
|
40
|
+
# @return [nil] when the response body is nil or cannot decompressed
|
41
|
+
def decompress
|
42
|
+
return nil if body.nil?
|
43
|
+
return body if encoding.nil? || encoding.strip.empty?
|
44
|
+
|
45
|
+
if supports_encoding?
|
46
|
+
decompress_supported_encoding
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def supports_encoding?
|
55
|
+
SupportedEncodings.keys.include?(encoding)
|
56
|
+
end
|
57
|
+
|
58
|
+
def decompress_supported_encoding
|
59
|
+
method = SupportedEncodings[encoding]
|
60
|
+
if respond_to?(method, true)
|
61
|
+
send(method)
|
62
|
+
else
|
63
|
+
raise NotImplementedError, "#{self.class.name} has not implemented a decompression method for #{encoding.inspect} encoding."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def none
|
68
|
+
body
|
69
|
+
end
|
70
|
+
|
71
|
+
def brotli
|
72
|
+
return nil unless defined?(::Brotli)
|
73
|
+
begin
|
74
|
+
::Brotli.inflate(body)
|
75
|
+
rescue StandardError
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def lzw
|
81
|
+
begin
|
82
|
+
if defined?(::LZWS::String)
|
83
|
+
::LZWS::String.decompress(body)
|
84
|
+
elsif defined?(::LZW::Simple)
|
85
|
+
::LZW::Simple.new.decompress(body)
|
86
|
+
end
|
87
|
+
rescue StandardError
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
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
|
@@ -1,12 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'httparty/logger/apache_formatter'
|
2
4
|
require 'httparty/logger/curl_formatter'
|
5
|
+
require 'httparty/logger/logstash_formatter'
|
3
6
|
|
4
7
|
module HTTParty
|
5
8
|
module Logger
|
6
9
|
def self.formatters
|
7
10
|
@formatters ||= {
|
8
11
|
:curl => Logger::CurlFormatter,
|
9
|
-
:apache => Logger::ApacheFormatter
|
12
|
+
:apache => Logger::ApacheFormatter,
|
13
|
+
:logstash => Logger::LogstashFormatter,
|
10
14
|
}
|
11
15
|
end
|
12
16
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
module Logger
|
5
|
+
class LogstashFormatter #:nodoc:
|
6
|
+
TAG_NAME = HTTParty.name
|
7
|
+
|
8
|
+
attr_accessor :level, :logger
|
9
|
+
|
10
|
+
def initialize(logger, level)
|
11
|
+
@logger = logger
|
12
|
+
@level = level.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def format(request, response)
|
16
|
+
@request = request
|
17
|
+
@response = response
|
18
|
+
|
19
|
+
logger.public_send level, logstash_message
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :request, :response
|
25
|
+
|
26
|
+
def logstash_message
|
27
|
+
{
|
28
|
+
'@timestamp' => current_time,
|
29
|
+
'@version' => 1,
|
30
|
+
'content_length' => content_length || '-',
|
31
|
+
'http_method' => http_method,
|
32
|
+
'message' => message,
|
33
|
+
'path' => path,
|
34
|
+
'response_code' => response.code,
|
35
|
+
'severity' => level,
|
36
|
+
'tags' => [TAG_NAME],
|
37
|
+
}.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
def message
|
41
|
+
"[#{TAG_NAME}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
|
42
|
+
end
|
43
|
+
|
44
|
+
def current_time
|
45
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
|
46
|
+
end
|
47
|
+
|
48
|
+
def http_method
|
49
|
+
@http_method ||= request.http_method.name.split('::').last.upcase
|
50
|
+
end
|
51
|
+
|
52
|
+
def path
|
53
|
+
@path ||= request.path.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def content_length
|
57
|
+
@content_length ||= response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
module ModuleInheritableAttributes #:nodoc:
|
3
5
|
def self.included(base)
|
@@ -9,12 +11,12 @@ module HTTParty
|
|
9
11
|
duplicate = hash.dup
|
10
12
|
|
11
13
|
duplicate.each_pair do |key, value|
|
12
|
-
|
13
|
-
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
duplicate[key] = hash_deep_dup(value)
|
14
16
|
elsif value.is_a?(Proc)
|
15
17
|
duplicate[key] = value.dup
|
16
18
|
else
|
17
|
-
value
|
19
|
+
duplicate[key] = value
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -36,7 +38,7 @@ module HTTParty
|
|
36
38
|
def inherited(subclass)
|
37
39
|
super
|
38
40
|
@mattr_inheritable_attrs.each do |inheritable_attribute|
|
39
|
-
ivar = "@#{inheritable_attribute}"
|
41
|
+
ivar = :"@#{inheritable_attribute}"
|
40
42
|
subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone)
|
41
43
|
|
42
44
|
if instance_variable_get(ivar).respond_to?(:merge)
|