Object
# 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
# 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
# File lib/httpclient/session.rb, line 559
559: def closed?
560: @state == :INIT
561: end
# 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
# 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
# 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
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
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
# 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
# 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
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
# 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
# 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
# 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
# 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
# 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 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
# 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.
Generated with the Darkfish Rdoc Generator 1.1.6.