httpclient 2.6.0.1 → 2.8.3

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.
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
3
  #
4
4
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
5
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -14,12 +14,18 @@
14
14
 
15
15
  require 'socket'
16
16
  require 'thread'
17
+ require 'timeout'
17
18
  require 'stringio'
18
19
  require 'zlib'
19
20
 
20
21
  require 'httpclient/timeout' # TODO: remove this once we drop 1.8 support
21
22
  require 'httpclient/ssl_config'
22
23
  require 'httpclient/http'
24
+ if RUBY_ENGINE == 'jruby'
25
+ require 'httpclient/jruby_ssl_socket'
26
+ else
27
+ require 'httpclient/ssl_socket'
28
+ end
23
29
 
24
30
 
25
31
  class HTTPClient
@@ -70,7 +76,7 @@ class HTTPClient
70
76
  def to_s # :nodoc:
71
77
  addr
72
78
  end
73
-
79
+
74
80
  # Returns true if scheme, host and port of the given URI matches with this.
75
81
  def match(uri)
76
82
  (@scheme == uri.scheme) and (@host == uri.host) and (@port == uri.port.to_i)
@@ -99,6 +105,8 @@ class HTTPClient
99
105
  attr_accessor :debug_dev
100
106
  # Boolean value for Socket#sync
101
107
  attr_accessor :socket_sync
108
+ # Boolean value to send TCP keepalive packets; no timing settings exist at present
109
+ attr_accessor :tcp_keepalive
102
110
 
103
111
  attr_accessor :connect_timeout
104
112
  # Maximum retry count. 0 for infinite.
@@ -109,6 +117,9 @@ class HTTPClient
109
117
  attr_accessor :read_block_size
110
118
  attr_accessor :protocol_retry_count
111
119
 
120
+ # Raise BadResponseError if response size does not match with Content-Length header in response.
121
+ attr_accessor :strict_response_size_check
122
+
112
123
  # Local address to bind local side of the socket to
113
124
  attr_accessor :socket_local
114
125
 
@@ -128,6 +139,7 @@ class HTTPClient
128
139
  @protocol_version = nil
129
140
  @debug_dev = client.debug_dev
130
141
  @socket_sync = true
142
+ @tcp_keepalive = false
131
143
  @chunk_size = ::HTTP::Message::Body::DEFAULT_CHUNK_SIZE
132
144
 
133
145
  @connect_timeout = 60
@@ -142,6 +154,7 @@ class HTTPClient
142
154
  @test_loopback_http_response = []
143
155
 
144
156
  @transparent_gzip_decompression = false
157
+ @strict_response_size_check = false
145
158
  @socket_local = Site.new
146
159
 
147
160
  @sess_pool = {}
@@ -206,6 +219,7 @@ class HTTPClient
206
219
  sess = Session.new(@client, site, @agent_name, @from)
207
220
  sess.proxy = via_proxy ? @proxy : nil
208
221
  sess.socket_sync = @socket_sync
222
+ sess.tcp_keepalive = @tcp_keepalive
209
223
  sess.requested_version = @protocol_version if @protocol_version
210
224
  sess.connect_timeout = @connect_timeout
211
225
  sess.connect_retry = @connect_retry
@@ -215,6 +229,7 @@ class HTTPClient
215
229
  sess.protocol_retry_count = @protocol_retry_count
216
230
  sess.ssl_config = @ssl_config
217
231
  sess.debug_dev = @debug_dev
232
+ sess.strict_response_size_check = @strict_response_size_check
218
233
  sess.socket_local = @socket_local
219
234
  sess.test_loopback_http_response = @test_loopback_http_response
220
235
  sess.transparent_gzip_decompression = @transparent_gzip_decompression
@@ -288,131 +303,6 @@ class HTTPClient
288
303
  end
289
304
 
290
305
 
