module WEBrick::HTTPUtils

HTTPUtils provides utility methods for working with the HTTP protocol.

This module is generally used internally by WEBrick

Constants

DefaultMimeTypes

Default mime types

HEADER_CLASSES

Public Class Methods

dequote(str) click to toggle source

Removes quotes and escapes from str

# File lib/webrick/httputils.rb, line 254
def dequote(str)
  ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
  ret.gsub!(/\\(.)/, "\\1")
  ret
end
load_mime_types(file) click to toggle source

Loads Apache-compatible mime.types in file.

# File lib/webrick/httputils.rb, line 122
def load_mime_types(file)
  # note: +file+ may be a "| command" for now; some people may
  # rely on this, but currently we do not use this method by default.
  File.open(file){ |io|
    hash = Hash.new
    io.each{ |line|
      next if /^#/ =~ line
      line.chomp!
      mimetype, ext0 = line.split(/\s+/, 2)
      next unless ext0
      next if ext0.empty?
      ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
    }
    hash
  }
end
mime_type(filename, mime_tab) click to toggle source

Returns the mime type of filename from the list in mime_tab. If no mime type was found application/octet-stream is returned.

# File lib/webrick/httputils.rb, line 144
def mime_type(filename, mime_tab)
  suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
  suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
  mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
end
normalize_path(path) click to toggle source

Normalizes a request path. Raises an exception if the path cannot be normalized.

# File lib/webrick/httputils.rb, line 31
def normalize_path(path)
  raise "abnormal path '#{path}'" if path[0] != ?/
  ret = path.dup

  ret.gsub!(%r{/+}o, '/')                    # //      => /
  while ret.sub!(%r'/\.(?:/|\Z)', '/'); end  # /.      => /
  while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo

  raise "abnormal path '#{path}'" if %r{/\.\.(/|\Z)} =~ ret
  ret
end
parse_form_data(io, boundary) click to toggle source

Parses form data in io with the given boundary

# File lib/webrick/httputils.rb, line 426
def parse_form_data(io, boundary)
  boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/
  form_data = Hash.new
  return form_data unless io
  data = nil
  io.each_line{|line|
    if boundary_regexp =~ line
      if data
        data.chop!
        key = data.name
        if form_data.has_key?(key)
          form_data[key].append_data(data)
        else
          form_data[key] = data
        end
      end
      data = FormData.new
      next
    else
      if data
        data << line
      end
    end
  }
  return form_data
end
parse_header(raw) click to toggle source
# File lib/webrick/httputils.rb, line 171
def parse_header(raw)
  header = Hash.new([].freeze)
  field = nil
  raw.each_line{|line|
    case line
    when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):([^\r\n\0]*?)\r\n\z/om
      field, value = $1, $2
      field.downcase!
      header[field] = HEADER_CLASSES[field].new unless header.has_key?(field)
      header[field] << value
    when /^[ \t]+([^\r\n\0]*?)\r\n/om
      unless field
        raise HTTPStatus::BadRequest, "bad header '#{line}'."
      end
      value = line
      value.gsub!(/\A[ \t]+/, '')
      value.slice!(-2..-1)
      header[field][-1] << " " << value
    else
      raise HTTPStatus::BadRequest, "bad header '#{line}'."
    end
  }
  header.each{|key, values|
    values.each{|value|
      value.gsub!(/\A[ \t]+/, '')
      value.gsub!(/[ \t]+\z/, '')
    }
  }
  header
end
parse_query(str) click to toggle source

Parses the query component of a URI in str

# File lib/webrick/httputils.rb, line 402
def parse_query(str)
  query = Hash.new
  if str
    str.split(/[&;]/).each{|x|
      next if x.empty?
      key, val = x.split(/=/,2)
      key = unescape_form(key)
      val = unescape_form(val.to_s)
      val = FormData.new(val)
      val.name = key
      if query.has_key?(key)
        query[key].append_data(val)
        next
      end
      query[key] = val
    }
  end
  query
end
parse_qvalues(value) click to toggle source

Parses q values in value as used in Accept headers.

