class Knjappserver::Httpsession::Http_request

This class parses the various HTTP requests into easy programmable objects. Get, post, cookie, meta and so on…

Attributes

clength[R]
get[R]
headers[R]
http_version[R]
meta[R]
page_path[R]
percent[R]
post[R]
read[R]
secs_left[R]
speed[R]

Public Class Methods

new(args) click to toggle source

Sets the various required data on the object. Knjappserver, crlf and arguments.

# File lib/include/class_httpsession_http_request.rb, line 13
def initialize(args)
        @args = args
        @kas = @args[:kas]
        @crlf = "\r\n"
end

Public Instance Methods

convert_post(seton, post_val, args = {}) click to toggle source

Converts post-result to the right type of hash.

# File lib/include/class_httpsession_http_request.rb, line 214
def convert_post(seton, post_val, args = {})
        post_val.each do |varname, value|
                Knj::Web.parse_name(seton, varname, value, args)
        end
end
modified_since() click to toggle source

Parses the if-modified-since header and returns it as a Time-object. Returns false is no if-modified-since-header is given or raises an RuntimeError if it cant be parsed.

# File lib/include/class_httpsession_http_request.rb, line 200
    def modified_since
return @modified_since if @modified_since
return false if !@meta["HTTP_IF_MODIFIED_SINCE"]

mod_match = @meta["HTTP_IF_MODIFIED_SINCE"].match(/^([A-z]+),\s+(\d+)\s+([A-z]+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(.+)$/)
raise "Could not parse 'HTTP_IF_MODIFIED_SINCE'." if !mod_match

month_no = Datet.month_str_to_no(mod_match[3])
@modified_since = Time.utc(mod_match[4].to_i, month_no, mod_match[2].to_i, mod_match[5].to_i, mod_match[6].to_i, mod_match[7].to_i)

return @modified_since
    end
read_socket(socket, cont) click to toggle source

Reads content from the socket until the end of headers. Also does various error-checks.

# File lib/include/class_httpsession_http_request.rb, line 20
def read_socket(socket, cont)
        loop do
                raise Errno::ECONNRESET, "Socket closed." if socket.closed?
                read = socket.gets
                raise Errno::ECONNRESET, "Socket returned non-string: '#{read.class.name}'." if !read.is_a?(String)
                cont << read
                break if cont[-4..-1] == "\r\n\r\n" or cont[-2..-1] == "\n\n"
        end
end
reset() click to toggle source
# File lib/include/class_httpsession_http_request.rb, line 30
    def reset
@modified_since = nil
@get = nil
@post = nil
@cookie = nil
@meta = nil
@page_path = nil
@headers = nil
@http_version = nil
@read = nil
@clength = nil
@speec = nil
@percent = nil
@secs_left = nil
    end
socket_parse(socket) click to toggle source

Generates data on object from the given socket.

# File lib/include/class_httpsession_http_request.rb, line 47
    def socket_parse(socket)
self.reset
            cont = ""
            self.read_socket(socket, cont)
            
            #Parse URI (page_path and get).
            match = cont.match(/^(GET|POST|HEAD)\s+(.+)\s+HTTP\/1\.(\d+)\s*/)
            raise "Could not parse request: '#{cont.split("\n").first}'." if !match

            @http_version = "1.#{match[3]}"
            
            method = match[1]
            cont = cont.gsub(match[0], "")
            
            uri = Knj::Web.parse_uri(match[2])
            
            page_filepath = Knj::Web.urldec(uri[:path])
            if page_filepath.length <= 0 or page_filepath == "/" or File.directory?("#{@kas.config[:doc_root]}/#{page_filepath}")
                    page_filepath = "#{page_filepath}/#{@kas.config[:default_page]}"
            end
            
            @page_path = "#{@kas.config[:doc_root]}/#{page_filepath}"
            @get = Knj::Web.parse_urlquery(uri[:query], {:urldecode => true, :force_utf8 => true})
            
if @get["_kas_httpsession_id"]
  @kas.httpsessions_ids[@get["_kas_httpsession_id"]] = @args[:httpsession]
            end

            begin
  #Parse headers, cookies and meta.
  @headers = {}
  @cookie = {}
  @meta = {
    "REQUEST_METHOD" => method,
    "QUERY_STRING" => uri[:query],
    "REQUEST_URI" => match[2],
    "SCRIPT_NAME" => uri[:path]
  }
  
  cont.scan(/^(\S+):\s*(.+)\r\n/) do |header_match|
    key = header_match[0].downcase
    val = header_match[1]
    
    @headers[key] = [] if !@headers.has_key?(key)
    @headers[key] << val
    
    case key
      when "cookie"
        Knj::Web.parse_cookies(val).each do |key, val|
          @cookie[key] = val
        end
      when "content-length"
        @clength = val.to_i
      else
        key = key.upcase.gsub("-", "_")
        @meta["HTTP_#{key}"] = val
    end
  end
  
  
  #Parse post
  @post = {}
  
  if method == "POST"
    post_treated = {}
    
    @speed = nil
    @read = 0
    post_data = ""
    
    Thread.new do
      begin
        time_cur = Time.now
        read_last = 0
        sleep 0.1
        
        while @clength and @read != nil and @read < @clength
          break if !@clength or !@read
          
          time_now = Time.now
          time_betw = time_now.to_f - time_cur.to_f
          read_betw = @read - read_last
          
          time_cur = time_now
          read_last = @read
          
          @percent = @read.to_f / @clength.to_f
          @speed = read_betw.to_f / time_betw.to_f
          
          bytes_left = @clength - read
          
          if @speed > 0 and bytes_left > 0
            @secs_left = bytes_left.to_f / @speed
          else
            @secs_left = false
          end
          
          sleep 2
        end
      rescue => e
        if @kas
          @kas.handle_error(e)
        else
          STDOUT.print Knj::Errors.error_str(e)
        end
      end
    end
    
    while @read < @clength
      read_size = @clength - @read
      read_size = 4096 if read_size > 4096
      
      raise Errno::ECONNRESET, "Socket closed." if socket.closed?
      read = socket.read(read_size)
      raise Errno::ECONNRESET, "Socket returned non-string: '#{read.class.name}'." if !read.is_a?(String)
      post_data << read
      @read += read.length
    end
    
    if @headers["content-type"] and match = @headers["content-type"].first.match(/^multipart\/form-data; boundary=(.+)\Z/)
      post_treated = Knjappserver::Httpsession::Post_multipart.new(
        "io" => StringIO.new("#{post_data}"),
        "boundary" => match[1],
        "crlf" => @crlf
      ).return
      
      self.convert_post(@post, post_treated, {:urldecode => false})
    else
      post_data.split("&").each do |splitted|
        splitted = splitted.split("=")
        key = Knj::Web.urldec(splitted[0]).to_s.encode("utf-8")
        val = splitted[1].to_s.encode("utf-8")
        post_treated[key] = val
      end
      
      self.convert_post(@post, post_treated, {:urldecode => true})
    end
  end
            ensure
  @read = nil
  @speed = nil
  @clength = nil
  @percent = nil
  @secs_left = nil
  
  #If it doesnt get unset we could have a serious memory reference GC problem.
  if @get["_kas_httpsession_id"]
    @kas.httpsessions_ids.delete(@get["_kas_httpsession_id"])
  end
end
    end