291
- # Wraps up OpenSSL::SSL::SSLSocket and offers debugging features.
292
- class SSLSocketWrap
293
- def initialize(socket, context, debug_dev = nil)
294
- unless SSLEnabled
295
- raise ConfigurationError.new('Ruby/OpenSSL module is required')
296
- end
297
- @context = context
298
- @socket = socket
299
- @ssl_socket = create_openssl_socket(@socket)
300
- @debug_dev = debug_dev
301
- end
302
-
303
- def ssl_connect(hostname = nil)
304
- if hostname && @ssl_socket.respond_to?(:hostname=)
305
- @ssl_socket.hostname = hostname
306
- end
307
- @ssl_socket.connect
308
- end
309
-
310
- def post_connection_check(host)
311
- verify_mode = @context.verify_mode || OpenSSL::SSL::VERIFY_NONE
312
- if verify_mode == OpenSSL::SSL::VERIFY_NONE
313
- return
314
- elsif @ssl_socket.peer_cert.nil? and
315
- check_mask(verify_mode, OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT)
316
- raise OpenSSL::SSL::SSLError.new('no peer cert')
317
- end
318
- hostname = host.host
319
- if @ssl_socket.respond_to?(:post_connection_check) and RUBY_VERSION > "1.8.4"
320
- @ssl_socket.post_connection_check(hostname)
321
- else
322
- @context.post_connection_check(@ssl_socket.peer_cert, hostname)
323
- end
324
- end
325
-
326
- def ssl_version
327
- @ssl_socket.ssl_version if @ssl_socket.respond_to?(:ssl_version)
328
- end
329
-
330
- def ssl_cipher
331
- @ssl_socket.cipher
332
- end
333
-
334
- def ssl_state
335
- @ssl_socket.state
336
- end
337
-
338
- def peer_cert
339
- @ssl_socket.peer_cert
340
- end
341
-
342
- def close
343
- @ssl_socket.close
344
- @socket.close
345
- end
346
-
347
- def closed?
348
- @socket.closed?
349
- end
350
-
351
- def eof?
352
- @ssl_socket.eof?
353
- end
354
-
355
- def gets(*args)
356
- str = @ssl_socket.gets(*args)
357
- debug(str)
358
- str
359
- end
360
-
361
- def read(*args)
362
- str = @ssl_socket.read(*args)
363
- debug(str)
364
- str
365
- end
366
-
367
- def readpartial(*args)
368
- str = @ssl_socket.readpartial(*args)
369
- debug(str)
370
- str
371
- end
372
-
373
- def <<(str)
374
- rv = @ssl_socket.write(str)
375
- debug(str)
376
- rv
377
- end
378
-
379
- def flush
380
- @ssl_socket.flush
381
- end
382
-
383
- def sync
384
- @ssl_socket.sync
385
- end
386
-
387
- def sync=(sync)
388
- @ssl_socket.sync = sync
389
- end
390
-
391
- private
392
-
393
- def check_mask(value, mask)
394
- value & mask == mask
395
- end
396
-
397
- def create_openssl_socket(socket)
398
- ssl_socket = nil
399
- if OpenSSL::SSL.const_defined?("SSLContext")
400
- ctx = OpenSSL::SSL::SSLContext.new
401
- @context.set_context(ctx)
402
- ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
403
- else
404
- ssl_socket = OpenSSL::SSL::SSLSocket.new(socket)
405
- @context.set_context(ssl_socket)
406
- end
407
- ssl_socket
408
- end
409
-
410
- def debug(str)
411
- @debug_dev << str if @debug_dev && str
412
- end
413
- end
414
-
415
-
416
306
  # Wraps up a Socket for method interception.
417
307
  module SocketWrap
418
308
  def initialize(socket, *args)
@@ -432,20 +322,20 @@ class HTTPClient
432
322
  @socket.eof?
433
323
  end
434
324
 
435
- def gets(*args)
436
- @socket.gets(*args)
325
+ def gets(rs)
326
+ @socket.gets(rs)
437
327
  end
438
328
 
439
- def read(*args)
440
- @socket.read(*args)
329
+ def read(size, buf = nil)
330
+ @socket.read(size, buf)
441
331
  end
442
332
 
443
- def readpartial(*args)
333
+ def readpartial(size, buf = nil)
444
334
  # StringIO doesn't support :readpartial
445
335
  if @socket.respond_to?(:readpartial)
446
- @socket.readpartial(*args)
336
+ @socket.readpartial(size, buf)
447
337
  else
448
- @socket.read(*args)
338
+ @socket.read(size, buf)
449
339
  end
450
340
  end
451
341
 
@@ -481,19 +371,19 @@ class HTTPClient
481
371
  debug("! CONNECTION CLOSED\n")
482
372
  end
483
373
 
