123456789_123456789_123456789_123456789_123456789_

Class: WEBrick::HTTPRequest

Relationships & Source Files
Inherits: Object
Defined in: lib/webrick/https.rb,
lib/webrick/httprequest.rb

Constant Summary

Request line

Request-URI

Header and entity body

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(config) ⇒ HTTPRequest

Creates a new HTTP request. Config::HTTP is the default configuration.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 152

def initialize(config)
  @config = config
  @buffer_size = @config[:InputBufferSize]
  @logger = config[:Logger]

  @request_line = @request_method =
    @unparsed_uri = @http_version = nil

  @request_uri = @host = @port = @path = nil
  @script_name = @path_info = nil
  @query_string = nil
  @query = nil
  @form_data = nil

  @raw_header = Array.new
  @header = nil
  @cookies = []
  @accept = []
  @accept_charset = []
  @accept_encoding = []
  @accept_language = []
  @body = ""

  @addr = @peeraddr = nil
  @attributes = {}
  @user = nil
  @keep_alive = false
  @request_time = nil

  @remaining_size = nil
  @socket = nil

  @forwarded_proto = @forwarded_host = @forwarded_port =
    @forwarded_server = @forwarded_for = nil
end

Instance Attribute Details

#accept (readonly)

The Accept header value

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 99

attr_reader :accept

#accept_charset (readonly)

The Accept-Charset header value

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 104

attr_reader :accept_charset

#accept_encoding (readonly)

The Accept-Encoding header value

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 109

attr_reader :accept_encoding

#accept_language (readonly)

The Accept-Language header value

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 114

attr_reader :accept_language

#addr (readonly)

The socket address of the server

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 126

attr_reader :addr

#attributes (readonly)

Hash of request attributes

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 136

attr_reader :attributes

#cipher (readonly)

HTTP request SSL cipher

[ GitHub ]

  
# File 'lib/webrick/https.rb', line 29

attr_reader :cipher

#client_cert (readonly)

HTTP request client certificate

[ GitHub ]

  
# File 'lib/webrick/https.rb', line 39

attr_reader :client_cert

#cookies (readonly)

The parsed request cookies

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 94

attr_reader :cookies

#header (readonly)

The parsed header of the request

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 89

attr_reader :header

#http_version (readonly)

The HTTP version of the request

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 50

attr_reader :http_version

#keep_alive (readonly)

Is this a keep-alive connection?

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 141

attr_reader :keep_alive

#keep_alive?Boolean (readonly)

Should the connection this request was made on be kept alive?

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 370

def keep_alive?
  @keep_alive
end

#path (readonly)

The request path

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 62

attr_reader :path

#path_info (rw)

The path info (CGI variable)

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 72

attr_accessor :path_info

#peeraddr (readonly)

The socket address of the client

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 131

attr_reader :peeraddr

#query_string (rw)

The query from the URI of the request

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 77

attr_accessor :query_string

#raw_header (readonly)

The raw header of the request

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 84

attr_reader :raw_header

#request_line (readonly)

The complete request line such as:

GET / HTTP/1.1
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 35

attr_reader :request_line

#request_method (readonly)

The request method, GET, POST, PUT, etc.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 40

attr_reader :request_method

#request_time (readonly)

The local time this request was received

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 146

attr_reader :request_time

#request_uri (readonly)

The parsed URI of the request

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 57

attr_reader :request_uri

#script_name (rw)

The script name (CGI variable)

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 67

attr_accessor :script_name

#server_cert (readonly)

HTTP request server certificate

[ GitHub ]

  
# File 'lib/webrick/https.rb', line 34

attr_reader :server_cert

#ssl?Boolean (readonly)

Is this an SSL request?

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 363

def ssl?
  return @request_uri.scheme == "https"
end

#unparsed_uri (readonly)

The unparsed URI of the request

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 45

attr_reader :unparsed_uri

#user (rw)

The remote user (CGI variable)

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 121

attr_accessor :user

Instance Method Details

#[](header_name)

Retrieves header_name

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 313

def [](header_name)
  if @header
    value = @header[header_name.downcase]
    value.empty? ? nil : value.join(", ")
  end
end

#_read_data(io, method, *arg) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 557

def _read_data(io, method, *arg)
  begin
    WEBrick::Utils.timeout(@config[:RequestTimeout]){
      return io.__send__(method, *arg)
    }
  rescue Errno::ECONNRESET
    return nil
  rescue Timeout::Error
    raise HTTPStatus::RequestTimeout
  end
end

#body(&block)

Returns the request body.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 254