# File lib/webrick/httputils.rb, line 233
def parse_qvalues(value)
  tmp = []
  if value
    parts = value.split(/,[ \t]*/)
    parts.each {|part|
      if m = %r{^([^ \t,]+?)(?:;[ \t]*q=(\d+(?:\.\d+)?))?$}.match(part)
        val = m[1]
        q = (m[2] or 1).to_f
        tmp.push([val, q])
      end
    }
    tmp = tmp.sort_by{|val, q| -q}
    tmp.collect!{|val, q| val}
  end
  return tmp
end
parse_range_header(ranges_specifier) click to toggle source

Parses a Range header value ranges_specifier

# File lib/webrick/httputils.rb, line 215
def parse_range_header(ranges_specifier)
  if /^bytes=(.*)/ =~ ranges_specifier
    byte_range_set = split_header_value($1)
    byte_range_set.collect{|range_spec|
      case range_spec
      when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
      when /^(\d+)-/      then $1.to_i .. -1
      when /^-(\d+)/      then -($1.to_i) .. -1
      else return nil
      end
    }
  end
end
quote(str) click to toggle source

Quotes and escapes quotes in str

# File lib/webrick/httputils.rb, line 264
def quote(str)
  +'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
end
split_header_value(str) click to toggle source

Splits a header value str according to HTTP specification.

# File lib/webrick/httputils.rb, line 206
def split_header_value(str)
  str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]++)+)
                (?:,[ \t]*|\Z)'xn).flatten
end

Public Instance Methods

escape(str) click to toggle source

Escapes HTTP reserved and unwise characters in str

# File lib/webrick/httputils.rb, line 498
def escape(str)
  _escape(str, UNESCAPED)
end
escape8bit(str) click to toggle source

Escapes 8 bit characters in str

# File lib/webrick/httputils.rb, line 539
def escape8bit(str)
  _escape(str, NONASCII)
end
escape_form(str) click to toggle source

Escapes form reserved characters in str

# File lib/webrick/httputils.rb, line 512
def escape_form(str)
  ret = _escape(str, UNESCAPED_FORM)
  ret.gsub!(/ /, "+")
  ret
end
escape_path(str) click to toggle source

Escapes path str

# File lib/webrick/httputils.rb, line 528
def escape_path(str)
  result = +""
  str.scan(%r{/([^/]*)}).each{|i|
    result << "/" << _escape(i[0], UNESCAPED_PCHAR)
  }
  return result
end
unescape(str) click to toggle source

Unescapes HTTP reserved and unwise characters in str

# File lib/webrick/httputils.rb, line 505
def unescape(str)
  _unescape(str, ESCAPED)
end
unescape_form(str) click to toggle source

Unescapes form reserved characters in str

# File lib/webrick/httputils.rb, line 521
def unescape_form(str)
  _unescape(str.gsub(/\+/, " "), ESCAPED)
end

Private Instance Methods

dequote(str) click to toggle source

Removes quotes and escapes from str

# File lib/webrick/httputils.rb, line 254
def dequote(str)
  ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
  ret.gsub!(/\\(.)/, "\\1")
  ret
end
load_mime_types(file) click to toggle source

Loads Apache-compatible mime.types in file.

# File lib/webrick/httputils.rb, line 122
def load_mime_types(file)
  # note: +file+ may be a "| command" for now; some people may
  # rely on this, but currently we do not use this method by default.
  File.open(file){ |io|
    hash = Hash.new
    io.each{ |line|
      next if /^#/ =~ line
      line.chomp!
      mimetype, ext0 = line.split(/\s+/, 2)
      next unless ext0
      next if ext0.empty?
      ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
    }
    hash
  }
end
mime_type(filename, mime_tab) click to toggle source

Returns the mime type of filename from the list in mime_tab. If no mime type was found application/octet-stream is returned.

# File lib/webrick/httputils.rb, line 144
def mime_type(filename, mime_tab)
  suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
  suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
  mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
end
normalize_path(path) click to toggle source

Normalizes a request path. Raises an exception if the path cannot be normalized.

