rest-client 2.0.2 → 2.1.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 (43) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.mailmap +10 -0
  4. data/.rubocop +2 -0
  5. data/.rubocop-disables.yml +46 -44
  6. data/.rubocop.yml +5 -0
  7. data/.travis.yml +31 -17
  8. data/AUTHORS +8 -0
  9. data/README.md +126 -9
  10. data/Rakefile +12 -4
  11. data/history.md +53 -0
  12. data/lib/restclient.rb +0 -1
  13. data/lib/restclient/abstract_response.rb +28 -2
  14. data/lib/restclient/exceptions.rb +3 -3
  15. data/lib/restclient/payload.rb +29 -4
  16. data/lib/restclient/raw_response.rb +17 -6
  17. data/lib/restclient/request.rb +94 -67
  18. data/lib/restclient/resource.rb +16 -6
  19. data/lib/restclient/response.rb +14 -4
  20. data/lib/restclient/utils.rb +47 -8
  21. data/lib/restclient/version.rb +2 -2
  22. data/rest-client.gemspec +3 -2
  23. data/spec/ISS.jpg +0 -0
  24. data/spec/helpers.rb +37 -5
  25. data/spec/integration/capath_digicert/3513523f.0 +22 -0
  26. data/spec/integration/capath_digicert/399e7759.0 +22 -0
  27. data/spec/integration/capath_digicert/digicert.crt +20 -17
  28. data/spec/integration/certs/digicert.crt +20 -17
  29. data/spec/integration/httpbin_spec.rb +41 -0
  30. data/spec/integration/integration_spec.rb +0 -7
  31. data/spec/unit/abstract_response_spec.rb +7 -7
  32. data/spec/unit/payload_spec.rb +51 -19
  33. data/spec/unit/raw_response_spec.rb +6 -2
  34. data/spec/unit/request2_spec.rb +8 -8
  35. data/spec/unit/request_spec.rb +53 -65
  36. data/spec/unit/resource_spec.rb +7 -7
  37. data/spec/unit/response_spec.rb +33 -22
  38. data/spec/unit/restclient_spec.rb +3 -2
  39. data/spec/unit/utils_spec.rb +10 -10
  40. metadata +34 -13
  41. data/spec/integration/capath_digicert/244b5494.0 +0 -19
  42. data/spec/integration/capath_digicert/81b9768f.0 +0 -19
  43. data/spec/unit/master_shake.jpg +0 -0
data/Rakefile CHANGED
@@ -38,12 +38,11 @@ desc 'Regenerate authors file'
38
38
  task :authors do
39
39
  Dir.chdir(File.dirname(__FILE__)) do
40
40
  File.open('AUTHORS', 'w') do |f|
41
- f.write( <<-EOM
41
+ f.write <<-EOM
42
42
  The Ruby REST Client would not be what it is today without the help of
43
43
  the following kind souls:
44
44
 
45
45
  EOM
46
- )
47
46
  end
48
47
 
49
48
  sh 'git shortlog -s | cut -f 2 >> AUTHORS'
@@ -70,8 +69,8 @@ namespace :all do
70
69
  task :build => ['ruby:build'] + \
71
70
  WindowsPlatforms.map {|p| "windows:#{p}:build"}
72
71
 
73
- desc "Create tag v#{RestClient::VERSION} and for all platforms build and push " \
74
- "rest-client #{RestClient::VERSION} to Rubygems"
72
+ desc "Create tag v#{RestClient::VERSION} and for all platforms build and " \
73
+ "push rest-client #{RestClient::VERSION} to Rubygems"
75
74
  task :release => ['build', 'ruby:release'] + \
76
75
  WindowsPlatforms.map {|p| "windows:#{p}:push"}
77
76
 
@@ -130,3 +129,12 @@ Rake::RDocTask.new do |t|
130
129
  t.rdoc_files.include('README.md')
131
130
  t.rdoc_files.include('lib/*.rb')
132
131
  end
