httparty 0.15.0 → 0.21.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.
Files changed (96) 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.md → Changelog.md} +137 -0
  7. data/Gemfile +8 -1
  8. data/Guardfile +3 -2
  9. data/README.md +6 -6
  10. data/docs/README.md +120 -38
  11. data/examples/README.md +28 -11
  12. data/examples/aaws.rb +6 -2
  13. data/examples/body_stream.rb +14 -0
  14. data/examples/custom_parsers.rb +4 -0
  15. data/examples/headers_and_user_agents.rb +7 -3
  16. data/examples/idn.rb +10 -0
  17. data/examples/logging.rb +3 -3
  18. data/examples/microsoft_graph.rb +52 -0
  19. data/examples/multipart.rb +22 -0
  20. data/examples/peer_cert.rb +9 -0
  21. data/examples/stream_download.rb +8 -2
  22. data/httparty.gemspec +7 -4
  23. data/lib/httparty/connection_adapter.rb +59 -16
  24. data/lib/httparty/cookie_hash.rb +10 -8
  25. data/lib/httparty/decompressor.rb +102 -0
  26. data/lib/httparty/exceptions.rb +4 -1
  27. data/lib/httparty/hash_conversions.rb +28 -12
  28. data/lib/httparty/headers_processor.rb +32 -0
  29. data/lib/httparty/logger/apache_formatter.rb +31 -6
  30. data/lib/httparty/logger/curl_formatter.rb +9 -7
  31. data/lib/httparty/logger/logger.rb +5 -1
  32. data/lib/httparty/logger/logstash_formatter.rb +61 -0
  33. data/lib/httparty/module_inheritable_attributes.rb +6 -4
  34. data/lib/httparty/net_digest_auth.rb +15 -15
  35. data/lib/httparty/parser.rb +25 -16
  36. data/lib/httparty/request/body.rb +105 -0
  37. data/lib/httparty/request/multipart_boundary.rb +13 -0
  38. data/lib/httparty/request.rb +96 -105
  39. data/lib/httparty/response/headers.rb +6 -2
  40. data/lib/httparty/response.rb +59 -8
  41. data/lib/httparty/response_fragment.rb +21 -0
  42. data/lib/httparty/text_encoder.rb +72 -0
  43. data/lib/httparty/utils.rb +13 -0
  44. data/lib/httparty/version.rb +3 -1
  45. data/lib/httparty.rb +70 -25
  46. data/website/css/common.css +1 -1
  47. metadata +38 -106
  48. data/.simplecov +0 -1
  49. data/.travis.yml +0 -8
  50. data/features/basic_authentication.feature +0 -20
  51. data/features/command_line.feature +0 -95
  52. data/features/deals_with_http_error_codes.feature +0 -26
  53. data/features/digest_authentication.feature +0 -30
  54. data/features/handles_compressed_responses.feature +0 -27
  55. data/features/handles_multiple_formats.feature +0 -57
  56. data/features/steps/env.rb +0 -27
  57. data/features/steps/httparty_response_steps.rb +0 -56
  58. data/features/steps/httparty_steps.rb +0 -43
  59. data/features/steps/mongrel_helper.rb +0 -127
  60. data/features/steps/remote_service_steps.rb +0 -92
  61. data/features/supports_read_timeout_option.feature +0 -13
  62. data/features/supports_redirection.feature +0 -22
  63. data/features/supports_timeout_option.feature +0 -13
  64. data/spec/fixtures/delicious.xml +0 -23
  65. data/spec/fixtures/empty.xml +0 -0
  66. data/spec/fixtures/google.html +0 -3
  67. data/spec/fixtures/ssl/generate.sh +0 -29
  68. data/spec/fixtures/ssl/generated/1fe462c2.0 +0 -1
  69. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  70. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  71. data/spec/fixtures/ssl/generated/ca.key +0 -15
  72. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  73. data/spec/fixtures/ssl/generated/server.crt +0 -13
  74. data/spec/fixtures/ssl/generated/server.key +0 -15
  75. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  76. data/spec/fixtures/twitter.csv +0 -2
  77. data/spec/fixtures/twitter.json +0 -1
  78. data/spec/fixtures/twitter.xml +0 -403
  79. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  80. data/spec/httparty/connection_adapter_spec.rb +0 -495
  81. data/spec/httparty/cookie_hash_spec.rb +0 -100
  82. data/spec/httparty/exception_spec.rb +0 -45
  83. data/spec/httparty/hash_conversions_spec.rb +0 -49
  84. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  85. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  86. data/spec/httparty/logger/logger_spec.rb +0 -38
  87. data/spec/httparty/net_digest_auth_spec.rb +0 -268
  88. data/spec/httparty/parser_spec.rb +0 -178
  89. data/spec/httparty/request_spec.rb +0 -1244
  90. data/spec/httparty/response_spec.rb +0 -347
  91. data/spec/httparty/ssl_spec.rb +0 -74
  92. data/spec/httparty_spec.rb +0 -877
  93. data/spec/spec_helper.rb +0 -59
  94. data/spec/support/ssl_test_helper.rb +0 -47
  95. data/spec/support/ssl_test_server.rb +0 -80
  96. data/spec/support/stub_response.rb +0 -49
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  module HTTParty
@@ -13,6 +15,8 @@ module HTTParty
13
15
  Net::HTTP::Move,
