Parent

Included Modules

Files

HTTPClient::Session

Deprecated. just for backward compatibility


Manages a HTTP session with a Site.

Constants

BadResponse
StatusParseRegexp
RS

Attributes

dest[R]

Destination site

proxy[RW]

Proxy site

socket_sync[RW]

Boolean value for Socket#sync

requested_version[RW]

Requested protocol version

debug_dev[RW]

Device for dumping log for debugging

connect_timeout[RW]
connect_retry[RW]
send_timeout[RW]
receive_timeout[RW]
read_block_size[RW]
protocol_retry_count[RW]
ssl_config[RW]
ssl_peer_cert[R]
test_loopback_http_response[RW]

Public Class Methods

new(client, dest, agent_name, from) click to toggle source
     # File lib/httpclient/session.rb, line 485
485:     def initialize(client, dest, agent_name, from)
486:       @client = client
487:       @dest = dest
488:       @proxy = nil
489:       @socket_sync = true
490:       @requested_version = nil
491: 
492:       @debug_dev = nil
493: 
494:       @connect_timeout = nil
495:       @connect_retry = 1
496:       @send_timeout = nil
497:       @receive_timeout = nil
498:       @read_block_size = nil
499:       @protocol_retry_count = 5
500: 
501:       @ssl_config = nil
502:       @ssl_peer_cert = nil
503: 
504:       @test_loopback_http_response = nil
505: 
506:       @agent_name = agent_name
507:       @from = from
508:       @state = :INIT
509: 
510:       @requests = []
511: 
512:       @status = nil
513:       @reason = nil
514:       @headers = []
515: 
516:       @socket = nil
517:       @readbuf = nil
518:     end

Public Instance Methods

close() click to toggle source
     # File lib/httpclient/session.rb, line 550
550:     def close
551:       if !@socket.nil? and !@socket.closed?
552:         # @socket.flush may block when it the socket is already closed by
553:         # foreign host and the client runs under MT-condition.
554:         @socket.close
555:       end
556:       @state = :INIT
557:     end
closed?() click to toggle source
     # File lib/httpclient/session.rb, line 559
559:     def closed?
560:       @state == :INIT
561:     end
eof?() click to toggle source
     # File lib/httpclient/session.rb, line 576
576:     def eof?
577:       if !@content_length.nil?
578:         @content_length == 0
579:       else
580:         @socket.closed? or @socket.eof?
581:       end
582:     end
get_body(&block) click to toggle source
     # File lib/httpclient/session.rb, line 584
584:     def get_body(&block)
585:       begin
586:         read_header if @state == :META
587:         return nil if @state != :DATA
588:         if @chunked
589:           read_body_chunked(&block)
590:         elsif @content_length
591:           read_body_length(&block)
592:         else
593:           read_body_rest(&block)
594:         end
595:       rescue
596:         close
597:         raise
598:       end
599:       if eof?
600:         if @next_connection
601:           @state = :WAIT
602:         else
603:           close
604:         end
605:       end
606:       nil
607:     end
get_header() click to toggle source
     # File lib/httpclient/session.rb, line 563
563:     def get_header
564:       begin
565:         if @state != :META
566:           raise RuntimeError.new("get_status must be called at the beginning of a session")
567:         end
568:         read_header
569:       rescue
570:         close
571:         raise
572:       end
573:       [@version, @status, @reason, @headers]
574:     end
query(req) click to toggle source

Send a request to the server

     # File lib/httpclient/session.rb, line 521
521:     def query(req)
522:       connect if @state == :INIT
523:       req.header.request_via_proxy = !@proxy.nil?
524:       begin
525:         timeout(@send_timeout, SendTimeoutError) do
526:           set_header(req)
527:           req.dump(@socket)
528:           # flush the IO stream as IO::sync mode is false
529:           @socket.flush unless @socket_sync
530:         end
531:       rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
532:         close
533:         raise KeepAliveDisconnected.new
534:       rescue HTTPClient::TimeoutError
535:         close
536:         raise
537:       rescue
538:         if SSLEnabled and $!.is_a?(OpenSSL::SSL::SSLError)
539:           raise KeepAliveDisconnected.new
540:         else
541:           raise
542:         end
543:       end
544: 
545:       @state = :META if @state == :WAIT
546:       @next_connection = nil
547:       @requests.push(req)
548:     end

