httpclient 2.1.5.2 → 2.1.6

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.
@@ -64,7 +64,11 @@ class HTTPClient
64
64
  # Retrieves a HTTP::Message instance of HTTP response. Do not invoke this
65
65
  # method twice for now. The second invocation will be blocked.
66
66
  def pop
67
- @queue.pop
67
+ response_or_exception = @queue.pop
68
+ if response_or_exception.is_a? Exception
69
+ raise response_or_exception
70
+ end
71
+ response_or_exception
68
72
  end
69
73
 
70
74
  def push(result) # :nodoc:
@@ -337,7 +337,6 @@ class WebAgent
337
337
 
338
338
  domainname = url.host
339
339
  domain_orig, path_orig = domain, path
340
- use_security = override
341
340
 
342
341
  if domain
343
342
 
@@ -372,7 +371,7 @@ class WebAgent
372
371
  # and IE does not check, too.
373
372
  end
374
373
 
375
- path ||= url.path.sub(%r|/[^/]*|, '')
374
+ path ||= url.path.sub(%r|/[^/]*\z|, '')
376
375
  domain ||= domainname
377
376
  @cookies.synchronize do
378
377
  cookie = find_cookie_info(domain, path, name)
@@ -95,7 +95,7 @@ module HTTP
95
95
 
96
96
  # Represents HTTP message header.
97
97
  class Headers
98
- # HTTP version in a HTTP header. Float.
98
+ # HTTP version in a HTTP header. String.
99
99
  attr_accessor :http_version
100
100
  # Size of body. nil when size is unknown (e.g. chunked response).
101
101
  attr_reader :body_size
@@ -109,7 +109,7 @@ module HTTP
109
109
  # Request only. Requested query.
110
110
  attr_accessor :request_query
111
111
  # Request only. Requested via proxy or not.
112
- attr_accessor :request_via_proxy
112
+ attr_accessor :request_absolute_uri
113
113
 
114
114
  # Response only. HTTP status
115
115
  attr_reader :status_code
@@ -151,14 +151,14 @@ module HTTP
151
151
  # Creates a Message::Headers. Use init_request, init_response, or
152
152
  # init_connect_request for acutual initialize.
153
153
  def initialize
154
- @http_version = 1.1
154
+ @http_version = '1.1'
155
155
  @body_size = nil
156
156
  @chunked = false
157
157
 
158
158
  @request_method = nil
159
159
  @request_uri = nil
160
160
  @request_query = nil
161
- @request_via_proxy = nil
161
+ @request_absolute_uri = nil
162
162
 
163
163
  @status_code = nil
164
164
  @reason_phrase = nil
@@ -178,7 +178,7 @@ module HTTP
178
178
  @request_method = 'CONNECT'
179
179
  @request_uri = uri
180
180
  @request_query = nil
181
- @http_version = 1.0
181
+ @http_version = '1.0'
182
182
  end
183
183
 
184
184
  # Placeholder URI object for nil uri.
@@ -189,7 +189,7 @@ module HTTP
189
189
  @request_method = method
190
190
  @request_uri = uri || NIL_URI
191
191
  @request_query = query
192
- @request_via_proxy = false
192
+ @request_absolute_uri = false
193
193
  end
194
194
 
195
195
  # Initialize this instance as a response.
@@ -285,11 +285,38 @@ module HTTP
285
285
  get(key).collect { |item| item[1] }
286
286
  end
287
287
 
288
+ def create_query_uri()
289
+ if @request_method == 'CONNECT'
290
+ return "#{@request_uri.host}:#{@request_uri.port}"
291
+ end
292
+ path = @request_uri.path
293
+ path = '/' if path.nil? or path.empty?
294
+ if query_str = create_query_part()
295
+ path += "?#{query_str}"
296
+ end
297
+ path
298
+ end
299
+
300
+ def create_query_part()
301
+ query_str = nil
302
+ if @request_uri.query
303
+ query_str = @request_uri.query
304
+ end
305
+ if @request_query
306
+ if query_str
307
+ query_str += "&#{Message.create_query_part_str(@request_query)}"
308
+ else
309
+ query_str = Message.create_query_part_str(@request_query)
310
+ end
311
+ end
312
+ query_str
313
+ end
314
+
288
315
  private
289
316
 
