httparty 0.14.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/workflows/ci.yml +26 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop_todo.yml +1 -1
  6. data/{History → Changelog.md} +222 -63
  7. data/Gemfile +8 -1
  8. data/Guardfile +3 -2
  9. data/README.md +8 -8
  10. data/bin/httparty +3 -1
  11. data/docs/README.md +128 -37
  12. data/examples/README.md +34 -12
  13. data/examples/aaws.rb +7 -3
  14. data/examples/body_stream.rb +14 -0
  15. data/examples/crack.rb +1 -1
  16. data/examples/custom_parsers.rb +5 -1
  17. data/examples/delicious.rb +4 -4
  18. data/examples/headers_and_user_agents.rb +7 -3
  19. data/examples/idn.rb +10 -0
  20. data/examples/logging.rb +4 -4
  21. data/examples/microsoft_graph.rb +52 -0
  22. data/examples/multipart.rb +22 -0
  23. data/examples/peer_cert.rb +9 -0
  24. data/examples/stackexchange.rb +1 -1
  25. data/examples/stream_download.rb +26 -0
  26. data/examples/tripit_sign_in.rb +1 -1
  27. data/examples/twitter.rb +2 -2
  28. data/examples/whoismyrep.rb +1 -1
  29. data/httparty.gemspec +7 -4
  30. data/lib/httparty/connection_adapter.rb +73 -16
  31. data/lib/httparty/cookie_hash.rb +10 -8
  32. data/lib/httparty/decompressor.rb +102 -0
  33. data/lib/httparty/exceptions.rb +4 -1
  34. data/lib/httparty/hash_conversions.rb +30 -12
  35. data/lib/httparty/headers_processor.rb +32 -0
  36. data/lib/httparty/logger/apache_formatter.rb +31 -6
  37. data/lib/httparty/logger/curl_formatter.rb +9 -7
  38. data/lib/httparty/logger/logger.rb +5 -1
  39. data/lib/httparty/logger/logstash_formatter.rb +61 -0
  40. data/lib/httparty/module_inheritable_attributes.rb +6 -4
  41. data/lib/httparty/net_digest_auth.rb +19 -19
  42. data/lib/httparty/parser.rb +25 -14
  43. data/lib/httparty/request/body.rb +105 -0
  44. data/lib/httparty/request/multipart_boundary.rb +13 -0
  45. data/lib/httparty/request.rb +141 -110
  46. data/lib/httparty/response/headers.rb +23 -19
  47. data/lib/httparty/response.rb +81 -22
  48. data/lib/httparty/response_fragment.rb +21 -0
  49. data/lib/httparty/text_encoder.rb +72 -0
  50. data/lib/httparty/utils.rb +13 -0
  51. data/lib/httparty/version.rb +3 -1
  52. data/lib/httparty.rb +79 -31
  53. data/website/css/common.css +1 -1
  54. metadata +39 -106
  55. data/.simplecov +0 -1
  56. data/.travis.yml +0 -9
  57. data/features/basic_authentication.feature +0 -20
  58. data/features/command_line.feature +0 -95
  59. data/features/deals_with_http_error_codes.feature +0 -26
  60. data/features/digest_authentication.feature +0 -30
  61. data/features/handles_compressed_responses.feature +0 -27
  62. data/features/handles_multiple_formats.feature +0 -57
  63. data/features/steps/env.rb +0 -27
  64. data/features/steps/httparty_response_steps.rb +0 -56
  65. data/features/steps/httparty_steps.rb +0 -43
  66. data/features/steps/mongrel_helper.rb +0 -127
  67. data/features/steps/remote_service_steps.rb +0 -92
  68. data/features/supports_read_timeout_option.feature +0 -13
  69. data/features/supports_redirection.feature +0 -22
  70. data/features/supports_timeout_option.feature +0 -13
  71. data/spec/fixtures/delicious.xml +0 -23
  72. data/spec/fixtures/empty.xml +0 -0
  73. data/spec/fixtures/google.html +0 -3
  74. data/spec/fixtures/ssl/generate.sh +0 -29
  75. data/spec/fixtures/ssl/generated/1fe462c2.0 +0 -16
  76. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  77. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  78. data/spec/fixtures/ssl/generated/ca.key +0 -15
  79. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  80. data/spec/fixtures/ssl/generated/server.crt +0 -13
  81. data/spec/fixtures/ssl/generated/server.key +0 -15
  82. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  83. data/spec/fixtures/twitter.csv +0 -2
  84. data/spec/fixtures/twitter.json +0 -1
  85. data/spec/fixtures/twitter.xml +0 -403
  86. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  87. data/spec/httparty/connection_adapter_spec.rb +0 -495
  88. data/spec/httparty/cookie_hash_spec.rb +0 -100
  89. data/spec/httparty/exception_spec.rb +0 -45
  90. data/spec/httparty/hash_conversions_spec.rb +0 -49
  91. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  92. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  93. data/spec/httparty/logger/logger_spec.rb +0 -38
  94. data/spec/httparty/net_digest_auth_spec.rb +0 -240
  95. data/spec/httparty/parser_spec.rb +0 -173
  96. data/spec/httparty/request_spec.rb +0 -1183
  97. data/spec/httparty/response_spec.rb +0 -291
  98. data/spec/httparty/ssl_spec.rb +0 -74
  99. data/spec/httparty_spec.rb +0 -872
  100. data/spec/spec_helper.rb +0 -59
  101. data/spec/support/ssl_test_helper.rb +0 -47
  102. data/spec/support/ssl_test_server.rb +0 -80
  103. data/spec/support/stub_response.rb +0 -49
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+
1
5
  module HTTParty
