class Knjappserver

The class that stands for the whole appserver / webserver.

Examples

appsrv = Knjappserver.new(
  :locales_root => "/some/path/locales",
  :locales_gettext_funcs => true,
  :magic_methods => true
)
appsrv.start
appsrv.join

Constants

DATABASE_SCHEMA

Attributes

cio[R]
config[R]
db[R]
db_handler[R]
debug[R]
error_emails_pending[R]
eruby_cache[R]
events[R]
gettext[R]
httpserv[R]
httpsessions_ids[R]
logs_access_pending[R]
magic_procs[R]
magic_vars[R]
mails_waiting[R]
mod_event[R]
ob[R]
paused[R]
served[RW]
sessions[R]
should_restart[RW]
should_restart_done[RW]
threadpool[R]
translations[R]
types[R]
vars[R]

Public Class Methods

const_missing(name) click to toggle source

Autoloader for subclasses.

# File lib/include/class_knjappserver.rb, line 24
def self.const_missing(name)
  require "#{File.dirname(__FILE__)}/class_#{name.to_s.downcase}.rb"
  return Knjappserver.const_get(name)
end
convert_fcgi_post(params) click to toggle source
# File lib/scripts/knjappserver_cgi.rb, line 35
def self.convert_fcgi_post(params)
  post_hash = {}
  
  params.each do |key, val|
    post_hash[key] = val.first
  end
  
  return post_hash
end
data() click to toggle source
# File lib/include/class_knjappserver.rb, line 446
def self.data
  raise "Could not register current thread." if !Thread.current[:knjappserver]
  return Thread.current[:knjappserver]
end
fcgi_start(cgi) click to toggle source
# File lib/scripts/knjappserver_fcgi.rb, line 19
def self.fcgi_start(cgi)
  raise "No HTTP_KNJAPPSERVER_CGI_CONFIG-header was given." if !cgi.env_table["HTTP_KNJAPPSERVER_CGI_CONFIG"]
  require cgi.env_table["HTTP_KNJAPPSERVER_CGI_CONFIG"]
  
  begin
    conf = Knjappserver::CGI_CONF
  rescue NameError
    raise "No 'Knjappserver::CGI_CONF'-constant was spawned by '#{cgi.env_table["HTTP_KNJAPPSERVER_CGI_CONFIG"]}'."
  end
  
  knjappserver_conf = Knjappserver::CGI_CONF["knjappserver"]
  knjappserver_conf.merge!(
    :cmdline => false,
    :port => 0 #Ruby picks random port and we get the actual port after starting the appserver.
  )
  
  knjappserver = Knjappserver.new(knjappserver_conf)
  knjappserver.start
  
  port = knjappserver.port
  http = Http2.new(:host => "localhost", :port => port)
  
  return [knjappserver, http]
end
new(config) click to toggle source
# File lib/include/class_knjappserver.rb, line 29
def initialize(config)
  raise "No arguments given." if !config.is_a?(Hash)
  
  @config = {
    :host => "0.0.0.0",
    :timeout => 30,
    :default_page => "index.rhtml",
    :default_filetype => "text/html",
    :max_requests_working => 20,
    :size_send => 1024,
    :cleaner_timeout => 300
  }.merge(config)
  
  @config[:smtp_args] = {"smtp_host" => "localhost", "smtp_port" => 25} if !@config[:smtp_args]
  @config[:timeout] = 30 if !@config.has_key?(:timeout)
  raise "No ':doc_root' was given in arguments." if !@config.has_key?(:doc_root)
  
  
  #Require gems.
  gems = %w[datet php4r]
  gems.each do |gem|
    puts "Loading gem: '#{gem}'." if @debug
    require gem
  end
  
  
  #Setup default handlers if none are given.
  if !@config.has_key?(:handlers)
    @erbhandler = Knjappserver::ERBHandler.new
    @config[:handlers] = [
      {
        :file_ext => "rhtml",
        :callback => @erbhandler.method(:erb_handler)
      },{
        :path => "/fckeditor",
        :mount => "/usr/share/fckeditor"
      }
    ]
  end
  
  
  #Add extra handlers if given.
  @config[:handlers] += @config[:handlers_extra] if @config[:handlers_extra]
  
  
  #Setup cache to make .rhtml-calls faster.
  @config[:handlers_cache] = {}
  @config[:handlers].each do |handler_info|
    next if !handler_info[:file_ext] or !handler_info[:callback]
    @config[:handlers_cache][handler_info[:file_ext]] = handler_info[:callback]
  end
  
  
  @debug = @config[:debug]
  @paused = 0
  @paused_mutex = Mutex.new
  @should_restart = false
  @mod_events = {}
  @served = 0
  @mod_files = {}
  @sessions = {}
  @eruby_cache = {}
  @httpsessions_ids = {}
  
  @path_knjappserver = File.dirname(__FILE__)
  if @config[:knjrbfw_path]
    @path_knjrbfw = @config[:knjrbfw_path]
  else
    @path_knjrbfw = ""
  end
  
  
  #If auto-restarting is enabled - start the modified events-module.
  if @config[:autorestart]
    paths = [
      "#{@path_knjappserver}/../knjappserver.rb",
      "#{@path_knjappserver}/class_knjappserver.rb",
      "#{@path_knjappserver}/class_customio.rb"
    ]
    
    print "Auto restarting.\n" if @debug
    @mod_event = Knj::Event_filemod.new(:wait => 2, :paths => paths, &self.method(:on_event_filemod))
  end
  
  
  #Set up default file-types and merge given filetypes into it.
  @types = {
    :ico => "image/x-icon",
    :jpeg => "image/jpeg",
    :jpg => "image/jpeg",
    :gif => "image/gif",
    :png => "image/png",
    :html => "text/html",
    :htm => "text/html",
    :rhtml => "text/html",
    :css => "text/css",
    :xml => "text/xml",
    :js => "text/javascript"
  }
  @types.merge!(@config[:filetypes]) if @config.has_key?(:filetypes)
  
  
  
  #Load various required files from knjrbfw and stuff in the knjappserver-framework.
  files = [
    "#{@path_knjrbfw}knjrbfw.rb",
    "#{@path_knjappserver}/class_knjappserver_errors.rb",
    "#{@path_knjappserver}/class_knjappserver_logging.rb",
    "#{@path_knjappserver}/class_knjappserver_mailing.rb",
    "#{@path_knjappserver}/class_knjappserver_sessions.rb",
    "#{@path_knjappserver}/class_knjappserver_translations.rb",
    "#{@path_knjappserver}/class_knjappserver_web.rb"
  ]
  
  files << "#{@path_knjrbfw}knj/gettext_threadded.rb" if @config[:locales_root]
  files.each do |file|
    STDOUT.print "Loading: '#{file}'.\n" if @debug
    self.loadfile(file)
  end
  
  
  print "Setting up database.\n" if @debug
  if @config[:db].is_a?(Knj::Db)
    @db = @config[:db]
  elsif @config[:db].is_a?(Hash)
    @db = Knj::Db.new(@config[:db])
  elsif !@config[:db] and @config[:db_args]
    @db = Knj::Db.new(@config[:db_args])
  else
    raise "Unknown object given as db: '#{@config[:db].class.name}'."
  end
  
  
  if !@config.key?(:dbrev) or @config[:dbrev]
    print "Updating database.\n" if @debug
    
    dbschemapath = "#{File.dirname(__FILE__)}/../files/database_schema.rb"
    raise "'#{dbschemapath}' did not exist." if !File.exists?(dbschemapath)
    require dbschemapath
    raise "No schema-variable was spawned." if !Knjappserver::DATABASE_SCHEMA
    dbrev_args = {"schema" => Knjappserver::DATABASE_SCHEMA, "db" => @db}
    dbrev_args.merge!(@config[:dbrev_args]) if @config.key?(:dbrev_args)
    Knj::Db::Revision.new.init_db(dbrev_args)
    dbrev_args = nil
  end
  
  
  print "Spawning objects.\n" if @debug
  @ob = Knj::Objects.new(
    :db => db,
    :class_path => @path_knjappserver,
    :module => Knjappserver,
    :datarow => true,
    :knjappserver => self
  )
  @ob.events.connect(:no_date, &self.method(:no_date))
  
  
  if @config[:httpsession_db_args]
    @db_handler = Knj::Db.new(@config[:httpsession_db_args])
  else
    @db_handler = @db
  end
  
  
  #Start the Knj::Gettext_threadded- and Knj::Translations modules for translations.
  print "Loading Gettext and translations.\n" if @debug
  @translations = Knj::Translations.new(:db => @db)
  @ob.requireclass(:Translation, {:require => false, :class => Knj::Translations::Translation})
  
  if @config[:locales_root]
    @gettext = Knj::Gettext_threadded.new("dir" => config[:locales_root])
  end
  
  require "#{@path_knjappserver}/gettext_funcs" if @config[:locales_gettext_funcs]
  
  if @config[:magic_methods] or !@config.has_key?(:magic_methods)
    print "Loading magic-methods.\n" if @debug
    require "#{@path_knjappserver}/magic_methods"
  end
  
  if @config[:customio] or !@config.has_key?(:customio)
    print "Loading custom-io.\n" if @debug
    
    if $stdout.class.name != "Knjappserver::CustomIO"
      require "#{@path_knjappserver}/class_customio.rb"
      @cio = Knjappserver::CustomIO.new
      $stdout = @cio
    end
  end
  
  
  #Save the PID to the run-file.
  print "Setting run-file.\n" if @debug
  tmpdir = "#{Knj::Os.tmpdir}/knjappserver"
  tmppath = "#{tmpdir}/run_#{@config[:title]}"
  
  if !File.exists?(tmpdir)
    Dir.mkdir(tmpdir)
    File.chmod(0777, tmpdir)
  end
  
  File.open(tmppath, "w") do |fp|
    fp.write(Process.pid)
  end
  File.chmod(0777, tmppath)
  
  
  #Set up various events for the appserver.
  if !@config.key?(:events) or @config[:events]
    print "Loading events.\n" if @debug
    @events = Knj::Event_handler.new
    @events.add_event(
      :name => :check_page_access,
      :connections_max => 1
    )
    @events.add_event(
      :name => :ob,
      :connections_max => 1
    )
    @events.add_event(
      :name => :trans_no_str,
      :connections_max => 1
    )
    @events.add_event(
      :name => :request_done,
      :connections_max => 1
    )
    @events.add_event(
      :name => :request_begin,
      :connections_max => 1
    )
    
    #This event is used if the user himself wants stuff to be cleaned up when the appserver is cleaning up stuff.
    @events.add_event(
      :name => :on_clean
    )
  end
  
  #Set up the 'vars'-variable that can be used to set custom global variables for web-requests.
  @vars = Knj::Hash_methods.new
  @magic_vars = {}
  @magic_procs = {}
  
  
  #Initialize the various feature-modules.
  print "Init sessions.\n" if @debug
  self.initialize_sessions
  
  if !@config.key?(:threadding) or @config[:threadding]
    self.loadfile("#{@path_knjappserver}/class_knjappserver_threadding.rb")
    self.loadfile("#{@path_knjappserver}/class_knjappserver_threadding_timeout.rb")
    print "Init threadding.\n" if @debug
    self.initialize_threadding
  end
  
  print "Init mailing.\n" if @debug
  self.initialize_mailing
  
  print "Init errors.\n" if @debug
  self.initialize_errors
  
  print "Init logging.\n" if @debug
  self.initialize_logging
  
  if !@config.key?(:cleaner) or @config[:cleaner]
    self.loadfile("#{@path_knjappserver}/class_knjappserver_cleaner.rb")
    print "Init cleaner.\n" if @debug
    self.initialize_cleaner
  end
  
  if !@config.key?(:cmdline) or @config[:cmdline]
    self.loadfile("#{@path_knjappserver}/class_knjappserver_cmdline.rb")
    print "Init cmdline.\n" if @debug
    self.initialize_cmdline
  end
  
  
  #Clear memory at exit.
  Kernel.at_exit(&self.method(:stop))
  
  
  print "Appserver spawned.\n" if @debug