484
- def gets(*args)
374
+ def gets(rs)
485
375
  str = super
486
376
  debug(str)
487
377
  str
488
378
  end
489
379
 
490
- def read(*args)
380
+ def read(size, buf = nil)
491
381
  str = super
492
382
  debug(str)
493
383
  str
494
384
  end
495
385
 
496
- def readpartial(*args)
386
+ def readpartial(size, buf = nil)
497
387
  str = super
498
388
  debug(str)
499
389
  str
@@ -533,6 +423,10 @@ class HTTPClient
533
423
  def <<(str)
534
424
  # ignored
535
425
  end
426
+
427
+ def peer_cert
428
+ nil
429
+ end
536
430
  end
537
431
 
538
432
 
@@ -547,6 +441,8 @@ class HTTPClient
547
441
  attr_accessor :proxy
548
442
  # Boolean value for Socket#sync
549
443
  attr_accessor :socket_sync
444
+ # Boolean value to send TCP keepalive packets; no timing settings exist at present
445
+ attr_accessor :tcp_keepalive
550
446
  # Requested protocol version
551
447
  attr_accessor :requested_version
552
448
  # Device for dumping log for debugging
@@ -559,6 +455,7 @@ class HTTPClient
559
455
  attr_accessor :read_block_size
560
456
  attr_accessor :protocol_retry_count
561
457
 
458
+ attr_accessor :strict_response_size_check
562
459
  attr_accessor :socket_local
563
460
 
564
461
  attr_accessor :ssl_config
@@ -573,6 +470,7 @@ class HTTPClient
573
470
  @dest = dest
574
471
  @proxy = nil
575
472
  @socket_sync = true
473
+ @tcp_keepalive = false
576
474
  @requested_version = nil
577
475
 
578
476
  @debug_dev = nil
@@ -588,6 +486,7 @@ class HTTPClient
588
486
  @ssl_peer_cert = nil
589
487
 
590
488
  @test_loopback_http_response = nil
489
+ @strict_response_size_check = false
591
490
  @socket_local = Site::EMPTY
592
491
 
593
492
  @agent_name = agent_name
@@ -613,7 +512,7 @@ class HTTPClient
613
512
  # Use absolute URI (not absolute path) iif via proxy AND not HTTPS.
614
513
  req.header.request_absolute_uri = !@proxy.nil? && !https?(@dest)
615
514
  begin
616
- timeout(@send_timeout, SendTimeoutError) do
515
+ ::Timeout.timeout(@send_timeout, SendTimeoutError) do
617
516
  set_header(req)
618
517
  req.dump(@socket)
619
518
  # flush the IO stream as IO::sync mode is false
@@ -679,18 +578,8 @@ class HTTPClient
679
578
  begin
680
579
  read_header if @state == :META
681
580
  return nil if @state != :DATA
682
- if @gzipped and @transparent_gzip_decompression
683
- # zlib itself has a functionality to decompress gzip stream.
684
- # - zlib 1.2.5 Manual
685
- # http://www.zlib.net/manual.html#Advanced
686
- # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
687
- # > windowBits to enable zlib and gzip decoding with automatic header detection,
688
- # > or add 16 to decode only the gzip format
689
- inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
690
- original_block = block
691
- block = Proc.new { |buf|
692
- original_block.call(inflate_stream.inflate(buf))
693
- }
581
+ if @transparent_gzip_decompression
582
+ block = content_inflater_block(@content_encoding, block)
694
583
  end
695
584
  if @chunked
696
585
  read_body_chunked(&block)
@@ -713,8 +602,121 @@ class HTTPClient
713
602
  nil
714
603
  end
715
604
 