2
6
  class Request #:nodoc:
3
7
  SupportedHTTPMethods = [
@@ -11,6 +15,8 @@ module HTTParty
11
15
  Net::HTTP::Move,
12
16
  Net::HTTP::Copy,
13
17
  Net::HTTP::Mkcol,
18
+ Net::HTTP::Lock,
19
+ Net::HTTP::Unlock,
14
20
  ]
15
21
 
16
22
  SupportedURISchemes = ['http', 'https', 'webcal', nil]
@@ -27,10 +33,35 @@ module HTTParty
27
33
  end.flatten.join('&')
28
34
  end
29
35
 
36
+ JSON_API_QUERY_STRING_NORMALIZER = proc do |query|
37
+ Array(query).sort_by { |a| a[0].to_s }.map do |key, value|
38
+ if value.nil?
39
+ key.to_s
40
+ elsif value.respond_to?(:to_ary)
41
+ values = value.to_ary.map{|v| ERB::Util.url_encode(v.to_s)}
42
+ "#{key}=#{values.join(',')}"
43
+ else
44
+ HashConversions.to_params(key => value)
45
+ end
46
+ end.flatten.join('&')
47
+ end
48
+
49
+ def self._load(data)
50
+ http_method, path, options, last_response, last_uri, raw_request = Marshal.load(data)
51
+ instance = new(http_method, path, options)
52
+ instance.last_response = last_response
53
+ instance.last_uri = last_uri
54
+ instance.instance_variable_set("@raw_request", raw_request)
55
+ instance
56
+ end
57
+
30
58
  attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
31
59
  attr_reader :path
32
60
 
33
61
  def initialize(http_method, path, o = {})
62
+ @changed_hosts = false
63
+ @credentials_sent = false
64
+
34
65
  self.http_method = http_method