end

Public Instance Methods

alert(msg) click to toggle source

Sends a javascript-alert to the HTML.

Examples

_kas.alert("Hello world!")
# File lib/include/class_knjappserver_web.rb, line 36
def alert(msg)
  _httpsession.alert_sent = true
  Knj::Web.alert(msg)
  return self
end
back() click to toggle source

Sends a javascript back to the browser and exits.

# File lib/include/class_knjappserver_web.rb, line 126
def back
  Knj::Web.back
end
clean() click to toggle source
# File lib/include/class_knjappserver_cleaner.rb, line 10
    def clean
self.clean_sessions
self.clean_autorestart
    end
clean_autorestart() click to toggle source
# File lib/include/class_knjappserver_cleaner.rb, line 15
    def clean_autorestart
begin
  if @config[:autorestart]
    time = 1
  else
    time = 15
  end
  
  loop do
    sleep time
    
    if @config.has_key?(:restart_when_used_memory) and !@should_restart
      mbs_used = (Php4r.memory_get_usage / 1024) / 1024
      STDOUT.print "Restart when over #{@config[:restart_when_used_memory]}mb\n" if @config[:debug]
      STDOUT.print "Used: #{mbs_used}mb\n" if @config[:debug]
      
      if mbs_used.to_i >= @config[:restart_when_used_memory].to_i
        STDOUT.print "Memory is over #{@config[:restart_when_used_memory]} - restarting.\n"
        @should_restart = true
      end
    end
    
    if @should_restart and !@should_restart_done and !@should_restart_runnning
      begin
        @should_restart_runnning = true
        
        #When we begin to restart it should go as fast as possible - so start by flushing out any emails waiting so it goes faster the last time...
        STDOUT.print "Flushing mails.\n"
        self.mail_flush
        
        #Lets try to find a time where no thread is working within the next 30 seconds. If we cant - we interrupt after 10 seconds and restart the server.
        begin
          Timeout.timeout(30) do
            loop do
              working_count = self.httpserv.working_count
              working = false
              
              if working_count and working_count > 0
                working = true
                STDOUT.print "Someone is working - wait two sec and try to restart again!\n"
              end
              
              if !working
                STDOUT.print "Found window where no sessions were active - restarting!\n"
                break
              else
                sleep 0.2
              end
              
              STDOUT.print "Trying to find window with no active sessions to restart...\n"
            end
          end
        rescue Timeout::Error
          STDOUT.print "Could not find a timing window for restarting... Forcing restart!\n"
        end
        
        #Flush emails again if any are pending (while we tried to find a window to restart)...
        STDOUT.print "Flushing mails.\n"
        self.mail_flush
        
        STDOUT.print "Stopping appserver.\n"
        self.stop
        
        STDOUT.print "Figuring out restart-command.\n"
        mycmd = @config[:restart_cmd]
        
        if !mycmd or mycmd.to_s.strip.length <= 0
          fpath = File.realpath("#{File.dirname(__FILE__)}/../knjappserver.rb")
          mycmd = Knj::Os.executed_cmd
          
          STDOUT.print "Previous cmd: #{mycmd}\n"
          mycmd = mycmd.gsub(/\s+knjappserver.rb/, " #{Knj::Strings.unixsafe(fpath)}")
        end
        
        STDOUT.print "Restarting knjAppServer with command: #{mycmd}\n"
        @should_restart_done = true
        print exec(mycmd)
        exit
      rescue => e
        STDOUT.puts e.inspect
        STDOUT.puts e.backtrace
      end
    end
  end
