httpclient 2.6.0.1 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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;
@@ -20,6 +20,11 @@ require 'zlib'
20
20
  require 'httpclient/timeout' # TODO: remove this once we drop 1.8 support
21
21
  require 'httpclient/ssl_config'
22
22
  require 'httpclient/http'
23
+ if defined?(JRuby)
24
+ require 'httpclient/jruby_ssl_socket'
25
+ else
26
+ require 'httpclient/ssl_socket'
27
+ end
23
28
 
24
29
 
25
30
  class HTTPClient
@@ -288,131 +293,6 @@ class HTTPClient
288
293
  end
289
294
 
290
295
 
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
296
  # Wraps up a Socket for method interception.
417
297
  module SocketWrap
418
298
  def initialize(socket, *args)
@@ -432,20 +312,20 @@ class HTTPClient
432
312
  @socket.eof?
433
313
  end
434
314
 
435
- def gets(*args)
436
- @socket.gets(*args)
315
+ def gets(rs)
316
+ @socket.gets(rs)
437
317
  end
438
318
 
439
- def read(*args)
440
- @socket.read(*args)
319
+ def read(size, buf = nil)
320
+ @socket.read(size, buf)
441
321
  end
442
322
 
443
- def readpartial(*args)
323
+ def readpartial(size, buf = nil)
444
324
  # StringIO doesn't support :readpartial
445
325
  if @socket.respond_to?(:readpartial)
446
- @socket.readpartial(*args)
326
+ @socket.readpartial(size, buf)
447
327
  else
448
- @socket.read(*args)
328
+ @socket.read(size, buf)
449
329
  end
450
330
  end
451
331
 
@@ -481,19 +361,19 @@ class HTTPClient
481
361
  debug("! CONNECTION CLOSED\n")
482
362
  end
483
363
 
484
- def gets(*args)
364
+ def gets(rs)
485
365
  str = super
486
366
  debug(str)
487
367
  str
488
368
  end
489
369
 
490
- def read(*args)
370
+ def read(size, buf = nil)
491
371
  str = super
492
372
  debug(str)
493
373
  str
494
374
  end
495
375
 
496
- def readpartial(*args)
376
+ def readpartial(size, buf = nil)
497
377
  str = super
498
378
  debug(str)
499
379
  str
@@ -533,6 +413,10 @@ class HTTPClient
533
413
  def <<(str)
534
414
  # ignored
535
415
  end
416
+
417
+ def peer_cert
418
+ nil
419
+ end
536
420
  end
537
421
 
538
422
 
@@ -679,18 +563,8 @@ class HTTPClient
679
563
  begin
680
564
  read_header if @state == :META
681
565
  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
- }
566
+ if @transparent_gzip_decompression
567
+ block = content_inflater_block(@content_encoding, block)
694
568
  end
695
569
  if @chunked
696
570
  read_body_chunked(&block)
@@ -713,8 +587,122 @@ class HTTPClient
713
587
  nil
714
588
  end
715
589
 
590
+ def create_socket(host, port)
591
+ socket = nil
592
+ begin
593
+ @debug_dev << "! CONNECT TO #{host}:#{port}\n" if @debug_dev
594
+ clean_host = host.delete("[]")
595
+ if @socket_local == Site::EMPTY
596
+ socket = TCPSocket.new(clean_host, port)
597
+ else
598
+ clean_local = @socket_local.host.delete("[]")
599
+ socket = TCPSocket.new(clean_host, port, clean_local, @socket_local.port)
600
+ end
601
+ if @debug_dev
602
+ @debug_dev << "! CONNECTION ESTABLISHED\n"
603
+ socket.extend(DebugSocket)
604
+ socket.debug_dev = @debug_dev
605
+ end
606
+ rescue SystemCallError => e
607
+ e.message << " (#{host}:#{port})"
608
+ raise
609
+ rescue SocketError => e
610
+ e.message << " (#{host}:#{port})"
611
+ raise
612
+ end
613
+ socket
614
+ end
615
+
616
+ def create_loopback_socket(host, port, str)
617
+ @debug_dev << "! CONNECT TO #{host}:#{port}\n" if @debug_dev
618
+ socket = LoopBackSocket.new(host, port, str)
619
+ if @debug_dev
620
+ @debug_dev << "! CONNECTION ESTABLISHED\n"
621
+ socket.extend(DebugSocket)
622
+ socket.debug_dev = @debug_dev
623
+ end
624
+ if https?(@dest) && @proxy
625
+ connect_ssl_proxy(socket, Util.urify(@dest.to_s))
626
+ end
627
+ socket
628
+ end
629
+
630
+ def connect_ssl_proxy(socket, uri)
631
+ req = HTTP::Message.new_connect_request(uri)
632
+ @client.request_filter.each do |filter|
633
+ filter.filter_request(req)
634
+ end
635
+ set_header(req)
636
+ req.dump(socket)
637
+ socket.flush unless @socket_sync
638
+ res = HTTP::Message.new_response('')
639
+ parse_header(socket)
640
+ res.http_version, res.status, res.reason = @version, @status, @reason
641
+ @headers.each do |key, value|
642
+ res.header.set(key.to_s, value)
643
+ end
644
+ commands = @client.request_filter.collect { |filter|
645
+ filter.filter_response(req, res)
646
+ }
647
+ if commands.find { |command| command == :retry }
648
+ raise RetryableResponse.new(res)
649
+ end
650
+ unless @status == 200
651
+ raise BadResponseError.new("connect to ssl proxy failed with status #{@status} #{@reason}", res)
652
+ end
653
+ end
654
+
716
655
  private
