httparty 0.13.7 → 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} +226 -59
  7. data/Gemfile +10 -3
  8. data/Guardfile +3 -2
  9. data/README.md +8 -7
  10. data/bin/httparty +3 -1
  11. data/docs/README.md +191 -0
  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 +17 -6
  27. data/examples/twitter.rb +2 -2
  28. data/examples/whoismyrep.rb +1 -1
  29. data/httparty.gemspec +7 -5
  30. data/lib/httparty/connection_adapter.rb +86 -20
  31. data/lib/httparty/cookie_hash.rb +10 -8
  32. data/lib/httparty/decompressor.rb +102 -0
  33. data/lib/httparty/exceptions.rb +8 -2
  34. data/lib/httparty/hash_conversions.rb +30 -8
  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 +68 -23
  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 +23 -21
  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 +160 -106
  46. data/lib/httparty/response/headers.rb +23 -19
  47. data/lib/httparty/response.rb +92 -13
  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 +98 -35
  53. data/website/css/common.css +1 -1
  54. metadata +34 -114
  55. data/.simplecov +0 -1
  56. data/.travis.yml +0 -7
  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 -52
  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 -90
  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 -468
  88. data/spec/httparty/cookie_hash_spec.rb +0 -83
  89. data/spec/httparty/exception_spec.rb +0 -38
  90. data/spec/httparty/hash_conversions_spec.rb +0 -41
  91. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  92. data/spec/httparty/logger/curl_formatter_spec.rb +0 -18
  93. data/spec/httparty/logger/logger_spec.rb +0 -38
  94. data/spec/httparty/net_digest_auth_spec.rb +0 -230
  95. data/spec/httparty/parser_spec.rb +0 -173
  96. data/spec/httparty/request_spec.rb +0 -1073
  97. data/spec/httparty/response_spec.rb +0 -241
  98. data/spec/httparty/ssl_spec.rb +0 -74
  99. data/spec/httparty_spec.rb +0 -850
  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 = [
@@ -9,7 +13,10 @@ module HTTParty
9
13
  Net::HTTP::Head,
10
14
  Net::HTTP::Options,
11
15
  Net::HTTP::Move,
12
- Net::HTTP::Copy
16
+ Net::HTTP::Copy,
17
+ Net::HTTP::Mkcol,
18
+ Net::HTTP::Lock,
19
+ Net::HTTP::Unlock,
13
20
  ]
14
21
 
15
22
  SupportedURISchemes = ['http', 'https', 'webcal', nil]
@@ -26,10 +33,35 @@ module HTTParty
26
33
  end.flatten.join('&')
27
34
  end
28
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
+
29
58
  attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
30
59
  attr_reader :path
31
60
 
32
61
  def initialize(http_method, path, o = {})
62
+ @changed_hosts = false
63
+ @credentials_sent = false
64
+
33
65
  self.http_method = http_method
34
66
  self.options = {
35
67
  limit: o.delete(:no_follow) ? 1 : 5,
@@ -50,7 +82,7 @@ module HTTParty
50
82
  @path = if uri.is_a?(uri_adapter)
51
83
  uri
52
84
  elsif String.try_convert(uri)
53
- uri_adapter.parse uri
85
+ uri_adapter.parse(uri).normalize
54
86
  else
55
87
  raise ArgumentError,
56
88
  "bad argument (expected #{uri_adapter} object or URI string)"
@@ -66,14 +98,20 @@ module HTTParty
66
98
  end
67
99
 
68
100
  def uri
69
- if redirect && path.relative? && path.path[0] != "/"
70
- last_uri_host = @last_uri.path.gsub(/[^\/]+$/, "")
101
+ if redirect && path.relative? && path.path[0] != '/'
102
+ last_uri_host = @last_uri.path.gsub(/[^\/]+$/, '')
71
103
 
72
- path.path = "/#{path.path}" if last_uri_host[-1] != "/"
73
- 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}"
74
106
  end
75
107
 
76
- 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
77
115
 
78
116
  # avoid double query string on redirects [#12]
79
117
  unless redirect
@@ -90,10 +128,10 @@ module HTTParty
90
128
  def base_uri
91
129
  if redirect
92
130
  base_uri = "#{@last_uri.scheme}://#{@last_uri.host}"
93
- base_uri += ":#{@last_uri.port}" if @last_uri.port != 80
131
+ base_uri = "#{base_uri}:#{@last_uri.port}" if @last_uri.port != 80
94
132
  base_uri
95
133
  else
96
- options[:base_uri]
134
+ options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri])
97
135
  end
98
136
  end
99
137
 
@@ -113,38 +151,52 @@ module HTTParty
113
151
  validate
114
152
  setup_raw_request
115
153
  chunked_body = nil
154
+ current_http = http
116
155
 
117
- self.last_response = http.request(@raw_request) do |http_response|
156
+ self.last_response = current_http.request(@raw_request) do |http_response|
118
157
  if block