35
66
  self.options = {
36
67
  limit: o.delete(:no_follow) ? 1 : 5,
@@ -51,7 +82,7 @@ module HTTParty
51
82
  @path = if uri.is_a?(uri_adapter)
52
83
  uri
53
84
  elsif String.try_convert(uri)
54
- uri_adapter.parse uri
85
+ uri_adapter.parse(uri).normalize
55
86
  else
56
87
  raise ArgumentError,
57
88
  "bad argument (expected #{uri_adapter} object or URI string)"
@@ -67,14 +98,20 @@ module HTTParty
67
98
  end
68
99
 
69
100
  def uri
70
- if redirect && path.relative? && path.path[0] != "/"
71
- last_uri_host = @last_uri.path.gsub(/[^\/]+$/, "")
101
+ if redirect && path.relative? && path.path[0] != '/'
102
+ last_uri_host = @last_uri.path.gsub(/[^\/]+$/, '')
72
103
 
73
- path.path = "/#{path.path}" if last_uri_host[-1] != "/"
74
- path.path = last_uri_host + path.path
104
+ path.path = "/#{path.path}" if last_uri_host[-1] != '/'
105
+ path.path = "#{last_uri_host}#{path.path}"
75
106
  end
76
107
 
77
- new_uri = path.relative? ? options[:uri_adapter].parse("#{base_uri}#{path}") : path.clone
108
+ if path.relative? && path.host
109
+ new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}").normalize
110
+ elsif path.relative?
111
+ new_uri = options[:uri_adapter].parse("#{base_uri}#{path}").normalize
112
+ else
113
+ new_uri = path.clone
114
+ end
78
115
 
79
116
  # avoid double query string on redirects [#12]
80
117
  unless redirect
@@ -91,7 +128,7 @@ module HTTParty
91
128
  def base_uri
92
129
  if redirect
93
130
  base_uri = "#{@last_uri.scheme}://#{@last_uri.host}"
94
- base_uri += ":#{@last_uri.port}" if @last_uri.port != 80
131
+ base_uri = "#{base_uri}:#{@last_uri.port}" if @last_uri.port != 80
95
132
  base_uri
96
133
  else
97
134
  options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri])
@@ -114,39 +151,52 @@ module HTTParty
114
151
  validate
115
152
  setup_raw_request
116
153
  chunked_body = nil
154
+ current_http = http
117
155
 
118
- self.last_response = http.request(@raw_request) do |http_response|
156
+ self.last_response = current_http.request(@raw_request) do |http_response|
119
157
  if block
120
158
  chunks = []
121
159
 
122
160
  http_response.read_body do |fragment|
123
- chunks << fragment unless options[:stream_body]
124
- block.call(fragment)
161
+ encoded_fragment = encode_text(fragment, http_response['content-type'])
162
+ chunks << encoded_fragment if !options[:stream_body]
163
+ block.call ResponseFragment.new(encoded_fragment, http_response, current_http)
125
164
  end
126
165
 
127
166
  chunked_body = chunks.join
128
167
  end
129
168
  end
130
169
 
131
- handle_deflation unless http_method == Net::HTTP::Head
132
170
  handle_host_redirection if response_redirects?
133
- handle_response(chunked_body, &block)
171
+ result = handle_unauthorized
172
+ result ||= handle_response(chunked_body, &block)
173
+ result
174
+ end
175
+
176
+ def handle_unauthorized(&block)
177
+ return unless digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
178
+ return if @credentials_sent
179
+ @credentials_sent = true
180
+ perform(&block)
134
181
  end
135
182
 
136
183
  def raw_body
137
184
  @raw_request.body
138
185
  end
139
186
 
187
+ def _dump(_level)
188
+ opts = options.dup
189
+ opts.delete(:logger)
190
+ opts.delete(:parser) if opts[:parser] && opts[:parser].is_a?(Proc)
191
+ Marshal.dump([http_method, path, opts, last_response, @last_uri, @raw_request])
192
+ end
193
+
140
194
  private
141
195
 
142
196
  def http
143
197
  connection_adapter.call(uri, options)
144
198
  end
145
199
 
146
- def body
147
- options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
148
- end
149
-
150
200
  def credentials
151
201
  (options[:basic_auth] || options[:digest_auth]).to_hash
152
202
  end
@@ -172,106 +222,78 @@ module HTTParty
172
222
  end
