httparty 0.16.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of httparty might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.editorconfig +18 -0
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +1 -0
- data/.rubocop_todo.yml +1 -1
- data/Changelog.md +72 -0
- data/Gemfile +5 -0
- data/README.md +5 -5
- data/docs/README.md +70 -5
- data/examples/README.md +28 -11
- data/examples/aaws.rb +6 -2
- data/examples/body_stream.rb +14 -0
- data/examples/idn.rb +10 -0
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/stream_download.rb +8 -2
- data/httparty.gemspec +3 -3
- data/lib/httparty/connection_adapter.rb +59 -16
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +92 -0
- data/lib/httparty/exceptions.rb +3 -1
- data/lib/httparty/hash_conversions.rb +10 -4
- data/lib/httparty/headers_processor.rb +32 -0
- data/lib/httparty/logger/apache_formatter.rb +31 -6
- data/lib/httparty/logger/curl_formatter.rb +9 -7
- data/lib/httparty/logger/logger.rb +5 -1
- data/lib/httparty/logger/logstash_formatter.rb +61 -0
- data/lib/httparty/module_inheritable_attributes.rb +6 -4
- data/lib/httparty/net_digest_auth.rb +15 -15
- data/lib/httparty/parser.rb +9 -5
- data/lib/httparty/request/body.rb +46 -27
- data/lib/httparty/request/multipart_boundary.rb +2 -0
- data/lib/httparty/request.rb +75 -96
- data/lib/httparty/response/headers.rb +4 -2
- data/lib/httparty/response.rb +51 -8
- data/lib/httparty/response_fragment.rb +21 -0
- data/lib/httparty/text_encoder.rb +72 -0
- data/lib/httparty/utils.rb +13 -0
- data/lib/httparty/version.rb +3 -1
- data/lib/httparty.rb +70 -24
- data/website/css/common.css +1 -1
- metadata +33 -104
- data/.travis.yml +0 -10
- data/features/basic_authentication.feature +0 -20
- data/features/command_line.feature +0 -95
- data/features/deals_with_http_error_codes.feature +0 -26
- data/features/digest_authentication.feature +0 -30
- data/features/handles_compressed_responses.feature +0 -27
- data/features/handles_multiple_formats.feature +0 -57
- data/features/steps/env.rb +0 -27
- data/features/steps/httparty_response_steps.rb +0 -56
- data/features/steps/httparty_steps.rb +0 -43
- data/features/steps/mongrel_helper.rb +0 -127
- data/features/steps/remote_service_steps.rb +0 -92
- data/features/supports_read_timeout_option.feature +0 -13
- data/features/supports_redirection.feature +0 -22
- data/features/supports_timeout_option.feature +0 -13
- data/spec/fixtures/delicious.xml +0 -23
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +0 -3
- data/spec/fixtures/ssl/generate.sh +0 -29
- data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
- data/spec/fixtures/ssl/generated/ca.crt +0 -16
- data/spec/fixtures/ssl/generated/ca.key +0 -15
- data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
- data/spec/fixtures/ssl/generated/server.crt +0 -13
- data/spec/fixtures/ssl/generated/server.key +0 -15
- data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
- data/spec/fixtures/tiny.gif +0 -0
- data/spec/fixtures/twitter.csv +0 -2
- data/spec/fixtures/twitter.json +0 -1
- data/spec/fixtures/twitter.xml +0 -403
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
- data/spec/httparty/connection_adapter_spec.rb +0 -498
- data/spec/httparty/cookie_hash_spec.rb +0 -100
- data/spec/httparty/exception_spec.rb +0 -45
- data/spec/httparty/hash_conversions_spec.rb +0 -56
- data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
- data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
- data/spec/httparty/logger/logger_spec.rb +0 -38
- data/spec/httparty/net_digest_auth_spec.rb +0 -270
- data/spec/httparty/parser_spec.rb +0 -190
- data/spec/httparty/request/body_spec.rb +0 -60
- data/spec/httparty/request_spec.rb +0 -1312
- data/spec/httparty/response_spec.rb +0 -347
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -896
- data/spec/spec_helper.rb +0 -51
- data/spec/support/ssl_test_helper.rb +0 -47
- data/spec/support/ssl_test_server.rb +0 -80
- data/spec/support/stub_response.rb +0 -49
data/lib/httparty/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
# The default parser used by HTTParty, supports xml, json, html, csv and
|
3
5
|
# plain text.
|
@@ -101,7 +103,7 @@ module HTTParty
|
|
101
103
|
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
|
102
104
|
def parse
|
103
105
|
return nil if body.nil?
|
104
|
-
return nil if body ==
|
106
|
+
return nil if body == 'null'
|
105
107
|
return nil if body.valid_encoding? && body.strip.empty?
|
106
108
|
if body.valid_encoding? && body.encoding == Encoding::UTF_8
|
107
109
|
@body = body.gsub(/\A#{UTF8_BOM}/, '')
|
@@ -119,7 +121,7 @@ module HTTParty
|
|
119
121
|
MultiXml.parse(body)
|
120
122
|
end
|
121
123
|
|
122
|
-
UTF8_BOM = "\xEF\xBB\xBF"
|
124
|
+
UTF8_BOM = "\xEF\xBB\xBF"
|
123
125
|
|
124
126
|
def json
|
125
127
|
JSON.parse(body, :quirks_mode => true, :allow_nan => true)
|
@@ -142,9 +144,11 @@ module HTTParty
|
|
142
144
|
end
|
143
145
|
|
144
146
|
def parse_supported_format
|
145
|
-
|
146
|
-
|
147
|
-
|
147
|
+
if respond_to?(format, true)
|
148
|
+
send(format)
|
149
|
+
else
|
150
|
+
raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format."
|
151
|
+
end
|
148
152
|
end
|
149
153
|
end
|
150
154
|
end
|
@@ -1,11 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'multipart_boundary'
|
2
4
|
|
3
5
|
module HTTParty
|
4
6
|
class Request
|
5
7
|
class Body
|
6
|
-
|
8
|
+
NEWLINE = "\r\n"
|
9
|
+
private_constant :NEWLINE
|
10
|
+
|
11
|
+
def initialize(params, query_string_normalizer: nil, force_multipart: false)
|
7
12
|
@params = params
|
8
13
|
@query_string_normalizer = query_string_normalizer
|
14
|
+
@force_multipart = force_multipart
|
9
15
|
end
|
10
16
|
|
11
17
|
def call
|
@@ -21,7 +27,7 @@ module HTTParty
|
|
21
27
|
end
|
22
28
|
|
23
29
|
def multipart?
|
24
|
-
params.respond_to?(:to_hash) && has_file?(params
|
30
|
+
params.respond_to?(:to_hash) && (force_multipart || has_file?(params))
|
25
31
|
end
|
26
32
|
|
27
33
|
private
|
@@ -29,40 +35,34 @@ module HTTParty
|
|
29
35
|
def generate_multipart
|
30
36
|
normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
|
31
37
|
|
32
|
-
multipart = normalized_params.inject('') do |memo, (key, value)|
|
33
|
-
memo
|
34
|
-
memo
|
38
|
+
multipart = normalized_params.inject(''.dup) do |memo, (key, value)|
|
39
|
+
memo << "--#{boundary}#{NEWLINE}"
|
40
|
+
memo << %(Content-Disposition: form-data; name="#{key}")
|
35
41
|
# value.path is used to support ActionDispatch::Http::UploadedFile
|
36
42
|
# https://github.com/jnunemaker/httparty/pull/585
|
37
|
-
memo
|
38
|
-
memo
|
39
|
-
memo
|
40
|
-
memo
|
41
|
-
memo
|
42
|
-
memo
|
43
|
+
memo << %(; filename="#{file_name(value)}") if file?(value)
|
44
|
+
memo << NEWLINE
|
45
|
+
memo << "Content-Type: #{content_type(value)}#{NEWLINE}" if file?(value)
|
46
|
+
memo << NEWLINE
|
47
|
+
memo << content_body(value)
|
48
|
+
memo << NEWLINE
|
43
49
|
end
|
44
50
|
|
45
|
-
multipart
|
51
|
+
multipart << "--#{boundary}--#{NEWLINE}"
|
46
52
|
end
|
47
53
|
|
48
|
-
def has_file?(
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
file?(value)
|
56
|
-
end
|
54
|
+
def has_file?(value)
|
55
|
+
if value.respond_to?(:to_hash)
|
56
|
+
value.to_hash.any? { |_, v| has_file?(v) }
|
57
|
+
elsif value.respond_to?(:to_ary)
|
58
|
+
value.to_ary.any? { |v| has_file?(v) }
|
59
|
+
else
|
60
|
+
file?(value)
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
64
|
def file?(object)
|
61
|
-
object.respond_to?(:path) && object.respond_to?(:read)
|
62
|
-
end
|
63
|
-
|
64
|
-
def includes_hash?(object)
|
65
|
-
object.respond_to?(:to_ary) && object.any? { |e| e.respond_to?(:hash) }
|
65
|
+
object.respond_to?(:path) && object.respond_to?(:read)
|
66
66
|
end
|
67
67
|
|
68
68
|
def normalize_query(query)
|
@@ -73,7 +73,26 @@ module HTTParty
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
|
76
|
+
def content_body(object)
|
77
|
+
if file?(object)
|
78
|
+
object = (file = object).read
|
79
|
+
file.rewind if file.respond_to?(:rewind)
|
80
|
+
end
|
81
|
+
|
82
|
+
object.to_s
|
83
|
+
end
|
84
|
+
|
85
|
+
def content_type(object)
|
86
|
+
return object.content_type if object.respond_to?(:content_type)
|
87
|
+
mime = MIME::Types.type_for(object.path)
|
88
|
+
mime.empty? ? 'application/octet-stream' : mime[0].content_type
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_name(object)
|
92
|
+
object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path)
|
93
|
+
end
|
94
|
+
|
95
|
+
attr_reader :params, :query_string_normalizer, :force_multipart
|
77
96
|
end
|
78
97
|
end
|
79
98
|
end
|
data/lib/httparty/request.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
|
-
require 'httparty/request/body'
|
3
4
|
|
4
5
|
module HTTParty
|
5
6
|
class Request #:nodoc:
|
@@ -14,6 +15,8 @@ module HTTParty
|
|
14
15
|
Net::HTTP::Move,
|
15
16
|
Net::HTTP::Copy,
|
16
17
|
Net::HTTP::Mkcol,
|
18
|
+
Net::HTTP::Lock,
|
19
|
+
Net::HTTP::Unlock,
|
17
20
|
]
|
18
21
|
|
19
22
|
SupportedURISchemes = ['http', 'https', 'webcal', nil]
|
@@ -43,6 +46,11 @@ module HTTParty
|
|
43
46
|
end.flatten.join('&')
|
44
47
|
end
|
45
48
|
|
49
|
+
def self._load(data)
|
50
|
+
http_method, path, options = Marshal.load(data)
|
51
|
+
new(http_method, path, options)
|
52
|
+
end
|
53
|
+
|
46
54
|
attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
|
47
55
|
attr_reader :path
|
48
56
|
|
@@ -70,7 +78,7 @@ module HTTParty
|
|
70
78
|
@path = if uri.is_a?(uri_adapter)
|
71
79
|
uri
|
72
80
|
elsif String.try_convert(uri)
|
73
|
-
uri_adapter.parse
|
81
|
+
uri_adapter.parse(uri).normalize
|
74
82
|
else
|
75
83
|
raise ArgumentError,
|
76
84
|
"bad argument (expected #{uri_adapter} object or URI string)"
|
@@ -86,17 +94,17 @@ module HTTParty
|
|
86
94
|
end
|
87
95
|
|
88
96
|
def uri
|
89
|
-
if redirect && path.relative? && path.path[0] !=
|
90
|
-
last_uri_host = @last_uri.path.gsub(/[^\/]+$/,
|
97
|
+
if redirect && path.relative? && path.path[0] != '/'
|
98
|
+
last_uri_host = @last_uri.path.gsub(/[^\/]+$/, '')
|
91
99
|
|
92
|
-
path.path = "/#{path.path}" if last_uri_host[-1] !=
|
93
|
-
path.path = last_uri_host
|
100
|
+
path.path = "/#{path.path}" if last_uri_host[-1] != '/'
|
101
|
+
path.path = "#{last_uri_host}#{path.path}"
|
94
102
|
end
|
95
103
|
|
96
104
|
if path.relative? && path.host
|
97
|
-
new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}")
|
105
|
+
new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}").normalize
|
98
106
|
elsif path.relative?
|
99
|
-
new_uri = options[:uri_adapter].parse("#{base_uri}#{path}")
|
107
|
+
new_uri = options[:uri_adapter].parse("#{base_uri}#{path}").normalize
|
100
108
|
else
|
101
109
|
new_uri = path.clone
|
102
110
|
end
|
@@ -116,7 +124,7 @@ module HTTParty
|
|
116
124
|
def base_uri
|
117
125
|
if redirect
|
118
126
|
base_uri = "#{@last_uri.scheme}://#{@last_uri.host}"
|
119
|
-
base_uri
|
127
|
+
base_uri = "#{base_uri}:#{@last_uri.port}" if @last_uri.port != 80
|
120
128
|
base_uri
|
121
129
|
else
|
122
130
|
options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri])
|
@@ -139,21 +147,22 @@ module HTTParty
|
|
139
147
|
validate
|
140
148
|
setup_raw_request
|
141
149
|
chunked_body = nil
|
150
|
+
current_http = http
|
142
151
|
|
143
|
-
self.last_response =
|
152
|
+
self.last_response = current_http.request(@raw_request) do |http_response|
|
144
153
|
if block
|
145
154
|
chunks = []
|
146
155
|
|
147
156
|
http_response.read_body do |fragment|
|
148
|
-
|
149
|
-
|
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)
|
150
160
|
end
|
151
161
|
|
152
162
|
chunked_body = chunks.join
|
153
163
|
end
|
154
164
|
end
|
155
165
|
|
156
|
-
|
157
166
|
handle_host_redirection if response_redirects?
|
158
167
|
result = handle_unauthorized
|
159
168
|
result ||= handle_response(chunked_body, &block)
|
@@ -171,6 +180,13 @@ module HTTParty
|
|
171
180
|
@raw_request.body
|
172
181
|
end
|
173
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
|
+
|
174
190
|
private
|
175
191
|
|
176
192
|
def http
|
@@ -202,24 +218,22 @@ module HTTParty
|
|
202
218
|
end
|
203
219
|
|
204
220
|
def setup_raw_request
|
205
|
-
@raw_request = http_method.new(request_uri(uri))
|
206
|
-
@raw_request.body_stream = options[:body_stream] if options[:body_stream]
|
207
|
-
|
208
221
|
if options[:headers].respond_to?(:to_hash)
|
209
222
|
headers_hash = options[:headers].to_hash
|
210
|
-
|
211
|
-
|
212
|
-
# If the caller specified a header of 'Accept-Encoding', assume they want to
|
213
|
-
# deal with encoding of content. Disable the internal logic in Net:HTTP
|
214
|
-
# that handles encoding, if the platform supports it.
|
215
|
-
if @raw_request.respond_to?(:decode_content) && (headers_hash.key?('Accept-Encoding') || headers_hash.key?('accept-encoding'))
|
216
|
-
# Using the '[]=' sets decode_content to false
|
217
|
-
@raw_request['accept-encoding'] = @raw_request['accept-encoding']
|
218
|
-
end
|
223
|
+
else
|
224
|
+
headers_hash = nil
|
219
225
|
end
|
220
226
|
|
227
|
+
@raw_request = http_method.new(request_uri(uri), headers_hash)
|
228
|
+
@raw_request.body_stream = options[:body_stream] if options[:body_stream]
|
229
|
+
|
221
230
|
if options[:body]
|
222
|
-
body = Body.new(
|
231
|
+
body = Body.new(
|
232
|
+
options[:body],
|
233
|
+
query_string_normalizer: query_string_normalizer,
|
234
|
+
force_multipart: options[:multipart]
|
235
|
+
)
|
236
|
+
|
223
237
|
if body.multipart?
|
224
238
|
content_type = "multipart/form-data; boundary=#{body.boundary}"
|
225
239
|
@raw_request['Content-Type'] = content_type
|
@@ -227,6 +241,8 @@ module HTTParty
|
|
227
241
|
@raw_request.body = body.call
|
228
242
|
end
|
229
243
|
|
244
|
+
@raw_request.instance_variable_set(:@decode_content, decompress_content?)
|
245
|
+
|
230
246
|
if options[:basic_auth] && send_authorization_header?
|
231
247
|
@raw_request.basic_auth(username, password)
|
232
248
|
@credentials_sent = true
|
@@ -238,6 +254,10 @@ module HTTParty
|
|
238
254
|
!!options[:digest_auth]
|
239
255
|
end
|
240
256
|
|
257
|
+
def decompress_content?
|
258
|
+
!options[:skip_decompression]
|
259
|
+
end
|
260
|
+
|
241
261
|
def response_unauthorized?
|
242
262
|
!!last_response && last_response.code == '401'
|
243
263
|
end
|
@@ -261,79 +281,15 @@ module HTTParty
|
|
261
281
|
query_string_parts << options[:query] unless options[:query].nil?
|
262
282
|
end
|
263
283
|
|
264
|
-
query_string_parts.reject!(&:empty?) unless query_string_parts == [
|
284
|
+
query_string_parts.reject!(&:empty?) unless query_string_parts == ['']
|
265
285
|
query_string_parts.size > 0 ? query_string_parts.join('&') : nil
|
266
286
|
end
|
267
287
|
|
268
|
-
def get_charset
|
269
|
-
content_type = last_response["content-type"]
|
270
|
-
if content_type.nil?
|
271
|
-
return nil
|
272
|
-
end
|
273
|
-
|
274
|
-
if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
|
275
|
-
return $1
|
276
|
-
end
|
277
|
-
|
278
|
-
if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
|
279
|
-
return $1.gsub(/\\(.)/, '\1')
|
280
|
-
end
|
281
|
-
|
282
|
-
nil
|
283
|
-
end
|
284
|
-
|
285
|
-
def encode_with_ruby_encoding(body, charset)
|
286
|
-
# NOTE: This will raise an argument error if the
|
287
|
-
# charset does not exist
|
288
|
-
encoding = Encoding.find(charset)
|
289
|
-
body.force_encoding(encoding.to_s)
|
290
|
-
rescue ArgumentError
|
291
|
-
body
|
292
|
-
end
|
293
|
-
|
294
288
|
def assume_utf16_is_big_endian
|
295
289
|
options[:assume_utf16_is_big_endian]
|
296
290
|
end
|
297
291
|
|
298
|
-
def
|
299
|
-
if body.bytesize >= 2
|
300
|
-
if body.getbyte(0) == 0xFF && body.getbyte(1) == 0xFE
|
301
|
-
return body.force_encoding("UTF-16LE")
|
302
|
-
elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
|
303
|
-
return body.force_encoding("UTF-16BE")
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
if assume_utf16_is_big_endian
|
308
|
-
body.force_encoding("UTF-16BE")
|
309
|
-
else
|
310
|
-
body.force_encoding("UTF-16LE")
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
def _encode_body(body)
|
315
|
-
charset = get_charset
|
316
|
-
|
317
|
-
if charset.nil?
|
318
|
-
return body
|
319
|
-
end
|
320
|
-
|
321
|
-
if "utf-16".casecmp(charset) == 0
|
322
|
-
encode_utf_16(body)
|
323
|
-
else
|
324
|
-
encode_with_ruby_encoding(body, charset)
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
def encode_body(body)
|
329
|
-
if "".respond_to?(:encoding)
|
330
|
-
_encode_body(body)
|
331
|
-
else
|
332
|
-
body
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
def handle_response(body, &block)
|
292
|
+
def handle_response(raw_body, &block)
|
337
293
|
if response_redirects?
|
338
294
|
options[:limit] -= 1
|
339
295
|
if options[:logger]
|
@@ -354,15 +310,26 @@ module HTTParty
|
|
354
310
|
capture_cookies(last_response)
|
355
311
|
perform(&block)
|
356
312
|
else
|
357
|
-
|
358
|
-
|
359
|
-
|
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)
|
360
327
|
end
|
361
328
|
end
|
362
329
|
|
363
330
|
def handle_host_redirection
|
364
331
|
check_duplicate_location_header
|
365
|
-
redirect_path = options[:uri_adapter].parse
|
332
|
+
redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
|
366
333
|
return if redirect_path.relative? || path.host == redirect_path.host
|
367
334
|
@changed_hosts = true
|
368
335
|
end
|
@@ -431,5 +398,17 @@ module HTTParty
|
|
431
398
|
@credentials_sent = true
|
432
399
|
end
|
433
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
|
434
413
|
end
|
435
414
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'delegate'
|
2
4
|
|
3
5
|
module HTTParty
|
@@ -22,10 +24,10 @@ module HTTParty
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def ==(other)
|
25
|
-
if other.is_a?(::Net::HTTPHeader)
|
27
|
+
if other.is_a?(::Net::HTTPHeader)
|
26
28
|
@header == other.instance_variable_get(:@header)
|
27
29
|
elsif other.is_a?(Hash)
|
28
|
-
@header == other || @header == Headers.new(other).instance_variable_get(:@header)
|
30
|
+
@header == other || @header == Headers.new(other).instance_variable_get(:@header)
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
data/lib/httparty/response.rb
CHANGED
@@ -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(
|
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
|
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(
|
61
|
+
name = klass.name.sub('Net::HTTP', '')
|
46
62
|
name = "#{underscore(name)}?".to_sym
|
47
63
|
|
48
64
|
define_method(name) do
|
@@ -51,18 +67,28 @@ module HTTParty
|
|
51
67
|
end
|
52
68
|
|
53
69
|
# Support old multiple_choice? method from pre 2.0.0 era.
|
54
|
-
if ::RUBY_VERSION >=
|
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
|
@@ -80,7 +106,7 @@ module HTTParty
|
|
80
106
|
parsed_response.display(port)
|
81
107
|
elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
|
82
108
|
response.body.display(port)
|
83
|
-
else
|
109
|
+
else
|
84
110
|
port.write(inspect)
|
85
111
|
end
|
86
112
|
end
|
@@ -89,7 +115,11 @@ module HTTParty
|
|
89
115
|
return true if super
|
90
116
|
parsed_response.respond_to?(name) || response.respond_to?(name)
|
91
117
|
end
|
92
|
-
|
118
|
+
|
119
|
+
def _dump(_level)
|
120
|
+
Marshal.dump([request, response, parsed_response, body])
|
121
|
+
end
|
122
|
+
|
93
123
|
protected
|
94
124
|
|
95
125
|
def method_missing(name, *args, &block)
|
@@ -107,6 +137,19 @@ module HTTParty
|
|
107
137
|
::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
|
108
138
|
end
|
109
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
|
110
153
|
end
|
111
154
|
end
|
112
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.dup
|
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
|