httparty 0.16.2 → 0.20.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.

Potentially problematic release.


This version of httparty might be problematic. Click here for more details.

Files changed (92) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/workflows/ci.yml +23 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop_todo.yml +1 -1
  6. data/Changelog.md +72 -0
  7. data/Gemfile +5 -0
  8. data/README.md +5 -5
  9. data/docs/README.md +70 -5
  10. data/examples/README.md +28 -11
  11. data/examples/aaws.rb +6 -2
  12. data/examples/body_stream.rb +14 -0
  13. data/examples/idn.rb +10 -0
  14. data/examples/microsoft_graph.rb +52 -0
  15. data/examples/multipart.rb +22 -0
  16. data/examples/peer_cert.rb +9 -0
  17. data/examples/stream_download.rb +8 -2
  18. data/httparty.gemspec +3 -3
  19. data/lib/httparty/connection_adapter.rb +59 -16
  20. data/lib/httparty/cookie_hash.rb +10 -8
  21. data/lib/httparty/decompressor.rb +92 -0
  22. data/lib/httparty/exceptions.rb +3 -1
  23. data/lib/httparty/hash_conversions.rb +10 -4
  24. data/lib/httparty/headers_processor.rb +32 -0
  25. data/lib/httparty/logger/apache_formatter.rb +31 -6
  26. data/lib/httparty/logger/curl_formatter.rb +9 -7
  27. data/lib/httparty/logger/logger.rb +5 -1
  28. data/lib/httparty/logger/logstash_formatter.rb +61 -0
  29. data/lib/httparty/module_inheritable_attributes.rb +6 -4
  30. data/lib/httparty/net_digest_auth.rb +15 -15
  31. data/lib/httparty/parser.rb +9 -5
  32. data/lib/httparty/request/body.rb +46 -27
  33. data/lib/httparty/request/multipart_boundary.rb +2 -0
  34. data/lib/httparty/request.rb +75 -96
  35. data/lib/httparty/response/headers.rb +4 -2
  36. data/lib/httparty/response.rb +51 -8
  37. data/lib/httparty/response_fragment.rb +21 -0
  38. data/lib/httparty/text_encoder.rb +72 -0
  39. data/lib/httparty/utils.rb +13 -0
  40. data/lib/httparty/version.rb +3 -1
  41. data/lib/httparty.rb +70 -24
  42. data/website/css/common.css +1 -1
  43. metadata +33 -104
  44. data/.travis.yml +0 -10
  45. data/features/basic_authentication.feature +0 -20
  46. data/features/command_line.feature +0 -95
  47. data/features/deals_with_http_error_codes.feature +0 -26
  48. data/features/digest_authentication.feature +0 -30
  49. data/features/handles_compressed_responses.feature +0 -27
  50. data/features/handles_multiple_formats.feature +0 -57
  51. data/features/steps/env.rb +0 -27
  52. data/features/steps/httparty_response_steps.rb +0 -56
  53. data/features/steps/httparty_steps.rb +0 -43
  54. data/features/steps/mongrel_helper.rb +0 -127
  55. data/features/steps/remote_service_steps.rb +0 -92
  56. data/features/supports_read_timeout_option.feature +0 -13
  57. data/features/supports_redirection.feature +0 -22
  58. data/features/supports_timeout_option.feature +0 -13
  59. data/spec/fixtures/delicious.xml +0 -23
  60. data/spec/fixtures/empty.xml +0 -0
  61. data/spec/fixtures/google.html +0 -3
  62. data/spec/fixtures/ssl/generate.sh +0 -29
  63. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  64. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  65. data/spec/fixtures/ssl/generated/ca.key +0 -15
  66. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  67. data/spec/fixtures/ssl/generated/server.crt +0 -13
  68. data/spec/fixtures/ssl/generated/server.key +0 -15
  69. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  70. data/spec/fixtures/tiny.gif +0 -0
  71. data/spec/fixtures/twitter.csv +0 -2
  72. data/spec/fixtures/twitter.json +0 -1
  73. data/spec/fixtures/twitter.xml +0 -403
  74. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  75. data/spec/httparty/connection_adapter_spec.rb +0 -498
  76. data/spec/httparty/cookie_hash_spec.rb +0 -100
  77. data/spec/httparty/exception_spec.rb +0 -45
  78. data/spec/httparty/hash_conversions_spec.rb +0 -56
  79. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  80. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  81. data/spec/httparty/logger/logger_spec.rb +0 -38
  82. data/spec/httparty/net_digest_auth_spec.rb +0 -270
  83. data/spec/httparty/parser_spec.rb +0 -190
  84. data/spec/httparty/request/body_spec.rb +0 -60
  85. data/spec/httparty/request_spec.rb +0 -1312
  86. data/spec/httparty/response_spec.rb +0 -347
  87. data/spec/httparty/ssl_spec.rb +0 -74
  88. data/spec/httparty_spec.rb +0 -896
  89. data/spec/spec_helper.rb +0 -51
  90. data/spec/support/ssl_test_helper.rb +0 -47
  91. data/spec/support/ssl_test_server.rb +0 -80
  92. data/spec/support/stub_response.rb +0 -49
