httparty 0.13.7 → 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.

Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/workflows/ci.yml +23 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop_todo.yml +1 -1
  6. data/{History → Changelog.md} +220 -59
  7. data/Gemfile +8 -3
  8. data/README.md +8 -7
  9. data/bin/httparty +3 -1
  10. data/docs/README.md +171 -0
  11. data/examples/README.md +34 -12
  12. data/examples/aaws.rb +7 -3
  13. data/examples/body_stream.rb +14 -0
  14. data/examples/crack.rb +1 -1
  15. data/examples/custom_parsers.rb +5 -1
  16. data/examples/delicious.rb +4 -4
  17. data/examples/headers_and_user_agents.rb +7 -3
  18. data/examples/idn.rb +10 -0
  19. data/examples/logging.rb +4 -4
  20. data/examples/microsoft_graph.rb +52 -0
  21. data/examples/multipart.rb +22 -0
  22. data/examples/peer_cert.rb +9 -0
  23. data/examples/stackexchange.rb +1 -1
  24. data/examples/stream_download.rb +26 -0
  25. data/examples/tripit_sign_in.rb +17 -6
  26. data/examples/twitter.rb +2 -2
  27. data/examples/whoismyrep.rb +1 -1
  28. data/httparty.gemspec +7 -5
  29. data/lib/httparty/connection_adapter.rb +86 -20
  30. data/lib/httparty/cookie_hash.rb +10 -8
  31. data/lib/httparty/decompressor.rb +92 -0
  32. data/lib/httparty/exceptions.rb +8 -2
  33. data/lib/httparty/hash_conversions.rb +30 -8
  34. data/lib/httparty/headers_processor.rb +32 -0
  35. data/lib/httparty/logger/apache_formatter.rb +31 -6
  36. data/lib/httparty/logger/curl_formatter.rb +68 -23
  37. data/lib/httparty/logger/logger.rb +5 -1
  38. data/lib/httparty/logger/logstash_formatter.rb +61 -0
  39. data/lib/httparty/module_inheritable_attributes.rb +6 -4
  40. data/lib/httparty/net_digest_auth.rb +23 -21
  41. data/lib/httparty/parser.rb +25 -14
  42. data/lib/httparty/request/body.rb +98 -0
  43. data/lib/httparty/request/multipart_boundary.rb +13 -0
  44. data/lib/httparty/request.rb +156 -106
  45. data/lib/httparty/response/headers.rb +23 -19
  46. data/lib/httparty/response.rb +92 -13
  47. data/lib/httparty/response_fragment.rb +21 -0
  48. data/lib/httparty/text_encoder.rb +72 -0
  49. data/lib/httparty/utils.rb +13 -0
  50. data/lib/httparty/version.rb +3 -1
  51. data/lib/httparty.rb +98 -34
  52. data/website/css/common.css +1 -1
  53. metadata +34 -113
  54. data/.travis.yml +0 -7
  55. data/features/basic_authentication.feature +0 -20
  56. data/features/command_line.feature +0 -95
  57. data/features/deals_with_http_error_codes.feature +0 -26
  58. data/features/digest_authentication.feature +0 -30
  59. data/features/handles_compressed_responses.feature +0 -27
  60. data/features/handles_multiple_formats.feature +0 -57
  61. data/features/steps/env.rb +0 -27
  62. data/features/steps/httparty_response_steps.rb +0 -52
  63. data/features/steps/httparty_steps.rb +0 -43
  64. data/features/steps/mongrel_helper.rb +0 -127
  65. data/features/steps/remote_service_steps.rb +0 -90
  66. data/features/supports_read_timeout_option.feature +0 -13
  67. data/features/supports_redirection.feature +0 -22
  68. data/features/supports_timeout_option.feature +0 -13
  69. data/spec/fixtures/delicious.xml +0 -23
  70. data/spec/fixtures/empty.xml +0 -0
  71. data/spec/fixtures/google.html +0 -3
  72. data/spec/fixtures/ssl/generate.sh +0 -29
  73. data/spec/fixtures/ssl/generated/1fe462c2.0 +0 -16
  74. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  75. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  76. data/spec/fixtures/ssl/generated/ca.key +0 -15
  77. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  78. data/spec/fixtures/ssl/generated/server.crt +0 -13
  79. data/spec/fixtures/ssl/generated/server.key +0 -15
  80. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  81. data/spec/fixtures/twitter.csv +0 -2
  82. data/spec/fixtures/twitter.json +0 -1
  83. data/spec/fixtures/twitter.xml +0 -403
  84. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  85. data/spec/httparty/connection_adapter_spec.rb +0 -468
  86. data/spec/httparty/cookie_hash_spec.rb +0 -83
  87. data/spec/httparty/exception_spec.rb +0 -38
  88. data/spec/httparty/hash_conversions_spec.rb +0 -41
  89. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  90. data/spec/httparty/logger/curl_formatter_spec.rb +0 -18
  91. data/spec/httparty/logger/logger_spec.rb +0 -38
  92. data/spec/httparty/net_digest_auth_spec.rb +0 -230
  93. data/spec/httparty/parser_spec.rb +0 -173
  94. data/spec/httparty/request_spec.rb +0 -1073
  95. data/spec/httparty/response_spec.rb +0 -241
  96. data/spec/httparty/ssl_spec.rb +0 -74
  97. data/spec/httparty_spec.rb +0 -850
  98. data/spec/spec_helper.rb +0 -59
  99. data/spec/support/ssl_test_helper.rb +0 -47
  100. data/spec/support/ssl_test_server.rb +0 -80
  101. 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,31 @@ 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 = Marshal.load(data)