290
317
  def request_line
291
- path = create_query_uri(@request_uri, @request_query)
292
- if @request_via_proxy
318
+ path = create_query_uri()
319
+ if @request_absolute_uri
293
320
  path = "#{ @request_uri.scheme }://#{ @request_uri.host }:#{ @request_uri.port }#{ path }"
294
321
  end
295
322
  "#{ @request_method } #{ path } HTTP/#{ @http_version }#{ CRLF }"
@@ -323,7 +350,7 @@ module HTTP
323
350
  elsif @body_size and (keep_alive or @body_size != 0)
324
351
  set('Content-Length', @body_size.to_s)
325
352
  end
326
- if @http_version >= 1.1
353
+ if @http_version >= '1.1' and get('Host').empty?
327
354
  if @request_uri.port == @request_uri.default_port
328
355
  # GFE/1.3 dislikes default port number (returns 404)
329
356
  set('Host', "#{@request_uri.host}")
@@ -358,29 +385,6 @@ module HTTP
358
385
  def charset_label(charset)
359
386
  CHARSET_MAP[charset] || 'us-ascii'
360
387
  end
361
-
362
- def create_query_uri(uri, query)
363
- if @request_method == 'CONNECT'
364
- return "#{uri.host}:#{uri.port}"
365
- end
366
- path = uri.path
367
- path = '/' if path.nil? or path.empty?
368
- query_str = nil
369
- if uri.query
370
- query_str = uri.query
371
- end
372
- if query
373
- if query_str
374
- query_str += "&#{Message.create_query_part_str(query)}"
375
- else
376
- query_str = Message.create_query_part_str(query)
377
- end
378
- end
379
- if query_str
380
- path += "?#{query_str}"
381
- end
382
- path
383
- end
384
388
  end
385
389
 
386
390
 
@@ -414,7 +418,9 @@ module HTTP
414
418
  # Initialize this instance as a response.
415
419
  def init_response(body = nil)
416
420
  @body = body
417
- if @body.respond_to?(:size)
421
+ if @body.respond_to?(:bytesize)
422
+ @size = @body.bytesize
423
+ elsif @body.respond_to?(:size)
418
424
  @size = @body.size
419
425
  else
420
426
  @size = nil
@@ -438,6 +444,7 @@ module HTTP
438
444
  while !part.read(@chunk_size, buf).nil?
439
445
  dev << buf
440
446
  end
447
+ part.rewind
441
448
  else
442
449
  dev << part
443
450
  end
@@ -496,7 +503,7 @@ module HTTP
496
503
  @size = @body.size
497
504
  else
498
505
  @body = Message.create_query_part_str(body)
499
- @size = @body.size
506
+ @size = @body.bytesize
500
507
  end
501
508
  end
502
509
 
@@ -517,7 +524,7 @@ module HTTP
517
524
  end
518
525
 
519
526
  def dump_chunk(str)
520
- dump_chunk_size(str.size) + (str + CRLF)
527
+ dump_chunk_size(str.bytesize) + (str + CRLF)
521
528
  end
522
529
 
523
530
  def dump_last_chunk
@@ -555,10 +562,10 @@ module HTTP
555
562
  end
556
563
  elsif @body[-1].is_a?(String)
557
564
  @body[-1] += part.to_s
558
- @size += part.to_s.size if @size
565
+ @size += part.to_s.bytesize if @size
559
566
  else
560
567
  @body << part.to_s
561
- @size += part.to_s.size if @size
568
+ @size += part.to_s.bytesize if @size
562
569
  end
563
570
  end
564
571
 
@@ -583,11 +590,21 @@ module HTTP
583
590
  }.join("; ")
584
591
  if value.respond_to?(:mime_type)
585
592
  content_type = value.mime_type
593
+ elsif value.respond_to?(:content_type)
594
+ content_type = value.content_type
586
595
  else
587
- content_type = Message.mime_type(value.path)
596
+ path = value.respond_to?(:path) ? value.path : nil
597
+ content_type = Message.mime_type(path)
588
598
  end