119
158
  chunks = []
120
159
 
121
160
  http_response.read_body do |fragment|
122
- chunks << fragment unless options[:stream_body]
123
- 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)
124
164
  end
125
165
 
126
166
  chunked_body = chunks.join
127
167
  end
128
168
  end
129
169
 
130
- handle_deflation unless http_method == Net::HTTP::Head
131
- handle_response(chunked_body, &block)
170
+ handle_host_redirection if response_redirects?
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)
132
181
  end
133
182
 
134
183
  def raw_body
135
184
  @raw_request.body
136
185
  end
137
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
+
138
194
  private
139
195
 
140
196
  def http
141
197
  connection_adapter.call(uri, options)
142
198
  end
143
199
 
144
- def body
145
- options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
146
- end
147
-
148
200
  def credentials
149
201
  (options[:basic_auth] || options[:digest_auth]).to_hash
150
202
  end
@@ -170,106 +222,78 @@ module HTTParty
170
222
  end
171
223
 
172
224
  def setup_raw_request
173
- @raw_request = http_method.new(request_uri(uri))
174
- @raw_request.body = body if body
175
- @raw_request.body_stream = options[:body_stream] if options[:body_stream]
176
- @raw_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
177
- @raw_request.basic_auth(username, password) if options[:basic_auth]
178
- setup_digest_auth if options[:digest_auth]
179
- end
180
-
181
- def setup_digest_auth
182
- auth_request = http_method.new(uri.request_uri)
183
- auth_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
184
- res = http.request(auth_request)
185
-
186
- if !res['www-authenticate'].nil? && res['www-authenticate'].length > 0
187
- @raw_request.digest_auth(username, password, res)
188
- end
189
- end
190
-
191
- def query_string(uri)
192
- query_string_parts = []
193
- query_string_parts << uri.query unless uri.query.nil?
194
-
195
- if options[:query].respond_to?(:to_hash)
196
- 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
197
227
  else
198
- query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
199
- query_string_parts << options[:query] unless options[:query].nil?
228
+ headers_hash = nil
200
229
  end
201
230
 
202
- query_string_parts.reject!(&:empty?) unless query_string_parts == [""]
203
- query_string_parts.size > 0 ? query_string_parts.join('&') : nil
204
- end
231
+ @raw_request = http_method.new(request_uri(uri), headers_hash)
232
+ @raw_request.body_stream = options[:body_stream] if options[:body_stream]
205
233
 
206
- def get_charset
207
- content_type = last_response["content-type"]
208
- if content_type.nil?
209
- return nil
210
- 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
+ )
211
240
 
212
- if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
213
- 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
214
246
  end
215
247
 
216
- if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
217
- return $1.gsub(/\\(.)/, '\1')
218
- end
248
+ @raw_request.instance_variable_set(:@decode_content, decompress_content?)
219
249
 
220
- 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?
221
255
  end
222
256
 
223
- def encode_with_ruby_encoding(body, charset)
224
- encoding = Encoding.find(charset)
225
- body.force_encoding(encoding)
226
- rescue
227
- body
257
+ def digest_auth?
258
+ !!options[:digest_auth]
228
259
  end
229
260
 
230
- def assume_utf16_is_big_endian
231
- options[:assume_utf16_is_big_endian]
261
+ def decompress_content?
262
+ !options[:skip_decompression]
232
263
  end
233
264
 
234
- def encode_utf_16(body)
235
- if body.bytesize >= 2
236
- if body.getbyte(0) == 0xFF && body.getbyte(1) == 0xFE
237
- return body.force_encoding("UTF-16LE")
238
- elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
239
- return body.force_encoding("UTF-16BE")
240
- end
241
- end
265
+ def response_unauthorized?
266
+ !!last_response && last_response.code == '401'
267
+ end
242
268
 
243
- if assume_utf16_is_big_endian
244
- body.force_encoding("UTF-16BE")
245
- else
246
- body.force_encoding("UTF-16LE")
247
- end
269
+ def response_has_digest_auth_challenge?
270
+ !last_response['www-authenticate'].nil? && last_response['www-authenticate'].length > 0
248
271
  end
249
272
 
250
- def _encode_body(body)
251
- charset = get_charset
273
+ def setup_digest_auth
274
+ @raw_request.digest_auth(username, password, last_response)
275
+ end
252
276
 
253
- if charset.nil?
254
- return body
255
- end
277
+ def query_string(uri)
278
+ query_string_parts = []
279
+ query_string_parts << uri.query unless uri.query.nil?
256
280
 
257
- if "utf-16".casecmp(charset) == 0
258
- 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))
259
283
  else
260
- 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?
261
286
  end
287
+
288
+ query_string_parts.reject!(&:empty?) unless query_string_parts == ['']
289
+ query_string_parts.size > 0 ? query_string_parts.join('&') : nil
262
290
  end
263
291
 