@@ -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
- # HTTPParty::ConnectionAdapter will make it easier. Just override
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(host, port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
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 options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
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 options[:read_timeout] && (options[:read_timeout].is_a?(Integer) || options[:read_timeout].is_a?(Float))
128
+ if add_timeout?(options[:read_timeout])
109
129
  http.read_timeout = options[:read_timeout]
110
130
  end
111
131
 
112
- if options[:open_timeout] && (options[:open_timeout].is_a?(Integer) || options[:open_timeout].is_a?(Float))
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
- if RUBY_VERSION >= "2.0.0"
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
- if RUBY_VERSION >= "2.0.0"
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 = OpenSSL::X509::Store.new
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::RSA.new(options[:pem], options[:pem_password])
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
 
@@ -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(value)
5
- case value
6
+ def add_cookies(data)
7
+ case data
6
8
  when Hash
7
- merge!(value)
9
+ merge!(data)
8
10
  when String
9
- value.split('; ').each do |cookie|
10
- array = cookie.split('=', 2)
11
- self[array[0].to_sym] = array[1]
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
- select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
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
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTParty
2
- # @abstact Exceptions raised by HTTParty inherit from Error
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  module HTTParty
@@ -26,8 +28,8 @@ module HTTParty
26
28
  def self.normalize_param(key, value)
27
29
  normalized_keys = normalize_keys(key, value)
28
30
 
29
- normalized_keys.flatten.each_slice(2).inject('') do |string, (k, v)|
30
- string + "#{k}=#{ERB::Util.url_encode(v.to_s)}&"
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)}&"
31
33
  end
32
34
  end
33
35
 
@@ -39,7 +41,9 @@ module HTTParty
39
41
  if value.empty?
40
42
  normalized_keys << ["#{key}[]", '']
41
43
  else
42
- normalized_keys = value.to_ary.flat_map { |element| normalize_keys("#{key}[]", element) }
44
+ normalized_keys = value.to_ary.flat_map do |element|
45
+ normalize_keys("#{key}[]", element)
46
+ end
43
47
  end
44
48
  elsif value.respond_to?(:to_hash)
45
49
  stack << [key, value.to_hash]
@@ -52,7 +56,9 @@ module HTTParty
52
56
  if child_value.respond_to?(:to_hash)
53
57
  stack << ["#{parent}[#{child_key}]", child_value.to_hash]
54
58
  elsif child_value.respond_to?(:to_ary)
55
- child_value.to_ary.each { |v| normalized_keys << normalize_keys("#{parent}[#{child_key}][]", v).flatten }
59
+ child_value.to_ary.each do |v|
60
+ normalized_keys << normalize_keys("#{parent}[#{child_key}][]", v).flatten
61
+ end
56
62
  else
57
63
  normalized_keys << normalize_keys("#{parent}[#{child_key}]", child_value).flatten
58
64
  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, :current_time
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
- current_time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
15
- http_method = request.http_method.name.split("::").last.upcase
16
- path = request.path.to_s
17
- content_length = response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
18
- @logger.send @level, "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
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 = '>'.freeze
6
- IN = '<'.freeze
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.send level, messages.join("\n")
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("::").last.upcase
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}] [#{time}] #{direction} #{line}"
85
+ messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}"
84
86
  end
85
87
 
86
- def time
87
- @time ||= Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
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
- duplicate[key] = if value.is_a?(Hash)
13
- hash_deep_dup(value)
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)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/md5'
2
4
  require 'net/http'
3
5
 
@@ -14,11 +16,11 @@ module Net
14
16
 
15
17
  authenticator.authorization_header.each do |v|
16
18
  add_field('Authorization', v)
17
- end
19
+ end
18
20
 
19
21
  authenticator.cookie_header.each do |v|
20
22
  add_field('Cookie', v)
21
- end
23
+ end
22
24
  end
23
25
 
24
26
  class DigestAuthenticator
@@ -44,12 +46,9 @@ module Net
44
46
  header << %(algorithm="#{@response['algorithm']}") if algorithm_present?
45
47
 
46
48
  if qop_present?
47
- fields = [
48
- %(cnonce="#{@cnonce}"),
49
- %(qop="#{@response['qop']}"),
50
- "nc=00000001"
51
- ]
52
- fields.each { |field| header << field }
49
+ header << %(cnonce="#{@cnonce}")
50
+ header << %(qop="#{@response['qop']}")
51
+ header << 'nc=00000001'
53
52
  end
54
53
 
55
54
  header << %(opaque="#{@response['opaque']}") if opaque_present?
@@ -64,7 +63,8 @@ module Net
64
63
 
65
64
  def parse(response_header)
66
65
  header = response_header['www-authenticate']
67
- .gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
66
+
67
+ header = header.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
68
68
 
69
69
  header =~ /Digest (.*)/
70
70
  params = {}
@@ -97,13 +97,13 @@ module Net
97
97
  end
98
98
 
99
99
  def random
100
- format "%x", (Time.now.to_i + rand(65535))
100
+ format '%x', (Time.now.to_i + rand(65535))
101
101
  end
102
102
 
103
103
  def request_digest
104
104
  a = [md5(a1), @response['nonce'], md5(a2)]
105
- a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present?
106
- md5(a.join(":"))
105
+ a.insert(2, '00000001', @cnonce, @response['qop']) if qop_present?
106
+ md5(a.join(':'))
107
107
  end
108
108
 
109
109
  def md5(str)
@@ -113,11 +113,11 @@ module Net
113
113
  def algorithm_present?
114
114
  @response.key?('algorithm') && !@response['algorithm'].empty?
115
115
  end
116
-
116
+
117
117
  def use_md5_sess?
118
118
  algorithm_present? && @response['algorithm'] == 'MD5-sess'
119
119
  end
120
-
120
+
121
121
  def a1
122
122
  a1_user_realm_pwd = [@username, @response['realm'], @password].join(':')
123
123
  if use_md5_sess?
@@ -128,7 +128,7 @@ module Net
128
128
  end
129
129
 
130
130
  def a2
131
- [@method, @path].join(":")
131
+ [@method, @path].join(':')
132
132
  end
133
133
  end
134
134
  end