173
223
 
174
224
  def setup_raw_request
175
- @raw_request = http_method.new(request_uri(uri))
176
- @raw_request.body = body if body
177
- @raw_request.body_stream = options[:body_stream] if options[:body_stream]
178
- @raw_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
179
- @raw_request.basic_auth(username, password) if options[:basic_auth] && send_authorization_header?
180
- setup_digest_auth if options[:digest_auth]
181
- end
182
-
183
- def setup_digest_auth
184
- auth_request = http_method.new(uri.request_uri)
185
- auth_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
186
- res = http.request(auth_request)
187
-
188
- if !res['www-authenticate'].nil? && res['www-authenticate'].length > 0
189
- @raw_request.digest_auth(username, password, res)
190
- end
191
- end
192
-
193
- def query_string(uri)
194
- query_string_parts = []
195
- query_string_parts << uri.query unless uri.query.nil?
196
-
197
- if options[:query].respond_to?(:to_hash)
198
- query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash))
225
+ if options[:headers].respond_to?(:to_hash)
226
+ headers_hash = options[:headers].to_hash
199
227
  else
200
- query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
201
- query_string_parts << options[:query] unless options[:query].nil?
228
+ headers_hash = nil
202
229
  end
203
230
 
204
- query_string_parts.reject!(&:empty?) unless query_string_parts == [""]
205
- query_string_parts.size > 0 ? query_string_parts.join('&') : nil
206
- end
231
+ @raw_request = http_method.new(request_uri(uri), headers_hash)
232
+ @raw_request.body_stream = options[:body_stream] if options[:body_stream]
207
233
 
208
- def get_charset
209
- content_type = last_response["content-type"]
210
- if content_type.nil?
211
- return nil
212
- end
234
+ if options[:body]
235
+ body = Body.new(
236
+ options[:body],
237
+ query_string_normalizer: query_string_normalizer,
238
+ force_multipart: options[:multipart]
239
+ )
213
240
 
214
- if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
215
- return $1
241
+ if body.multipart?
242
+ content_type = "multipart/form-data; boundary=#{body.boundary}"
243
+ @raw_request['Content-Type'] = content_type
244
+ end
245
+ @raw_request.body = body.call
216
246
  end
217
247
 
218
- if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
219
- return $1.gsub(/\\(.)/, '\1')
220
- end
248
+ @raw_request.instance_variable_set(:@decode_content, decompress_content?)
221
249
 
222
- nil
250
+ if options[:basic_auth] && send_authorization_header?
251
+ @raw_request.basic_auth(username, password)
252
+ @credentials_sent = true
253
+ end
254
+ setup_digest_auth if digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
223
255
  end
224
256
 
225
- def encode_with_ruby_encoding(body, charset)
226
- encoding = Encoding.find(charset)
227
- body.force_encoding(encoding)
228
- rescue
229
- body
257
+ def digest_auth?
258
+ !!options[:digest_auth]
230
259
  end
231
260
 
232
- def assume_utf16_is_big_endian
233
- options[:assume_utf16_is_big_endian]
261
+ def decompress_content?
262
+ !options[:skip_decompression]
234
263
  end
235
264
 
236
- def encode_utf_16(body)
237
- if body.bytesize >= 2
238
- if body.getbyte(0) == 0xFF && body.getbyte(1) == 0xFE
239
- return body.force_encoding("UTF-16LE")
240
- elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
241
- return body.force_encoding("UTF-16BE")
242
- end
243
- end
265
+ def response_unauthorized?
266
+ !!last_response && last_response.code == '401'
267
+ end
244
268
 
245
- if assume_utf16_is_big_endian
246
- body.force_encoding("UTF-16BE")
247
- else
248
- body.force_encoding("UTF-16LE")
249
- end
269
+ def response_has_digest_auth_challenge?
270
+ !last_response['www-authenticate'].nil? && last_response['www-authenticate'].length > 0
250
271
  end
251
272
 