51
+ new(http_method, path, options)
52
+ end
53
+
29
54
  attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
30
55
  attr_reader :path
31
56
 
32
57
  def initialize(http_method, path, o = {})
58
+ @changed_hosts = false
59
+ @credentials_sent = false
60
+
33
61
  self.http_method = http_method
34
62
  self.options = {
35
63
  limit: o.delete(:no_follow) ? 1 : 5,
@@ -50,7 +78,7 @@ module HTTParty
50
78
  @path = if uri.is_a?(uri_adapter)
51
79
  uri
52
80
  elsif String.try_convert(uri)
53
- uri_adapter.parse uri
81
+ uri_adapter.parse(uri).normalize
54
82
  else
55
83
  raise ArgumentError,
56
84
  "bad argument (expected #{uri_adapter} object or URI string)"
@@ -66,14 +94,20 @@ module HTTParty
66
94
  end
67
95
 
68
96
  def uri
69
- if redirect && path.relative? && path.path[0] != "/"
70
- last_uri_host = @last_uri.path.gsub(/[^\/]+$/, "")
97
+ if redirect && path.relative? && path.path[0] != '/'
98
+ last_uri_host = @last_uri.path.gsub(/[^\/]+$/, '')
71
99
 
72
- path.path = "/#{path.path}" if last_uri_host[-1] != "/"
73
- path.path = last_uri_host + path.path
100
+ path.path = "/#{path.path}" if last_uri_host[-1] != '/'
101
+ path.path = "#{last_uri_host}#{path.path}"
74
102
  end
75
103
 
76
- new_uri = path.relative? ? options[:uri_adapter].parse("#{base_uri}#{path}") : path.clone
104
+ if path.relative? && path.host
105
+ new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}").normalize
106
+ elsif path.relative?
107
+ new_uri = options[:uri_adapter].parse("#{base_uri}#{path}").normalize
108
+ else
109
+ new_uri = path.clone
110
+ end
77
111
 
78
112
  # avoid double query string on redirects [#12]
79
113
  unless redirect
@@ -90,10 +124,10 @@ module HTTParty
90
124
  def base_uri
91
125
  if redirect
92
126
  base_uri = "#{@last_uri.scheme}://#{@last_uri.host}"
93
- base_uri += ":#{@last_uri.port}" if @last_uri.port != 80
127
+ base_uri = "#{base_uri}:#{@last_uri.port}" if @last_uri.port != 80
94
128
  base_uri
95
129
  else
96
- options[:base_uri]
130
+ options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri])
97
131
  end
98
132
  end
99
133
 
@@ -113,38 +147,52 @@ module HTTParty
113
147
  validate
114
148
  setup_raw_request
115
149
  chunked_body = nil
150
+ current_http = http
116
151
 