14
16
  Net::HTTP::Copy,
15
17
  Net::HTTP::Mkcol,
18
+ Net::HTTP::Lock,
19
+ Net::HTTP::Unlock,
16
20
  ]
17
21
 
18
22
  SupportedURISchemes = ['http', 'https', 'webcal', nil]
@@ -42,6 +46,15 @@ module HTTParty
42
46
  end.flatten.join('&')
43
47
  end
44
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
+
45
58
  attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
46
59
  attr_reader :path
47
60
 
@@ -60,7 +73,7 @@ module HTTParty
60
73
  connection_adapter: ConnectionAdapter
61
74
  }.merge(o)
62
75
  self.path = path
63
- set_basic_auth_from_uri
76
+ set_basic_auth_from_uri
64
77
  end
65
78
 
66
79
  def path=(uri)
@@ -69,7 +82,7 @@ module HTTParty
69
82
  @path = if uri.is_a?(uri_adapter)
70
83
  uri
71
84
  elsif String.try_convert(uri)
72
- uri_adapter.parse uri
85
+ uri_adapter.parse(uri).normalize
73
86
  else
74
87
  raise ArgumentError,
75
88
  "bad argument (expected #{uri_adapter} object or URI string)"
@@ -85,17 +98,17 @@ module HTTParty
85
98
  end
86
99
 
87
100
  def uri
88
- if redirect && path.relative? && path.path[0] != "/"
89
- last_uri_host = @last_uri.path.gsub(/[^\/]+$/, "")
101
+ if redirect && path.relative? && path.path[0] != '/'
102
+ last_uri_host = @last_uri.path.gsub(/[^\/]+$/, '')
90
103
 
91
- path.path = "/#{path.path}" if last_uri_host[-1] != "/"
92
- 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}"
93
106
  end
94
107
 
95
108
  if path.relative? && path.host
96
- new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}")
109
+ new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}").normalize
97
110
  elsif path.relative?
98
- new_uri = options[:uri_adapter].parse("#{base_uri}#{path}")
111
+ new_uri = options[:uri_adapter].parse("#{base_uri}#{path}").normalize
99
112
  else
100
113
  new_uri = path.clone
101
114
  end
@@ -115,7 +128,7 @@ module HTTParty
115
128
  def base_uri
116
129
  if redirect
117
130
  base_uri = "#{@last_uri.scheme}://#{@last_uri.host}"
118
- base_uri += ":#{@last_uri.port}" if @last_uri.port != 80
131
+ base_uri = "#{base_uri}:#{@last_uri.port}" if @last_uri.port != 80
119
132
  base_uri
120
133
  else
121
134
  options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri])
@@ -138,31 +151,32 @@ module HTTParty
138
151
  validate
139
152
  setup_raw_request
140
153
  chunked_body = nil
154
+ current_http = http
141
155
 
142
- self.last_response = http.request(@raw_request) do |http_response|
156
+ self.last_response = current_http.request(@raw_request) do |http_response|
143
157
  if block
144
158
  chunks = []
145
159
 
146
160
  http_response.read_body do |fragment|
147
- chunks << fragment unless options[:stream_body]
148
- 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)
149
164
  end
150
165
 
151
166
  chunked_body = chunks.join