# File lib/webrick/httputils.rb, line 31
def normalize_path(path)
  raise "abnormal path '#{path}'" if path[0] != ?/
  ret = path.dup

  ret.gsub!(%r{/+}o, '/')                    # //      => /
  while ret.sub!(%r'/\.(?:/|\Z)', '/'); end  # /.      => /
  while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo

  raise "abnormal path '#{path}'" if %r{/\.\.(/|\Z)} =~ ret
  ret
end
parse_form_data(io, boundary) click to toggle source

Parses form data in io with the given boundary

# File lib/webrick/httputils.rb, line 426
def parse_form_data(io, boundary)
  boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/
  form_data = Hash.new
  return form_data unless io
  data = nil
  io.each_line{|line|
    if boundary_regexp =~ line
      if data
        data.chop!
        key = data.name
        if form_data.has_key?(key)
          form_data[key].append_data(data)
        else
          form_data[key] = data
        end
      end
      data = FormData.new
      next
    else
      if data
        data << line
      end
    end
  }
  return form_data
end
parse_header(raw) click to toggle source
# File lib/webrick/httputils.rb, line 171
def parse_header(raw)
  header = Hash.new([].freeze)
  field = nil
  raw.each_line{|line|
    case line
    when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):([^\r\n\0]*?)\r\n\z/om
      field, value = $1, $2
      field.downcase!
      header[field] = HEADER_CLASSES[field].new unless header.has_key?(field)
      header[field] << value
    when /^[ \t]+([^\r\n\0]*?)\r\n/om
      unless field
        raise HTTPStatus::BadRequest, "bad header '#{line}'."
      end
      value = line
      value.gsub!(/\A[ \t]+/, '')
      value.slice!(-2..-1)
      header[field][-1] << " " << value
    else
      raise HTTPStatus::BadRequest, "bad header '#{line}'."
    end
  }
  header.each{|key, values|
    values.each{|value|
      value.gsub!(/\A[ \t]+/, '')
      value.gsub!(/[ \t]+\z/, '')
    }
  }
  header
end
parse_query(str) click to toggle source

Parses the query component of a URI in str

# File lib/webrick/httputils.rb, line 402
def parse_query(str)
  query = Hash.new
  if str
    str.split(/[&;]/).each{|x|
      next if x.empty?
      key, val = x.split(/=/,2)
      key = unescape_form(key)
      val = unescape_form(val.to_s)
      val = FormData.new(val)
      val.name = key
      if query.has_key?(key)
        query[key].append_data(val)
        next
      end
      query[key] = val
    }
  end
  query
end
parse_qvalues(value) click to toggle source

Parses q values in value as used in Accept headers.

# File lib/webrick/httputils.rb, line 233
def parse_qvalues(value)
  tmp = []
  if value
    parts = value.split(/,[ \t]*/)
    parts.each {|part|
      if m = %r{^([^ \t,]+?)(?:;[ \t]*q=(\d+(?:\.\d+)?))?$}.match(part)
        val = m[1]
        q = (m[2] or 1).to_f
        tmp.push([val, q])
      end
    }
    tmp = tmp.sort_by{|val, q| -q}
    tmp.collect!{|val, q| val}
  end
  return tmp
end
parse_range_header(ranges_specifier) click to toggle source

Parses a Range header value ranges_specifier

# File lib/webrick/httputils.rb, line 215
def parse_range_header(ranges_specifier)
  if /^bytes=(.*)/ =~ ranges_specifier
    byte_range_set = split_header_value($1)
    byte_range_set.collect{|range_spec|
      case range_spec
      when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
      when /^(\d+)-/      then $1.to_i .. -1
      when /^-(\d+)/      then -($1.to_i) .. -1
      else return nil
      end
    }
  end
end
quote(str) click to toggle source

Quotes and escapes quotes in str

# File lib/webrick/httputils.rb, line 264
def quote(str)
  +'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
end
split_header_value(str) click to toggle source

Splits a header value str according to HTTP specification.

# File lib/webrick/httputils.rb, line 206
def split_header_value(str)
  str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]++)+)
                (?:,[ \t]*|\Z)'xn).flatten
end