117
- self.last_response = http.request(@raw_request) do |http_response|
152
+ self.last_response = current_http.request(@raw_request) do |http_response|
118
153
  if block
119
154
  chunks = []
120
155
 
121
156
  http_response.read_body do |fragment|
122
- chunks << fragment unless options[:stream_body]
123
- block.call(fragment)
157
+ encoded_fragment = encode_text(fragment, http_response['content-type'])
158
+ chunks << encoded_fragment if !options[:stream_body]
159
+ block.call ResponseFragment.new(encoded_fragment, http_response, current_http)
124
160
  end
125
161
 
126
162
  chunked_body = chunks.join
127
163
  end
128
164
  end
129
165
 
130
- handle_deflation unless http_method == Net::HTTP::Head
131
- handle_response(chunked_body, &block)
166
+ handle_host_redirection if response_redirects?
167
+ result = handle_unauthorized
168
+ result ||= handle_response(chunked_body, &block)
169
+ result
170
+ end
171
+
172
+ def handle_unauthorized(&block)
173
+ return unless digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
174
+ return if @credentials_sent
175
+ @credentials_sent = true
176
+ perform(&block)
132
177
  end
133
178
 
134
179
  def raw_body
135
180
  @raw_request.body
136
181
  end
137
182
 
183
+ def _dump(_level)
184
+ opts = options.dup
185
+ opts.delete(:logger)
186
+ opts.delete(:parser) if opts[:parser] && opts[:parser].is_a?(Proc)
187
+ Marshal.dump([http_method, path, opts])
188
+ end
189
+
138
190
  private
139
191
 
140
192
  def http
141
193
  connection_adapter.call(uri, options)
142
194
  end
143
195
 
144
- def body
145
- options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
146
- end
147
-
148
196
  def credentials
149
197
  (options[:basic_auth] || options[:digest_auth]).to_hash
150
198
  end
@@ -170,106 +218,78 @@ module HTTParty
170
218
  end
171
219
 
172
220
  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))
221
+ if options[:headers].respond_to?(:to_hash)
222
+ headers_hash = options[:headers].to_hash
197
223
  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?
224
+ headers_hash = nil
200
225
  end
201
226
 
202
- query_string_parts.reject!(&:empty?) unless query_string_parts == [""]
203
- query_string_parts.size > 0 ? query_string_parts.join('&') : nil
204
- end
227
+ @raw_request = http_method.new(request_uri(uri), headers_hash)
228
+ @raw_request.body_stream = options[:body_stream] if options[:body_stream]
205
229
 
206
- def get_charset
207
- content_type = last_response["content-type"]
208
- if content_type.nil?
209
- return nil
210
- end
230
+ if options[:body]
231
+ body = Body.new(
232
+ options[:body],
233
+ query_string_normalizer: query_string_normalizer,
234
+ force_multipart: options[:multipart]
235
+ )
211
236
 
212
- if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
213
- return $1
237
+ if body.multipart?
238
+ content_type = "multipart/form-data; boundary=#{body.boundary}"
239
+ @raw_request['Content-Type'] = content_type
240
+ end
241
+ @raw_request.body = body.call
214
242
  end
215
243
 
216
- if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
217
- return $1.gsub(/\\(.)/, '\1')
218
- end
244
+ @raw_request.instance_variable_set(:@decode_content, decompress_content?)
219
245
 
220
- nil
246
+ if options[:basic_auth] && send_authorization_header?
247
+ @raw_request.basic_auth(username, password)
248
+ @credentials_sent = true
249
+ end
250
+ setup_digest_auth if digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
221
251
  end
222
252
 
223
- def encode_with_ruby_encoding(body, charset)
224
- encoding = Encoding.find(charset)
225
- body.force_encoding(encoding)
226
- rescue
227
- body
253
+ def digest_auth?
254
+ !!options[:digest_auth]
228
255
  end
229
256
 
230
- def assume_utf16_is_big_endian
231
- options[:assume_utf16_is_big_endian]
257
+ def decompress_content?
258
+ !options[:skip_decompression]
232
259
  end
233
260
 
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
261
+ def response_unauthorized?
262
+ !!last_response && last_response.code == '401'
263
+ end
242
264
 