152
167
  end
153
168
  end
154
-
155
-
169
+
156
170
  handle_host_redirection if response_redirects?
157
171
  result = handle_unauthorized
158
172
  result ||= handle_response(chunked_body, &block)
159
- result
173
+ result
160
174
  end
161
175
 
162
176
  def handle_unauthorized(&block)
163
177
  return unless digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
164
178
  return if @credentials_sent
165
- @credentials_sent = true
179
+ @credentials_sent = true
166
180
  perform(&block)
167
181
  end
168
182
 
@@ -170,16 +184,19 @@ module HTTParty
170
184
  @raw_request.body
171
185
  end
172
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
+
173
194
  private
174
195
 
175
196
  def http
176
197
  connection_adapter.call(uri, options)
177
198
  end
178
199
 
179
- def body
180
- options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
181
- end
182
-
183
200
  def credentials
184
201
  (options[:basic_auth] || options[:digest_auth]).to_hash
185
202
  end
@@ -205,25 +222,35 @@ module HTTParty
205
222
  end
206
223
 
207
224
  def setup_raw_request
208
- @raw_request = http_method.new(request_uri(uri))
209
- @raw_request.body = body if body
210
- @raw_request.body_stream = options[:body_stream] if options[:body_stream]
211
225
  if options[:headers].respond_to?(:to_hash)
212
226
  headers_hash = options[:headers].to_hash
213
-
214
- @raw_request.initialize_http_header(headers_hash)
215
- # If the caller specified a header of 'Accept-Encoding', assume they want to
216
- # deal with encoding of content. Disable the internal logic in Net:HTTP
217
- # that handles encoding, if the platform supports it.
218
- if @raw_request.respond_to?(:decode_content) && (headers_hash.key?('Accept-Encoding') || headers_hash.key?('accept-encoding'))
219
- # Using the '[]=' sets decode_content to false
220
- @raw_request['accept-encoding'] = @raw_request['accept-encoding']
227
+ else
228
+ headers_hash = nil
229
+ end
230
+
231
+ @raw_request = http_method.new(request_uri(uri), headers_hash)
232
+ @raw_request.body_stream = options[:body_stream] if options[:body_stream]
233
+
234
+ if options[:body]
235
+ body = Body.new(
236
+ options[:body],
237
+ query_string_normalizer: query_string_normalizer,
238
+ force_multipart: options[:multipart]
239
+ )
240
+
241
+ if body.multipart?
242
+ content_type = "multipart/form-data; boundary=#{body.boundary}"
243
+ @raw_request['Content-Type'] = content_type
221
244
  end
245
+ @raw_request.body = body.call
222
246
  end
223
- if options[:basic_auth] && send_authorization_header?
247
+
248
+ @raw_request.instance_variable_set(:@decode_content, decompress_content?)
249
+
250
+ if options[:basic_auth] && send_authorization_header?
224
251
  @raw_request.basic_auth(username, password)
225
252
  @credentials_sent = true
226
- end
253
+ end
227
254
  setup_digest_auth if digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
228
255
  end
229
256
 
@@ -231,6 +258,10 @@ module HTTParty
231
258
  !!options[:digest_auth]
232
259
  end
233
260
 
261
+ def decompress_content?
262
+ !options[:skip_decompression]
263
+ end
264
+
234
265
  def response_unauthorized?
235
266
  !!last_response && last_response.code == '401'
236
267
  end
@@ -240,7 +271,7 @@ module HTTParty
240
271
  end
241
272
 
242
273
  def setup_digest_auth
243
- @raw_request.digest_auth(username, password, last_response)
274
+ @raw_request.digest_auth(username, password, last_response)
244
275
  end
245
276
 
246
277
  def query_string(uri)
@@ -254,78 +285,15 @@ module HTTParty
254
285
  query_string_parts << options[:query] unless options[:query].nil?
255
286
  end
256
287
 
257
- query_string_parts.reject!(&:empty?) unless query_string_parts == [""]
288
+ query_string_parts.reject!(&:empty?) unless query_string_parts == ['']
258
289
  query_string_parts.size > 0 ? query_string_parts.join('&') : nil
259
290
  end
260
291
 
