In Files

  • webrick/httpproxy.rb

WEBrick::HTTPProxyServer

An HTTP Proxy server which proxies GET, HEAD and POST requests.

Constants

HopByHop

Some header fields should not be transferred.

ShouldNotTransfer

Public Class Methods

new(config={}, default=Config::HTTP) click to toggle source

Proxy server configurations. The proxy server handles the following configuration items in addition to those supported by HTTPServer:

:ProxyAuthProc

Called with a request and response to authorize a request

:ProxyVia

Appended to the via header

:ProxyURI

The proxy server’s URI

:ProxyContentHandler

Called with a request and resopnse and allows modification of the response

:ProxyTimeout

Sets the proxy timeouts to 30 seconds for open and 60 seconds for read operations

 
               # File webrick/httpproxy.rb, line 54
def initialize(config={}, default=Config::HTTP)
  super(config, default)
  c = @config
  @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
end
            

Public Instance Methods

do_CONNECT(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 102
def do_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").delete("\n")
    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/m =~ 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.bytesize} byte from User-Agent")
        os.syswrite(buf)
      elsif fds[0].member?(os)
        buf = os.sysread(1024);
        @logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
        ua.syswrite(buf)
      end
    end
  rescue => ex
    os.close
    @logger.debug("CONNECT #{host}:#{port}: closed")
  end

  raise HTTPStatus::EOFError
end
            
do_GET(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 182
def do_GET(req, res)
  perform_proxy_request(req, res) do |http, path, header|
    http.get(path, header)
  end
end
            
do_HEAD(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 188
def do_HEAD(req, res)
  perform_proxy_request(req, res) do |http, path, header|
    http.head(path, header)
  end
end
            
do_OPTIONS(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 200
def do_OPTIONS(req, res)
  res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
end
            
do_POST(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 194
def do_POST(req, res)
  perform_proxy_request(req, res) do |http, path, header|
    http.post(path, req.body || "", header)
  end
end
            
proxy_auth(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 70
def proxy_auth(req, res)
  if proc = @config[:ProxyAuthProc]
    proc.call(req, res)
  end
  req.header.delete("proxy-authorization")
end
            
proxy_service(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 82
def proxy_service(req, res)
  # Proxy Authentication
  proxy_auth(req, res)

  begin
    self.send("do_#{req.request_method}", req, res)
  rescue NoMethodError
    raise HTTPStatus::MethodNotAllowed,
      "unsupported method `#{req.request_method}'."
  rescue => err
    logger.debug("#{err.class}: #{err.message}")
    raise HTTPStatus::ServiceUnavailable, err.message
  end

  # 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 77
def proxy_uri(req, res)
  # should return upstream proxy server's URI
  return @config[:ProxyURI]
end
            
service(req, res) click to toggle source
 
               # File webrick/httpproxy.rb, line 60
def service(req, res)
  if req.request_method == "CONNECT"
    do_CONNECT(req, res)
  elsif req.unparsed_uri =~ %r^http://!
    proxy_service(req, res)
  else
    super(req, res)
  end
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