class Riddl::Protocols::HTTP::Parser

Constants

FORM_CONTENT_TYPES
MULTIPART_CONTENT_TYPES

Attributes

params[R]

Public Class Methods

new(query_string,input,content_type,content_length,content_disposition,content_id,riddl_type) click to toggle source
# File lib/ruby/riddl/protocols/http/parser.rb, line 162
def initialize(query_string,input,content_type,content_length,content_disposition,content_id,riddl_type)
  #{{{
  # rewind because in some cases it is not at start (when multipart without length)

  begin
    input.rewind if input.respond_to?(:rewind)
  rescue Errno::ESPIPE
    # Handles exceptions raised by input streams that cannot be rewound
    # such as when using plain CGI under Apache
  end

  media_type = content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
  @params = Riddl::Parameter::Array.new
  parse_nested_query(query_string,:query)
  if MULTIPART_CONTENT_TYPES.include?(media_type)
    parse_multipart(input,content_type,content_length.to_i)
  elsif FORM_CONTENT_TYPES.include?(media_type)
    # sub is a fix for Safari Ajax postings that always append \0
    parse_nested_query(input.read.sub(/\0\z/, ''),:body)
  else
    parse_content(input,content_type,content_length.to_i,content_disposition||'',content_id||'',riddl_type||'')
  end

  begin
    input.rewind if input.respond_to?(:rewind)
  rescue Errno::ESPIPE
    # Handles exceptions raised by input streams that cannot be rewound
    # such as when using plain CGI under Apache
  end
  #}}}
end

Private Instance Methods

add_to_params(name,body,filename,ctype,head) click to toggle source
# File lib/ruby/riddl/protocols/http/parser.rb, line 125
def add_to_params(name,body,filename,ctype,head)
  #{{{
  if filename == ""
    # filename is blank which means no file has been selected
  elsif filename && ctype
    body.rewind

    # Take the basename of the upload's original filename.
    # This handles the full Windows paths given by Internet Explorer
    # (and perhaps other broken user agents) without affecting
    # those which give the lone filename.
    filename =~ /^(?:.*[:\\\/])?(.*)/m
    filename = $1

    @params << Parameter::Complex.new(name, ctype, body, filename, head)
  elsif !filename && ctype
    body.rewind

    # Generic multipart cases, not coming from a form
    @params << Parameter::Complex.new(name, ctype, body, nil, head)
  else
    @params << Parameter::Simple.new(name, body, :body)
  end
  #}}}
end
parse_content(input,ctype,content_length,content_disposition,content_id,riddl_type) click to toggle source
# File lib/ruby/riddl/protocols/http/parser.rb, line 22
def parse_content(input,ctype,content_length,content_disposition,content_id,riddl_type)
  #{{{
  # fixing for chunked?

  ctype = nil if riddl_type == 'simple'
  filename = content_disposition[/ filename="?([^\";]*)"?/ni, 1]
  name = content_disposition[/ name="?([^\";]*)"?/ni, 1] || content_id

  if ctype || filename
    body = Parameter::Tempfile.new("RiddlMultipart")
    body.binmode if body.respond_to?(:binmode)
  else
    body = ''
  end

  if content_length == 0
    bufsize = 16384
    until input.eof?
      c = input.read(bufsize)
      raise EOFError, "bad content body"  if c.nil? || c.empty?
      body << c
      content_length -= c.size
    end
    body << input.read
  else
    bufsize = 16384
    until content_length <= 0
      c = input.read(bufsize < content_length ? bufsize : content_length)
      raise EOFError, "bad content body"  if c.nil? || c.empty?
      body << c
      content_length -= c.size
    end
  end
  body.rewind if body.respond_to?(:binmode)

  add_to_params(name,body,filename,ctype,nil) if body.length > 0
  #}}}
end
parse_multipart(input,content_type,content_length) click to toggle source
# File lib/ruby/riddl/protocols/http/parser.rb, line 62
def parse_multipart(input,content_type,content_length)
  #{{{
  content_type =~ %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
  boundary = "--#{$1}"

  boundary_size = boundary.size + EOL.size
  content_length -= boundary_size
  status = input.read(boundary_size)
  raise EOFError, "bad content body" unless status == boundary + EOL

  rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n

  buf = ""
  bufsize = 16384
  loop do
    head = nil
    body = ''
    filename = ctype = name = nil

    until head && buf =~ rx
      if !head && i = buf.index(EOL+EOL)
        head = buf.slice!(0, i+2) # First \r\n
        buf.slice!(0, 2)          # Second \r\n

        filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
        ctype = head[/Content-Type: (.*)#{EOL}/ni, 1]
        name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]

        if ctype || filename
          body = Parameter::Tempfile.new("RiddlMultipart")
          body.binmode  if body.respond_to?(:binmode)
        end

        next
      end

      # Save the read body part.
      if head && (boundary_size+4 < buf.size)
        body << buf.slice!(0, buf.size - (boundary_size+4))
      end

      c = input.read(bufsize < content_length ? bufsize : content_length)
      raise EOFError, "bad content body"  if c.nil? || c.empty?
      content_length -= c.size

      buf << c
    end

    # Save the rest.
    if i = buf.index(rx)
      body << buf.slice!(0, i)
      buf.slice!(0, boundary_size+2)
      content_length = -1  if $1 == "--"
    end

    add_to_params(name,body,filename == '' ? nil : filename,ctype,head)

    break if buf.empty? || content_length == -1
  end
  #}}}
end
parse_nested_query(qs, type) click to toggle source
# File lib/ruby/riddl/protocols/http/parser.rb, line 152
def parse_nested_query(qs, type)
  #{{{
  (qs || '').split(/[#{D}] */n).each do |p|
    k, v = Riddl::Protocols::Utils::unescape(p).split('=', 2)
    @params << Parameter::Simple.new(k,v,type)
  end
  #}}}
end