264
- def encode_body(body)
265
- if "".respond_to?(:encoding)
266
- _encode_body(body)
267
- else
268
- body
269
- end
292
+ def assume_utf16_is_big_endian
293
+ options[:assume_utf16_is_big_endian]
270
294
  end
271
295
 
272
- def handle_response(body, &block)
296
+ def handle_response(raw_body, &block)
273
297
  if response_redirects?
274
298
  options[:limit] -= 1
275
299
  if options[:logger]
@@ -290,25 +314,41 @@ module HTTParty
290
314
  capture_cookies(last_response)
291
315
  perform(&block)
292
316
  else
293
- body ||= last_response.body
294
- body = encode_body(body)
295
- Response.new(self, last_response, lambda { parse_response(body) }, body: body)
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'])
323
+
324
+ if decompress_content?
325
+ last_response.delete('content-encoding')
326
+ raw_body = body
327
+ end
328
+ end
329
+
330
+ Response.new(self, last_response, lambda { parse_response(body) }, body: raw_body)
296
331
  end
297
332
  end
298
333
 
299
- # Inspired by Ruby 1.9
300
- def handle_deflation
301
- case last_response["content-encoding"]
302
- when "gzip", "x-gzip"
303
- body_io = StringIO.new(last_response.body)
304
- last_response.body.replace Zlib::GzipReader.new(body_io).read
305
- last_response.delete('content-encoding')
306
- when "deflate"
307
- last_response.body.replace Zlib::Inflate.inflate(last_response.body)
308
- last_response.delete('content-encoding')
334
+ def handle_host_redirection
335
+ check_duplicate_location_header
336
+ redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
337
+ return if redirect_path.relative? || path.host == redirect_path.host
338
+ @changed_hosts = true
339
+ end
340
+
341
+ def check_duplicate_location_header
342
+ location = last_response.get_fields('location')
343
+ if location.is_a?(Array) && location.count > 1
344
+ raise DuplicateLocationHeader.new(last_response)
309
345
  end
310
346
  end
311
347
 
348
+ def send_authorization_header?
349
+ !@changed_hosts
350
+ end
351
+
312
352
  def response_redirects?
313
353
  case last_response
314
354
  when Net::HTTPNotModified # 304
@@ -327,6 +367,7 @@ module HTTParty
327
367
  cookies_hash = HTTParty::CookieHash.new
328
368
  cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie']
329
369
  response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
370
+
330
371
  options[:headers] ||= {}
331
372
  options[:headers]['Cookie'] = cookies_hash.to_cookie_string
332
373
  end
@@ -358,7 +399,20 @@ module HTTParty
358
399
  if path.userinfo
359
400
  username, password = path.userinfo.split(':')
360
401
  options[:basic_auth] = {username: username, password: password}
402
+ @credentials_sent = true
361
403
  end
362
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
363
417
  end
364
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,50 +22,102 @@ 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
32
+
33
+ throw_exception
20
34
  end
21
35
 
22
36
  def parsed_response
23
37
  @parsed_response ||= @parsed_block.call
24
38
  end
25
39
 
26
- def class
27
- Response
28
- end
29
-
30
40
  def code
31
41
  response.code.to_i
32
42
  end
33
43
 
44
+ def http_version
45
+ response.http_version
46
+ end
47
+
34
48
  def tap
35
49
  yield self
36
50
  self
37
51
  end
38
52
 
39
53
  def inspect
40
- inspect_id = ::Kernel::format "%x", (object_id * 2)
54
+ inspect_id = ::Kernel::format '%x', (object_id * 2)
41
55
  %(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
42
56
  end
43
57
 
44
58
  CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
45
59
 
46
60
  CODES_TO_OBJ.each do |response_code, klass|
47
- name = klass.name.sub("Net::HTTP", '')
48
- define_method("#{underscore(name)}?") do
61
+ name = klass.name.sub('Net::HTTP', '')
62
+ name = "#{underscore(name)}?".to_sym
63
+
64
+ define_method(name) do
49
65
  klass === response
50
66
  end
51
67
  end
52
68
 
53
69
  # Support old multiple_choice? method from pre 2.0.0 era.
54
- if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
70
+ if ::RUBY_VERSION >= '2.0.0' && ::RUBY_PLATFORM != 'java'
55
71
  alias_method :multiple_choice?, :multiple_choices?
56
72
  end
57
73
 
58
- def respond_to?(name, include_all = false)
59
- return true if [:request, :response, :parsed_response, :body, :headers].include?(name)
60
- 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])
61
121
  end
62
122
 
63
123
  protected
@@ -71,6 +131,25 @@ module HTTParty
71
131
  super
72
132
  end
73
133
  end
134
+
135
+ def throw_exception
136
+ if @request.options[:raise_on] && @request.options[:raise_on].include?(code)
137
+ ::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
138
+ end
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
74
153
  end
75
154
  end
76
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