605
+ def create_socket(host, port)
606
+ socket = nil
607
+ begin
608
+ @debug_dev << "! CONNECT TO #{host}:#{port}\n" if @debug_dev
609
+ clean_host = host.delete("[]")
610
+ if @socket_local == Site::EMPTY
611
+ socket = TCPSocket.new(clean_host, port)
612
+ else
613
+ clean_local = @socket_local.host.delete("[]")
614
+ socket = TCPSocket.new(clean_host, port, clean_local, @socket_local.port)
615
+ end
616
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if @tcp_keepalive
617
+ if @debug_dev
618
+ @debug_dev << "! CONNECTION ESTABLISHED\n"
619
+ socket.extend(DebugSocket)
620
+ socket.debug_dev = @debug_dev
621
+ end
622
+ rescue SystemCallError => e
623
+ raise e.class, e.message + " (#{host}:#{port})"
624
+ rescue SocketError => e
625
+ raise e.class, e.message + " (#{host}:#{port})"
626
+ end
627
+ socket
628
+ end
629
+
630
+ def create_loopback_socket(host, port, str)
631
+ @debug_dev << "! CONNECT TO #{host}:#{port}\n" if @debug_dev
632
+ socket = LoopBackSocket.new(host, port, str)
633
+ if @debug_dev
634
+ @debug_dev << "! CONNECTION ESTABLISHED\n"
635
+ socket.extend(DebugSocket)
636
+ socket.debug_dev = @debug_dev
637
+ end
638
+ if https?(@dest) && @proxy
639
+ connect_ssl_proxy(socket, Util.urify(@dest.to_s))
640
+ end
641
+ socket
642
+ end
643
+
644
+ def connect_ssl_proxy(socket, uri)
645
+ req = HTTP::Message.new_connect_request(uri)
646
+ @client.request_filter.each do |filter|
647
+ filter.filter_request(req)
648
+ end
649
+ set_header(req)
650
+ req.dump(socket)
651
+ socket.flush unless @socket_sync
652
+ res = HTTP::Message.new_response('')
653
+ parse_header(socket)
654
+ res.http_version, res.status, res.reason = @version, @status, @reason
655
+ @headers.each do |key, value|
656
+ res.header.set(key.to_s, value)
657
+ end
658
+ commands = @client.request_filter.collect { |filter|
659
+ filter.filter_response(req, res)
660
+ }
661
+ if commands.find { |command| command == :retry }
662
+ raise RetryableResponse.new(res)
663
+ end
664
+ unless @status == 200
665
+ raise BadResponseError.new("connect to ssl proxy failed with status #{@status} #{@reason}", res)
666
+ end
667
+ end
668
+
716
669
  private
717
670
 
671
+ # This inflater allows deflate compression with/without zlib header
672
+ class LenientInflater
673
+ def initialize
674
+ @inflater = Zlib::Inflate.new(Zlib::MAX_WBITS)
675
+ @first = true
676
+ end
677
+
678
+ def inflate(body)
679
+ if @first
680
+ first_inflate(body)
681
+ else
682
+ @inflater.inflate(body)
683
+ end
684
+ end
685
+
686
+ private
687
+
688
+ def first_inflate(body)
689
+ @first = false
690
+ begin
691
+ @inflater.inflate(body)
692
+ rescue Zlib::DataError
693
+ # fallback to deflate without zlib header
694
+ @inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
695
+ @inflater.inflate(body)
696
+ end
697
+ end
698
+ end
699
+
700
+ def content_inflater_block(content_encoding, block)
701
+ case content_encoding
702
+ when 'gzip', 'x-gzip'
703
+ # zlib itself has a functionality to decompress gzip stream.
704
+ # - zlib 1.2.5 Manual
705
+ # http://www.zlib.net/manual.html#Advanced
706
+ # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
707
+ # > windowBits to enable zlib and gzip decoding with automatic header detection,
708
+ # > or add 16 to decode only the gzip format
709
+ inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
710
+ when 'deflate'
711
+ inflate_stream = LenientInflater.new
712
+ else
713
+ return block
714
+ end
715
+ Proc.new { |buf|
716
+ block.call(inflate_stream.inflate(buf))
717
+ }
718
+ end
719
+
718
720
  def set_header(req)
719
721
  if @requested_version
720
722
  if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version
@@ -743,30 +745,15 @@ class HTTPClient
743
745
  site = @proxy || @dest
744
746
  retry_number = 0
745
747
  begin