rescue => e
  self.handle_error(e)
end
    end
clean_sessions() click to toggle source

This method can be used to clean the appserver. Dont call this from a HTTP-request.

# File lib/include/class_knjappserver_cleaner.rb, line 105
    def clean_sessions
STDOUT.print "Cleaning sessions on appserver.\n" if @config[:debug]

#Clean up various inactive sessions.
session_not_ids = []
time_check = Time.now.to_i - 300
newsessions = {}
@sessions.each do |session_hash, session_data|
  session_data[:dbobj].flush
  
  if session_data[:time_lastused].to_i > time_check
    newsessions[session_hash] = session_data
    session_not_ids << session_data[:dbobj].id
  end
end

@sessions = newsessions

STDOUT.print "Delete sessions...\n" if @config[:debug]
@ob.list(:Session, {"id_not" => session_not_ids, "date_lastused_below" => (Time.now - 5356800)}) do |session|
  idhash = session[:idhash]
  STDOUT.print "Deleting session: '#{session.id}'.\n" if @config[:debug]
  @ob.delete(session)
  @sessions.delete(idhash)
end

#Clean database weak references from the tables-module.
@db.clean

#Clean the object-handler.
@ob.clean_all

#Call various user-connected methods.
@events.call(:on_clean) if @events
    end
cmd_connect(cmd, &block) click to toggle source

Connects a proc to a specific command in the command-line (key should be a regex).

# File lib/include/class_knjappserver_cmdline.rb, line 48
def cmd_connect(cmd, &block)
  @cmds[cmd] = [] if !@cmds.key?(cmd)
  @cmds[cmd] << {:block => block}
end
cmdline_on_restart_cmd(data) click to toggle source
# File lib/include/class_knjappserver_cmdline.rb, line 37
def cmdline_on_restart_cmd(data)
  print "Restart will begin shortly.\n"
  self.should_restart = true
end
cmdline_on_stop_cmd(data) click to toggle source
# File lib/include/class_knjappserver_cmdline.rb, line 42
def cmdline_on_stop_cmd(data)
  print "Stopping appserver.\n"
  self.stop
end
debugs(str) click to toggle source

Prints a string with a single file-line-backtrace prepended which is useful for debugging.

# File lib/include/class_knjappserver_errors.rb, line 129
def debugs(str)
  #Get backtrace.
  backtrace_str = caller[0]
  backtrace_match = backtrace_str.match(/^(.+):(\d+):in /)
  STDOUT.print "#{File.basename(backtrace_match[1])}:#{backtrace_match[2]}: #{str}\n"
end
define_magic_proc(method_name, &block) click to toggle source
# File lib/include/class_knjappserver.rb, line 486
def define_magic_proc(method_name, &block)
  raise "No block given." if !block_given?
  @magic_procs[method_name] = block
  
  if !Object.respond_to?(method_name)
    Object.send(:define_method, method_name) do
      return Thread.current[:knjappserver][:kas].magic_procs[method_name].call(:kas => self) if Thread.current[:knjappserver] and Thread.current[:knjappserver][:kas]
      raise "Could not figure out the object: '#{method_name}'."
    end
  end
end
define_magic_var(method_name, var) click to toggle source

Defines a variable as a method bound to the threads spawned by this instance of Knjappserver.

# File lib/include/class_knjappserver.rb, line 475
def define_magic_var(method_name, var)
  @magic_vars[method_name] = var
  
  if !Object.respond_to?(method_name)
    Object.send(:define_method, method_name) do
      return Thread.current[:knjappserver][:kas].magic_vars[method_name] if Thread.current[:knjappserver] and Thread.current[:knjappserver][:kas]
      raise "Could not figure out the object: '#{method_name}'."
    end
  end
end
dprint(obj) click to toggle source

Prints a detailed overview of the object in the terminal from where the appserver was started. This can be used for debugging.

# File lib/include/class_knjappserver_errors.rb, line 124
def dprint(obj)
  STDOUT.print Php4r.print_r(obj, true)
end
flush_access_log() click to toggle source

Writes all queued access-logs to the database.

# File lib/include/class_knjappserver_logging.rb, line 12
    def flush_access_log
return nil if @logs_access_pending.empty?

            @logs_mutex.synchronize do
                    ins_arr = @logs_access_pending
                    @logs_access_pending = []
                    inserts = []
                    inserts_links = []
                    
                    ins_arr.each do |ins|
                            gothrough = [{
                                    :col => :get_keys_data_id,
                                    :hash => ins[:get],
                                    :type => :keys
                            },{
                                    :col => :get_values_data_id,
                                    :hash => ins[:get],
                                    :type => :values
                            },{
                                    :col => :post_keys_data_id,
                                    :hash => ins[:post],
                                    :type => :keys
                            },{
                                    :col => :post_values_data_id,
                                    :hash => ins[:post],
                                    :type => :values
                            },{
                                    :col => :cookie_keys_data_id,
                                    :hash => ins[:cookie],
                                    :type => :keys
                            },{
                                    :col => :cookie_values_data_id,
                                    :hash => ins[:cookie],
                                    :type => :values
                            },{
                                    :col => :meta_keys_data_id,
                                    :hash => ins[:meta],
                                    :type => :keys
                            },{
                                    :col => :meta_values_data_id,
                                    :hash => ins[:meta],
                                    :type => :values
                            }]
                            ins_hash = {
                                    :session_id => ins[:session_id],
                                    :date_request => ins[:date_request]
                            }
                            
                            gothrough.each do |data|
                                    if data[:type] == :keys
                                            hash = Knj::ArrayExt.hash_keys_hash(data[:hash])
                                    else
                                            hash = Knj::ArrayExt.hash_values_hash(data[:hash])
                                    end
                                    
                                    data_id = @ob.static(:Log_data, :by_id_hash, hash)
                                    if !data_id
                                            data_id = @db.insert(:Log_data, {"id_hash" => hash}, {:return_id => true})
                                            
                                            link_count = 0
                                            data[:hash].keys.sort.each do |key|
                                                    if data[:type] == :keys
                                                            ins_data = "#{key.to_s}"
                                                    else
                                                            ins_data = "#{data[:hash][key]}"
                                                    end
                                                    
                                                    ins_data = ins_data.force_encoding("UTF-8") if ins_data.respond_to?(:force_encoding)
                                                    data_value_id = @ob.static(:Log_data_value, :force_id, ins_data)
                                                    inserts_links << {:no => link_count, :data_id => data_id, :value_id => data_value_id}
                                                    link_count += 1
                                            end
                                    end
                                    
                                    ins_hash[data[:col]] = data_id
                            end
                            
                            hash = Knj::ArrayExt.array_hash(ins[:ips])
                            data_id = @ob.static(:Log_data, :by_id_hash, hash)
                            
                            if !data_id
                                    data_id = @db.insert(:Log_data, {"id_hash" => hash}, {:return_id => true})
                                    
                                    link_count = 0
                                    ins[:ips].each do |ip|
        data_value_id = @ob.static(:Log_data_value, :force_id, ip)
                                            inserts_links << {:no => link_count, :data_id => data_id, :value_id => data_value_id}
                                            link_count += 1
                                    end
                            end
                            
                            ins_hash[:ip_data_id] = data_id
                            inserts << ins_hash
                    end
                    
                    @db.insert_multi(:Log_access, inserts)
                    @db.insert_multi(:Log_data_link, inserts_links)
                    @ob.unset_class([:Log_access, :Log_data, :Log_data_link, :Log_data_value])
            end
    end
flush_error_emails() click to toggle source