252
- def _encode_body(body)
253
- charset = get_charset
273
+ def setup_digest_auth
274
+ @raw_request.digest_auth(username, password, last_response)
275
+ end
254
276
 
255
- if charset.nil?
256
- return body
257
- end
277
+ def query_string(uri)
278
+ query_string_parts = []
279
+ query_string_parts << uri.query unless uri.query.nil?
258
280
 
259
- if "utf-16".casecmp(charset) == 0
260
- encode_utf_16(body)
281
+ if options[:query].respond_to?(:to_hash)
282
+ query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash))
261
283
  else
262
- encode_with_ruby_encoding(body, charset)
284
+ query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
285
+ query_string_parts << options[:query] unless options[:query].nil?
263
286
  end
287
+
288
+ query_string_parts.reject!(&:empty?) unless query_string_parts == ['']
289
+ query_string_parts.size > 0 ? query_string_parts.join('&') : nil
264
290
  end
265
291
 
266
- def encode_body(body)
267
- if "".respond_to?(:encoding)
268
- _encode_body(body)
269
- else
270
- body
271
- end
292
+ def assume_utf16_is_big_endian
293
+ options[:assume_utf16_is_big_endian]
272
294
  end
273
295
 
274
- def handle_response(body, &block)
296
+ def handle_response(raw_body, &block)
275
297
  if response_redirects?
276
298
  options[:limit] -= 1
277
299
  if options[:logger]
@@ -292,31 +314,26 @@ module HTTParty
292
314
  capture_cookies(last_response)
293
315
  perform(&block)
294
316
  else
295
- body ||= last_response.body
296
- body = encode_body(body)
297
- Response.new(self, last_response, lambda { parse_response(body) }, body: body)
298
- end
299
- end
317
+ raw_body ||= last_response.body
318
+
319
+ body = decompress(raw_body, last_response['content-encoding']) unless raw_body.nil?
320
+
321
+ unless body.nil?
322
+ body = encode_text(body, last_response['content-type'])
300
323
 
301
- # Inspired by Ruby 1.9
302
- def handle_deflation
303
- return if response_redirects?
304
- return if last_response.body.nil?
324
+ if decompress_content?
325
+ last_response.delete('content-encoding')
326
+ raw_body = body
327
+ end
328
+ end
305
329
 
306
- case last_response["content-encoding"]
307
- when "gzip", "x-gzip"
308
- body_io = StringIO.new(last_response.body)
309
- last_response.body.replace Zlib::GzipReader.new(body_io).read
310
- last_response.delete('content-encoding')
311
- when "deflate"
312
- last_response.body.replace Zlib::Inflate.inflate(last_response.body)
313
- last_response.delete('content-encoding')
330
+ Response.new(self, last_response, lambda { parse_response(body) }, body: raw_body)
314
331
  end
315
332
  end
316
333
 
317
334
  def handle_host_redirection
318
335
  check_duplicate_location_header
319
- redirect_path = options[:uri_adapter].parse last_response['location']
336
+ redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
320
337
  return if redirect_path.relative? || path.host == redirect_path.host
321
338
  @changed_hosts = true
322
339
  end
@@ -329,7 +346,7 @@ module HTTParty
329
346
  end
330
347
 
331
348
  def send_authorization_header?
332
- !defined?(@changed_hosts)
349
+ !@changed_hosts
333
350
  end
334
351
 
335
352
  def response_redirects?
@@ -350,6 +367,7 @@ module HTTParty
350
367
  cookies_hash = HTTParty::CookieHash.new
351
368
  cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie']
352
369
  response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
370
+
353
371
  options[:headers] ||= {}
354
372
  options[:headers]['Cookie'] = cookies_hash.to_cookie_string
355
373
  end
@@ -381,7 +399,20 @@ module HTTParty
381
399
  if path.userinfo
382
400
  username, password = path.userinfo.split(':')
383
401
  options[:basic_auth] = {username: username, password: password}
402
+ @credentials_sent = true
384
403
  end