243
- if assume_utf16_is_big_endian
244
- body.force_encoding("UTF-16BE")
245
- else
246
- body.force_encoding("UTF-16LE")
247
- end
265
+ def response_has_digest_auth_challenge?
266
+ !last_response['www-authenticate'].nil? && last_response['www-authenticate'].length > 0
248
267
  end
249
268
 
250
- def _encode_body(body)
251
- charset = get_charset
269
+ def setup_digest_auth
270
+ @raw_request.digest_auth(username, password, last_response)
271
+ end
252
272
 
253
- if charset.nil?
254
- return body
255
- end
273
+ def query_string(uri)
274
+ query_string_parts = []
275
+ query_string_parts << uri.query unless uri.query.nil?
256
276
 
257
- if "utf-16".casecmp(charset) == 0
258
- encode_utf_16(body)
277
+ if options[:query].respond_to?(:to_hash)
278
+ query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash))
259
279
  else
260
- encode_with_ruby_encoding(body, charset)
280
+ query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
281
+ query_string_parts << options[:query] unless options[:query].nil?
261
282
  end
283
+
284
+ query_string_parts.reject!(&:empty?) unless query_string_parts == ['']
285
+ query_string_parts.size > 0 ? query_string_parts.join('&') : nil
262
286
  end
263
287
 
264
- def encode_body(body)
265
- if "".respond_to?(:encoding)
266
- _encode_body(body)
267
- else
268
- body
269
- end
288
+ def assume_utf16_is_big_endian
289
+ options[:assume_utf16_is_big_endian]
270
290
  end
271
291
 
272
- def handle_response(body, &block)
292
+ def handle_response(raw_body, &block)
273
293
  if response_redirects?
274
294
  options[:limit] -= 1
275
295
  if options[:logger]
@@ -290,25 +310,41 @@ module HTTParty
290
310
  capture_cookies(last_response)
291
311
  perform(&block)
292
312
  else
293
- body ||= last_response.body
294
- body = encode_body(body)
295
- Response.new(self, last_response, lambda { parse_response(body) }, body: body)
313
+ raw_body ||= last_response.body
314
+
315
+ body = decompress(raw_body, last_response['content-encoding']) unless raw_body.nil?
316
+
317
+ unless body.nil?
318
+ body = encode_text(body, last_response['content-type'])
319
+
320
+ if decompress_content?
321
+ last_response.delete('content-encoding')
322
+ raw_body = body
323
+ end
324
+ end
325
+
326
+ Response.new(self, last_response, lambda { parse_response(body) }, body: raw_body)
296
327
  end
297
328
  end
298
329
 
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')
330
+ def handle_host_redirection
331
+ check_duplicate_location_header
332
+ redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
333
+ return if redirect_path.relative? || path.host == redirect_path.host
334
+ @changed_hosts = true
335
+ end
336
+
337
+ def check_duplicate_location_header
338
+ location = last_response.get_fields('location')
339
+ if location.is_a?(Array) && location.count > 1
340
+ raise DuplicateLocationHeader.new(last_response)
309
341
  end
310
342
  end
311
343
 
344
+ def send_authorization_header?
345
+ !@changed_hosts
346
+ end
347
+
312
348
  def response_redirects?
313
349
  case last_response
314
350
  when Net::HTTPNotModified # 304
@@ -327,6 +363,7 @@ module HTTParty
327
363
  cookies_hash = HTTParty::CookieHash.new
328
364
  cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie']
329
365
  response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
366
+
330
367
  options[:headers] ||= {}
331
368
  options[:headers]['Cookie'] = cookies_hash.to_cookie_string
332
369
  end
@@ -358,7 +395,20 @@ module HTTParty
358
395
  if path.userinfo
359
396
  username, password = path.userinfo.split(':')
360
397
  options[:basic_auth] = {username: username, password: password}
398
+ @credentials_sent = true
361
399
  end
362
400
  end
401
+
402
+ def decompress(body, encoding)
403
+ Decompressor.new(body, encoding).decompress
404
+ end
405
+
406
+ def encode_text(text, content_type)
407
+ TextEncoder.new(
408
+ text,
409
+ content_type: content_type,
410
+ assume_utf16_is_big_endian: assume_utf16_is_big_endian
411
+ ).call
412
+ end
363
413
  end
364
414
  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