717
656
 
657
+ # This inflater allows deflate compression with/without zlib header
658
+ class LenientInflater
659
+ def initialize
660
+ @inflater = Zlib::Inflate.new(Zlib::MAX_WBITS)
661
+ @first = true
662
+ end
663
+
664
+ def inflate(body)
665
+ if @first
666
+ first_inflate(body)
667
+ else
668
+ @inflater.inflate(body)
669
+ end
670
+ end
671
+
672
+ private
673
+
674
+ def first_inflate(body)
675
+ @first = false
676
+ begin
677
+ @inflater.inflate(body)
678
+ rescue Zlib::DataError
679
+ # fallback to deflate without zlib header
680
+ @inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
681
+ @inflater.inflate(body)
682
+ end
683
+ end
684
+ end
685
+
686
+ def content_inflater_block(content_encoding, block)
687
+ case content_encoding
688
+ when 'gzip', 'x-gzip'
689
+ # zlib itself has a functionality to decompress gzip stream.
690
+ # - zlib 1.2.5 Manual
691
+ # http://www.zlib.net/manual.html#Advanced
692
+ # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
693
+ # > windowBits to enable zlib and gzip decoding with automatic header detection,
694
+ # > or add 16 to decode only the gzip format
695
+ inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
696
+ when 'deflate'
697
+ inflate_stream = LenientInflater.new
698
+ else
699
+ return block
700
+ end
701
+ Proc.new { |buf|
702
+ block.call(inflate_stream.inflate(buf))
703
+ }
704
+ end
705
+
718
706
  def set_header(req)
719
707
  if @requested_version
720
708
  if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version
@@ -744,29 +732,14 @@ class HTTPClient
744
732
  retry_number = 0
745
733
  begin
746
734
  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
735
+ if str = @test_loopback_http_response.shift
736
+ @socket = create_loopback_socket(site.host, site.port, str)
737
+ elsif https?(@dest)
738
+ @socket = SSLSocket.create_socket(self)
739
+ @ssl_peer_cert = @socket.peer_cert
740
+ else
741
+ @socket = create_socket(site.host, site.port)
766
742
  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
743
  @socket.sync = @socket_sync
771
744
  end
772
745
  rescue RetryableResponse
@@ -788,71 +761,13 @@ class HTTPClient
788
761
  @state = :WAIT
789
762
  end
790
763
 
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
764
  # Read status block.
850
765
  def read_header
851
766
  @content_length = nil
852
767
  @chunked = false
853
- @gzipped = false
768
+ @content_encoding = nil
854
769
  @chunk_length = 0
855
- parse_header
770
+ parse_header(@socket)
856
771
  # Header of the request has been parsed.
857
772
  @state = :DATA
858
773
  req = @requests.shift
@@ -868,12 +783,12 @@ class HTTPClient
868
783
  end
869
784
 
870
785
  StatusParseRegexp = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?\r?\n\z)
871
- def parse_header
786
+ def parse_header(socket)
872
787
  timeout(@receive_timeout, ReceiveTimeoutError) do
873
788
  initial_line = nil
874
789
  begin
875
790
  begin
876
- initial_line = @socket.gets("\n")
791
+ initial_line = socket.gets("\n")
877
792
  if initial_line.nil?
878
793
  close
879
794
  raise KeepAliveDisconnected.new(self)
@@ -896,7 +811,7 @@ class HTTPClient
896
811
  @next_connection = HTTP::Message.keep_alive_enabled?(@version)
897
812
  @headers = []
898
813
  while true
899
- line = @socket.gets("\n")
814
+ line = socket.gets("\n")
900
815
  unless line
901
816
  raise BadResponseError.new('unexpected EOF')
902
817
  end
@@ -908,7 +823,7 @@ class HTTPClient
908
823
  last << line.strip
909
824
  else
910
825
  key, value = line.strip.split(/\s*:\s*/, 2)
911
- parse_keepalive_header(key, value)
826
+ parse_content_header(key, value)
912
827
  @headers << [key, value]
913
828
  end
914
829
  end
@@ -921,18 +836,20 @@ class HTTPClient
921
836
  ((status >= 100 && status < 200) || status == 204 || status == 304)
922
837
  end
923
838
 
924
- def parse_keepalive_header(key, value)
839
+ def parse_content_header(key, value)
925
840
  key = key.downcase
926
- if key == 'content-length'
841
+ case key
842
+ when 'content-length'
927
843
  @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'
844
+ when 'content-encoding'
845
+ @content_encoding = value.downcase
846
+ when 'transfer-encoding'
847
+ if value.downcase == 'chunked'
848
+ @chunked = true
849
+ @chunk_length = 0
850
+ @content_length = nil
851
+ end
852
+ when 'connection', 'proxy-connection'
936
853
  if value.downcase == 'keep-alive'
937
854
  @next_connection = true
938
855
  else