def body(&block) # :yields: body_chunk
  block ||= Proc.new{|chunk| @body << chunk }
  read_body(@socket, block)
  @body.empty? ? nil : @body
end

#body_reader

Prepares the HTTPRequest object for use as the source for IO.copy_stream

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 264

def body_reader
  @body_tmp = []
  @body_rd = Fiber.new do
    body do |buf|
      @body_tmp << buf
      Fiber.yield
    end
  end
  @body_rd.resume # grab the first chunk and yield
  self
end

#content_length

The content-length header

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 299

def content_length
  return Integer(self['content-length'])
end

#content_type

The content-type header

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 306

def content_type
  return self['content-type']
end

#continue

This method is for internal use only.

Generate HTTP/1.1 100 continue response if the client expects it, otherwise does nothing.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 244

def continue # :nodoc:
  if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1"
    @socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}"
    @header.delete('expect')
  end
end

#each

Iterates over the request headers

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 323

def each
  if @header
    @header.each{|k, v|
      value = @header[k]
      yield(k, value.empty? ? nil : value.join(", "))
    }
  end
end

#fixup

This method is for internal use only.

Consumes any remaining body and updates keep-alive status

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 385

def fixup() # :nodoc:
  begin
    body{|chunk| }   # read remaining body
  rescue HTTPStatus::Error => ex
    @logger.error("HTTPRequest#fixup: #{ex.class} occurred.")
    @keep_alive = false
  rescue => ex
    @logger.error(ex)
    @keep_alive = false
  end
end

#host

The host this request is for

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 335

def host
  return @forwarded_host || @host
end

#meta_vars

This method is for internal use only.

This method provides the metavariables defined by the revision 3 of “The WWW Common Gateway Interface Version 1.1” To browse the current document of CGI Version 1.1, see below: tools.ietf.org/html/rfc3875

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 402

def meta_vars
  meta = orig_meta_vars
  if server_cert
    meta["HTTPS"] = "on"
    meta["SSL_SERVER_CERT"] = @server_cert.to_pem
    meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
    if @client_cert_chain
      @client_cert_chain.each_with_index{|cert, i|
        meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem
      }
    end
    meta["SSL_CIPHER"] = @cipher[0]
    meta["SSL_PROTOCOL"] = @cipher[1]
    meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s
    meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s
  end
  meta
end

#orig_meta_vars

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/https.rb', line 65

alias orig_meta_vars meta_vars

#orig_parse

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/https.rb', line 43

alias orig_parse parse

#orig_parse_uri

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/https.rb', line 55

alias orig_parse_uri parse_uri

#parse(socket = nil)

This method is for internal use only.

Parses a request from socket. This is called internally by HTTPServer.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 192

def parse(socket=nil)
  if socket.respond_to?(:cert)
    @server_cert = socket.cert || @config[:SSLCertificate]
    @client_cert = socket.peer_cert
    @client_cert_chain = socket.peer_cert_chain
    @cipher      = socket.cipher
  end
  orig_parse(socket)
end

#parse_query (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 577

def parse_query()
  begin
    if @request_method == "GET" || @request_method == "HEAD"
      @query = HTTPUtils::parse_query(@query_string)
    elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
      @query = HTTPUtils::parse_query(body)
    elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
      boundary = HTTPUtils::dequote($1)
      @query = HTTPUtils::parse_form_data(body, boundary)
    else
      @query = Hash.new
    end
  rescue => ex
    raise HTTPStatus::BadRequest, ex.message
  end
end

#parse_uri(str, scheme = "http") (private)

This method is for internal use only.

See additional method definition at file lib/webrick/https.rb line 57.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 479

def parse_uri(str, scheme="https")
  if server_cert
    return orig_parse_uri(str, scheme)
  end
  return orig_parse_uri(str)
end

#port

The port this request is for

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 342

def port
  return @forwarded_port || @port
end

#query

Request query as a Hash

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 289

def query
  unless @query
    parse_query()
  end
  @query
end

#read_body(socket, block) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 502

def read_body(socket, block)
  return unless socket
  if tc = self['transfer-encoding']
    case tc
    when /\Achunked\z/io then read_chunked(socket, block)
    else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
    end
  elsif self['content-length'] || @remaining_size
    @remaining_size ||= self['content-length'].to_i
    while @remaining_size > 0
      sz = [@buffer_size, @remaining_size].min
      break unless buf = read_data(socket, sz)
      @remaining_size -= buf.bytesize
      block.call(buf)
    end
    if @remaining_size > 0 && @socket.eof?
      raise HTTPStatus::BadRequest, "invalid body size."
    end
  elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
    raise HTTPStatus::LengthRequired
  end
  return @body
end

