rest-client 1.7.0.rc1-x64-mingw32

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 rest-client might be problematic. Click here for more details.

Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +14 -0
  5. data/AUTHORS +81 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +21 -0
  8. data/README.rdoc +325 -0
  9. data/Rakefile +117 -0
  10. data/bin/restclient +93 -0
  11. data/history.md +166 -0
  12. data/lib/rest-client.rb +2 -0
  13. data/lib/rest_client.rb +2 -0
  14. data/lib/restclient.rb +164 -0
  15. data/lib/restclient/abstract_response.rb +106 -0
  16. data/lib/restclient/exceptions.rb +203 -0
  17. data/lib/restclient/payload.rb +240 -0
  18. data/lib/restclient/platform.rb +30 -0
  19. data/lib/restclient/raw_response.rb +34 -0
  20. data/lib/restclient/request.rb +582 -0
  21. data/lib/restclient/resource.rb +169 -0
  22. data/lib/restclient/response.rb +24 -0
  23. data/lib/restclient/version.rb +7 -0
  24. data/lib/restclient/windows.rb +8 -0
  25. data/lib/restclient/windows/root_certs.rb +105 -0
  26. data/rest-client.gemspec +30 -0
  27. data/rest-client.windows.gemspec +19 -0
  28. data/spec/integration/capath_digicert/244b5494.0 +19 -0
  29. data/spec/integration/capath_digicert/81b9768f.0 +19 -0
  30. data/spec/integration/capath_digicert/README +8 -0
  31. data/spec/integration/capath_digicert/digicert.crt +19 -0
  32. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  33. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  34. data/spec/integration/capath_verisign/README +8 -0
  35. data/spec/integration/capath_verisign/verisign.crt +14 -0
  36. data/spec/integration/certs/digicert.crt +19 -0
  37. data/spec/integration/certs/verisign.crt +14 -0
  38. data/spec/integration/integration_spec.rb +35 -0
  39. data/spec/integration/request_spec.rb +104 -0
  40. data/spec/spec_helper.rb +12 -0
  41. data/spec/unit/abstract_response_spec.rb +85 -0
  42. data/spec/unit/exceptions_spec.rb +95 -0
  43. data/spec/unit/master_shake.jpg +0 -0
  44. data/spec/unit/payload_spec.rb +245 -0
  45. data/spec/unit/raw_response_spec.rb +17 -0
  46. data/spec/unit/request2_spec.rb +32 -0
  47. data/spec/unit/request_spec.rb +905 -0
  48. data/spec/unit/resource_spec.rb +133 -0
  49. data/spec/unit/response_spec.rb +166 -0
  50. data/spec/unit/restclient_spec.rb +79 -0
  51. data/spec/unit/windows/root_certs_spec.rb +22 -0
  52. metadata +241 -0