Send error-emails based on error-emails-cache (cached so the same error isnt send out every time it occurrs to prevent spamming).

# File lib/include/class_knjappserver_errors.rb, line 20
def flush_error_emails
  @error_emails_pending_mutex.synchronize do
    send_time_older_than = Time.new.to_i - @error_emails_time
    
    @error_emails_pending.each do |backtrace_hash, error_email|
      if send_time_older_than < error_email[:last_time].to_i and error_email[:messages].length < 1000
        next
      end
      
      @config[:error_report_emails].each do |email|
        next if !email or error_email[:messages].length <= 0
        
        if error_email[:messages].length == 1
          html = error_email[:messages].first
        else
          html = "<b>First time:</b> #{Datet.in(error_email[:first_time]).out}<br />"
          html << "<b>Last time:</b> #{Datet.in(error_email[:last_time]).out}<br />"
          html << "<b>Number of errors:</b> #{error_email[:messages].length}<br />"
          count = 0
          
          error_email[:messages].each do |error_msg|
            count += 1
            
            if count > 10
              html << "<br /><br /><b><i>Limiting to showing 10 out of #{error_email[:messages].length} messages.</i></b>"
              break
            end
            
            html << "<br /><br />"
            html << "<b>Message #{count}</b><br />"
            html << error_msg
          end
        end
        
        self.mail(
          :to => email,
          :subject => error_email[:subject],
          :html => html,
          :from => @config[:error_report_from]
        )
      end
      
      @error_emails_pending.delete(backtrace_hash)
    end
  end
end
get_parse_arrays(arg = nil, ob = nil) click to toggle source

Hashes with numeric keys will be turned into arrays instead. This is not done automatically because it can wrongly corrupt data if not used correctly.