Private Instance Methods

connect() click to toggle source

Connect to the server

     # File lib/httpclient/session.rb, line 627
627:     def connect
628:       site = @proxy || @dest
629:       retry_number = 0
630:       begin
631:         timeout(@connect_timeout, ConnectTimeoutError) do
632:           @socket = create_socket(site)
633:           if @dest.scheme == 'https'
634:             if @socket.is_a?(LoopBackSocket)
635:               connect_ssl_proxy(@socket, URI.parse(@dest.to_s)) if @proxy
636:             else
637:               @socket = create_ssl_socket(@socket)
638:               connect_ssl_proxy(@socket, URI.parse(@dest.to_s)) if @proxy
639:               @socket.ssl_connect
640:               @socket.post_connection_check(@dest)
641:               @ssl_peer_cert = @socket.peer_cert
642:             end
643:           end
644:           # Use Ruby internal buffering instead of passing data immediately
645:           # to the underlying layer
646:           # => we need to to call explicitly flush on the socket
647:           @socket.sync = @socket_sync
648:         end
649:       rescue RetryableResponse
650:         retry_number += 1
651:         if retry_number < @protocol_retry_count
652:           retry
653:         end
654:         raise BadResponseError.new("connect to the server failed with status #{@status} #{@reason}")
655:       rescue TimeoutError
656:         if @connect_retry == 0
657:           retry
658:         else
659:           retry_number += 1
660:           retry if retry_number < @connect_retry
661:         end
662:         close
663:         raise
664:       end
665:       @state = :WAIT
666:     end
connect_ssl_proxy(socket, uri) click to toggle source
     # File lib/httpclient/session.rb, line 697
697:     def connect_ssl_proxy(socket, uri)
698:       req = HTTP::Message.new_connect_request(uri)
699:       @client.request_filter.each do |filter|
700:         filter.filter_request(req)
701:       end
702:       set_header(req)
703:       req.dump(@socket)
704:       @socket.flush unless @socket_sync
705:       res = HTTP::Message.new_response('')
706:       parse_header
707:       res.version, res.status, res.reason = @version, @status, @reason
708:       @headers.each do |key, value|
709:         res.header.set(key, value)
710:       end
711:       commands = @client.request_filter.collect { |filter|
712:         filter.filter_response(req, res)
713:       }
714:       if commands.find { |command| command == :retry }
715:         raise RetryableResponse.new
716:       end
717:       unless @status == 200
718:         raise BadResponseError.new("connect to ssl proxy failed with status #{@status} #{@reason}", res)
719:       end
720:     end
create_socket(site) click to toggle source
     # File lib/httpclient/session.rb, line 668
668:     def create_socket(site)
669:       socket = nil
670:       begin
671:         @debug_dev << "! CONNECT TO #{site.host}:#{site.port}\n" if @debug_dev
672:         if str = @test_loopback_http_response.shift
673:           socket = LoopBackSocket.new(site.host, site.port, str)
674:         else
675:           socket = TCPSocket.new(site.host, site.port)
676:         end
677:         if @debug_dev
678:           @debug_dev << "! CONNECTION ESTABLISHED\n"
679:           socket.extend(DebugSocket)
680:           socket.debug_dev = @debug_dev
681:         end
682:       rescue SystemCallError => e
683:         e.message << " (#{site})"
684:         raise
685:       rescue SocketError => e
686:         e.message << " (#{site})"
687:         raise
688:       end
689:       socket
690:     end
create_ssl_socket(raw_socket) click to toggle source

wrap socket with OpenSSL.

     # File lib/httpclient/session.rb, line 693
693:     def create_ssl_socket(raw_socket)
694:       SSLSocketWrap.new(raw_socket, @ssl_config, @debug_dev)
695:     end
parse_header() click to toggle source
     # File lib/httpclient/session.rb, line 745