261
- def get_charset
262
- content_type = last_response["content-type"]
263
- if content_type.nil?
264
- return nil
265
- end
266
-
267
- if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
268
- return $1
269
- end
270
-
271
- if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
272
- return $1.gsub(/\\(.)/, '\1')
273
- end
274
-
275
- nil
276
- end
277
-
278
- def encode_with_ruby_encoding(body, charset)
279
- if Encoding.name_list.include?(charset)
280
- body.force_encoding(charset)
281
- else
282
- body
283
- end
284
- end
285
-
286
292
  def assume_utf16_is_big_endian
287
293
  options[:assume_utf16_is_big_endian]
288
294
  end
289
295
 
290
- def encode_utf_16(body)
291
- if body.bytesize >= 2
292
- if body.getbyte(0) == 0xFF && body.getbyte(1) == 0xFE
293
- return body.force_encoding("UTF-16LE")
294
- elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
295
- return body.force_encoding("UTF-16BE")
296
- end
297
- end
298
-
299
- if assume_utf16_is_big_endian
300
- body.force_encoding("UTF-16BE")
301
- else
302
- body.force_encoding("UTF-16LE")
303
- end
304
- end
305
-
306
- def _encode_body(body)
307
- charset = get_charset
308
-
309
- if charset.nil?
310
- return body
311
- end
312
-
313
- if "utf-16".casecmp(charset) == 0
314
- encode_utf_16(body)
315
- else
316
- encode_with_ruby_encoding(body, charset)
317
- end
318
- end
319
-
320
- def encode_body(body)
321
- if "".respond_to?(:encoding)
322
- _encode_body(body)
323
- else
324
- body
325
- end
326
- end
327
-
328
- def handle_response(body, &block)
296
+ def handle_response(raw_body, &block)
329
297
  if response_redirects?
330
298
  options[:limit] -= 1
331
299
  if options[:logger]
@@ -346,15 +314,26 @@ module HTTParty
346
314
  capture_cookies(last_response)
347
315
  perform(&block)
348
316
  else
349
- body ||= last_response.body
350
- body = encode_body(body)
351
- 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)
352
331
  end
353
332
  end
354
333
 
355
334
  def handle_host_redirection
356
335
  check_duplicate_location_header
357
- redirect_path = options[:uri_adapter].parse last_response['location']
336
+ redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
358
337
  return if redirect_path.relative? || path.host == redirect_path.host
359
338
  @changed_hosts = true
360
339
  end
@@ -367,7 +346,7 @@ module HTTParty
367
346
  end
368
347
 
369
348
  def send_authorization_header?
370
- !@changed_hosts
349
+ !@changed_hosts
371
350
  end
372
351
 
373
352
  def response_redirects?
@@ -388,7 +367,7 @@ module HTTParty
388
367
  cookies_hash = HTTParty::CookieHash.new
389
368
  cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie']
390
369
  response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
391
-
370
+
392
371
  options[:headers] ||= {}
393
372
  options[:headers]['Cookie'] = cookies_hash.to_cookie_string
394
373
  end
@@ -423,5 +402,17 @@ module HTTParty
423
402
  @credentials_sent = true
424
403
  end
425
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
426
417
  end
427
418
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
1
5
  module HTTParty
2
6
  class Response #:nodoc:
3
7
  class Headers < ::SimpleDelegator
@@ -20,10 +24,10 @@ module HTTParty
20
24
  end
21
25
 
22
26
  def ==(other)
23
- if other.is_a?(::Net::HTTPHeader)
27
+ if other.is_a?(::Net::HTTPHeader)
24
28
  @header == other.instance_variable_get(:@header)
25
29
  elsif other.is_a?(Hash)
26
- @header == other || @header == Headers.new(other).instance_variable_get(:@header)
30
+ @header == other || @header == Headers.new(other).instance_variable_get(:@header)
27
31
  end
28
32
  end
29
33
  end
@@ -1,9 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTParty
2
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
 
@@ -29,20 +41,24 @@ module HTTParty
29
41
  response.code.to_i
30
42
  end
31
43
 
44
+ def http_version
45
+ response.http_version
46
+ end
47
+
32
48
  def tap
33
49
  yield self
34
50
  self
35
51
  end
36
52
 
37
53
  def inspect
