httparty 0.13.7 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.editorconfig +18 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +1 -1
- data/{History → Changelog.md} +226 -59
- data/Gemfile +10 -3
- data/Guardfile +3 -2
- data/README.md +8 -7
- data/bin/httparty +3 -1
- data/docs/README.md +191 -0
- data/examples/README.md +34 -12
- data/examples/aaws.rb +7 -3
- data/examples/body_stream.rb +14 -0
- data/examples/crack.rb +1 -1
- data/examples/custom_parsers.rb +5 -1
- data/examples/delicious.rb +4 -4
- data/examples/headers_and_user_agents.rb +7 -3
- data/examples/idn.rb +10 -0
- data/examples/logging.rb +4 -4
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/stackexchange.rb +1 -1
- data/examples/stream_download.rb +26 -0
- data/examples/tripit_sign_in.rb +17 -6
- data/examples/twitter.rb +2 -2
- data/examples/whoismyrep.rb +1 -1
- data/httparty.gemspec +7 -5
- data/lib/httparty/connection_adapter.rb +86 -20
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +102 -0
- data/lib/httparty/exceptions.rb +8 -2
- data/lib/httparty/hash_conversions.rb +30 -8
- data/lib/httparty/headers_processor.rb +32 -0
- data/lib/httparty/logger/apache_formatter.rb +31 -6
- data/lib/httparty/logger/curl_formatter.rb +68 -23
- 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 +23 -21
- data/lib/httparty/parser.rb +25 -14
- data/lib/httparty/request/body.rb +105 -0
- data/lib/httparty/request/multipart_boundary.rb +13 -0
- data/lib/httparty/request.rb +160 -106
- data/lib/httparty/response/headers.rb +23 -19
- data/lib/httparty/response.rb +92 -13
- 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 +98 -35
- data/website/css/common.css +1 -1
- metadata +34 -114
- data/.simplecov +0 -1
- data/.travis.yml +0 -7
- 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 -52
- data/features/steps/httparty_steps.rb +0 -43
- data/features/steps/mongrel_helper.rb +0 -127
- data/features/steps/remote_service_steps.rb +0 -90
- 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/1fe462c2.0 +0 -16
- 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/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 -468
- data/spec/httparty/cookie_hash_spec.rb +0 -83
- data/spec/httparty/exception_spec.rb +0 -38
- data/spec/httparty/hash_conversions_spec.rb +0 -41
- data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
- data/spec/httparty/logger/curl_formatter_spec.rb +0 -18
- data/spec/httparty/logger/logger_spec.rb +0 -38
- data/spec/httparty/net_digest_auth_spec.rb +0 -230
- data/spec/httparty/parser_spec.rb +0 -173
- data/spec/httparty/request_spec.rb +0 -1073
- data/spec/httparty/response_spec.rb +0 -241
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -850
- data/spec/spec_helper.rb +0 -59
- 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/request.rb
CHANGED
@@ -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
|
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
|
104
|
+
path.path = "/#{path.path}" if last_uri_host[-1] != '/'
|
105
|
+
path.path = "#{last_uri_host}#{path.path}"
|
74
106
|
end
|
75
107
|
|
76
|
-
|
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
|
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 =
|
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
|
-
|
123
|
-
|
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
|
-
|
131
|
-
|
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
|
-
|
174
|
-
|
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
|
-
|
199
|
-
query_string_parts << options[:query] unless options[:query].nil?
|
228
|
+
headers_hash = nil
|
200
229
|
end
|
201
230
|
|
202
|
-
|
203
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
213
|
-
|
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
|
-
|
217
|
-
return $1.gsub(/\\(.)/, '\1')
|
218
|
-
end
|
248
|
+
@raw_request.instance_variable_set(:@decode_content, decompress_content?)
|
219
249
|
|
220
|
-
|
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
|
224
|
-
|
225
|
-
body.force_encoding(encoding)
|
226
|
-
rescue
|
227
|
-
body
|
257
|
+
def digest_auth?
|
258
|
+
!!options[:digest_auth]
|
228
259
|
end
|
229
260
|
|
230
|
-
def
|
231
|
-
options[:
|
261
|
+
def decompress_content?
|
262
|
+
!options[:skip_decompression]
|
232
263
|
end
|
233
264
|
|
234
|
-
def
|
235
|
-
|
236
|
-
|
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
|
-
|
244
|
-
|
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
|
251
|
-
|
273
|
+
def setup_digest_auth
|
274
|
+
@raw_request.digest_auth(username, password, last_response)
|
275
|
+
end
|
252
276
|
|
253
|
-
|
254
|
-
|
255
|
-
|
277
|
+
def query_string(uri)
|
278
|
+
query_string_parts = []
|
279
|
+
query_string_parts << uri.query unless uri.query.nil?
|
256
280
|
|
257
|
-
if
|
258
|
-
|
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
|
-
|
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
|
265
|
-
|
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(
|
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
|
-
|
294
|
-
|
295
|
-
|
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
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
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(
|
7
|
-
@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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
data/lib/httparty/response.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
|
-
class Response <
|
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(
|
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
|
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(
|
48
|
-
|
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 >=
|
70
|
+
if ::RUBY_VERSION >= '2.0.0' && ::RUBY_PLATFORM != 'java'
|
55
71
|
alias_method :multiple_choice?, :multiple_choices?
|
56
72
|
end
|
57
73
|
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|