In Files

  • webrick/httpproxy.rb

WEBrick::HTTPProxyServer

Constants

HopByHop

Some header fields should not be transferred.

ShouldNotTransfer

Public Class Methods

new(config) click to toggle source
 
               # File webrick/httpproxy.rb, line 27
def initialize(config)
  super
  c = @config
  @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
end
            

Public Instance Methods

choose_header(src, dst) click to toggle source
 
               # File webrick/httpproxy.rb, line 56
def choose_header(src, dst)
  connections = split_field(src['connection'])
  src.each{|key, value|
    key = key.downcase
    if HopByHop.member?(key)          || # RFC2616: 13.5.1
       connections.member?(key)       || # RFC2616: 14.10
       ShouldNotTransfer.member?(key)    # pragmatics
      @logger.debug("choose_header: `#{key}: #{value}'")
      next
    end
    dst[key] = value
  }
end
            
do_OPTIONS(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 250
def do_OPTIONS(req, res)
  res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
end
            
proxy_auth(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 43
def proxy_auth(req, res)
  if proc = @config[:ProxyAuthProc]
    proc.call(req, res)
  end
  req.header.delete("proxy-authorization")
end
            
proxy_connect(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 169
def proxy_connect(req, res)
  # Proxy Authentication
  proxy_auth(req, res)

  ua = Thread.current[:WEBrickSocket]  # User-Agent
  raise HTTPStatus::InternalServerError,
    "[BUG] cannot get socket" unless ua

  host, port = req.unparsed_uri.split(":", 2)
  # Proxy authentication for upstream proxy server
  if proxy = proxy_uri(req, res)
    proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
    if proxy.userinfo
      credentials = "Basic " + [proxy.userinfo].pack("m*")
      credentials.chomp!
    end
    host, port = proxy.host, proxy.port
  end

  begin
    @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.")
    os = TCPSocket.new(host, port)     # origin server

    if proxy
      @logger.debug("CONNECT: sending a Request-Line")
      os << proxy_request_line << CRLF
      @logger.debug("CONNECT: > #{proxy_request_line}")
      if credentials
        @logger.debug("CONNECT: sending a credentials")
        os << "Proxy-Authorization: " << credentials << CRLF
      end
      os << CRLF
      proxy_status_line = os.gets(LF)
      @logger.debug("CONNECT: read a Status-Line form the upstream server")
      @logger.debug("CONNECT: < #{proxy_status_line}")
      if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
        while line = os.gets(LF)
          break if /\A(#{CRLF}|#{LF})\z/om =~ line
        end
      else
        raise HTTPStatus::BadGateway
      end
    end
    @logger.debug("CONNECT #{host}:#{port}: succeeded")
    res.status = HTTPStatus::RC_OK
  rescue => ex
    @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'")
    res.set_error(ex)
    raise HTTPStatus::EOFError
  ensure
    if handler = @config[:ProxyContentHandler]
      handler.call(req, res)
    end
    res.send_response(ua)
    access_log(@config, req, res)

    # Should clear request-line not to send the sesponse twice.
    # see: HTTPServer#run
    req.parse(NullReader) rescue nil
  end

  begin
    while fds = IO::select([ua, os])
      if fds[0].member?(ua)
        buf = ua.sysread(1024);
        @logger.debug("CONNECT: #{buf.size} byte from User-Agent")
        os.syswrite(buf)
      elsif fds[0].member?(os)
        buf = os.sysread(1024);
        @logger.debug("CONNECT: #{buf.size} byte from #{host}:#{port}")
        ua.syswrite(buf)
      end
    end
  rescue => ex
    os.close
    @logger.debug("CONNECT #{host}:#{port}: closed")
  end

  raise HTTPStatus::EOFError
end
            
proxy_service(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 102
def proxy_service(req, res)
  # Proxy Authentication
  proxy_auth(req, res)      

  # Create Request-URI to send to the origin server
  uri  = req.request_uri
  path = uri.path.dup
  path << "?" << uri.query if uri.query

  # Choose header fields to transfer
  header = Hash.new
  choose_header(req, header)
  set_via(header)

  # select upstream proxy server
  if proxy = proxy_uri(req, res)
    proxy_host = proxy.host
    proxy_port = proxy.port
    if proxy.userinfo
      credentials = "Basic " + [proxy.userinfo].pack("m*")
      credentials.chomp!
      header['proxy-authorization'] = credentials
    end
  end

  response = nil
  begin
    http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
    http.start{
      if @config[:ProxyTimeout]
        ##################################   these issues are 
        http.open_timeout = 30   # secs  #   necessary (maybe bacause
        http.read_timeout = 60   # secs  #   Ruby's bug, but why?)
        ##################################
      end
      case req.request_method
      when "GET"  then response = http.get(path, header)
      when "POST" then response = http.post(path, req.body || "", header)
      when "HEAD" then response = http.head(path, header)
      else
        raise HTTPStatus::MethodNotAllowed,
          "unsupported method `#{req.request_method}'."
      end
    }
  rescue => err
    logger.debug("#{err.class}: #{err.message}")
    raise HTTPStatus::ServiceUnavailable, err.message
  end
  
  # Persistent connction requirements are mysterious for me.
  # So I will close the connection in every response.
  res['proxy-connection'] = "close"
  res['connection'] = "close"

  # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPProxy
  res.status = response.code.to_i
  choose_header(response, res)
  set_cookie(response, res)
  set_via(res)
  res.body = response.body

  # Process contents
  if handler = @config[:ProxyContentHandler]
    handler.call(req, res)
  end
end
            
proxy_uri(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 98
def proxy_uri(req, res)
  @config[:ProxyURI]
end
            
service(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 33
def service(req, res)
  if req.request_method == "CONNECT"
    proxy_connect(req, res)
  elsif req.unparsed_uri =~ %r^http://!
    proxy_service(req, res)
  else
    super(req, res)
  end
end
            
set_via(h) click to toggle source
 
               # File webrick/httpproxy.rb, line 88
def set_via(h)
  if @config[:ProxyVia]
    if  h['via']
      h['via'] << ", " << @via
    else
      h['via'] = @via
    end
  end
end
            
split_field(f) click to toggle source
 
               # File webrick/httpproxy.rb, line 54
def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
            

Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.

If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.

If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.

If you want to help improve the Ruby documentation, please visit Documenting-ruby.org.

blog comments powered by Disqus