class HTTPClient::Session

Deprecated. just for backward compatibility

def close

def close
  if !@socket.nil? and !@socket.closed?
    # @socket.flush may block when it the socket is already closed by
    # foreign host and the client runs under MT-condition.
    @socket.close
  end
  @state = :INIT
end

def closed?

def closed?
  @state == :INIT
end

def connect

Connect to the server
def connect
  site = @proxy || @dest
  retry_number = 0
  begin
    ::Timeout.timeout(@connect_timeout, ConnectTimeoutError) do
      if str = @test_loopback_http_response.shift
        @socket = create_loopback_socket(site.host, site.port, str)
      elsif https?(@dest)
        @socket = SSLSocket.create_socket(self)
        @ssl_peer_cert = @socket.peer_cert
      else
        @socket = create_socket(site.host, site.port)
      end
      @socket.sync = @socket_sync
    end
  rescue RetryableResponse
    retry_number += 1
    if retry_number < @protocol_retry_count
      retry
    end
    raise BadResponseError.new("connect to the server failed with status #{@status} #{@reason}")
  rescue TimeoutError
    if @connect_retry == 0
      retry
    else
      retry_number += 1
      retry if retry_number < @connect_retry
    end
    close
    raise
  end
  @state = :WAIT
end

def connect_ssl_proxy(socket, uri)

def connect_ssl_proxy(socket, uri)
  req = HTTP::Message.new_connect_request(uri)
  @client.request_filter.each do |filter|
    filter.filter_request(req)
  end
  set_header(req)
  req.dump(socket)
  socket.flush unless @socket_sync
  res = HTTP::Message.new_response('')
  parse_header(socket)
  res.http_version, res.status, res.reason = @version, @status, @reason
  @headers.each do |key, value|
    res.header.set(key.to_s, value)
  end
  commands = @client.request_filter.collect { |filter|
    filter.filter_response(req, res)
  }
  if commands.find { |command| command == :retry }
    raise RetryableResponse.new(res)
  end
  unless @status == 200
    raise BadResponseError.new("connect to ssl proxy failed with status #{@status} #{@reason}", res)
  end
end

def content_inflater_block(content_encoding, block)

def content_inflater_block(content_encoding, block)
  case content_encoding
  when 'gzip', 'x-gzip'
    # zlib itself has a functionality to decompress gzip stream.
    # - zlib 1.2.5 Manual
    #   http://www.zlib.net/manual.html#Advanced
    # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
    # > windowBits to enable zlib and gzip decoding with automatic header detection,
    # > or add 16 to decode only the gzip format
    inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
  when 'deflate'
    inflate_stream = LenientInflater.new
  else
    return block
  end
  Proc.new { |buf|
    block.call(inflate_stream.inflate(buf))
  }
end

def create_loopback_socket(host, port, str)

def create_loopback_socket(host, port, str)
  @debug_dev << "! CONNECT TO #{host}:#{port}\n" if @debug_dev
  socket = LoopBackSocket.new(host, port, str)
  if @debug_dev
    @debug_dev << "! CONNECTION ESTABLISHED\n"
    socket.extend(DebugSocket)
    socket.debug_dev = @debug_dev
  end
  if https?(@dest) && @proxy
    connect_ssl_proxy(socket, Util.urify(@dest.to_s))
  end
  socket
end

def create_socket(host, port)

def create_socket(host, port)
  socket = nil
  begin
    @debug_dev << "! CONNECT TO #{host}:#{port}\n" if @debug_dev
    clean_host = host.delete("[]")
    if @socket_local == Site::EMPTY
      socket = TCPSocket.new(clean_host, port)
    else
      clean_local = @socket_local.host.delete("[]")
      socket = TCPSocket.new(clean_host, port, clean_local, @socket_local.port)
    end
    socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if @tcp_keepalive
    if @debug_dev
      @debug_dev << "! CONNECTION ESTABLISHED\n"
      socket.extend(DebugSocket)
      socket.debug_dev = @debug_dev
    end
  rescue SystemCallError => e
    raise e.class, e.message + " (#{host}:#{port})"
  rescue SocketError => e
    raise e.class, e.message + " (#{host}:#{port})"
  end
  socket