745:     def parse_header
746:       timeout(@receive_timeout, ReceiveTimeoutError) do
747:         begin
748:           initial_line = @socket.gets("\n")
749:           if initial_line.nil?
750:             raise KeepAliveDisconnected.new
751:           end
752:           if StatusParseRegexp !~ initial_line
753:             @version = '0.9'
754:             @status = nil
755:             @reason = nil
756:             @next_connection = false
757:             @content_length = nil
758:             @readbuf = initial_line
759:             break
760:           end
761:           @version, @status, @reason = $1, $2.to_i, $3
762:           @next_connection = HTTP::Message.keep_alive_enabled?(@version.to_f)
763:           @headers = []
764:           while true
765:             line = @socket.gets("\n")
766:             unless line
767:               raise BadResponseError.new('unexpected EOF')
768:             end
769:             line.chomp!
770:             break if line.empty?
771:             key, value = line.split(/\s*:\s*/, 2)
772:             parse_keepalive_header(key, value)
773:             @headers << [key, value]
774:           end
775:         end while (@version == '1.1' && @status == 100)
776:       end
777:     end
parse_keepalive_header(key, value) click to toggle source
     # File lib/httpclient/session.rb, line 779
779:     def parse_keepalive_header(key, value)
780:       key = key.downcase
781:       if key == 'content-length'
782:         @content_length = value.to_i
783:       elsif key == 'transfer-encoding' and value.downcase == 'chunked'
784:         @chunked = true
785:         @chunk_length = 0
786:         @content_length = nil
787:       elsif key == 'connection' or key == 'proxy-connection'
788:         if value.downcase == 'keep-alive'
789:           @next_connection = true
790:         else
791:           @next_connection = false
792:         end
793:       end
794:     end
read_body_chunked(&block) click to toggle source
     # File lib/httpclient/session.rb, line 820
820:     def read_body_chunked(&block)
821:       buf = ''
822:       while true
823:         len = @socket.gets(RS)
824:         @chunk_length = len.hex
825:         if @chunk_length == 0
826:           @content_length = 0
827:           @socket.gets(RS)
828:           return
829:         end
830:         timeout(@receive_timeout, ReceiveTimeoutError) do
831:           @socket.read(@chunk_length + 2, buf)
832:         end
833:         unless buf.empty?
834:           yield buf.slice(0, @chunk_length)
835:         end
836:       end
837:     end
read_body_length(&block) click to toggle source
     # File lib/httpclient/session.rb, line 796
796:     def read_body_length(&block)
797:       return nil if @content_length == 0
798:       buf = ''
799:       while true
800:         maxbytes = @read_block_size
801:         maxbytes = @content_length if maxbytes > @content_length
802:         timeout(@receive_timeout, ReceiveTimeoutError) do
803:           begin
804:             @socket.readpartial(maxbytes, buf)
805:           rescue EOFError
806:             buf = nil
807:           end
808:         end
809:         if buf && buf.length > 0
810:           @content_length -= buf.length
811:           yield buf
812:         else
813:           @content_length = 0
814:         end
815:         return if @content_length == 0
816:       end
817:     end
read_body_rest() click to toggle source
     # File lib/httpclient/session.rb, line 839
839:     def read_body_rest
840:       if @readbuf and @readbuf.length > 0
841:         yield @readbuf
842:         @readbuf = nil
843:       end
844:       buf = ''
845:       while true
846:         timeout(@receive_timeout, ReceiveTimeoutError) do
847:           begin
848:             @socket.readpartial(@read_block_size, buf)
849:           rescue EOFError
850:             buf = nil
851:           end
852:         end
853:         if buf && buf.length > 0
854:           yield buf
855:         else
856:           return
857:         end
858:       end
859:     end
read_header() click to toggle source

Read status block.

     # File lib/httpclient/session.rb, line 723
723:     def read_header
724:       @content_length = nil
725:       @chunked = false
726:       @chunk_length = 0
727:       parse_header
728: 
729:       # Head of the request has been parsed.
730:       @state = :DATA
731:       req = @requests.shift
732: 
733:       if req.header.request_method == 'HEAD'
734:         @content_length = 0
735:         if @next_connection
736:           @state = :WAIT
737:         else
738:           close
739:         end
740:       end
741:       @next_connection = false unless @content_length
742:     end
set_header(req) click to toggle source
     # File lib/httpclient/session.rb, line 611
611:     def set_header(req)
612:       if @requested_version
613:         if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version
614:           req.version = $1.to_f
615:         end
616:       end
617:       if @agent_name
618:         req.header.set('User-Agent', "#{@agent_name} #{LIB_NAME}")
619:       end
620:       if @from
621:         req.header.set('From', @from)
622:       end
623:       req.header.set('Date', Time.now.httpdate)
624:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.