385
404
  end
405
+
406
+ def decompress(body, encoding)
407
+ Decompressor.new(body, encoding).decompress
408
+ end
409
+
410
+ def encode_text(text, content_type)
411
+ TextEncoder.new(
412
+ text,
413
+ content_type: content_type,
414
+ assume_utf16_is_big_endian: assume_utf16_is_big_endian
415
+ ).call
416
+ end
386
417
  end
387
418
  end
@@ -1,31 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
1
5
  module HTTParty
2
6
  class Response #:nodoc:
3
- class Headers
7
+ class Headers < ::SimpleDelegator
4
8
  include ::Net::HTTPHeader
5
9
 
6
- def initialize(header = {})
7
- @header = header
10
+ def initialize(header_values = nil)
11
+ @header = {}
12
+ if header_values
13
+ header_values.each_pair do |k,v|
14
+ if v.is_a?(Array)
15
+ v.each do |sub_v|
16
+ add_field(k, sub_v)
17
+ end
18
+ else
19
+ add_field(k, v)
20
+ end
21
+ end
22
+ end
23
+ super(@header)
8
24
  end
9
25
 
10
26
  def ==(other)
11
- @header == other
12
- end
13
-
14
- def inspect
15
- @header.inspect
16
- end
17
-
18
- def method_missing(name, *args, &block)
19
- if @header.respond_to?(name)
20
- @header.send(name, *args, &block)
21
- else
22
- super
27
+ if other.is_a?(::Net::HTTPHeader)
28
+ @header == other.instance_variable_get(:@header)
29
+ elsif other.is_a?(Hash)
30
+ @header == other || @header == Headers.new(other).instance_variable_get(:@header)
23
31
  end
24
32
  end
25
-
26
- def respond_to?(method, include_all = false)
27
- super || @header.respond_to?(method, include_all)
28
- end
29
33
  end
30
34
  end
31
35
  end
@@ -1,9 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTParty
2
- class Response < BasicObject
4
+ class Response < Object
3
5
  def self.underscore(string)
4
6
  string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
5
7
  end
6
8
 
9
+ def self._load(data)
10
+ req, resp, parsed_resp, resp_body = Marshal.load(data)
11
+
12
+ new(req, resp, -> { parsed_resp }, body: resp_body)
13
+ end
14
+
7
15
  attr_reader :request, :response, :body, :headers
8
16
 
9
17
  def initialize(request, response, parsed_block, options = {})
@@ -14,7 +22,11 @@ module HTTParty
14
22
  @headers = Headers.new(response.to_hash)
15
23
 
16
24
  if request.options[:logger]
17
- logger = ::HTTParty::Logger.build(request.options[:logger], request.options[:log_level], request.options[:log_format])
25
+ logger = ::HTTParty::Logger.build(
26
+ request.options[:logger],
27
+ request.options[:log_level],
28
+ request.options[:log_format]
29
+ )
18
30
  logger.format(request, self)
19
31
  end
20
32
 
@@ -25,53 +37,87 @@ module HTTParty
25
37
  @parsed_response ||= @parsed_block.call
26
38
  end
27
39
 
28
- def class
29
- Response
30
- end
31
-
32
- def is_a?(klass)
33
- self.class == klass || self.class < klass
34
- end
35
-
36
- alias_method :kind_of?, :is_a?
37
-
38
40
  def code
39
41
  response.code.to_i
40
42
  end
41
43
 
44
+ def http_version
45
+ response.http_version
46
+ end
47
+
42
48
  def tap
43
49
  yield self
44
50
  self
45
51
  end
46
52
 
47
53
  def inspect