end

def empty_bin_str

def empty_bin_str
  str = ''.dup
  str.force_encoding('BINARY') if str.respond_to?(:force_encoding)
  str
end

def eof?

def eof?
  if !@content_length.nil?
    @content_length == 0
  else
    @socket.closed? or @socket.eof?
  end
end

def get_body(&block)

def get_body(&block)
  begin
    read_header if @state == :META
    return nil if @state != :DATA
    if @transparent_gzip_decompression
      block = content_inflater_block(@content_encoding, block)
    end
    if @chunked
      read_body_chunked(&block)
    elsif @content_length
      read_body_length(&block)
    else
      read_body_rest(&block)
    end
  rescue
    close
    raise
  end
  if eof?
    if @next_connection
      @state = :WAIT
    else
      close
    end
  end
  nil
end

def get_header

def get_header
  begin
    if @state != :META
      raise RuntimeError.new("get_status must be called at the beginning of a session")
    end
    read_header
  rescue
    close
    raise
  end
  [@version, @status, @reason, @headers]
end

def initialize(client, dest, agent_name, from)

def initialize(client, dest, agent_name, from)
  @client = client
  @dest = dest
  @proxy = nil
  @socket_sync = true
  @tcp_keepalive = false
  @requested_version = nil
  @debug_dev = nil
  @connect_timeout = nil
  @connect_retry = 1
  @send_timeout = nil
  @receive_timeout = nil
  @read_block_size = nil
  @protocol_retry_count = 5
  @ssl_config = nil
  @ssl_peer_cert = nil
  @test_loopback_http_response = nil
  @strict_response_size_check = false
  @socket_local = Site::EMPTY
  @agent_name = agent_name
  @from = from
  @state = :INIT
  @requests = []
  @status = nil
  @reason = nil
  @headers = []
  @socket = nil
  @readbuf = nil
  @transparent_gzip_decompression = false
  @last_used = nil
end

def no_message_body?(status)

def no_message_body?(status)
  !status.nil? && # HTTP/0.9
    ((status >= 100 && status < 200) || status == 204 || status == 304)
end

def parse_content_header(key, value)

def parse_content_header(key, value)
  key = key.downcase
  case key
  when 'content-length'
    @content_length = value.to_i
  when 'content-encoding'
    @content_encoding = value.downcase
  when 'transfer-encoding'
    if value.downcase == 'chunked'
      @chunked = true
      @chunk_length = 0
      @content_length = nil
    end
  when 'connection', 'proxy-connection'
    if value.downcase == 'keep-alive'
      @next_connection = true
    else
      @next_connection = false
    end
  end
end

def parse_header(socket)

def parse_header(socket)
  ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
    initial_line = nil
    begin
      begin
        initial_line = socket.gets("\n")
        if initial_line.nil?
          close
          raise KeepAliveDisconnected.new(self)
        end
      rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
        # JRuby can raise IOError instead of ECONNRESET for now
        close
        raise KeepAliveDisconnected.new(self, $!)
      end
      if StatusParseRegexp !~ initial_line
        @version = '0.9'
        @status = nil
        @reason = nil
        @next_connection = false
        @content_length = nil
        @readbuf = initial_line
        break
      end
      @version, @status, @reason = $1, $2.to_i, $3
      @next_connection = HTTP::Message.keep_alive_enabled?(@version)
      @headers = []
      while true
        line = socket.gets("\n")
        unless line
          raise BadResponseError.new('unexpected EOF')
        end
        line.chomp!
        break if line.empty?
        if line[0] == ?\  or line[0] == ?\t
          last = @headers.last[1]
          last << ' ' unless last.empty?
          last << line.strip
        else
          key, value = line.strip.split(/\s*:\s*/, 2)
          parse_content_header(key, value)
          @headers << [key, value]
        end
      end
    end while (@version == '1.1' && @status == 100)
  end