589
599
  headers << %{Content-Disposition: form-data; name="#{attr}"; #{param_str}}
590
600
  headers << %{Content-Type: #{content_type}}
601
+ elsif attr.is_a?(Hash)
602
+ h = attr
603
+ value = h[:content]
604
+ h.each do |h_key, h_val|
605
+ headers << %{#{h_key}: #{h_val}} if h_key != :content
606
+ end
607
+ remember_pos(value) if Message.file?(value)
591
608
  else
592
609
  headers << %{Content-Disposition: form-data; name="#{attr}"}
593
610
  end
@@ -601,7 +618,8 @@ module HTTP
601
618
 
602
619
  def params_from_file(value)
603
620
  params = {}
604
- params['filename'] = File.basename(value.path || '')
621
+ path = value.respond_to?(:path) ? value.path : nil
622
+ params['filename'] = File.basename(path || '')
605
623
  # Creation time is not available from File::Stat
606
624
  if value.respond_to?(:mtime)
607
625
  params['modification-date'] = value.mtime.rfc822
@@ -724,9 +742,9 @@ module HTTP
724
742
  end
725
743
 
726
744
  # Returns true if the given HTTP version allows keep alive connection.
727
- # version:: Float
745
+ # version:: String
728
746
  def keep_alive_enabled?(version)
729
- version >= 1.1
747
+ version >= '1.1'
730
748
  end
731
749
 
732
750
  # Returns true if the given query (or body) has a multiple parameter.
@@ -736,15 +754,14 @@ module HTTP
736
754
 
737
755
  # Returns true if the given object is a File. In HTTPClient, a file is;
738
756
  # * must respond to :read for retrieving String chunks.
739
- # * must respond to :path and returns a path for Content-Disposition.
740
757
  # * must respond to :pos and :pos= to rewind for reading.
741
758
  # Rewinding is only needed for following HTTP redirect. Some IO impl
742
759
  # defines :pos= but raises an Exception for pos= such as StringIO
743
760
  # but there's no problem as far as using it for non-following methods
744
761
  # (get/post/etc.)
745
762
  def file?(obj)
746
- obj.respond_to?(:read) and obj.respond_to?(:path) and
747
- obj.respond_to?(:pos) and obj.respond_to?(:pos=)
763
+ obj.respond_to?(:read) and obj.respond_to?(:pos) and
764
+ obj.respond_to?(:pos=)
748
765
  end
749
766
 
750
767
  def create_query_part_str(query) # :nodoc:
@@ -758,7 +775,7 @@ module HTTP
758
775
  end
759
776
 
760
777
  def escape_query(query) # :nodoc:
761
- query.collect { |attr, value|
778
+ query.sort_by { |attr, value| attr.to_s }.collect { |attr, value|
762
779
  if value.respond_to?(:read)
763
780
  value = value.read
764
781
  end
@@ -768,9 +785,36 @@ module HTTP
768
785
 
769
786
  # from CGI.escape
770
787
  def escape(str) # :nodoc:
771
- str.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
772
- '%' + $1.unpack('H2' * $1.size).join('%').upcase
773
- }.tr(' ', '+')
788
+ if defined?(Encoding::ASCII_8BIT)
789
+ str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(/([^ a-zA-Z0-9_.-]+)/) {
790
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
791
+ }.tr(' ', '+')
792
+ else
793
+ str.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
794
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
795
+ }.tr(' ', '+')
796
+ end
797
+ end
798
+
799
+ # from CGI.parse
800
+ def parse(query)
801
+ params = Hash.new([].freeze)
802
+ query.split(/[&;]/n).each do |pairs|
803
+ key, value = pairs.split('=',2).collect{|v| unescape(v) }
804
+ if params.has_key?(key)
805
+ params[key].push(value)
806
+ else
807
+ params[key] = [value]
808
+ end
809
+ end
810
+ params
811
+ end
812
+
813
+ # from CGI.unescape
814
+ def unescape(string)
815
+ string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
816
+ [$1.delete('%')].pack('H*')
817
+ end
774
818
  end
775
819
  end
776
820
 
@@ -813,13 +857,24 @@ module HTTP
813
857
  @header.body_size = @body.size if @header
814
858
  end
815
859
 
816
- # Returns HTTP version in a HTTP header. Float.
817
- def version
860
+ # Returns HTTP version in a HTTP header. String.
861
+ def http_version
818
862
  @header.http_version
819
863
  end
820
864
 
821
- # Sets HTTP version in a HTTP header. Float.
865
+ # Sets HTTP version in a HTTP header. String.
866
+ def http_version=(http_version)
867
+ @header.http_version = http_version
868
+ end
869
+
870
+ VERSION_WARNING = 'Message#version (Float) is deprecated. Use Message#http_version (String) instead.'
871
+ def version
872
+ warn(VERSION_WARNING)
873
+ @header.http_version.to_f
874
+ end
875
+
822
876
  def version=(version)
877
+ warn(VERSION_WARNING)
823
878
  @header.http_version = version
824
879
  end
825
880
 
@@ -13,6 +13,7 @@
13
13
  require 'socket'
14
14
  require 'thread'
15
15
  require 'stringio'
16
+ require 'zlib'
16
17
 
17
18
  require 'httpclient/timeout'
18
19
  require 'httpclient/ssl_config'
@@ -106,6 +107,8 @@ class HTTPClient
106
107
 
107
108
  attr_reader :test_loopback_http_response
108
109
 
110
+ attr_accessor :transparent_gzip_decompression
111
+
109
112
  def initialize(client)
110
113
  @client = client
111
114
  @proxy = client.proxy
@@ -128,6 +131,7 @@ class HTTPClient
128
131
  @ssl_config = nil
129
132
  @test_loopback_http_response = []
130
133
 
134
+ @transparent_gzip_decompression = false
131
135
  @sess_pool = []
132
136
  @sess_pool_mutex = Mutex.new
133
137
  end
@@ -165,6 +169,16 @@ class HTTPClient
165
169
  add_cached_session(sess)
166
170
  end
167
171
 
172
+ def invalidate(site)
173
+ @sess_pool_mutex.synchronize do
174
+ @sess_pool.each do |sess|
175
+ if sess.dest == site
176
+ sess.invalidate
177
+ end
178
+ end
179
+ end
180
+ end
181
+
168
182
  private
169
183
 
170
184
  def open(uri, via_proxy = false)
@@ -185,6 +199,7 @@ class HTTPClient
185
199
  sess.ssl_config = @ssl_config
186
200
  sess.debug_dev = @debug_dev
187
201
  sess.test_loopback_http_response = @test_loopback_http_response
202
+ sess.transparent_gzip_decompression = @transparent_gzip_decompression
188
203
  end
189
204
  sess
190
205
  end
@@ -198,6 +213,7 @@ class HTTPClient
198
213
  @sess_pool.clear
199
214
  end
200
215
 
216
+ # This method might not work as you expected...
201
217
  def close(dest)
202
218
  if cached = get_cached_session(dest)
203
219
  cached.close
@@ -212,7 +228,9 @@ class HTTPClient
212
228
  @sess_pool_mutex.synchronize do
213
229
  new_pool = []
214
230
  @sess_pool.each do |s|
215
- if s.dest.match(uri)
231
+ if s.invalidated?
232
+ s.close # close & remove from the pool
233
+ elsif !cached && s.dest.match(uri)
216
234
  cached = s
217
235
  else
218
236
  new_pool << s
@@ -435,7 +453,15 @@ class HTTPClient
435
453
  private
436
454
 
437
455
  def debug(str)
438
- @debug_dev << str if str && @debug_dev
456
+ if str && @debug_dev
457
+ if str.index("\0")
458
+ require 'hexdump'
459
+ str.force_encoding('BINARY') if str.respond_to?(:force_encoding)
460
+ @debug_dev << HexDump.encode(str).join("\n")
461
+ else
462
+ @debug_dev << str
463
+ end
464
+ end
439
465
  end
440
466
  end
441
467
 
@@ -459,6 +485,7 @@ class HTTPClient
459
485
  # Manages a HTTP session with a Site.
460
486
  class Session
461
487
  include HTTPClient::Timeout
488
+ include Util
462
489
 
463
490
  # Destination site
464
491
  attr_reader :dest
@@ -482,9 +509,12 @@ class HTTPClient
482
509
  attr_reader :ssl_peer_cert
483
510
  attr_accessor :test_loopback_http_response
484
511
 
512
+ attr_accessor :transparent_gzip_decompression
513
+
485
514
  def initialize(client, dest, agent_name, from)
486
515
  @client = client
487
516
  @dest = dest
517
+ @invalidated = false
488
518
  @proxy = nil
489
519
  @socket_sync = true
490
520
  @requested_version = nil
@@ -515,12 +545,15 @@ class HTTPClient
515
545
 
516
546
  @socket = nil
517
547
  @readbuf = nil
548
+
549
+ @transparent_gzip_decompression = false
518
550
  end
519
551
 
520
552
  # Send a request to the server
521
553
  def query(req)
522
554
  connect if @state == :INIT
523
- req.header.request_via_proxy = !@proxy.nil?
555
+ # Use absolute URI (not absolute path) iif via proxy AND not HTTPS.
556
+ req.header.request_absolute_uri = !@proxy.nil? and !https?(@dest)
524
557
  begin
525
558
  timeout(@send_timeout, SendTimeoutError) do
526
559
  set_header(req)
@@ -529,14 +562,16 @@ class HTTPClient
529
562
  @socket.flush unless @socket_sync
530
563
  end
531
564
  rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
565
+ # JRuby can raise IOError instead of ECONNRESET for now
532
566
  close
533
- raise KeepAliveDisconnected.new
567
+ raise KeepAliveDisconnected.new(self)
534
568
  rescue HTTPClient::TimeoutError
535
569
  close
536
570
  raise
537
571
  rescue
572
+ close
538
573
  if SSLEnabled and $!.is_a?(OpenSSL::SSL::SSLError)
539
- raise KeepAliveDisconnected.new
574
+ raise KeepAliveDisconnected.new(self)
540
575
  else
541
576
  raise
542
577
  end
@@ -560,6 +595,14 @@ class HTTPClient
560
595
  @state == :INIT
561
596
  end
562
597
 
598
+ def invalidate
599
+ @invalidated = true
600
+ end
601
+
602
+ def invalidated?
603
+ @invalidated
604
+ end
605
+
563
606
  def get_header
564
607
  begin
565
608
  if @state != :META
@@ -585,6 +628,19 @@ class HTTPClient
585
628
  begin
586
629
  read_header if @state == :META
587
630
  return nil if @state != :DATA
631
+ if @gzipped and @transparent_gzip_decompression
632
+ # zlib itself has a functionality to decompress gzip stream.
633
+ # - zlib 1.2.5 Manual
634
+ # http://www.zlib.net/manual.html#Advanced
635
+ # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
636
+ # > windowBits to enable zlib and gzip decoding with automatic header detection,
637
+ # > or add 16 to decode only the gzip format
638
+ inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
639
+ original_block = block
640
+ block = Proc.new { |buf|
641
+ original_block.call(inflate_stream.inflate(buf))
642
+ }
643
+ end
588
644
  if @chunked
589
645
  read_body_chunked(&block)
590
646
  elsif @content_length
@@ -611,7 +667,7 @@ class HTTPClient
611
667
  def set_header(req)
612
668
  if @requested_version
613
669
  if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version
614
- req.version = $1.to_f
670
+ req.http_version = $1
615
671
  end
616
672
  end
617
673
  if @agent_name
@@ -620,6 +676,9 @@ class HTTPClient
620
676
  if @from
621
677
  req.header.set('From', @from)
622
678
  end
679
+ if @transparent_gzip_decompression
680
+ req.header.set('Accept-Encoding', 'gzip,deflate')
681
+ end
623
682
  req.header.set('Date', Time.now.httpdate)
624
683
  end
625
684
 
@@ -630,7 +689,7 @@ class HTTPClient
630
689
  begin
631
690
  timeout(@connect_timeout, ConnectTimeoutError) do
632
691
  @socket = create_socket(site)
633
- if @dest.scheme == 'https'
692
+ if https?(@dest)
634
693
  if @socket.is_a?(LoopBackSocket)
635
694
  connect_ssl_proxy(@socket, URI.parse(@dest.to_s)) if @proxy
636
695
  else
@@ -704,7 +763,7 @@ class HTTPClient
704
763
  @socket.flush unless @socket_sync
705
764
  res = HTTP::Message.new_response('')
706
765
  parse_header
707
- res.version, res.status, res.reason = @version, @status, @reason
766
+ res.http_version, res.status, res.reason = @version, @status, @reason
708
767
  @headers.each do |key, value|
709
768
  res.header.set(key, value)
710
769
  end
@@ -723,14 +782,13 @@ class HTTPClient
723
782
  def read_header
724
783
  @content_length = nil
725
784
  @chunked = false
785
+ @gzipped = false
726
786
  @chunk_length = 0
727
787
  parse_header
728
-
729
- # Head of the request has been parsed.
788
+ # Header of the request has been parsed.
730
789
  @state = :DATA
731
790
  req = @requests.shift
732
-
733
- if req.header.request_method == 'HEAD'
791
+ if req.header.request_method == 'HEAD' or no_message_body?(@status)
734
792
  @content_length = 0
735
793
  if @next_connection
736
794
  @state = :WAIT
@@ -744,11 +802,19 @@ class HTTPClient
744
802
  StatusParseRegexp = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?\r?\n\z)
745
803
  def parse_header
746
804
  timeout(@receive_timeout, ReceiveTimeoutError) do
805
+ initial_line = nil
747
806
  begin
748
807
  initial_line = @socket.gets("\n")
749
808
  if initial_line.nil?
750
- raise KeepAliveDisconnected.new
809
+ close
810
+ raise KeepAliveDisconnected.new(self)
751
811
  end
812
+ rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
813
+ # JRuby can raise IOError instead of ECONNRESET for now
814
+ close
815
+ raise KeepAliveDisconnected.new(self)
816
+ end
817
+ begin
752
818
  if StatusParseRegexp !~ initial_line
753
819
  @version = '0.9'
754
820
  @status = nil
@@ -759,7 +825,7 @@ class HTTPClient
759
825
  break
760
826
  end
761
827
  @version, @status, @reason = $1, $2.to_i, $3
762
- @next_connection = HTTP::Message.keep_alive_enabled?(@version.to_f)
828
+ @next_connection = HTTP::Message.keep_alive_enabled?(@version)
763
829
  @headers = []
764
830
  while true
765
831
  line = @socket.gets("\n")
@@ -768,18 +834,32 @@ class HTTPClient
768
834
  end
769
835
  line.chomp!
770
836
  break if line.empty?
771
- key, value = line.split(/\s*:\s*/, 2)
772
- parse_keepalive_header(key, value)
773
- @headers << [key, value]
837
+ if line[0] == ?\ or line[0] == ?\t
838
+ last = @headers.last[1]
839
+ last << ' ' unless last.empty?
840
+ last << line.strip
841
+ else
842
+ key, value = line.strip.split(/\s*:\s*/, 2)
843
+ parse_keepalive_header(key, value)
844
+ @headers << [key, value]
845
+ end
774
846
  end
775
847
  end while (@version == '1.1' && @status == 100)
776
848
  end
777
849
  end
778
850
 
851
+ def no_message_body?(status)
852
+ !status.nil? && # HTTP/0.9
853
+ ((status >= 100 && status < 200) || status == 204 || status == 304)
854
+ end
855
+
779
856
  def parse_keepalive_header(key, value)
780
857
  key = key.downcase
781
858
  if key == 'content-length'
782
859
  @content_length = value.to_i
860
+ elsif key == 'content-encoding' and ( value.downcase == 'gzip' or
861
+ value.downcase == 'x-gzip' or value.downcase == 'deflate' )
862
+ @gzipped = true
783
863
  elsif key == 'transfer-encoding' and value.downcase == 'chunked'
784
864
  @chunked = true
785
865
  @chunk_length = 0
@@ -806,8 +886,8 @@ class HTTPClient
806
886
  buf = nil
807
887
  end
808
888
  end
809
- if buf && buf.length > 0
810
- @content_length -= buf.length
889
+ if buf && buf.bytesize > 0
890
+ @content_length -= buf.bytesize
811
891
  yield buf
812
892
  else
813
893
  @content_length = 0
@@ -837,7 +917,7 @@ class HTTPClient
837
917
  end
838
918
 
839
919
  def read_body_rest
840
- if @readbuf and @readbuf.length > 0
920
+ if @readbuf and @readbuf.bytesize > 0
841
921
  yield @readbuf
842
922
  @readbuf = nil
843
923
  end
@@ -850,7 +930,7 @@ class HTTPClient
850
930
  buf = nil
851
931
  end
852
932
  end
853
- if buf && buf.length > 0
933
+ if buf && buf.bytesize > 0
854
934
  yield buf
855
935
  else
856
936
  return