48
- inspect_id = ::Kernel::format "%x", (object_id * 2)
54
+ inspect_id = ::Kernel::format '%x', (object_id * 2)
49
55
  %(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
50
56
  end
51
57
 
52
- RESPOND_TO_METHODS = [:request, :response, :parsed_response, :body, :headers]
53
-
54
58
  CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
55
59
 
56
60
  CODES_TO_OBJ.each do |response_code, klass|
57
- name = klass.name.sub("Net::HTTP", '')
61
+ name = klass.name.sub('Net::HTTP', '')
58
62
  name = "#{underscore(name)}?".to_sym
59
63
 
60
- RESPOND_TO_METHODS << name
61
-
62
64
  define_method(name) do
63
65
  klass === response
64
66
  end
65
67
  end
66
68
 
67
69
  # Support old multiple_choice? method from pre 2.0.0 era.
68
- if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
70
+ if ::RUBY_VERSION >= '2.0.0' && ::RUBY_PLATFORM != 'java'
69
71
  alias_method :multiple_choice?, :multiple_choices?
70
72
  end
71
73
 
72
- def respond_to?(name, include_all = false)
73
- return true if RESPOND_TO_METHODS.include?(name)
74
- parsed_response.respond_to?(name, include_all) || response.respond_to?(name, include_all)
74
+ # Support old status codes method from pre 2.6.0 era.
75
+ if ::RUBY_VERSION >= '2.6.0' && ::RUBY_PLATFORM != 'java'
76
+ alias_method :gateway_time_out?, :gateway_timeout?
77
+ alias_method :request_entity_too_large?, :payload_too_large?
78
+ alias_method :request_time_out?, :request_timeout?
79
+ alias_method :request_uri_too_long?, :uri_too_long?
80
+ alias_method :requested_range_not_satisfiable?, :range_not_satisfiable?
81
+ end
82
+
83
+ def nil?
84
+ warn_about_nil_deprecation
85
+ response.nil? || response.body.nil? || response.body.empty?
86
+ end
87
+
88
+ def to_s
89
+ if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s)
90
+ response.body.to_s
91
+ else
92
+ inspect
93
+ end
94
+ end
95
+
96
+ def pretty_print(pp)
97
+ if !parsed_response.nil? && parsed_response.respond_to?(:pretty_print)
98
+ parsed_response.pretty_print(pp)
99
+ else
100
+ super
101
+ end
102
+ end
103
+
104
+ def display(port=$>)
105
+ if !parsed_response.nil? && parsed_response.respond_to?(:display)
106
+ parsed_response.display(port)
107
+ elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
108
+ response.body.display(port)
109
+ else
110
+ port.write(inspect)
111
+ end
112
+ end
113
+
114
+ def respond_to_missing?(name, *args)
115
+ return true if super
116
+ parsed_response.respond_to?(name) || response.respond_to?(name)
117
+ end
118
+
119
+ def _dump(_level)
120
+ Marshal.dump([request, response, parsed_response, body])
75
121
  end
76
122
 
77
123
  protected
@@ -91,6 +137,19 @@ module HTTParty
91
137
  ::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
92
138
  end
93
139
  end
140
+
141
+ private
142
+
143
+ def warn_about_nil_deprecation
144
+ trace_line = caller.reject { |line| line.include?('httparty') }.first
145
+ warning = "[DEPRECATION] HTTParty will no longer override `response#nil?`. " \
146
+ "This functionality will be removed in future versions. " \
147
+ "Please, add explicit check `response.body.nil? || response.body.empty?`. " \
148
+ "For more info refer to: https://github.com/jnunemaker/httparty/issues/568\n" \
149
+ "#{trace_line}"
150
+
151
+ warn(warning)
152
+ end
94
153
  end
95
154
  end
96
155
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
5
+ module HTTParty
6
+ # Allow access to http_response and code by delegation on fragment
7
+ class ResponseFragment < SimpleDelegator
8
+ attr_reader :http_response, :connection
9
+
10
+ def code
11
+ @http_response.code.to_i
12
+ end
13
+
14
+ def initialize(fragment, http_response, connection)
15
+ @fragment = fragment
16
+ @http_response = http_response
17
+ @connection = connection
18
+ super fragment
19
+ end
20
+ end
21
+ end