# File lib/include/class_knjappserver_web.rb, line 153
def get_parse_arrays(arg = nil, ob = nil)
  arg = _get.clone if !arg
  
  #Parses key-numeric-hashes into arrays and converts special model-strings into actual models.
  if arg.is_a?(Hash) and Knj::ArrayExt.hash_numeric_keys?(arg)
    arr = []
    
    arg.each do |key, val|
      arr << val
    end
    
    return self.get_parse_arrays(arr, ob)
  elsif arg.is_a?(Hash)
    arg.each do |key, val|
      arg[key] = self.get_parse_arrays(val, ob)
    end
    
    return arg
  elsif arg.is_a?(Array)
    arg.each_index do |key|
      arg[key] = self.get_parse_arrays(arg[key], ob)
    end
    
    return arg
  elsif arg.is_a?(String) and match = arg.match(/^#<Model::(.+?)::(\d+)>$/)
    ob = @ob if !ob
    return ob.get(match[1], match[2])
  else
    return arg
  end
end
handle_error(e, args = {}) click to toggle source

Handels a given error. Sends to the admin-emails.

# File lib/include/class_knjappserver_errors.rb, line 68
def handle_error(e, args = {})
  @error_emails_pending_mutex.synchronize do
    if !Thread.current[:knjappserver] or !Thread.current[:knjappserver][:httpsession]
      STDOUT.print "#{Knj::Errors.error_str(e)}\n\n"
    end
    
    browser = _httpsession.browser if _httpsession
    
    send_email = true
    send_email = false if !@config[:smtp_args]
    send_email = false if !@config[:error_report_emails]
    send_email = false if args.has_key?(:email) and !args[:email]
    send_email = false if @config.key?(:error_report_bots) and !@config[:error_report_bots] and browser and browser["browser"] == "bot"
    
    if send_email
      backtrace_hash = Knj::ArrayExt.array_hash(e.backtrace)
      
      if !@error_emails_pending.has_key?(backtrace_hash)
        @error_emails_pending[backtrace_hash] = {
          :first_time => Time.new,
          :messages => [],
          :subject => sprintf("Error @ %s", @config[:title]) + " (#{Knj::Strings.shorten(e.message, 100)})"
        }
      end
      
      html = "An error occurred.<br /><br />"
      html << "<b>#{Knj::Web.html(e.class.name)}: #{Knj::Web.html(e.message)}</b><br /><br />"
      
      e.backtrace.each do |line|
        html << "#{Knj::Web.html(line)}<br />"
      end
      
      html << "<br /><b>Post:</b><br /><pre>#{Php4r.print_r(_post, true)}</pre>" if _post
      html << "<br /><b>Get:</b><br /><pre>#{Php4r.print_r(_get, true)}</pre>" if _get
      html << "<br /><b>Server:</b><br /><pre>#{Php4r.print_r(_server, true).html}</pre>" if _server
      html << "<br /><b>Cookie:</b><br /><pre>#{Php4r.print_r(_cookie, true).html}</pre>" if _meta
      html << "<br /><b>Session:</b><br /><pre>#{Php4r.print_r(_session, true).html}</pre>" if _session
      html << "<br /><b>Session hash:</b><br /><pre>#{Php4r.print_r(_session_hash, true).html}</pre>" if _session_hash
      
      error_hash = @error_emails_pending[backtrace_hash]
      error_hash[:last_time] = Time.new
      error_hash[:messages] << html
    end
  end
end
header(key, val) click to toggle source

Sends a header to the clients browser.

Examples

_kas.header("Content-Type", "text/javascript")
# File lib/include/class_knjappserver_web.rb, line 56
def header(key, val)
  raise "No HTTP-session attached to this thread." if !_httpsession
  raise "HTTP-session not active." if !_httpsession.resp
  _httpsession.resp.header(key, val)
  return nil
end
header_raw(str) click to toggle source

Sends a raw header-line to the clients browser.

# File lib/include/class_knjappserver_web.rb, line 64
def header_raw(str)
  raise "No HTTP-session attached to this thread." if !_httpsession
  raise "HTTP-session not active." if !_httpsession.resp
  Php4r.header(str)
  return nil
end
headers_send_size=(newsize) click to toggle source

Define the size for when to automatically send headers. If you want to send hundres of kilobytes and then a header, you can use this method to do so.

Examples

Set the size to 200 kb.

_kas.headers_send_size = (1024 * 200)
# File lib/include/class_knjappserver_web.rb, line 83
def headers_send_size=(newsize)
  raise "The headers are already sent and you cannot modify the send-size any more." if self.headers_sent?
  _httpsession.size_send = newsize.to_i
  return nil
end
headers_sent?() click to toggle source

Returns true if the headers are already sent.

Examples

_kas.headers_sent? #=> true
# File lib/include/class_knjappserver_web.rb, line 74
def headers_sent?
  return true if _httpsession.resp.headers_sent
  return false
end
import(filepath) click to toggle source

Imports a .rhtml-file and executes it.

Examples

_kas.import("/some/path/page.rhtml")
# File lib/include/class_knjappserver_web.rb, line 5
def import(filepath)
  if filepath.to_s.index("../proc/self") != nil
    raise Errno::EACCES, "Possible attempt to hack the appserver."
  end
  
  _httpsession.eruby.import(filepath)
end
initialize_cleaner() click to toggle source
# File lib/include/class_knjappserver_cleaner.rb, line 2
    def initialize_cleaner
#This should not be runned via _kas.timeout because timeout wont run when @should_restart is true! - knj
Thread.new(&self.method(:clean_autorestart))

#This flushes (writes) all session-data to the server and deletes old unused sessions from the database.
self.timeout(:time => @config[:cleaner_timeout], &self.method(:clean_sessions))
    end
initialize_cmdline() click to toggle source
# File lib/include/class_knjappserver_cmdline.rb, line 2
def initialize_cmdline
  @cmds = {}
  
  Thread.new do
    begin
      $stdin.each_line do |line|
        called = 0
        @cmds.each do |key, connects|
          data = {}
          
          if key.is_a?(Regexp)
            if line.match(key)
              connects.each do |conn|
                called += 1
                conn[:block].call(data)
              end
            end
          else
            raise "Unknown class for 'cmd_connect': '#{key.class.name}'."
          end
        end
        
        if called == 0
          print "Unknown command: '#{line.strip}'.\n"
        end
      end
    rescue => e
      self.handle_error(e)
    end
  end
  
  self.cmd_connect(/^\s*restart\s*$/i, &self.method(:cmdline_on_restart_cmd))
  self.cmd_connect(/^\s*stop\s*$/i, &self.method(:cmdline_on_stop_cmd))
end
initialize_errors() click to toggle source
# File lib/include/class_knjappserver_errors.rb, line 4
def initialize_errors
  @error_emails_pending = {}
  @error_emails_pending_mutex = Mutex.new
  
  if @config[:error_emails_time]
    @error_emails_time = @config[:error_emails_time]
  elsif @config[:debug]
    @error_emails_time = 5
  else
    @error_emails_time = 180
  end
  
  self.timeout(:time => @error_emails_time, &self.method(:flush_error_emails))
end
initialize_logging() click to toggle source
# File lib/include/class_knjappserver_logging.rb, line 2
def initialize_logging
        @logs_access_pending = []
        @logs_mutex = Mutex.new
        
        if @config[:logging] and @config[:logging][:access_db]
                self.timeout(:time => 30, &self.method(:flush_access_log))
        end
end
initialize_mailing() click to toggle source
# File lib/include/class_knjappserver_mailing.rb, line 4
    def initialize_mailing
require "knj/autoload/ping"
require "monitor"

            @mails_waiting = []
            @mails_mutex = Monitor.new
            @mails_queue_mutex = Monitor.new
            @mails_timeout = self.timeout(:time => 30, &self.method(:mail_flush))
    end
initialize_sessions() click to toggle source
# File lib/include/class_knjappserver_sessions.rb, line 2
def initialize_sessions
  @sessions = Tsafe::MonHash.new
end
initialize_threadding() click to toggle source
# File lib/include/class_knjappserver_threadding.rb, line 2
def initialize_threadding
  @config[:threadding] = {} if !@config.has_key?(:threadding)
  @config[:threadding][:max_running] = 8 if !@config[:threadding].has_key?(:max_running)
  
  @threadpool = Knj::Threadpool.new(:threads => @config[:threadding][:max_running], :sleep => 0.1)
  @threadpool.events.connect(:on_error, &self.method(:threadpool_on_error))
end
inputs(*args) click to toggle source

Draw a input in a table.

# File lib/include/class_knjappserver_web.rb, line 131
def inputs(*args)
  return Knj::Web.inputs(args)
end
join() click to toggle source

Sleeps until the server is stopped.

# File lib/include/class_knjappserver.rb, line 452
def join
  raise "No http-server or http-server not running." if !@httpserv or !@httpserv.thread_accept
  
  begin
    @httpserv.thread_accept.join
    @httpserv.thread_restart.join if @httpserv and @httpserv.thread_restart
  rescue Interrupt => e
    self.stop
  end
  
  if @should_restart
    loop do
      if @should_restart_done
        STDOUT.print "Ending join because the restart is done.\n"
        break
      end
      
      sleep 1
    end
  end
end
loadfile(fpath) click to toggle source

If you want to use auto-restart, every file reloaded through loadfile will be watched for changes. When changed the server will do a restart to reflect that.

# File lib/include/class_knjappserver.rb, line 325
def loadfile(fpath)
  if !@config[:autorestart]
    require fpath
    return nil
  end
  
  rpath = File.realpath(fpath)
  raise "No such filepath: #{fpath}" if !rpath or !File.exists?(rpath)
  
  return true if @mod_files[rpath]
  
  @mod_event.args[:paths] << rpath
  @mod_files = rpath
  
  require rpath
  return false
end
log(msg, objs, args = {}) click to toggle source

Writes a custom log to the database.

# File lib/include/class_knjappserver_logging.rb, line 215
    def log(msg, objs, args = {})
#This can come in handy if migrating logs to appserver-database.
if args[:date_saved]
  date_saved = args[:date_saved]
else
  date_saved = Time.now
end

objs = [objs] if !objs.is_a?(Array)

            @logs_mutex.synchronize do
                    log_value_id = @ob.static(:Log_data_value, :force_id, msg)
                    
                    ins_data = {
                            :date_saved => date_saved,
                            :text_value_id => log_value_id
                    }
                    
                    get_hash = log_hash_ins(_get) if _get
                    if get_hash
                            ins_data[:get_keys_data_id] = get_hash[:keys_data_id]
                            ins_data[:get_values_data_id] = get_hash[:values_data_id]
                    end
                    
                    post_hash = log_hash_ins(_post) if _post
                    if post_hash
                            ins_data[:post_keys_data_id] = post_hash[:keys_data_id]
                            ins_data[:post_values_data_id] = post_hash[:values_data_id]
                    end
                    
                    cookie_hash = log_hash_ins(_cookie) if _cookie
                    if cookie_hash
    ins_data[:post_keys_data_id] = cookie_hash[:keys_data_id]
    ins_data[:post_values_data_id] = cookie_hash[:values_data_id]
                    end
                    
                    meta_hash = log_hash_ins(_meta) if _meta
  if cookie_hash
    ins_data[:meta_keys_data_id] = meta_hash[:keys_data_id]
    ins_data[:meta_values_data_id] = meta_hash[:values_data_id]
  end
  
  session_hash = log_hash_ins(_session) if _session
  if session_hash
    ins_data[:session_keys_data_id] = session_hash[:keys_data_id]
    ins_data[:session_values_data_id] = session_hash[:values_data_id]
  end
  
  if args[:tag]
    tag_value_id = @ob.static(:Log_data_value, :force_id, args[:tag])
    ins_data[:tag_data_id] = tag_value_id
  end
  
  if args[:comment]
    comment_value_id = @ob.static(:Log_data_value, :force_id, args[:comment])
    ins_data[:comment_data_id] = comment_value_id
  end
                    
                    log_id = @db.insert(:Log, ins_data, {:return_id => true})
                    
                    log_links = []
                    objs.each do |obj|
    class_data_id = @ob.static(:Log_data_value, :force_id, obj.class.name)
    
    log_links << {
      :object_class_value_id => class_data_id,
      :object_id => obj.id,
      :log_id => log_id
    }
                    end
                    
                    @db.insert_multi(:Log_link, log_links)
            end
    end
log_data_hash(keys_id, values_id) click to toggle source
# File lib/include/class_knjappserver_logging.rb, line 176
    def log_data_hash(keys_id, values_id)
begin
  keys_data_obj = @ob.get(:Log_data, keys_id)
  values_data_obj = @ob.get(:Log_data, values_id)
rescue Errno::ENOENT
  return {}
end
            
            sql = "
                    SELECT
                            key_value.value AS `key`,
                            value_value.value AS value
                    
                    FROM
                            Log_data_link AS key_links,
                            Log_data_link AS value_links,
                            Log_data_value AS key_value,
                            Log_data_value AS value_value
                    
                    WHERE
                            key_links.data_id = '#{keys_id}' AND
                            value_links.data_id = '#{values_id}' AND
                            key_links.no = value_links.no AND
                            key_value.id = key_links.value_id AND
                            value_value.id = value_links.value_id
                    
                    ORDER BY
                            key_links.no
            "
            
            hash = {}
            db.q(sql) do |d_hash|
                    hash[d_hash[:key].to_sym] = d_hash[:value]
            end
            
            return hash
    end
log_hash_ins(hash_obj) click to toggle source

Handles the hashes that should be logged.

# File lib/include/class_knjappserver_logging.rb, line 130
    def log_hash_ins(hash_obj)
#Sort out fileuploads - it would simply bee too big to log this.
hash_obj = self.log_hash_safe(hash_obj)

            inserts_links = []
            ret = {}
            [:keys, :values].each do |type|
                    if type == :keys
                            hash = Knj::ArrayExt.hash_keys_hash(hash_obj)
                    else
                            hash = Knj::ArrayExt.hash_values_hash(hash_obj)
                    end
                    
                    data_id = @db.single(:Log_data, {"id_hash" => hash})
                    data_id = data_id[:id] if data_id
                    
                    if !data_id
                            data_id = @db.insert(:Log_data, {"id_hash" => hash}, {:return_id => true})
                            
                            link_count = 0
                            hash_obj.keys.sort.each do |key|
                                    if type == :keys
                                            ins_data = "#{key.to_s}"
                                    else
                                            ins_data = "#{hash_obj[key].to_s}"
                                    end
                                    
                                    ins_data = ins_data.force_encoding("UTF-8") if ins_data.respond_to?(:force_encoding)
                                    data_value_id = @ob.static(:Log_data_value, :force_id, ins_data)
                                    inserts_links << {:no => link_count, :data_id => data_id, :value_id => data_value_id}
                                    link_count += 1
                            end
                    end
                    
                    if type == :keys
                            ret[:keys_data_id] = data_id
                    else
                            ret[:values_data_id] = data_id
                    end
            end
            
            @db.insert_multi(:Log_data_link, inserts_links)
            
            return ret
    end
log_hash_safe(hash) click to toggle source

Converts fileuploads into strings so logging wont be crazy big.

# File lib/include/class_knjappserver_logging.rb, line 114
    def log_hash_safe(hash)
hash_obj = {}
hash.each do |key, val|
  if val.is_a?(Knjappserver::Httpsession::Post_multipart::File_upload)
    hash_obj[key] = "<Fileupload>"
  elsif val.is_a?(Hash)
    hash_obj[key] = self.log_hash_safe(val)
  else
    hash_obj[key] = val
  end
end

return hash_obj
    end
logs_delete(obj) click to toggle source

Deletes all logs for an object.

# File lib/include/class_knjappserver_logging.rb, line 291
    def logs_delete(obj)
@db.q_buffer do |db_buffer|
  buffer_hash = {:db_buffer => db_buffer}
  
  @ob.list(:Log_link, {"object_class" => obj.class.name, "object_id" => obj.id}) do |log_link|
    log = log_link.log
    @ob.delete(log_link, buffer_hash)
    @ob.delete(log, buffer_hash) if log and log.links("count" => true) <= 0
  end
end
    end
logs_delete_dead(args) click to toggle source

Removes all logs for objects that have been deleted.

Examples

Remember to pass Knj::Objects-object handler to the method.

appsrv.logs_delete_dead(:ob => ob, :debug => false)
# File lib/include/class_knjappserver_logging.rb, line 365
    def logs_delete_dead(args)
raise "No :ob-argument given." if !args[:ob]

@db.q_buffer do |db_buffer|
  STDOUT.puts "Starting to look for dead log-links." if @debug or args[:debug]
  @ob.list(:Log_link, :cloned_ubuf => true) do |log_link|
    classname = log_link.object_class.to_s.split("::").last
    obj_exists = args[:ob].exists?(classname, log_link[:object_id])
    next if obj_exists
    
    log = log_link.log
    
    STDOUT.puts "Deleting log-link #{log_link.id} for #{classname}(#{log_link[:object_id]})." if @debug or args[:debug]
    @ob.delete(log_link, :db_buffer => db_buffer)
    
    links_count = log.links("count" => true)
    
    if links_count <= 0
      STDOUT.puts "Deleting log #{log.id} because it has no more links." if @debug or args[:debug]
      @ob.delete(log, :db_buffer => db_buffer)
    end
  end
  
  STDOUT.puts "Starting to look for logs with no links." if @debug or args[:debug]
  @ob.list(:Log, {
    [:Log_link, "id"] => {:type => :sqlval, :val => :null},
    :cloned_ubuf => true
  }) do |log|
    STDOUT.puts "Deleting log #{log.id} because it has no links: '#{log.text}'." if @debug or args[:debug]
    @ob.delete(log, :db_buffer => db_buffer)
  end
end
    end
logs_table(obj, args = {}) click to toggle source

Returns the HTML for a table with logs from a given object.

# File lib/include/class_knjappserver_logging.rb, line 304
  def logs_table(obj, args = {})
          links = @ob.list(:Log_link, {"object_class" => obj.class.name, "object_id" => obj.id, "limit" => 500, "orderby" => [["id", "desc"]]})
          
          html = ""
          
          html << "<table class=\"list knjappserver_log_table\">"
          html << "<thead>"
          html << "<tr>"
          html << "<th>ID</th>"
          html << "<th>Message</th>"
          html << "<th style=\"width: 130px;\">Date &amp; time</th>"
          html << "<th>Tag</th>"
          html << "<th>Objects</th>" if args[:ob_use]
          html << "<th>IP</th>" if args[:show_ip]
          html << "</tr>"
          html << "</thead>"
          html << "<tbody>"
          
          links.each do |link|
log = link.log

                  msg_lines = log.text.split("\n")
                  first_line = msg_lines[0].to_s
                  
                  classes = ["knjappserver_log", "knjappserver_log_#{log.id}"]
                  classes << "knjappserver_log_multiple_lines" if msg_lines.length > 1
                  
                  html << "<tr class=\"#{classes.join(" ")}\">"
                  html << "<td>#{log.id}</td>"
                  html << "<td>#{first_line.html}</td>"
                  html << "<td>#{log.date_saved_str}</td>"
                  html << "<td>#{log.tag.html}</td>"
                  
                  if args[:ob_use]
  begin
    html << "<td>#{log.objects_html(args[:ob_use])}</td>"
  rescue => e
    html << "<td>#{e.message.html}</td>"
  end
end

                  html << "<td>#{log.ip}</td>" if args[:show_ip]
                  html << "</tr>"
          end
          
          if links.empty?
                  html << "<tr>"
                  html << "<td colspan=\"2\" class=\"error\">No logs were found for that object.</td>"
                  html << "</tr>"
          end
          
          html << "</tbody>"
          html << "</table>"
          
          return html
  end
mail(mail_args) click to toggle source

Queue a mail for sending. Possible keys are: :subject, :from, :to, :text and :html.

# File lib/include/class_knjappserver_mailing.rb, line 15
    def mail(mail_args)
raise "'smtp_args' has not been given for the Knjappserver." if !@config[:smtp_args]

            @mails_queue_mutex.synchronize do
                    count_wait = 0
                    while @mails_waiting.length > 100
                            if count_wait >= 30
                                    raise "Could not send email - too many emails was pending and none of them were being sent?"
                            end
                            
                            count_wait += 1
                            sleep 1
                    end
                    
                    mailobj = Knjappserver::Mail.new({:kas => self, :errors => {}, :status => :waiting}.merge(mail_args))
                    STDOUT.print "Added mail '#{mailobj.__id__}' to the mail-send-queue.\n" if debug
                    @mails_waiting << mailobj
                    self.mail_flush if mail_args[:now]
                    return mailobj
            end
    end
mail_flush() click to toggle source

Sends all queued mails to the respective servers, if we are online.

# File lib/include/class_knjappserver_mailing.rb, line 38
  def mail_flush
          @mails_mutex.synchronize do
STDOUT.print "Flushing mails.\n" if @debug

if @mails_waiting.length <= 0
  STDOUT.print "No mails to flush - skipping.\n" if @debug
  return false
end

STDOUT.print "Trying to ping Google to figure out if we are online...\n" if @debug
status = Ping.pingecho("google.dk", 10, 80)
if !status
  STDOUT.print "We are not online - skipping mail flush.\n"
  return false  #Dont run if we dont have a connection to the internet and then properly dont have a connection to the SMTP as well.
end

begin
  #Use subprocessing to avoid the mail-framework (activesupport and so on, also possible memory leaks in those large frameworks).
  STDOUT.print "Starting subprocess for mailing.\n" if @debug
  require "knj/process_meta"
  subproc = Knj::Process_meta.new("debug" => @debug, "debug_err" => true, "id" => "knjappserver_mailing")
  subproc.static("Object", "require", "rubygems")
  subproc.static("Object", "require", "mail")
  subproc.static("Object", "require", "#{@config[:knjrbfw_path]}knjrbfw")
  subproc.static("Object", "require", "knj/autoload")
  
  STDOUT.print "Flushing emails." if @debug
  @mails_waiting.each do |mail|
    begin
      STDOUT.print "Sending email: #{mail.__id__}\n" if @debug
      if mail.send("proc" => subproc)
        STDOUT.print "Email sent: #{mail.__id__}\n" if @debug
        @mails_waiting.delete(mail)
      end
    rescue Timeout::Error
      #ignore -
    rescue => e
      @mails_waiting.delete(mail)
      self.handle_error(e, {:email => false})
    end
    
    sleep 1 #sleep so we dont take up too much bandwidth.
  end
ensure
  subproc.destroy if subproc
  subproc = nil
end

return nil
          end
  end
no_date(event, classname) click to toggle source
# File lib/include/class_knjappserver.rb, line 314
def no_date(event, classname)
  return "[no date]"
end
num(*args) click to toggle source

Returns a number localized as a string.

# File lib/include/class_knjappserver_web.rb, line 148
def num(*args)
  return Knj::Locales.number_out(*args)
end
on_error_go_back(&block) click to toggle source

Takes a proc and executes it. On error it alerts the error-message with javascript to the server, sends a javascript back and exits.

# File lib/include/class_knjappserver_errors.rb, line 115
def on_error_go_back(&block)
  begin
    block.call
  rescue => e
    self.alert(e.message).back
  end
end
on_event_filemod(event, path) click to toggle source
# File lib/include/class_knjappserver.rb, line 318
def on_event_filemod(event, path)
  print "File changed - restart server: #{path}\n"
  @should_restart = true
  @mod_event.destroy if @mod_event
end
pause() click to toggle source

Stop running any more HTTP-requests - make them wait.

# File lib/include/class_knjappserver.rb, line 408
def pause
  @paused += 1
end
paused?() click to toggle source

Returns true if paued - otherwise false.

# File lib/include/class_knjappserver.rb, line 418
def paused?
  return true if @paused > 0
  return false
end
paused_exec() { || ... } click to toggle source

Will stop handeling any more HTTP-requests, run the proc given and return handeling HTTP-requests.

# File lib/include/class_knjappserver.rb, line 424
def paused_exec
  raise "No block given." if !block_given?
  self.pause
  
  begin
    sleep 0.2 while @httpserv and @httpserv.working_count and @httpserv.working_count > 0
    @paused_mutex.synchronize do
      Timeout.timeout(15) do
        yield
      end
    end
  ensure
    self.unpause
  end
end
port() click to toggle source

Returns the socket-port the appserver is currently running on.

# File lib/include/class_knjappserver_web.rb, line 186
def port
  raise "Http-server not spawned yet. Call Knjappserver#start to spawn it." if !@httpserv
  return @httpserv.server.addr[1]
end
redirect(url, args = {}) click to toggle source

Redirects to another URL.

Examples

_kas.redirect("someotherpage.rhtml")
_kas.redirect("newpage.rhtml", :perm => true)
# File lib/include/class_knjappserver_web.rb, line 17
def redirect(url, args = {})
  #Header way
  if !_httpsession.alert_sent and !self.headers_sent?
    if args[:perm]
      _httpsession.resp.status = 301 if !self.headers_sent?
    else
      _httpsession.resp.status = 303 if !self.headers_sent?
    end
    
    self.header("Location", url) if !self.headers_sent?
  end
  
  print "<script type=\"text/javascript\">location.href=\"#{url}\";</script>"
  exit
end
serve_file(filepath) click to toggle source

Serves the given filepath and enables caching for it. No other content should be written to the page when using this method.

Examples

_kas.header("Content-Type", "text/javascript")
_kas.serve_file("somefile.js")
# File lib/include/class_knjappserver_web.rb, line 93
def serve_file(filepath)
  raise "File doesnt exist: '#{filepath}'." if !File.exists?(filepath)
  httpsess = _httpsession
  headers = httpsess.headers
  resp = httpsess.resp
  
  if headers["cache-control"] and headers["cache-control"][0]
    cache_control = {}
    headers["cache-control"][0].scan(/(.+)=(.+)/) do |match|
      cache_control[match[1]] = match[2]
    end
  end
  
  cache_dont = true if cache_control and cache_control.key?("max-age") and cache_control["max-age"].to_i <= 0
  lastmod = File.mtime(filepath)
  
  self.header("Last-Modified", lastmod.httpdate)
  self.header("Expires", (Time.now + 86400).httpdate) #next day.
  
  if !cache_dont and headers["if-modified-since"] and headers["if-modified-since"][0]
    request_mod = Datet.in(headers["if-modified-since"].first).time
    
    if request_mod == lastmod
      resp.status = 304
      return nil
    end
  end
  
  httpsess.force_content(:type => :file, :path => filepath)
  return nil
end
session_fromid(ip, idhash, meta) click to toggle source

Returns or adds session based on idhash and meta-data.

# File lib/include/class_knjappserver_sessions.rb, line 7
def session_fromid(ip, idhash, meta)
  ip = "bot" if idhash == "bot"
  
  if !@sessions.key?(idhash)
    session = @ob.get_by(:Session, {"idhash" => idhash})
    if !session
      session = @ob.add(:Session, {
        :idhash => idhash,
        :user_agent => meta["HTTP_USER_AGENT"],
        :ip => ip
      })
    end
    
    hash = {}
    @sessions[idhash] = {
      :dbobj => session,
      :hash => hash
    }
  else
    session = @sessions[idhash][:dbobj]
    hash = @sessions[idhash][:hash]
  end
  
  if ip != "bot" and !session.remember? and ip.to_s != session[:ip].to_s
    raise ArgumentError, "Invalid IP."
  end
  
  @sessions[idhash][:time_lastused] = Time.now
  return [session, hash]
end
session_generate_id(meta) click to toggle source

Generates a new session-ID by the meta data.

# File lib/include/class_knjappserver_sessions.rb, line 39
def session_generate_id(meta)
  return Digest::MD5.hexdigest("#{Time.now.to_f}_#{meta["HTTP_HOST"]}_#{meta["REMOTE_HOST"]}_#{meta["HTTP_X_FORWARDED_SERVER"]}_#{meta["HTTP_X_FORWARDED_FOR"]}_#{meta["HTTP_X_FORWARDED_HOST"]}_#{meta["REMOTE_ADDR"]}_#{meta["HTTP_USER_AGENT"]}")
end
session_remember() click to toggle source

Will make the session rememberable for a year. IP wont be checked any more.

# File lib/include/class_knjappserver_sessions.rb, line 44
def session_remember
  session = _httpsession.session
  session[:remember] = 1
  
  self.cookie(
    "name" => "KnjappserverSession",
    "value" => _httpsession.session_id,
    "path" => "/",
    "expires" => Time.now + 32140800 #add around 12 months
  )
end
sessions_flush() click to toggle source

Writes all session-data to the database (normally it is cached in memory and not updated on change).

# File lib/include/class_knjappserver_sessions.rb, line 57
def sessions_flush
  if @sessions
    @sessions.each do |session_hash, session_data|
      STDOUT.print "Flushing session: #{session_data[:dbobj].id}\n" if @debug
      session_data[:dbobj].flush
    end
  end
end
sessions_reset() click to toggle source

Writes all session-data and resets the hash.

# File lib/include/class_knjappserver_sessions.rb, line 67
def sessions_reset
  self.sessions_flush
  @sessions = {}
end
start() click to toggle source

Starts the HTTP-server and threadpool.

# File lib/include/class_knjappserver.rb, line 344
def start
  #Start the appserver.
  print "Spawning appserver.\n" if @debug
  @httpserv = Knjappserver::Httpserver.new(self)
  
  
  #Start Leakproxy-module if defined in config.
  if @config[:leakproxy]
    require "#{File.dirname(__FILE__)}/class_knjappserver_leakproxy_server.rb"
    @leakproxy_server = Knjappserver::Leakproxy_server.new(:kas => self)
  end
  
  
  STDOUT.print "Starting appserver.\n" if @debug
  Thread.current[:knjappserver] = {:kas => self} if !Thread.current[:knjappserver]
  
  if @config[:autoload]
    STDOUT.print "Autoloading #{@config[:autoload]}\n" if @debug
    require @config[:autoload]
  end
  
  begin
    @threadpool.start if @threadpool
    STDOUT.print "Threadpool startet.\n" if @debug
    @httpserv.start
    STDOUT.print "Appserver startet.\n" if @debug
  rescue Interrupt => e
    STDOUT.print "Got interrupt - trying to stop appserver.\n" if @debug
    self.stop
    raise e
  end
end
stop() click to toggle source

Stops the entire app and releases join.

# File lib/include/class_knjappserver.rb, line 378
def stop
  return nil if @stop_called
  @stop_called = true
  
  proc_stop = proc{
    STDOUT.print "Stopping appserver.\n" if @debug
    @httpserv.stop if @httpserv and @httpserv.respond_to?(:stop)
    
    STDOUT.print "Stopping threadpool.\n" if @debug
    @threadpool.stop if @threadpool
    
    #This should be done first to be sure it finishes (else we have a serious bug).
    STDOUT.print "Flush out loaded sessions.\n" if @debug
    self.sessions_flush
    
    STDOUT.print "Stopping done...\n" if @debug
  }
  
  #If we cant get a paused-execution in 5 secs - we just force the stop.
  begin
    Timeout.timeout(5) do
      self.paused_exec(&proc_stop)
    end
  rescue Timeout::Error, SystemExit, Interrupt
    STDOUT.print "Forcing stop-appserver - couldnt get timing window.\n" if @debug
    proc_stop.call
  end
end
thread(args = {}) { |*args| ... } click to toggle source

Spawns a new thread with access to magic methods, _db-method and various other stuff in the appserver.

# File lib/include/class_knjappserver_threadding.rb, line 23
def thread(args = {})
  raise "No block given." if !block_given?
  args[:args] = [] if !args[:args]
  
  thread_obj = Knjappserver::Thread_instance.new(
    :running => false,
    :error => false,
    :done => false
  )
  
  @threadpool.run_async do
    @ob.db.get_and_register_thread if @ob.db.opts[:threadsafe]
    @db_handler.get_and_register_thread if @db_handler.opts[:threadsafe]
    
    Thread.current[:knjappserver] = {
      :kas => self,
      :db => @db_handler
    }
    
    begin
      thread_obj.args[:running] = true
      yield(*args[:args])
    rescue => e
      self.handle_error(e)
      thread_obj.args[:error] = true
      thread_obj.args[:error_obj] = e
    ensure
      STDOUT.print "Free thread ob-db.\n" if @debug
      @ob.db.free_thread if @ob.db.opts[:threadsafe]
      
      STDOUT.print "Free thread db-handler.\n" if @debug
      @db_handler.free_thread if @db_handler.opts[:threadsafe]
      
      STDOUT.print "Set args on thread.\n" if @debug
      thread_obj.args[:running] = false
      thread_obj.args[:done] = true
    end
  end
  
  return thread_obj
end
thread_init(thread = nil) click to toggle source

Inits the thread so it has access to the appserver and various magic methods can be used.

# File lib/include/class_knjappserver_threadding.rb, line 16
def thread_init(thread = nil)
  thread = Thread.current if thread == nil
  thread[:knjappserver] = {} if !thread[:knjappserver]
  thread[:knjappserver][:kas] = self
end
threadded_content(&block) click to toggle source

Spawns a thread to run the given proc and add the output of that block in the correct order to the HTML.

# File lib/include/class_knjappserver_threadding.rb, line 75
def threadded_content(&block)
  _httpsession.threadded_content(block)
  return nil
end
threadpool_on_error(event, error) click to toggle source

Callback for when an error occurs in the threadpool.

# File lib/include/class_knjappserver_threadding.rb, line 11
def threadpool_on_error(event, error)
  self.handle_error(error)
end
timeout(args = {}, &block) click to toggle source

Runs a proc every number of seconds.

# File lib/include/class_knjappserver_threadding.rb, line 66
def timeout(args = {}, &block)
  return Knjappserver::Threadding_timeout.new({
    :kas => self,
    :block => block,
    :args => []
  }.merge(args)).start
end
trans(obj, key, args = {}) click to toggle source

Translates a given key for a given object.

Examples

print _kas.trans(obj, :title) #=> "Trala"
# File lib/include/class_knjappserver_translations.rb, line 5
def trans(obj, key, args = {})
  args[:locale] = self.trans_locale if !args[:locale]
  trans_val = @translations.get(obj, key, args).to_s
  trans_val = @events.call(:trans_no_str, {:obj => obj, :key => key, :args => args}) if trans_val.length <= 0
  return trans_val
end
trans_del(obj) click to toggle source

Deletes all translations for the given object.

Examples

_kas.trans_del(obj)
# File lib/include/class_knjappserver_translations.rb, line 40
def trans_del(obj)
  @translations.delete(obj)
end
trans_locale(args = {}) click to toggle source

Returns the locale for the current thread.

# File lib/include/class_knjappserver_translations.rb, line 13
def trans_locale(args = {})
  if args.is_a?(Hash) and args[:locale]
    return args[:locale]
  elsif _session and _session[:locale]
    return _session[:locale]
  elsif _httpsession and _httpsession.data[:locale]
    return _httpsession.data[:locale]
  elsif Thread.current[:locale]
    return Thread.current[:locale]
  elsif @config[:locale_default]
    return @config[:locale_default]
  end
  
  raise "Could not figure out locale."
end
trans_set(obj, values, args = {}) click to toggle source

Sets new translations for the given object.

Examples

_kas.trans_set(obj, {:title => "Trala"})
# File lib/include/class_knjappserver_translations.rb, line 32
def trans_set(obj, values, args = {})
  args[:locale] = self.trans_locale if !args[:locale]
  @translations.set(obj, values, args)
end
unpause() click to toggle source

Unpause - start handeling HTTP-requests again.

# File lib/include/class_knjappserver.rb, line 413
def unpause
  @paused -= 1
end
urldec(str) click to toggle source

Urldecodes a string.

# File lib/include/class_knjappserver_web.rb, line 143
def urldec(str)
  return Knj::Web.urldec(str)
end
urlenc(str) click to toggle source

Urlencodes a string.

Examples

_kas.redirect("mypage.rhtml?arg=#{_kas.urlenc(value_variable)}")
# File lib/include/class_knjappserver_web.rb, line 138
def urlenc(str)
  return Knj::Web.urlenc(str)
end
working?() click to toggle source

Returns true if a HTTP-request is working. Otherwise false.

# File lib/include/class_knjappserver.rb, line 441
def working?
  return true if @httpserv and @httpserv.working_count > 0
  return false
end