132
+
133
+ ############################
134
+
135
+ require 'rubocop/rake_task'
136
+
137
+ RuboCop::RakeTask.new(:rubocop) do |t|
138
+ t.options = ['--display-cop-names']
139
+ end
140
+ alias_task(:lint, :rubocop)
data/history.md CHANGED
@@ -1,3 +1,40 @@
1
+ # 2.1.0
2
+
3
+ - Add a dependency on http-accept for parsing Content-Type charset headers.
4
+ This works around a bad memory leak introduced in MRI Ruby 2.4.0 and fixed in
5
+ Ruby 2.4.2. (#615)
6
+ - Use mime/types/columnar from mime-types 2.6.1+, which is leaner in memory
7
+ usage than the older storage model of mime-types. (#393)
8
+ - Add `:log` option to individual requests. This allows users to set a log on a
9
+ per-request / per-resource basis instead of the kludgy global log. (#538)
10
+ - Log request duration by tracking request start and end times. Make
11
+ `log_response` a method on the Response object, and ensure the `size` method
12
+ works on RawResponse objects. (#126)
13
+ - `# => 200 OK | text/html 1270 bytes, 0.08s`
14
+ - Also add a new `:stream_log_percent` parameter, which is applicable only
15
+ when `:raw_response => true` is set. This causes progress logs to be
16
+ emitted only on every N% (default 10%) of the total download size rather
17
+ than on every chunk.
18
+ - Drop custom handling of compression and use built-in Net::HTTP support for
19
+ supported Content-Encodings like gzip and deflate. Don't set any explicit
20
+ `Accept-Encoding` header, rely instead on Net::HTTP defaults. (#597)
21
+ - Note: this changes behavior for compressed responses when using
22
+ `:raw_response => true`. Previously the raw response would not have been
23
+ uncompressed by rest-client, but now Net::HTTP will uncompress it.
24
+ - The previous fix to avoid having Netrc username/password override an
25
+ Authorization header was case-sensitive and incomplete. Fix this by
26
+ respecting existing Authorization headers, regardless of letter case. (#550)
27
+ - Handle ParamsArray payloads. Previously, rest-client would silently drop a
28
+ ParamsArray passed as the payload. Instead, automatically use
29
+ Payload::Multipart if the ParamsArray contains a file handle, or use
30
+ Payload::UrlEncoded if it doesn't. (#508)
31
+ - Gracefully handle Payload objects (Payload::Base or subclasses) that are
32
+ passed as a payload argument. Previously, `Payload.generate` would wrap a
33
+ Payload object in Payload::Streamed, creating a pointlessly nested payload.
34
+ Also add a `closed?` method to Payload objects, and don't error in
35
+ `short_inspect` if `size` returns nil. (#603)
36
+ - Test with an image in the public domain to avoid licensing complexity. (#607)
37
+
1
38
  # 2.0.2
2
39
 
3
40
  - Suppress the header override warning introduced in 2.0.1 if the value is the
@@ -179,6 +216,22 @@ release:
179
216
  - Disable timeouts with :timeout => nil rather than :timeout => -1
180
217
  - Drop all Net::HTTP monkey patches
181
218
 
219
+ # 1.6.14
220
+
221
+ - This release is unchanged from 1.6.9. It was published in order to supersede
222
+ the malicious 1.6.10-13 versions, even for users who are still pinning to the
223
+ legacy 1.6.x series. All users are encouraged to upgrade to rest-client 2.x.
224
+
225
+ # 1.6.10, 1.6.11, 1.6.12, 1.6.13 (CVE-2019-15224)
226
+
227
+ - These versions were pushed by a malicious actor and included a backdoor permitting
228
+ remote code execution in Rails environments. (#713)
229
+ - They were live for about five days before being yanked.
230
+
231
+ # 1.6.9
232
+
233
+ - Move rdoc to a development dependency
234
+
182
235
  # 1.6.8
183
236
 
184
237
  - The 1.6.x series will be the last to support Ruby 1.8.7
@@ -2,7 +2,6 @@ require 'net/http'
2
2
  require 'openssl'
3
3
  require 'stringio'
4
4
  require 'uri'
5
- require 'zlib'
6
5
 
7
6
  require File.dirname(__FILE__) + '/restclient/version'
8
7
  require File.dirname(__FILE__) + '/restclient/platform'
@@ -5,12 +5,27 @@ module RestClient
5
5
 
6
6
  module AbstractResponse
7
7
 
8
- attr_reader :net_http_res, :request
8
+ attr_reader :net_http_res, :request, :start_time, :end_time, :duration
9
9
 
10
10
  def inspect
11
11
  raise NotImplementedError.new('must override in subclass')
12
12
  end
13
13
 
14
+ # Logger from the request, potentially nil.
15
+ def log
16
+ request.log
17
+ end
18
+
19
+ def log_response
20
+ return unless log
21
+
22
+ code = net_http_res.code
23
+ res_name = net_http_res.class.to_s.gsub(/\ANet::HTTP/, '')
24
+ content_type = (net_http_res['Content-type'] || '').gsub(/;.*\z/, '')
25
+
26
+ log << "# => #{code} #{res_name} | #{content_type} #{size} bytes, #{sprintf('%.2f', duration)}s\n"
27
+ end
28
+
14
29
  # HTTP status code
15
30
  def code
16
31
  @code ||= @net_http_res.code.to_i
@@ -31,9 +46,20 @@ module RestClient
31
46
  @raw_headers ||= @net_http_res.to_hash
32
47
  end
33
48
 
34
- def response_set_vars(net_http_res, request)
49
+ # @param [Net::HTTPResponse] net_http_res
50
+ # @param [RestClient::Request] request
51
+ # @param [Time] start_time
52
+ def response_set_vars(net_http_res, request, start_time)
35
53
  @net_http_res = net_http_res
36
54
  @request = request
55
+ @start_time = start_time
56
+ @end_time = Time.now
57
+
58
+ if @start_time
59
+ @duration = @end_time - @start_time
60
+ else
61
+ @duration = nil
62
+ end
37
63
 
38
64
  # prime redirection history
39
65
  history
@@ -148,7 +148,7 @@ module RestClient
148
148
  end
149
149
 
150
150
  # Compatibility
151
- class ExceptionWithResponse < Exception
151
+ class ExceptionWithResponse < RestClient::Exception
152
152
  end
153
153
 
154
154
  # The request failed with an error code not managed by the code
@@ -228,14 +228,14 @@ module RestClient
228
228
  # The server broke the connection prior to the request completing. Usually
229
229
  # this means it crashed, or sometimes that your network connection was
230
230
  # severed before it could complete.
231
- class ServerBrokeConnection < Exception
231
+ class ServerBrokeConnection < RestClient::Exception
232
232
  def initialize(message = 'Server broke connection')
233
233
  super nil, nil
234
234
  self.message = message
235
235
  end
236
236
  end
237
237
 
238
- class SSLCertificateNotVerified < Exception
238
+ class SSLCertificateNotVerified < RestClient::Exception
239
239
  def initialize(message = 'SSL certificate not verified')
240
240
  super nil, nil
241
241
  self.message = message
@@ -2,14 +2,22 @@ require 'tempfile'
2
2
  require 'securerandom'
3
3
  require 'stringio'
4
4
 
5
- require 'mime/types'
5
+ begin
6
+ # Use mime/types/columnar if available, for reduced memory usage
7
+ require 'mime/types/columnar'
8
+ rescue LoadError
9
+ require 'mime/types'
10
+ end
6
11
 
7
12
  module RestClient
8
13
  module Payload
9
14
  extend self
10
15
 
11
16
  def generate(params)
12
- if params.is_a?(String)
17
+ if params.is_a?(RestClient::Payload::Base)
18
+ # pass through Payload objects unchanged
19
+ params
20
+ elsif params.is_a?(String)
13
21
  Base.new(params)
14
22
  elsif params.is_a?(Hash)
15
23
  if params.delete(:multipart) == true || has_file?(params)
@@ -17,6 +25,12 @@ module RestClient
17
25
  else
18
26
  UrlEncoded.new(params)
19
27
  end
28
+ elsif params.is_a?(ParamsArray)
29
+ if _has_file?(params)
30
+ Multipart.new(params)
31
+ else
32
+ UrlEncoded.new(params)
33
+ end
20
34
  elsif params.respond_to?(:read)
21
35
  Streamed.new(params)
22
36
  else
@@ -76,12 +90,20 @@ module RestClient
76
90
  @stream.close unless @stream.closed?
77
91
  end
78
92
 
93
+ def closed?
94
+ @stream.closed?
95
+ end
96
+
79
97
  def to_s_inspect
80
98
  to_s.inspect
81
99
  end
82
100
 
83
101
  def short_inspect
84
- (size > 500 ? "#{size} byte(s) length" : to_s_inspect)
102
+ if size && size > 500
103
+ "#{size} byte(s) length"
104
+ else
105
+ to_s_inspect
106
+ end
85
107
  end
86
108
 
87
109
  end
@@ -99,6 +121,9 @@ module RestClient
99
121
  end
100
122
  end
101
123
 
124
+ # TODO (breaks compatibility): ought to use mime_for() to autodetect the
125
+ # Content-Type for stream objects that have a filename.
126
+
102
127
  alias :length :size
103
128
  end
104
129
 
@@ -119,7 +144,7 @@ module RestClient
119
144
  def build_stream(params)
120
145
  b = '--' + boundary
121
146
 
122
- @stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
147
+ @stream = Tempfile.new('rest-client.multipart.')
123
148
  @stream.binmode
124
149
  @stream.write(b + EOL)
125
150
 
@@ -13,25 +13,36 @@ module RestClient
13
13
 
14
14
  include AbstractResponse
15
15
 
16
- attr_reader :file, :request
16
+ attr_reader :file, :request, :start_time, :end_time
17
17
 
18
18
  def inspect
19
19
  "<RestClient::RawResponse @code=#{code.inspect}, @file=#{file.inspect}, @request=#{request.inspect}>"
20
20
  end
21
21
 
22
- def initialize(tempfile, net_http_res, request)
23
- @net_http_res = net_http_res
22
+ # @param [Tempfile] tempfile The temporary file containing the body
23
+ # @param [Net::HTTPResponse] net_http_res
24
+ # @param [RestClient::Request] request
25
+ # @param [Time] start_time
26
+ def initialize(tempfile, net_http_res, request, start_time=nil)
24
27
  @file = tempfile
25
- @request = request
28
+
29
+ # reopen the tempfile so we can read it
30
+ @file.open
31
+
32
+ response_set_vars(net_http_res, request, start_time)
26
33
  end
27
34
 
28
35
  def to_s
29
- @file.open
36
+ body
37
+ end
38
+
39
+ def body
40
+ @file.rewind
30
41
  @file.read
31
42
  end
32
43
 
33
44
  def size
34
- File.size file
45
+ file.size
35
46
  end
36
47
 
37
48
  end
@@ -1,9 +1,15 @@
1
1
  require 'tempfile'
2
- require 'mime/types'
3
2
  require 'cgi'
4
3
  require 'netrc'
5
4
  require 'set'
6
5
 
6
+ begin
7
+ # Use mime/types/columnar if available, for reduced memory usage
8
+ require 'mime/types/columnar'
9
+ rescue LoadError
10
+ require 'mime/types'
11
+ end
12
+
7
13
  module RestClient
8
14
  # This class is used internally by RestClient to send the request, but you can also
9
15
  # call it directly if you'd like to use a method not supported by the
@@ -22,6 +28,11 @@ module RestClient
22
28
  # * :user and :password for basic auth, will be replaced by a user/password available in the :url
23
29
  # * :block_response call the provided block with the HTTPResponse as parameter
24
30
  # * :raw_response return a low-level RawResponse instead of a Response
31
+ # * :log Set the log for this request only, overriding RestClient.log, if
32
+ # any.
33
+ # * :stream_log_percent (Only relevant with :raw_response => true) Customize
34
+ # the interval at which download progress is logged. Defaults to every
35
+ # 10% complete.
25
36
  # * :max_redirects maximum number of redirections (default to 10)
26
37
  # * :proxy An HTTP proxy URI to use for this request. Any value here
27
38
  # (including nil) will override RestClient.proxy.
@@ -92,6 +103,12 @@ module RestClient
92
103
  @block_response = args[:block_response]
93
104
  @raw_response = args[:raw_response] || false
94
105
 
106
+ @stream_log_percent = args[:stream_log_percent] || 10
107
+ if @stream_log_percent <= 0 || @stream_log_percent > 100
108
+ raise ArgumentError.new(
109
+ "Invalid :stream_log_percent #{@stream_log_percent.inspect}")
110
+ end
111
+
95
112
  @proxy = args.fetch(:proxy) if args.include?(:proxy)
96
113
 
97
114
  @ssl_opts = {}
@@ -131,9 +148,10 @@ module RestClient
131
148
  end
132
149
  end
133
150
 
134
- @tf = nil # If you are a raw request, this is your tempfile
151
+ @log = args[:log]
135
152
  @max_redirects = args[:max_redirects] || 10
136
153
  @processed_headers = make_headers headers
154
+ @processed_headers_lowercase = Hash[@processed_headers.map {|k, v| [k.downcase, v]}]
137
155
  @args = args
138
156
 
139
157
  @before_execution_proc = args[:before_execution_proc]
@@ -356,6 +374,13 @@ module RestClient
356
374
  # - headers from the payload object (e.g. Content-Type, Content-Lenth)
357
375
  # - cookie headers from #make_cookie_header
358
376
  #
377
+ # BUG: stringify_headers does not alter the capitalization of headers that
378
+ # are passed as strings, it only normalizes those passed as symbols. This
379
+ # behavior will probably remain for a while for compatibility, but it means
380
+ # that the warnings that attempt to detect accidental header overrides may
381
+ # not always work.
382
+ # https://github.com/rest-client/rest-client/issues/599
383
+ #
359
384
  # @param [Hash] user_headers User-provided headers to include
360
385
  #
361
386
  # @return [Hash<String, String>] A hash of HTTP headers => values
@@ -493,24 +518,6 @@ module RestClient
493
518
  cert_store
494
519
  end
495
520
 
496
- def self.decode content_encoding, body
497
- if (!body) || body.empty?
498
- body
499
- elsif content_encoding == 'gzip'
500
- Zlib::GzipReader.new(StringIO.new(body)).read
501
- elsif content_encoding == 'deflate'
502
- begin
503
- Zlib::Inflate.new.inflate body
504
- rescue Zlib::DataError
505
- # No luck with Zlib decompression. Let's try with raw deflate,
506
- # like some broken web servers do.
507
- Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate body
508
- end
509
- else
510
- body
511
- end
512
- end
513
-
514
521
  def redacted_uri
515
522
  if uri.password
516
523
  sanitized_uri = uri.dup
@@ -525,30 +532,29 @@ module RestClient
525
532
  redacted_uri.to_s
526
533
  end
527
534
 
535
+ # Default to the global logger if there's not a request-specific one
536
+ def log
537
+ @log || RestClient.log
538
+ end
539
+
528
540
  def log_request
529
- return unless RestClient.log
541
+ return unless log
530
542
 
531
543
  out = []
532
544
 
533
545
  out << "RestClient.#{method} #{redacted_url.inspect}"
534
546
  out << payload.short_inspect if payload
535
547
  out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
536
- RestClient.log << out.join(', ') + "\n"
537
- end
538
-
539
- def log_response res
540
- return unless RestClient.log
541
-
542
- size = if @raw_response
543
- File.size(@tf.path)
544
- else
545
- res.body.nil? ? 0 : res.body.size
546
- end
547
-
548
- RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
548
+ log << out.join(', ') + "\n"
549
549
  end
550
550
 
551
551
  # Return a hash of headers whose keys are capitalized strings
552
+ #
553
+ # BUG: stringify_headers does not fix the capitalization of headers that
554
+ # are already Strings. Leaving this behavior as is for now for
555
+ # backwards compatibility.
556
+ # https://github.com/rest-client/rest-client/issues/599
557
+ #
552
558
  def stringify_headers headers
553
559
  headers.inject({}) do |result, (key, value)|
554
560
  if key.is_a? Symbol
@@ -573,10 +579,13 @@ module RestClient
573
579
  end
574
580
  end
575
581
 
582
+ # Default headers set by RestClient. In addition to these headers, servers
583
+ # will receive headers set by Net::HTTP, such as Accept-Encoding and Host.
584
+ #
585
+ # @return [Hash<Symbol, String>]
576
586
  def default_headers
577
587
  {
578
588
  :accept => '*/*',
579
- :accept_encoding => 'gzip, deflate',
580
589
  :user_agent => RestClient::Platform.default_user_agent,
581
590
  }
582
591
  end
@@ -712,6 +721,9 @@ module RestClient
712
721
 
713
722
  log_request
714
723
 
724
+ start_time = Time.now
725
+ tempfile = nil
726
+
715
727
  net.start do |http|
716
728
  established_connection = true
717
729
 
@@ -719,10 +731,16 @@ module RestClient
719
731
  net_http_do_request(http, req, payload, &@block_response)
720
732
  else
721
733
  res = net_http_do_request(http, req, payload) { |http_response|
722
- fetch_body(http_response)
734
+ if @raw_response
735
+ # fetch body into tempfile
736
+ tempfile = fetch_body_to_tempfile(http_response)
737
+ else
738
+ # fetch body
739
+ http_response.read_body
740
+ end
741
+ http_response
723
742
  }
724
- log_response res
725
- process_result res, & block
743
+ process_result(res, start_time, tempfile, &block)
726
744
  end
727
745
  end
728
746
  rescue EOFError
@@ -762,47 +780,56 @@ module RestClient
762
780
  end
763
781
 
764
782
  def setup_credentials(req)
765
- req.basic_auth(user, password) if user && !headers.has_key?("Authorization")
783
+ if user && !@processed_headers_lowercase.include?('authorization')
784
+ req.basic_auth(user, password)
785
+ end
766
786
  end
767
787
 
768
- def fetch_body(http_response)
769
- if @raw_response
770
- # Taken from Chef, which as in turn...
771
- # Stolen from http://www.ruby-forum.com/topic/166423
772
- # Kudos to _why!
773
- @tf = Tempfile.new('rest-client.')
774
- @tf.binmode
775
- size, total = 0, http_response['Content-Length'].to_i
776
- http_response.read_body do |chunk|
777
- @tf.write chunk
778
- size += chunk.size
779
- if RestClient.log
780
- if size == 0
781
- RestClient.log << "%s %s done (0 length file)\n" % [@method, @url]
782
- elsif total == 0
783
- RestClient.log << "%s %s (zero content length)\n" % [@method, @url]
784
- else
785
- RestClient.log << "%s %s %d%% done (%d of %d)\n" % [@method, @url, (size * 100) / total, size, total]
788
+ def fetch_body_to_tempfile(http_response)
789
+ # Taken from Chef, which as in turn...
790
+ # Stolen from http://www.ruby-forum.com/topic/166423
791
+ # Kudos to _why!
792
+ tf = Tempfile.new('rest-client.')
793
+ tf.binmode
794
+
795
+ size = 0
796
+ total = http_response['Content-Length'].to_i
797
+ stream_log_bucket = nil
798
+
799
+ http_response.read_body do |chunk|
800
+ tf.write chunk
801
+ size += chunk.size
802
+ if log
803
+ if total == 0
804
+ log << "streaming %s %s (%d of unknown) [0 Content-Length]\n" % [@method.upcase, @url, size]
805
+ else
806
+ percent = (size * 100) / total
807
+ current_log_bucket, _ = percent.divmod(@stream_log_percent)
808
+ if current_log_bucket != stream_log_bucket
809
+ stream_log_bucket = current_log_bucket
810
+ log << "streaming %s %s %d%% done (%d of %d)\n" % [@method.upcase, @url, (size * 100) / total, size, total]
786
811
  end
787
812
  end
788
813
  end
789
- @tf.close
790
- @tf
791
- else
792
- http_response.read_body
793
814
  end
794
- http_response
815
+ tf.close
816
+ tf
795
817
  end
796
818
 
797
- def process_result res, & block
819
+ # @param res The Net::HTTP response object
820
+ # @param start_time [Time] Time of request start
821
+ def process_result(res, start_time, tempfile=nil, &block)
798
822
  if @raw_response
799
- # We don't decode raw requests
800
- response = RawResponse.new(@tf, res, self)
823
+ unless tempfile
824
+ raise ArgumentError.new('tempfile is required')
825
+ end
826
+ response = RawResponse.new(tempfile, res, self, start_time)
801
827
  else
802
- decoded = Request.decode(res['content-encoding'], res.body)
803
- response = Response.create(decoded, res, self)
828
+ response = Response.create(res.body, res, self, start_time)
804
829
  end
805
830
 
831
+ response.log_response
832
+
806
833
  if block_given?
807
834
  block.call(response, self, res, & block)
808
835
  else