746
- timeout(@connect_timeout, ConnectTimeoutError) do
747
- @socket = create_socket(site)
748
- if https?(@dest)
749
- if @socket.is_a?(LoopBackSocket)
750
- connect_ssl_proxy(@socket, urify(@dest.to_s)) if @proxy
751
- else
752
- @socket = create_ssl_socket(@socket)
753
- connect_ssl_proxy(@socket, urify(@dest.to_s)) if @proxy
754
- begin
755
- @socket.ssl_connect(@dest.host)
756
- ensure
757
- if $DEBUG
758
- warn("Protocol version: #{@socket.ssl_version}")
759
- warn("Cipher: #{@socket.ssl_cipher.inspect}")
760
- warn("State: #{@socket.ssl_state}")
761
- end
762
- end
763
- @socket.post_connection_check(@dest)
764
- @ssl_peer_cert = @socket.peer_cert
765
- end
748
+ ::Timeout.timeout(@connect_timeout, ConnectTimeoutError) do
749
+ if str = @test_loopback_http_response.shift
750
+ @socket = create_loopback_socket(site.host, site.port, str)
751
+ elsif https?(@dest)
752
+ @socket = SSLSocket.create_socket(self)
753
+ @ssl_peer_cert = @socket.peer_cert
754
+ else
755
+ @socket = create_socket(site.host, site.port)
766
756
  end
767
- # Use Ruby internal buffering instead of passing data immediately
768
- # to the underlying layer
769
- # => we need to to call explicitly flush on the socket
770
757
  @socket.sync = @socket_sync
771
758
  end
772
759
  rescue RetryableResponse
@@ -788,71 +775,13 @@ class HTTPClient
788
775
  @state = :WAIT
789
776
  end
790
777
 
791
- def create_socket(site)
792
- socket = nil
793
- begin
794
- @debug_dev << "! CONNECT TO #{site.host}:#{site.port}\n" if @debug_dev
795
- clean_host = site.host.delete("[]")
796
- if str = @test_loopback_http_response.shift
797
- socket = LoopBackSocket.new(clean_host, site.port, str)
798
- elsif @socket_local == Site::EMPTY
799
- socket = TCPSocket.new(clean_host, site.port)
800
- else
801
- clean_local = @socket_local.host.delete("[]")
802
- socket = TCPSocket.new(clean_host, site.port, clean_local, @socket_local.port)
803
- end
804
- if @debug_dev
805
- @debug_dev << "! CONNECTION ESTABLISHED\n"
806
- socket.extend(DebugSocket)
807
- socket.debug_dev = @debug_dev
808
- end
809
- rescue SystemCallError => e
810
- e.message << " (#{site})"
811
- raise
812
- rescue SocketError => e
813
- e.message << " (#{site})"
814
- raise
815
- end
816
- socket
817
- end
818
-
819
- # wrap socket with OpenSSL.
820
- def create_ssl_socket(raw_socket)
821
- SSLSocketWrap.new(raw_socket, @ssl_config, @debug_dev)
822
- end
823
-
824
- def connect_ssl_proxy(socket, uri)
825
- req = HTTP::Message.new_connect_request(uri)
826
- @client.request_filter.each do |filter|
827
- filter.filter_request(req)
828
- end
829
- set_header(req)
830
- req.dump(@socket)
831
- @socket.flush unless @socket_sync
832
- res = HTTP::Message.new_response('')
833
- parse_header
834
- res.http_version, res.status, res.reason = @version, @status, @reason
835
- @headers.each do |key, value|
836
- res.header.set(key.to_s, value)
837
- end
838
- commands = @client.request_filter.collect { |filter|
839
- filter.filter_response(req, res)
840
- }
841
- if commands.find { |command| command == :retry }
842
- raise RetryableResponse.new(res)
843
- end
844
- unless @status == 200
845
- raise BadResponseError.new("connect to ssl proxy failed with status #{@status} #{@reason}", res)
846
- end
847
- end
848
-
849
778
  # Read status block.
850
779
  def read_header
851
780
  @content_length = nil
852
781
  @chunked = false
853
- @gzipped = false
782
+ @content_encoding = nil
854
783
  @chunk_length = 0
855
- parse_header
784
+ parse_header(@socket)
856
785
  # Header of the request has been parsed.
857
786
  @state = :DATA
858
787
  req = @requests.shift
@@ -868,12 +797,12 @@ class HTTPClient
868
797
  end
869
798
 
870
799
  StatusParseRegexp = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?\r?\n\z)
871
- def parse_header
872
- timeout(@receive_timeout, ReceiveTimeoutError) do
800
+ def parse_header(socket)
801
+ ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
873
802
  initial_line = nil
874
803
  begin
875
804
  begin
876
- initial_line = @socket.gets("\n")
805
+ initial_line = socket.gets("\n")
877
806
  if initial_line.nil?