@@ -0,0 +1,30 @@
1
+ module RestClient
2
+ module Platform
3
+ # Return true if we are running on a darwin-based Ruby platform. This will
4
+ # be false for jruby even on OS X.
5
+ #
6
+ # @return [Boolean]
7
+ def self.mac?
8
+ RUBY_PLATFORM.include?('darwin')
9
+ end
10
+
11
+ # Return true if we are running on Windows.
12
+ #
13
+ # @return [Boolean]
14
+ #
15
+ def self.windows?
16
+ # Ruby only sets File::ALT_SEPARATOR on Windows, and the Ruby standard
17
+ # library uses that to test what platform it's on.
18
+ !!File::ALT_SEPARATOR
19
+ end
20
+
21
+ # Return true if we are running on jruby.
22
+ #
23
+ # @return [Boolean]
24
+ #
25
+ def self.jruby?
26
+ # defined on mri >= 1.9
27
+ RUBY_ENGINE == 'jruby'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ module RestClient
2
+ # The response from RestClient on a raw request looks like a string, but is
3
+ # actually one of these. 99% of the time you're making a rest call all you
4
+ # care about is the body, but on the occassion you want to fetch the
5
+ # headers you can:
6
+ #
7
+ # RestClient.get('http://example.com').headers[:content_type]
8
+ #
9
+ # In addition, if you do not use the response as a string, you can access
10
+ # a Tempfile object at res.file, which contains the path to the raw
11
+ # downloaded request body.
12
+ class RawResponse
13
+
14
+ include AbstractResponse
15
+
16
+ attr_reader :file
17
+
18
+ def initialize tempfile, net_http_res, args
19
+ @net_http_res = net_http_res
20
+ @args = args
21
+ @file = tempfile
22
+ end
23
+
24
+ def to_s
25
+ @file.open
26
+ @file.read
27
+ end
28
+
29
+ def size
30
+ File.size file
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,582 @@
1
+ require 'tempfile'
2
+ require 'mime/types'
3
+ require 'cgi'
4
+ require 'netrc'
5
+ require 'set'
6
+
7
+ module RestClient
8
+ # This class is used internally by RestClient to send the request, but you can also
9
+ # call it directly if you'd like to use a method not supported by the
10
+ # main API. For example:
11
+ #
12
+ # RestClient::Request.execute(:method => :head, :url => 'http://example.com')
13
+ #
14
+ # Mandatory parameters:
15
+ # * :method
16
+ # * :url
17
+ # Optional parameters (have a look at ssl and/or uri for some explanations):
18
+ # * :headers a hash containing the request headers
19
+ # * :cookies will replace possible cookies in the :headers
20
+ # * :user and :password for basic auth, will be replaced by a user/password available in the :url
21
+ # * :block_response call the provided block with the HTTPResponse as parameter
22
+ # * :raw_response return a low-level RawResponse instead of a Response
23
+ # * :max_redirects maximum number of redirections (default to 10)
24
+ # * :verify_ssl enable ssl verification, possible values are constants from
25
+ # OpenSSL::SSL::VERIFY_*, defaults to OpenSSL::SSL::VERIFY_PEER
26
+ # * :timeout and :open_timeout are how long to wait for a response and to
27
+ # open a connection, in seconds. Pass nil to disable the timeout.
28
+ # * :ssl_client_cert, :ssl_client_key, :ssl_ca_file, :ssl_ca_path,
29
+ # :ssl_cert_store, :ssl_verify_callback, :ssl_verify_callback_warnings
30
+ # * :ssl_version specifies the SSL version for the underlying Net::HTTP connection
31
+ # * :ssl_ciphers sets SSL ciphers for the connection. See
32
+ # OpenSSL::SSL::SSLContext#ciphers=
33
+ class Request
34
+
35
+ attr_reader :method, :url, :headers, :cookies,
36
+ :payload, :user, :password, :timeout, :max_redirects,
37
+ :open_timeout, :raw_response, :processed_headers, :args,
38
+ :ssl_opts
39
+
40
+ def self.execute(args, & block)
41
+ new(args).execute(& block)
42
+ end
43
+
44
+ # This is similar to the list now in ruby core, but adds HIGH and RC4-MD5
45
+ # for better compatibility (similar to Firefox) and moves AES-GCM cipher
46
+ # suites above DHE/ECDHE CBC suites (similar to Chromium).
47
+ # https://github.com/ruby/ruby/commit/699b209cf8cf11809620e12985ad33ae33b119ee
48
+ #
49
+ # This list will be used by default if the Ruby global OpenSSL default
50
+ # ciphers appear to be a weak list.
51
+ DefaultCiphers = %w{
52
+ !aNULL
53
+ !eNULL
54
+ !EXPORT
55
+ !SSLV2
56
+ !LOW
57
+
58
+ ECDHE-ECDSA-AES128-GCM-SHA256
59
+ ECDHE-RSA-AES128-GCM-SHA256
60
+ ECDHE-ECDSA-AES256-GCM-SHA384
61
+ ECDHE-RSA-AES256-GCM-SHA384
62
+ DHE-RSA-AES128-GCM-SHA256
63
+ DHE-DSS-AES128-GCM-SHA256
64
+ DHE-RSA-AES256-GCM-SHA384
65
+ DHE-DSS-AES256-GCM-SHA384
66
+ AES128-GCM-SHA256
67
+ AES256-GCM-SHA384
68
+ ECDHE-ECDSA-AES128-SHA256
69
+ ECDHE-RSA-AES128-SHA256
70
+ ECDHE-ECDSA-AES128-SHA
71
+ ECDHE-RSA-AES128-SHA
72
+ ECDHE-ECDSA-AES256-SHA384
73
+ ECDHE-RSA-AES256-SHA384
74
+ ECDHE-ECDSA-AES256-SHA
75
+ ECDHE-RSA-AES256-SHA
76
+ DHE-RSA-AES128-SHA256
77
+ DHE-RSA-AES256-SHA256
78
+ DHE-RSA-AES128-SHA
79
+ DHE-RSA-AES256-SHA
80
+ DHE-DSS-AES128-SHA256
81
+ DHE-DSS-AES256-SHA256
82
+ DHE-DSS-AES128-SHA
83
+ DHE-DSS-AES256-SHA
84
+ AES128-SHA256
85
+ AES256-SHA256
86
+ AES128-SHA
87
+ AES256-SHA
88
+ ECDHE-ECDSA-RC4-SHA
89
+ ECDHE-RSA-RC4-SHA
90
+ RC4-SHA
91
+
92
+ HIGH
93
+ +RC4
94
+ RC4-MD5
95
+ }.join(":")
96
+
97
+ # A set of weak default ciphers that we will override by default.
98
+ WeakDefaultCiphers = Set.new([
99
+ "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
100
+ ])
101
+
102
+ SSLOptionList = %w{client_cert client_key ca_file ca_path cert_store
103
+ version ciphers verify_callback verify_callback_warnings}
104
+
105
+ def initialize args
106
+ @method = args[:method] or raise ArgumentError, "must pass :method"
107
+ @headers = args[:headers] || {}
108
+ if args[:url]
109
+ @url = process_url_params(args[:url], headers)
110
+ else
111
+ raise ArgumentError, "must pass :url"
112
+ end
113
+ @cookies = @headers.delete(:cookies) || args[:cookies] || {}
114
+ @payload = Payload.generate(args[:payload])
115
+ @user = args[:user]
116
+ @password = args[:password]
117
+ if args.include?(:timeout)
118
+ @timeout = args[:timeout]
119
+ end
120
+ if args.include?(:open_timeout)
121
+ @open_timeout = args[:open_timeout]
122
+ end
123
+ @block_response = args[:block_response]
124
+ @raw_response = args[:raw_response] || false
125
+
126
+ @ssl_opts = {}
127
+
128
+ if args.include?(:verify_ssl)
129
+ v_ssl = args.fetch(:verify_ssl)
130
+ if v_ssl
131
+ if v_ssl == true
132
+ # interpret :verify_ssl => true as VERIFY_PEER
133
+ @ssl_opts[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
134
+ else
135
+ # otherwise pass through any truthy values
136
+ @ssl_opts[:verify_ssl] = v_ssl
137
+ end
138
+ else
139
+ # interpret all falsy :verify_ssl values as VERIFY_NONE
140
+ @ssl_opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
141
+ end
142
+ else
143
+ # if :verify_ssl was not passed, default to VERIFY_PEER
144
+ @ssl_opts[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
145
+ end
146
+
147
+ SSLOptionList.each do |key|
148
+ source_key = ('ssl_' + key).to_sym
149
+ if args.has_key?(source_key)
150
+ @ssl_opts[key.to_sym] = args.fetch(source_key)
151
+ end
152
+ end
153
+
154
+ # If there's no CA file, CA path, or cert store provided, use default
155
+ if !ssl_ca_file && !ssl_ca_path && !@ssl_opts.include?(:cert_store)
156
+ @ssl_opts[:cert_store] = self.class.default_ssl_cert_store
157
+ end
158
+
159
+ unless @ssl_opts.include?(:ciphers)
160
+ # If we're on a Ruby version that has insecure default ciphers,
161
+ # override it with our default list.
162
+ if WeakDefaultCiphers.include?(
163
+ OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.fetch(:ciphers))
164
+ @ssl_opts[:ciphers] = DefaultCiphers
165
+ end
166
+ end
167
+
168
+ @tf = nil # If you are a raw request, this is your tempfile
169
+ @max_redirects = args[:max_redirects] || 10
170
+ @processed_headers = make_headers headers
171
+ @args = args
172
+ end
173
+
174
+ def execute & block
175
+ uri = parse_url_with_auth(url)
176
+ transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, & block
177
+ ensure
178
+ payload.close if payload
179
+ end
180
+
181
+ # SSL-related options
182
+ def verify_ssl
183
+ @ssl_opts.fetch(:verify_ssl)
184
+ end
185
+ SSLOptionList.each do |key|
186
+ define_method('ssl_' + key) do
187
+ @ssl_opts[key.to_sym]
188
+ end
189
+ end
190
+
191
+ # Extract the query parameters and append them to the url
192
+ def process_url_params url, headers
193
+ url_params = {}
194
+ headers.delete_if do |key, value|
195
+ if 'params' == key.to_s.downcase && value.is_a?(Hash)
196
+ url_params.merge! value
197
+ true
198
+ else
199
+ false
200
+ end
201
+ end
202
+ unless url_params.empty?
203
+ query_string = url_params.collect { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
204
+ url + "?#{query_string}"
205
+ else
206
+ url
207
+ end
208
+ end
209
+
210
+ def make_headers user_headers
211
+ unless @cookies.empty?
212
+
213
+ # Validate that the cookie names and values look sane. If you really
214
+ # want to pass scary characters, just set the Cookie header directly.
215
+ # RFC6265 is actually much more restrictive than we are.
216
+ @cookies.each do |key, val|
217
+ unless valid_cookie_key?(key)
218
+ raise ArgumentError.new("Invalid cookie name: #{key.inspect}")
219
+ end
220
+ unless valid_cookie_value?(val)
221
+ raise ArgumentError.new("Invalid cookie value: #{val.inspect}")
222
+ end
223
+ end
224
+
225
+ user_headers[:cookie] = @cookies.map { |key, val| "#{key}=#{val}" }.sort.join('; ')
226
+ end
227
+ headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
228
+ headers.merge!(@payload.headers) if @payload
229
+ headers
230
+ end
231
+
232
+ # Do some sanity checks on cookie keys.
233
+ #
234
+ # Properly it should be a valid TOKEN per RFC 2616, but lots of servers are
235
+ # more liberal.
236
+ #
237
+ # Disallow the empty string as well as keys containing control characters,
238
+ # equals sign, semicolon, comma, or space.
239
+ #
240
+ def valid_cookie_key?(string)
241
+ return false if string.empty?
242
+
243
+ ! Regexp.new('[\x0-\x1f\x7f=;, ]').match(string)
244
+ end
245
+
246
+ # Validate cookie values. Rather than following RFC 6265, allow anything
247
+ # but control characters, comma, and semicolon.
248
+ def valid_cookie_value?(value)
249
+ ! Regexp.new('[\x0-\x1f\x7f,;]').match(value)
250
+ end
251
+
252
+ def net_http_class
253
+ if RestClient.proxy
254
+ proxy_uri = URI.parse(RestClient.proxy)
255
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
256
+ else
257
+ Net::HTTP
258
+ end
259
+ end
260
+
261
+ def net_http_request_class(method)
262
+ Net::HTTP.const_get(method.to_s.capitalize)
263
+ end
264
+
265
+ def net_http_do_request(http, req, body=nil, &block)
266
+ if body != nil && body.respond_to?(:read)
267
+ req.body_stream = body
268
+ return http.request(req, nil, &block)
269
+ else
270
+ return http.request(req, body, &block)
271
+ end
272
+ end
273
+
274
+ def parse_url(url)
275
+ url = "http://#{url}" unless url.match(/^http/)
276
+ URI.parse(url)
277
+ end
278
+
279
+ def parse_url_with_auth(url)
280
+ uri = parse_url(url)
281
+ @user = CGI.unescape(uri.user) if uri.user
282
+ @password = CGI.unescape(uri.password) if uri.password
283
+ if !@user && !@password
284
+ @user, @password = Netrc.read[uri.host]
285
+ end
286
+ uri
287
+ end
288
+
289
+ def process_payload(p=nil, parent_key=nil)
290
+ unless p.is_a?(Hash)
291
+ p
292
+ else
293
+ @headers[:content_type] ||= 'application/x-www-form-urlencoded'
294
+ p.keys.map do |k|
295
+ key = parent_key ? "#{parent_key}[#{k}]" : k
296
+ if p[k].is_a? Hash
297
+ process_payload(p[k], key)
298
+ else
299
+ value = parser.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
300
+ "#{key}=#{value}"
301
+ end
302
+ end.join("&")
303
+ end
304
+ end
305
+
306
+ # Return a certificate store that can be used to validate certificates with
307
+ # the system certificate authorities. This will probably not do anything on
308
+ # OS X, which monkey patches OpenSSL in terrible ways to insert its own
309
+ # validation. On most *nix platforms, this will add the system certifcates
310
+ # using OpenSSL::X509::Store#set_default_paths. On Windows, this will use
311
+ # RestClient::Windows::RootCerts to look up the CAs trusted by the system.
312
+ #
313
+ # @return [OpenSSL::X509::Store]
314
+ #
315
+ def self.default_ssl_cert_store
316
+ cert_store = OpenSSL::X509::Store.new
317
+ cert_store.set_default_paths
318
+
319
+ # set_default_paths() doesn't do anything on Windows, so look up
320
+ # certificates using the win32 API.
321
+ if RestClient::Platform.windows?
322
+ RestClient::Windows::RootCerts.instance.to_a.uniq.each do |cert|
323
+ cert_store.add_cert(cert)
324
+ end
325
+ end
326
+
327
+ cert_store
328
+ end
329
+
330
+ def print_verify_callback_warnings
331
+ warned = false
332
+ if RestClient::Platform.mac?
333
+ warn('warning: ssl_verify_callback return code is ignored on OS X')
334
+ warned = true
335
+ end
336
+ if RestClient::Platform.jruby?
337
+ warn('warning: SSL verify_callback may not work correctly in jruby')
338
+ warn('see https://github.com/jruby/jruby/issues/597')
339
+ warned = true
340
+ end
341
+ warned
342
+ end
343
+
344
+ def transmit uri, req, payload, & block
345
+ setup_credentials req
346
+
347
+ net = net_http_class.new(uri.host, uri.port)
348
+ net.use_ssl = uri.is_a?(URI::HTTPS)
349
+ net.ssl_version = ssl_version if ssl_version
350
+ net.ciphers = ssl_ciphers if ssl_ciphers
351
+
352
+ net.verify_mode = verify_ssl
353
+
354
+ net.cert = ssl_client_cert if ssl_client_cert
355
+ net.key = ssl_client_key if ssl_client_key
356
+ net.ca_file = ssl_ca_file if ssl_ca_file
357
+ net.ca_path = ssl_ca_path if ssl_ca_path
358
+ net.cert_store = ssl_cert_store if ssl_cert_store
359
+
360
+ # We no longer rely on net.verify_callback for the main SSL verification
361
+ # because it's not well supported on all platforms (see comments below).
362
+ # But do allow users to set one if they want.
363
+ if ssl_verify_callback
364
+ net.verify_callback = ssl_verify_callback
365
+
366
+ # Hilariously, jruby only calls the callback when cert_store is set to
367
+ # something, so make sure to set one.
368
+ # https://github.com/jruby/jruby/issues/597
369
+ if RestClient::Platform.jruby?
370
+ net.cert_store ||= OpenSSL::X509::Store.new
371
+ end
372
+
373
+ if ssl_verify_callback_warnings != false
374
+ if print_verify_callback_warnings
375
+ warn('pass :ssl_verify_callback_warnings => false to silence this')
376
+ end
377
+ end
378
+ end
379
+
380
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE
381
+ warn('WARNING: OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE')
382
+ warn('This dangerous monkey patch leaves you open to MITM attacks!')
383
+ warn('Try passing :verify_ssl => false instead.')
384
+ end
385
+
386
+ if defined? @timeout
387
+ if @timeout == -1
388
+ warn 'To disable read timeouts, please set timeout to nil instead of -1'
389
+ @timeout = nil
390
+ end
391
+ net.read_timeout = @timeout
392
+ end
393
+ if defined? @open_timeout
394
+ if @open_timeout == -1
395
+ warn 'To disable open timeouts, please set open_timeout to nil instead of -1'
396
+ @open_timeout = nil
397
+ end
398
+ net.open_timeout = @open_timeout
399
+ end
400
+
401
+ RestClient.before_execution_procs.each do |before_proc|
402
+ before_proc.call(req, args)
403
+ end
404
+
405
+ log_request
406
+
407
+
408
+ net.start do |http|
409
+ if @block_response
410
+ net_http_do_request(http, req, payload ? payload.to_s : nil,
411
+ & @block_response)
412
+ else
413
+ res = net_http_do_request(http, req, payload ? payload.to_s : nil) \
414
+ { |http_response| fetch_body(http_response) }
415
+ log_response res
416
+ process_result res, & block
417
+ end
418
+ end
419
+ rescue EOFError
420
+ raise RestClient::ServerBrokeConnection
421
+ rescue Timeout::Error, Errno::ETIMEDOUT
422
+ raise RestClient::RequestTimeout
423
+
424
+ rescue OpenSSL::SSL::SSLError => error
425
+ # TODO: deprecate and remove RestClient::SSLCertificateNotVerified and just
426
+ # pass through OpenSSL::SSL::SSLError directly.
427
+ #
428
+ # Exceptions in verify_callback are ignored [1], and jruby doesn't support
429
+ # it at all [2]. RestClient has to catch OpenSSL::SSL::SSLError and either
430
+ # re-throw it as is, or throw SSLCertificateNotVerified based on the
431
+ # contents of the message field of the original exception.
432
+ #
433
+ # The client has to handle OpenSSL::SSL::SSLError exceptions anyway, so
434
+ # we shouldn't make them handle both OpenSSL and RestClient exceptions.
435
+ #
436
+ # [1] https://github.com/ruby/ruby/blob/89e70fe8e7/ext/openssl/ossl.c#L238
437
+ # [2] https://github.com/jruby/jruby/issues/597
438
+
439
+ if error.message.include?("certificate verify failed")
440
+ raise SSLCertificateNotVerified.new(error.message)
441
+ else
442
+ raise error
443
+ end
444
+ end
445
+
446
+ def setup_credentials(req)
447
+ req.basic_auth(user, password) if user
448
+ end
449
+
450
+ def fetch_body(http_response)
451
+ if @raw_response
452
+ # Taken from Chef, which as in turn...
453
+ # Stolen from http://www.ruby-forum.com/topic/166423
454
+ # Kudos to _why!
455
+ @tf = Tempfile.new("rest-client")
456
+ @tf.binmode
457
+ size, total = 0, http_response.header['Content-Length'].to_i
458
+ http_response.read_body do |chunk|
459
+ @tf.write chunk
460
+ size += chunk.size
461
+ if RestClient.log
462
+ if size == 0
463
+ RestClient.log << "%s %s done (0 length file\n)" % [@method, @url]
464
+ elsif total == 0
465
+ RestClient.log << "%s %s (zero content length)\n" % [@method, @url]
466
+ else
467
+ RestClient.log << "%s %s %d%% done (%d of %d)\n" % [@method, @url, (size * 100) / total, size, total]
468
+ end
469
+ end
470
+ end
471
+ @tf.close
472
+ @tf
473
+ else
474
+ http_response.read_body
475
+ end
476
+ http_response
477
+ end
478
+
479
+ def process_result res, & block
480
+ if @raw_response
481
+ # We don't decode raw requests
482
+ response = RawResponse.new(@tf, res, args)
483
+ else
484
+ response = Response.create(Request.decode(res['content-encoding'], res.body), res, args)
485
+ end
486
+
487
+ if block_given?
488
+ block.call(response, self, res, & block)
489
+ else
490
+ response.return!(self, res, & block)
491
+ end
492
+
493
+ end
494
+
495
+ def self.decode content_encoding, body
496
+ if (!body) || body.empty?
497
+ body
498
+ elsif content_encoding == 'gzip'
499
+ Zlib::GzipReader.new(StringIO.new(body)).read
500
+ elsif content_encoding == 'deflate'
501
+ begin
502
+ Zlib::Inflate.new.inflate body
503
+ rescue Zlib::DataError
504
+ # No luck with Zlib decompression. Let's try with raw deflate,
505
+ # like some broken web servers do.
506
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate body
507
+ end
508
+ else
509
+ body
510
+ end
511
+ end
512
+
513
+ def log_request
514
+ if RestClient.log
515
+ out = []
516
+ out << "RestClient.#{method} #{url.inspect}"
517
+ out << payload.short_inspect if payload
518
+ out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
519
+ RestClient.log << out.join(', ') + "\n"
520
+ end
521
+ end
522
+
523
+ def log_response res
524
+ if RestClient.log
525
+ size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
526
+ RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
527
+ end
528
+ end
529
+
530
+ # Return a hash of headers whose keys are capitalized strings
531
+ def stringify_headers headers
532
+ headers.inject({}) do |result, (key, value)|
533
+ if key.is_a? Symbol
534
+ key = key.to_s.split(/_/).map { |w| w.capitalize }.join('-')
535
+ end
536
+ if 'CONTENT-TYPE' == key.upcase
537
+ target_value = value.to_s
538
+ result[key] = MIME::Types.type_for_extension target_value
539
+ elsif 'ACCEPT' == key.upcase
540
+ # Accept can be composed of several comma-separated values
541
+ if value.is_a? Array
542
+ target_values = value
543
+ else
544
+ target_values = value.to_s.split ','
545
+ end
546
+ result[key] = target_values.map { |ext| MIME::Types.type_for_extension(ext.to_s.strip) }.join(', ')
547
+ else
548
+ result[key] = value.to_s
549
+ end
550
+ result
551
+ end
552
+ end
553
+
554
+ def default_headers
555
+ {:accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate'}
556
+ end
557
+
558
+ private
559
+
560
+ def parser
561
+ URI.const_defined?(:Parser) ? URI::Parser.new : URI
562
+ end
563
+
564
+ end
565
+ end
566
+
567
+ module MIME
568
+ class Types
569
+
570
+ # Return the first found content-type for a value considered as an extension or the value itself
571
+ def type_for_extension ext
572
+ candidates = @extension_index[ext]
573
+ candidates.empty? ? ext : candidates[0].content_type
574
+ end
575
+
576
+ class << self
577
+ def type_for_extension ext
578
+ @__types__.type_for_extension ext
579
+ end
580
+ end
581
+ end
582
+ end