38
- inspect_id = ::Kernel::format "%x", (object_id * 2)
54
+ inspect_id = ::Kernel::format '%x', (object_id * 2)
39
55
  %(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
40
56
  end
41
57
 
42
58
  CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
43
59
 
44
60
  CODES_TO_OBJ.each do |response_code, klass|
45
- name = klass.name.sub("Net::HTTP", '')
61
+ name = klass.name.sub('Net::HTTP', '')
46
62
  name = "#{underscore(name)}?".to_sym
47
63
 
48
64
  define_method(name) do
@@ -51,28 +67,46 @@ module HTTParty
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
 
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
+
58
83
  def nil?
84
+ warn_about_nil_deprecation
59
85
  response.nil? || response.body.nil? || response.body.empty?
60
86
  end
61
87
 
62
- def to_s
88
+ def to_s
63
89
  if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s)
64
90
  response.body.to_s
65
- else
91
+ else
66
92
  inspect
67
93
  end
68
94
  end
69
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
+
70
104
  def display(port=$>)
71
105
  if !parsed_response.nil? && parsed_response.respond_to?(:display)
72
106
  parsed_response.display(port)
73
107
  elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
74
108
  response.body.display(port)
75
- else
109
+ else
76
110
  port.write(inspect)
77
111
  end
78
112
  end
@@ -81,7 +115,11 @@ module HTTParty
81
115
  return true if super
82
116
  parsed_response.respond_to?(name) || response.respond_to?(name)
83
117
  end
84
-
118
+
119
+ def _dump(_level)
120
+ Marshal.dump([request, response, parsed_response, body])
121
+ end
122
+
85
123
  protected
86
124
 
87
125
  def method_missing(name, *args, &block)
@@ -99,6 +137,19 @@ module HTTParty
99
137
  ::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
100
138
  end
101
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
102
153
  end
103
154
  end
104
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
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTParty
4
+ class TextEncoder
5
+ attr_reader :text, :content_type, :assume_utf16_is_big_endian
6
+
7
+ def initialize(text, assume_utf16_is_big_endian: true, content_type: nil)
8
+ @text = +text
9
+ @content_type = content_type
10
+ @assume_utf16_is_big_endian = assume_utf16_is_big_endian
11
+ end
12
+
13
+ def call
14
+ if can_encode?
15
+ encoded_text
16
+ else
17
+ text
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def can_encode?
24
+ ''.respond_to?(:encoding) && charset
25
+ end
26
+
27
+ def encoded_text
28
+ if 'utf-16'.casecmp(charset) == 0
29
+ encode_utf_16
30
+ else
31
+ encode_with_ruby_encoding
32
+ end
33
+ end
34
+
35
+ def encode_utf_16
36
+ if text.bytesize >= 2
37
+ if text.getbyte(0) == 0xFF && text.getbyte(1) == 0xFE
38
+ return text.force_encoding('UTF-16LE')
39
+ elsif text.getbyte(0) == 0xFE && text.getbyte(1) == 0xFF
40
+ return text.force_encoding('UTF-16BE')
41
+ end
42
+ end
43
+
44
+ if assume_utf16_is_big_endian # option
45
+ text.force_encoding('UTF-16BE')
46
+ else
47
+ text.force_encoding('UTF-16LE')
48
+ end
49
+ end
50
+
51
+ def encode_with_ruby_encoding
52
+ # NOTE: This will raise an argument error if the
53
+ # charset does not exist
54
+ encoding = Encoding.find(charset)
55
+ text.force_encoding(encoding.to_s)
56
+ rescue ArgumentError
57
+ text
58
+ end
59
+
60
+ def charset
61
+ return nil if content_type.nil?
62
+
63
+ if (matchdata = content_type.match(/;\s*charset\s*=\s*([^=,;"\s]+)/i))
64
+ return matchdata.captures.first
65
+ end
66
+
67
+ if (matchdata = content_type.match(/;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i))
68
+ return matchdata.captures.first.gsub(/\\(.)/, '\1')
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTParty
4
+ module Utils
5
+ def self.stringify_keys(hash)
6
+ return hash.transform_keys(&:to_s) if hash.respond_to?(:transform_keys)
7
+
8
+ hash.each_with_object({}) do |(key, value), new_hash|
9
+ new_hash[key.to_s] = value
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTParty
2
- VERSION = "0.15.0"
4
+ VERSION = '0.21.0'
3
5
  end