end

def query(req)

Send a request to the server
def query(req)
  connect if @state == :INIT
  # Use absolute URI (not absolute path) iif via proxy AND not HTTPS.
  req.header.request_absolute_uri = !@proxy.nil? && !https?(@dest)
  begin
    ::Timeout.timeout(@send_timeout, SendTimeoutError) do
      set_header(req)
      req.dump(@socket)
      # flush the IO stream as IO::sync mode is false
      @socket.flush unless @socket_sync
    end
  rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
    # JRuby can raise IOError instead of ECONNRESET for now
    close
    raise KeepAliveDisconnected.new(self, $!)
  rescue HTTPClient::TimeoutError
    close
    raise
  rescue => e
    close
    if SSLEnabled and e.is_a?(OpenSSL::SSL::SSLError)
      raise KeepAliveDisconnected.new(self, e)
    else
      raise
    end
  end
  @state = :META if @state == :WAIT
  @next_connection = nil
  @requests.push(req)
  @last_used = Time.now
end

def read_body_chunked(&block)

def read_body_chunked(&block)
  buf = empty_bin_str
  while true
   ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
      len = @socket.gets(RS)
      if len.nil? # EOF
        close
        return
      end
      @chunk_length = len.hex
      if @chunk_length == 0
        @content_length = 0
        @socket.gets(RS)
        return
      end
      @socket.read(@chunk_length, buf)
      @socket.read(2)
    end
    unless buf.empty?
      yield buf
    end
  end
end

def read_body_length(&block)

def read_body_length(&block)
  return nil if @content_length == 0
  while true
    buf = empty_bin_str
    maxbytes = @read_block_size
    maxbytes = @content_length if maxbytes > @content_length && @content_length > 0
    ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
      begin
        @socket.readpartial(maxbytes, buf)
      rescue EOFError
        close
        buf = nil
        if @strict_response_size_check
          raise BadResponseError.new("EOF while reading rest #{@content_length} bytes")
        end
      end
    end
    if buf && buf.bytesize > 0
      @content_length -= buf.bytesize
      yield buf
    else
      @content_length = 0
    end
    return if @content_length == 0
  end
end

def read_body_rest

def read_body_rest
  if @readbuf and @readbuf.bytesize > 0
    yield @readbuf
    @readbuf = nil
  end
  while true
    buf = empty_bin_str
    ::Timeout.timeout(@receive_timeout, ReceiveTimeoutError) do
      begin
        @socket.readpartial(@read_block_size, buf)
      rescue EOFError
        buf = nil
        if @strict_response_size_check
          raise BadResponseError.new("EOF while reading chunked response")
        end
      end
    end
    if buf && buf.bytesize > 0
      yield buf
    else
      return
    end
  end
end

def read_header

Read status block.
def read_header
  @content_length = nil
  @chunked = false
  @content_encoding = nil
  @chunk_length = 0
  parse_header(@socket)
  # Header of the request has been parsed.
  @state = :DATA
  req = @requests.shift
  if req.header.request_method == 'HEAD' or no_message_body?(@status)
    @content_length = 0
    if @next_connection
      @state = :WAIT
    else
      close
    end
  end
  @next_connection = false if !@content_length and !@chunked
end

def set_header(req)

def set_header(req)
  if @requested_version
    if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version
      req.http_version = $1
    end
  end
  if @agent_name && req.header.get('User-Agent').empty?
    req.header.set('User-Agent', "#{@agent_name} #{LIB_NAME}")
  end
  if @from && req.header.get('From').empty?
    req.header.set('From', @from)
  end
  if req.header.get('Accept').empty?
    req.header.set('Accept', '*/*')
  end
  if @transparent_gzip_decompression
    req.header.set('Accept-Encoding', 'gzip,deflate')
  end
  if req.header.get('Date').empty?
    req.header.set_date_header
  end
end