878
807
  close
879
808
  raise KeepAliveDisconnected.new(self)
@@ -896,7 +825,7 @@ class HTTPClient
896
825
  @next_connection = HTTP::Message.keep_alive_enabled?(@version)
897
826
  @headers = []
898
827
  while true
899
- line = @socket.gets("\n")
828
+ line = socket.gets("\n")
900
829
  unless line
901
830
  raise BadResponseError.new('unexpected EOF')
902
831
  end
@@ -908,7 +837,7 @@ class HTTPClient
908
837
  last << line.strip
909
838
  else
910
839
  key, value = line.strip.split(/\s*:\s*/, 2)
911
- parse_keepalive_header(key, value)
840
+ parse_content_header(key, value)
912
841
  @headers << [key, value]
913
842
  end
914
843
  end
@@ -921,18 +850,20 @@ class HTTPClient
921
850
  ((status >= 100 && status < 200) || status == 204 || status == 304)
922
851
  end
923
852
 
924
- def parse_keepalive_header(key, value)
853
+ def parse_content_header(key, value)
925
854
  key = key.downcase
926
- if key == 'content-length'
855
+ case key
856
+ when 'content-length'
927
857
  @content_length = value.to_i
928
- elsif key == 'content-encoding' and ( value.downcase == 'gzip' or
929
- value.downcase == 'x-gzip' or value.downcase == 'deflate' )
930
- @gzipped = true
931
- elsif key == 'transfer-encoding' and value.downcase == 'chunked'
932
- @chunked = true
933
- @chunk_length = 0
934
- @content_length = nil
935
- elsif key == 'connection' or key == 'proxy-connection'
858
+ when 'content-encoding'
859
+ @content_encoding = value.downcase
860
+ when 'transfer-encoding'
861
+ if value.downcase == 'chunked'
862
+ @chunked = true
863
+ @chunk_length = 0
864
+ @content_length = nil
865
+ end
866
+ when 'connection', 'proxy-connection'
936
867
  if value.downcase == 'keep-alive'
937
868
  @next_connection = true
938
869
  else
@@ -947,12 +878,15 @@ class HTTPClient
947
878
  buf = empty_bin_str
948
879
  maxbytes = @read_block_size
949
880
  maxbytes = @content_length if maxbytes > @content_length && @content_length > 0
950
- timeout(@receive_timeout, ReceiveTimeoutError) do
881
+ ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
951
882
  begin
952
883
  @socket.readpartial(maxbytes, buf)
953
884
  rescue EOFError
954
885
  close
955
886
  buf = nil
887
+ if @strict_response_size_check
888
+ raise BadResponseError.new("EOF while reading rest #{@content_length} bytes")
889
+ end
956
890
  end
957
891
  end
958
892
  if buf && buf.bytesize > 0
@@ -969,18 +903,18 @@ class HTTPClient
969
903
  def read_body_chunked(&block)
970
904
  buf = empty_bin_str
971
905
  while true
972
- len = @socket.gets(RS)
973
- if len.nil? # EOF
974
- close
975
- return
976
- end
977
- @chunk_length = len.hex
978
- if @chunk_length == 0
979
- @content_length = 0
980
- @socket.gets(RS)
981
- return
982
- end
983
- timeout(@receive_timeout, ReceiveTimeoutError) do
906
+ ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
907
+ len = @socket.gets(RS)
908
+ if len.nil? # EOF
909
+ close
910
+ return
911
+ end
912
+ @chunk_length = len.hex
913
+ if @chunk_length == 0
914
+ @content_length = 0
915
+ @socket.gets(RS)
916
+ return
917
+ end
984
918
  @socket.read(@chunk_length, buf)
985
919
  @socket.read(2)
986
920
  end
@@ -997,11 +931,14 @@ class HTTPClient
997
931
  end
998
932
  while true
999
933
  buf = empty_bin_str
1000
- timeout(@receive_timeout, ReceiveTimeoutError) do
934
+ ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
1001
935
  begin
1002
936
  @socket.readpartial(@read_block_size, buf)
1003
937
  rescue EOFError
1004
938
  buf = nil
939
+ if @strict_response_size_check
940
+ raise BadResponseError.new("EOF while reading chunked response")
941
+ end
1005
942
  end
1006
943
  end
1007
944
  if buf && buf.bytesize > 0