#read_chunk_size(socket) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 526

def read_chunk_size(socket)
  line = read_line(socket)
  if /^([0-9a-fA-F])(?:;(\S))?/ =~ line
    chunk_size = $1.hex
    chunk_ext = $2
    [ chunk_size, chunk_ext ]
  else
    raise HTTPStatus::BadRequest, "bad chunk `#{line}'."
  end
end

#read_chunked(socket, block) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 537

def read_chunked(socket, block)
  chunk_size, = read_chunk_size(socket)
  while chunk_size > 0
    begin
      sz = [ chunk_size, @buffer_size ].min
      data = read_data(socket, sz) # read chunk-data
      if data.nil? || data.bytesize != sz
        raise HTTPStatus::BadRequest, "bad chunk data size."
      end
      block.call(data)
    end while (chunk_size -= sz) > 0

    read_line(socket)                    # skip CRLF
    chunk_size, = read_chunk_size(socket)
  end
  read_header(socket)                    # trailer + CRLF
  @header.delete("transfer-encoding")
  @remaining_size = 0
end

#read_data(io, size) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 573

def read_data(io, size)
  _read_data(io, :read, size)
end

#read_header(socket) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 466

def read_header(socket)
  if socket
    while line = read_line(socket)
      break if /\A(#{CRLF}|#{LF})\z/om =~ line
      if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH
        raise HTTPStatus::RequestEntityTooLarge, 'headers too large'
      end
      @raw_header << line
    end
  end
  @header = HTTPUtils::parse_header(@raw_header.join)
end

#read_line(io, size = 4096) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 569

def read_line(io, size=4096)
  _read_data(io, :gets, LF, size)
end

#read_request_line(socket) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 446

def read_request_line(socket)
  @request_line = read_line(socket, MAX_URI_LENGTH) if socket
  raise HTTPStatus::EOFError unless @request_line

  @request_bytes = @request_line.bytesize
  if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
    raise HTTPStatus::RequestURITooLarge
  end

  @request_time = Time.now
  if /^(\S)\s(\S)(?:\sHTTP\/(\d\.\d))?\r?\n/mo =~ @request_line
    @request_method = $1
    @unparsed_uri   = $2
    @http_version   = HTTPVersion.new($3 ? $3 : "0.9")
  else
    rl = @request_line.sub(/\x0d?\x0a\z/o, '')
    raise HTTPStatus::BadRequest, "bad Request-Line `#{rl}'."
  end
end

#readpartial(size, buf = ''.b)

for IO.copy_stream. Note: we may return a larger string than size here; but IO.copy_stream does not care.

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 278

def readpartial(size, buf = ''.b) # :nodoc
  res = @body_tmp.shift or raise EOFError, 'end of file reached'
  buf.replace(res)
  res.clear
  @body_rd.resume # get more chunks
  buf
end

#remote_ip

The client’s IP address

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 356

def remote_ip
  return self["client-ip"] || @forwarded_for || @peeraddr[3]
end

#server_name

The server name this request is for

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 349

def server_name
  return @forwarded_server || @config[:ServerName]
end

#setup_forwarded_info (private)

This method is for internal use only.

It’s said that all X-Forwarded-* headers will contain more than one (comma-separated) value if the original request already contained one of these headers. Since we could use these values as Host header, we choose the initial(first) value. (apr_table_mergen() adds new value after the existing value with “, ” prefix)

[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 605

def setup_forwarded_info
  if @forwarded_server = self["x-forwarded-server"]
    @forwarded_server = @forwarded_server.split(",", 2).first
  end
  if @forwarded_proto = self["x-forwarded-proto"]
    @forwarded_proto = @forwarded_proto.split(",", 2).first
  end
  if host_port = self["x-forwarded-host"]
    host_port = host_port.split(",", 2).first
    if host_port =~ /\A(\[[0-9a-fA-F:]\])(?::(\d))?\z/
      @forwarded_host = $1
      tmp = $2
    else
      @forwarded_host, tmp = host_port.split(":", 2)
    end
    @forwarded_port = (tmp || (@forwarded_proto == "https" ? 443 : 80)).to_i
  end
  if addrs = self["x-forwarded-for"]
    addrs = addrs.split(",").collect(&:strip)
    addrs.reject!{|ip| PrivateNetworkRegexp =~ ip }
    @forwarded_for = addrs.first
  end
end

#to_s

This method is for internal use only.
[ GitHub ]

  
# File 'lib/webrick/httprequest.rb', line 374

def to_s # :nodoc:
  ret = @request_line.dup
  @raw_header.each{|line| ret << line }
  ret << CRLF
  ret << body if body
  ret
end