Class: Hayabusa

Inherits:
Object
  • Object
show all
Defined in:
lib/hayabusa.rb,
lib/hayabusa_ext/web.rb,
lib/hayabusa_ext/errors.rb,
lib/hayabusa_ext/cleaner.rb,
lib/hayabusa_ext/cmdline.rb,
lib/hayabusa_ext/logging.rb,
lib/hayabusa_ext/mailing.rb,
lib/hayabusa_ext/sessions.rb,
lib/hayabusa_ext/threadding.rb,
lib/hayabusa_ext/translations.rb

Overview

The class that stands for the whole appserver / webserver.

Examples

appsrv = Hayabusa.new(

:locales_root => "/some/path/locales",
:locales_gettext_funcs => true,
:magic_methods => true

) appsrv.start appsrv.join

Defined Under Namespace

Classes: Cgi, Cgi_session, Cgi_tools, Client_session, Custom_io, Database, Erb_handler, Fcgi, Fcgi_server, Http_server, Http_session, Mail, Models, Thread_instance, Threadding_timeout

Constant Summary collapse

@@path =
File.dirname(__FILE__)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Hayabusa

Returns a new instance of Hayabusa.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/hayabusa.rb', line 27

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 => 60,
    :mailing_time => 30
  }.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.
  require "rubygems"
  gems = [
    [:Knj, "knjrbfw"],
    [:Erubis, "erubis"],
    [:Tsafe, "tsafe"],
    [:Tpool, "tpool"]
  ]
  
  gems.each do |gem_i|
    if Kernel.const_defined?(gem_i[0])
      puts "Gem already loaded: '#{gem_i[1]}'." if @debug
      next
    end
    
    fpath = "#{@@path}/../../#{gem_i[1]}/lib/#{gem_i[1]}.rb"
    
    if File.exists?(fpath)
      puts "Loading custom gem-path: '#{fpath}'." if @debug
      require fpath
    else
      puts "Loading gem: '#{gem_i[1]}'." if @debug
      require gem_i[1]
    end
  end
  
  
  #Setup default handlers if none are given.
  if !@config.has_key?(:handlers)
    @erb_handler = Hayabusa::Erb_handler.new
    @config[:handlers] = [
      {
        :file_ext => "rhtml",
        :callback => @erb_handler.method(:erb_handler)
      },{
        :path => "/fckeditor",
        :mount => "/usr/share/fckeditor"
      }
    ]
  end
  
  if @config[:handlers_extra]
    @config[:handlers] += @config[:handlers_extra]
  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]
  if !@config.key?(:debug_log) or @config[:debug_log]
    @debug_log = true
  else
    @debug_log = false
  end
  
  if @config[:debug_print]
    @debug_print = true
  else
    @debug_print = false
  end
  
  if !@config.key?(:debug_print_err) or @config[:debug_print_err]
    @debug_print_err = true
  end
  
  @paused = 0
  @paused_mutex = Mutex.new
  @should_restart = false
  @mod_events = {}
  @served = 0
  @mod_files = {}
  @sessions = {}
  @eruby_cache = {}
  @httpsessions_ids = {}
  
  if @debug_log
    @log_fp = File.open("/tmp/hayabusa_#{@config[:title]}.log", "w")
    @log_fp.sync = true
  end
  
  @path_hayabusa = File.dirname(__FILE__)
  
  
  #If auto-restarting is enabled - start the modified events-module.
  if @config[:autorestart]
    paths = [
      "#{@path_hayabusa}/class_customio.rb"
    ]
    
    self.log_puts "Auto restarting." 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.key?(:filetypes)
  
  
  
  #Load various required files in the hayabusa-framework.
  files = [
    "#{@path_hayabusa}/hayabusa_ext/errors.rb",
    "#{@path_hayabusa}/hayabusa_ext/logging.rb",
    "#{@path_hayabusa}/hayabusa_ext/mailing.rb",
    "#{@path_hayabusa}/hayabusa_ext/sessions.rb",
    "#{@path_hayabusa}/hayabusa_ext/translations.rb",
    "#{@path_hayabusa}/hayabusa_ext/web.rb"
  ]
  
  files.each do |file|
    self.log_puts "Loading: '#{file}'." if @debug
    self.loadfile(file)
  end
  
  
  self.log_puts "Setting up database." 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_args]
    @db = Knj::Db.new(@config[:db_args])
  else
    if @config[:title]
      db_title = @config[:title]
    else
      db_title = Time.now.to_f.to_s.hash
    end
    
    db_path = "#{Knj::Os.tmpdir}/hayabusa_fallback_db_#{db_title}.sqlite3"
    @config[:dbrev] = true
    
    require "sqlite3" if RUBY_ENGINE != "jruby"
    @db = Knj::Db.new(
      :type => "sqlite3",
      :path => db_path,
      :return_keys => "symbols",
      :index_append_table_name => true
    )
  end
  
  
  if !@config.key?(:dbrev) or @config[:dbrev]
    self.log_puts "Updating database." if @debug
    dbrev_args = {"schema" => Hayabusa::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
  
  
  self.log_puts "Spawning objects." if @debug
  @ob = Knj::Objects.new(
    :db => db,
    :class_path => @path_hayabusa,
    :module => Hayabusa::Models,
    :datarow => true,
    :hayabusa => self,
    :require => false
  )
  @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
  
  
  if @config[:locales_root]
    @gettext = Knj::Gettext_threadded.new("dir" => config[:locales_root])
  end
  
  require "#{@path_hayabusa}/kernel_ext/gettext_methods" if @config[:locales_gettext_funcs]
  
  if @config[:magic_methods] or !@config.has_key?(:magic_methods)
    self.log_puts "Loading magic-methods." if @debug
    require "#{@path_hayabusa}/kernel_ext/magic_methods"
  end
  
  if @config[:customio] or !@config.has_key?(:customio)
    self.log_puts "Loading custom-io." if @debug
    
    if $stdout.class.name != "Hayabusa::Custom_io"
      @cio = Hayabusa::Custom_io.new
      $stdout = @cio
    end
  end
  
  
  #Save the PID to the run-file.
  self.log_puts "Setting run-file." if @debug
  tmpdir = "#{Knj::Os.tmpdir}/hayabusa"
  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]
    self.log_puts "Loading events." 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
    )
    @events.add_event(:name => :http_session_destruct)
    
    #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.
  self.log_puts "Init sessions." if @debug
  self.initialize_sessions
  
  if !@config.key?(:threadding) or @config[:threadding]
    self.loadfile("#{@path_hayabusa}/hayabusa_ext/threadding.rb")
    self.loadfile("#{@path_hayabusa}/hayabusa_ext/threadding_timeout.rb")
    self.log_puts "Init threadding." if @debug
    self.initialize_threadding
  end
  
  self.log_puts "Init mailing." if @debug
  self.initialize_mailing
  
  self.log_puts "Init errors." if @debug
  self.initialize_errors
  
  self.log_puts "Init logging." if @debug
  self.initialize_logging
  
  if !@config.key?(:cleaner) or @config[:cleaner]
    self.loadfile("#{@path_hayabusa}/hayabusa_ext/cleaner.rb")
    self.log_puts "Init cleaner." if @debug
    self.initialize_cleaner
  end
  
  if !@config.key?(:cmdline) or @config[:cmdline]
    self.loadfile("#{@path_hayabusa}/hayabusa_ext/cmdline.rb")
    self.log_puts "Init cmdline." if @debug
    self.initialize_cmdline
  end
  
  
  #Clear memory, flush emails, flush sessions and more at exit.
  Kernel.at_exit(&self.method(:stop))
  
  
  self.log_puts "Appserver spawned." if @debug
end

Instance Attribute Details

#cioObject (readonly)

Returns the value of attribute cio.



18
19
20
# File 'lib/hayabusa.rb', line 18

def cio
  @cio
end

#configObject (readonly)

Returns the value of attribute config.



18
19
20
# File 'lib/hayabusa.rb', line 18

def config
  @config
end

#dbObject (readonly)

Returns the value of attribute db.



18
19
20
# File 'lib/hayabusa.rb', line 18

def db
  @db
end

#db_handlerObject (readonly)

Returns the value of attribute db_handler.



18
19
20
# File 'lib/hayabusa.rb', line 18

def db_handler
  @db_handler
end

#debugObject (readonly)

Returns the value of attribute debug.



18
19
20
# File 'lib/hayabusa.rb', line 18

def debug
  @debug
end

#error_emails_pendingObject (readonly)

Returns the value of attribute error_emails_pending.



2
3
4
# File 'lib/hayabusa_ext/errors.rb', line 2

def error_emails_pending
  @error_emails_pending
end

#eruby_cacheObject (readonly)

Returns the value of attribute eruby_cache.



18
19
20
# File 'lib/hayabusa.rb', line 18

def eruby_cache
  @eruby_cache
end

#eventsObject (readonly)

Returns the value of attribute events.



18
19
20
# File 'lib/hayabusa.rb', line 18

def events
  @events
end

#gettextObject (readonly)

Returns the value of attribute gettext.



18
19
20
# File 'lib/hayabusa.rb', line 18

def gettext
  @gettext
end

#httpservObject (readonly)

Returns the value of attribute httpserv.



18
19
20
# File 'lib/hayabusa.rb', line 18

def httpserv
  @httpserv
end

#httpsessions_idsObject (readonly)

Returns the value of attribute httpsessions_ids.



18
19
20
# File 'lib/hayabusa.rb', line 18

def httpsessions_ids
  @httpsessions_ids
end

#logs_access_pendingObject (readonly)

Returns the value of attribute logs_access_pending.



18
19
20
# File 'lib/hayabusa.rb', line 18

def logs_access_pending
  @logs_access_pending
end

#magic_procsObject (readonly)

Returns the value of attribute magic_procs.



18
19
20
# File 'lib/hayabusa.rb', line 18

def magic_procs
  @magic_procs
end

#magic_varsObject (readonly)

Returns the value of attribute magic_vars.



18
19
20
# File 'lib/hayabusa.rb', line 18

def magic_vars
  @magic_vars
end

#mails_waitingObject (readonly)

Returns the value of attribute mails_waiting.



4
5
6
# File 'lib/hayabusa_ext/mailing.rb', line 4

def mails_waiting
  @mails_waiting
end

#mod_eventObject (readonly)

Returns the value of attribute mod_event.



18
19
20
# File 'lib/hayabusa.rb', line 18

def mod_event
  @mod_event
end

#obObject (readonly)

Returns the value of attribute ob.



18
19
20
# File 'lib/hayabusa.rb', line 18

def ob
  @ob
end

#pausedObject (readonly)

Returns the value of attribute paused.



18
19
20
# File 'lib/hayabusa.rb', line 18

def paused
  @paused
end

#servedObject

Returns the value of attribute served.



19
20
21
# File 'lib/hayabusa.rb', line 19

def served
  @served
end

#sessionsObject (readonly)

Returns the value of attribute sessions.



18
19
20
# File 'lib/hayabusa.rb', line 18

def sessions
  @sessions
end

#should_restartObject

Returns the value of attribute should_restart.



18
19
20
# File 'lib/hayabusa.rb', line 18

def should_restart
  @should_restart
end

#should_restart_doneObject

Returns the value of attribute should_restart_done.



19
20
21
# File 'lib/hayabusa.rb', line 19

def should_restart_done
  @should_restart_done
end

#threadpoolObject (readonly)

Returns the value of attribute threadpool.



18
19
20
# File 'lib/hayabusa.rb', line 18

def threadpool
  @threadpool
end

#translationsObject (readonly)

Returns the value of attribute translations.



18
19
20
# File 'lib/hayabusa.rb', line 18

def translations
  @translations
end

#typesObject (readonly)

Returns the value of attribute types.



18
19
20
# File 'lib/hayabusa.rb', line 18

def types
  @types
end

#varsObject (readonly)

Returns the value of attribute vars.



18
19
20
# File 'lib/hayabusa.rb', line 18

def vars
  @vars
end

Class Method Details

.const_missing(name) ⇒ Object

Autoloader for subclasses.



22
23
24
25
# File 'lib/hayabusa.rb', line 22

def self.const_missing(name)
  require "#{File.dirname(__FILE__)}/hayabusa_#{name.to_s.downcase}.rb"
  return Hayabusa.const_get(name)
end

.dataObject



484
485
486
487
# File 'lib/hayabusa.rb', line 484

def self.data
  raise "Could not register current thread." if !Thread.current[:hayabusa]
  return Thread.current[:hayabusa]
end

Instance Method Details

#cleanObject



10
11
12
# File 'lib/hayabusa_ext/cleaner.rb', line 10

def clean
  self.clean_sessions
end

#clean_autorestartObject



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/hayabusa_ext/cleaner.rb', line 14

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
        self.log_puts("Restart when over #{@config[:restart_when_used_memory]}mb") if @debug
        self.log_puts("Used: #{mbs_used}mb") if @debug
        
        if mbs_used.to_i >= @config[:restart_when_used_memory].to_i
          self.log_puts("Memory is over #{@config[:restart_when_used_memory]} - restarting.") if @debug
          @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...
          self.log_puts("Flushing mails.") if @debug
          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
                  self.log_puts("Someone is working - wait two sec and try to restart again!") if @debug
                end
                
                if !working
                  self.log_puts("Found window where no sessions were active - restarting!") if @debug
                  break
                else
                  sleep 0.2
                end
                
                self.log_puts("Trying to find window with no active sessions to restart...") if @debug
              end
            end
          rescue Timeout::Error
            self.log_puts("Could not find a timing window for restarting... Forcing restart!") if @debug
          end
          
          #Flush emails again if any are pending (while we tried to find a window to restart)...
          self.log_puts("Flushing mails.") if @debug
          self.mail_flush
          
          self.log_puts("Stopping appserver.") if @debug
          self.stop
          
          self.log_puts("Figuring out restart-command.") if @debug
          mycmd = @config[:restart_cmd]
          
          if !mycmd or mycmd.to_s.strip.length <= 0
            fpath = File.realpath("#{File.dirname(__FILE__)}/../hayabusa.rb")
            mycmd = Knj::Os.executed_cmd
            
            self.log_puts("Previous cmd: #{mycmd}") if @debug
            mycmd = mycmd.gsub(/\s+hayabusa.rb/, " #{Knj::Strings.unixsafe(fpath)}")
          end
          
          self.log_puts("Restarting knjAppServer with command: #{mycmd}") if @debug
          @should_restart_done = true
          print exec(mycmd)
          exit
        rescue => e
          self.log_puts(e.inspect)
          self.log_puts(e.backtrace)
        end
      end
    end
  rescue => e
    self.handle_error(e)
  end
end

#clean_sessionsObject

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



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/hayabusa_ext/cleaner.rb', line 104

def clean_sessions
  self.log_puts("Cleaning sessions on appserver.") if @debug
  
  #Clean up various inactive sessions.
  session_not_ids = []
  time_check = Time.now.to_i - 300
  @sessions.delete_if do |session_hash, session_data|
    session_data[:dbobj].flush
    
    if session_data[:dbobj].date_lastused.to_i > time_check
      session_not_ids << session_data[:dbobj].id
      false
    else
      true
    end
  end
  
  self.log_puts("Delete sessions...") if @debug
  @ob.list(:Session, {"id_not" => session_not_ids, "date_lastused_below" => (Time.now - 5356800)}) do |session|
    idhash = session[:idhash]
    self.log_puts("Deleting session: '#{session.id}'.") if @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) ⇒ Object

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



48
49
50
51
# File 'lib/hayabusa_ext/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) ⇒ Object



37
38
39
40
# File 'lib/hayabusa_ext/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) ⇒ Object



42
43
44
45
# File 'lib/hayabusa_ext/cmdline.rb', line 42

def cmdline_on_stop_cmd(data)
  print "Stopping appserver.\n"
  self.stop
end

Define a cookies in the clients browser.

Examples

_hb.cookie(:name => "MyCookie", :value => "Trala")


16
17
18
19
20
21
22
# File 'lib/hayabusa_ext/web.rb', line 16

def cookie(cookie)
  raise "No HTTP-session attached to this thread." if !_httpsession
  raise "HTTP-session not active." if !_httpsession.resp
  raise "Not a hash: '#{cookie.class.name}', '#{cookie}'." unless cookie.is_a?(Hash)
  _httpsession.resp.cookie(cookie)
  return nil
end

#current_urlObject

Returns the currelt URL.



177
178
179
# File 'lib/hayabusa_ext/web.rb', line 177

def current_url
  return Knj::Web.url
end

#debugs(str) ⇒ Object

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



129
130
131
132
133
134
# File 'lib/hayabusa_ext/errors.rb', line 129

def debugs(str)
  #Get backtrace.
  backtrace_str = caller[0]
  backtrace_match = backtrace_str.match(/^(.+):(\d+):in /)
  self.log_puts("#{File.basename(backtrace_match[1])}:#{backtrace_match[2]}: #{str}")
end

#define_magic_proc(method_name, &block) ⇒ Object



525
526
527
528
529
530
531
532
533
534
535
# File 'lib/hayabusa.rb', line 525

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[:hayabusa][:hb].magic_procs[method_name].call(:hb => self) if Thread.current[:hayabusa] and Thread.current[:hayabusa][:hb]
      raise "Could not figure out the object: '#{method_name}'."
    end
  end
end

#define_magic_var(method_name, var) ⇒ Object

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



514
515
516
517
518
519
520
521
522
523
# File 'lib/hayabusa.rb', line 514

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[:hayabusa][:hb].magic_vars[method_name] if Thread.current[:hayabusa] and Thread.current[:hayabusa][:hb]
      raise "Could not figure out the object: '#{method_name}'."
    end
  end
end

#dprint(obj) ⇒ Object

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



124
125
126
# File 'lib/hayabusa_ext/errors.rb', line 124

def dprint(obj)
  self.log_puts(Php4r.print_r(obj, true))
end

#flush_access_logObject

Writes all queued access-logs to the database.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/hayabusa_ext/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(args = {}) ⇒ Object

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



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/hayabusa_ext/errors.rb', line 20

def flush_error_emails(args = {})
  @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 !args[:ignore_time] and 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) ⇒ Object

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.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/hayabusa_ext/web.rb', line 119

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 = {}) ⇒ Object

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



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/hayabusa_ext/errors.rb', line 68

def handle_error(e, args = {})
  @error_emails_pending_mutex.synchronize do
    if !Thread.current[:hayabusa] or !Thread.current[:hayabusa][:httpsession]
      self.log_puts("#{Knj::Errors.error_str(e)}\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) ⇒ Object

Sends a header to the clients browser.

Examples

_hb.header("Content-Type", "text/javascript")


27
28
29
30
31
32
# File 'lib/hayabusa_ext/web.rb', line 27

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) ⇒ Object

Sends a raw header-line to the clients browser.



35
36
37
38
39
40
# File 'lib/hayabusa_ext/web.rb', line 35

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) ⇒ Object

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.

_hb.headers_send_size = (1024 * 200)


54
55
56
57
58
# File 'lib/hayabusa_ext/web.rb', line 54

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?Boolean

Returns true if the headers are already sent.

Examples

_hb.headers_sent? #=> true

Returns:

  • (Boolean)


45
46
47
48
# File 'lib/hayabusa_ext/web.rb', line 45

def headers_sent?
  return true if _httpsession.resp.headers_sent
  return false
end

#import(filepath) ⇒ Object

Imports a .rhtml-file and executes it.

Examples

_hb.import("/some/path/page.rhtml")


5
6
7
8
9
10
11
# File 'lib/hayabusa_ext/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_cleanerObject



2
3
4
5
6
7
8
# File 'lib/hayabusa_ext/cleaner.rb', line 2

def initialize_cleaner
  #This should not be runned via _hb.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_cmdlineObject



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/hayabusa_ext/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_errorsObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
# File 'lib/hayabusa_ext/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_loggingObject



2
3
4
5
6
7
8
9
# File 'lib/hayabusa_ext/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_mailingObject



6
7
8
9
10
11
12
13
# File 'lib/hayabusa_ext/mailing.rb', line 6

def initialize_mailing
  require "knj/autoload/ping"
  
  @mails_waiting = []
  @mails_mutex = ::Monitor.new
  @mails_queue_mutex = ::Monitor.new
  @mails_timeout = self.timeout(:time => @config[:mailing_time], &self.method(:mail_flush))
end

#initialize_sessionsObject



4
5
6
# File 'lib/hayabusa_ext/sessions.rb', line 4

def initialize_sessions
  @sessions = Tsafe::MonHash.new
end

#initialize_threaddingObject



2
3
4
5
6
7
8
9
10
11
# File 'lib/hayabusa_ext/threadding.rb', line 2

def initialize_threadding
  @config[:threadding] = {} if !@config.has_key?(:threadding)
  @config[:threadding][:max_running] = 16 if !@config[:threadding].has_key?(:max_running)
  
  threadpool_args = {:threads => @config[:threadding][:max_running]}
  threadpool_args[:priority] = @config[:threadding][:priority] if @config[:threadding].key?(:priority)
  
  @threadpool = Tpool.new(threadpool_args)
  @threadpool.on_error(&self.method(:threadpool_on_error))
end

#inputs(*args) ⇒ Object

Draw a input in a table.



97
98
99
# File 'lib/hayabusa_ext/web.rb', line 97

def inputs(*args)
  return Knj::Web.inputs(args)
end

#ip(args = nil) ⇒ Object

Returns the real IP of the client. Even if it has been proxied through multiple proxies.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/hayabusa_ext/web.rb', line 158

def ip(args = nil)
  if args and args[:meta]
    meta = args[:meta]
  elsif httpsession = Thread.current[:hayabusa][:httpsession]
    meta = httpsession.meta
  else
    raise "Could not figure out meta-data."
  end
  
  if !meta["HTTP_X_FORWARDED_FOR"].to_s.strip.empty? and ips = meta["HTTP_X_FORWARDED_FOR"].split(/\s*,\s*/)
    return ips.first.to_s.strip
  elsif ip = meta["REMOTE_ADDR"].to_s.strip and !ip.empty?
    return ip
  else
    raise "Could not figure out IP from meta-data."
  end
end

#joinObject

Sleeps until the server is stopped.



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/hayabusa.rb', line 490

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.log_puts "Trying to stop because of interrupt - please wait while various data is beging flushed." if @debug
    self.stop
  end
  
  if @should_restart
    loop do
      if @should_restart_done
        self.log_puts "Ending join because the restart is done." if @debug
        break
      end
      
      sleep 1
    end
  end
end

#loadfile(fpath) ⇒ Object

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.



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/hayabusa.rb', line 381

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 = {}) ⇒ Object

Writes a custom log to the database.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/hayabusa_ext/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) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/hayabusa_ext/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) ⇒ Object

Handles the hashes that should be logged.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/hayabusa_ext/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) ⇒ Object

Converts fileuploads into strings so logging wont be crazy big.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/hayabusa_ext/logging.rb', line 114

def log_hash_safe(hash)
  hash_obj = {}
  hash.each do |key, val|
    if val.is_a?(Hayabusa::Http_session::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

#log_puts(str) ⇒ Object

Outputs to stderr and logs it.



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/hayabusa.rb', line 353

def log_puts(str)
  if @debug_log
    @log_fp.sync = true
    @log_fp.puts str
  end
  
  if @debug_print
    STDOUT.sync = true
    STDOUT.puts str
  end
  
  if @debug_print_err
    STDERR.sync = true
    STDERR.puts str
  end
end

#logs_delete(obj) ⇒ Object

Deletes all logs for an object.



291
292
293
294
295
296
297
298
299
300
301
# File 'lib/hayabusa_ext/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) ⇒ Object

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)



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/hayabusa_ext/logging.rb', line 369

def logs_delete_dead(args)
  raise "No :ob-argument given." if !args[:ob]
  
  @db.q_buffer do |db_buffer|
    self.log_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
      
      self.log_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
        self.log_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
    
    self.log_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|
      self.log_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 = {}) ⇒ Object

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



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/hayabusa_ext/logging.rb', line 304

def logs_table(obj, args = {})
  if args[:out]
    html = args[:out]
  else
    html = $stdout
  end
  
  html << "<table class=\"list hayabusa_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>"
  
  count = 0
  @ob.list(:Log_link, {"object_class" => obj.class.name, "object_id" => obj.id, "limit" => 500, "orderby" => [["id", "desc"]]}) do |link|
    count += 1
    log = link.log
    
    msg_lines = log.text.split("\n")
    first_line = msg_lines[0].to_s
    
    classes = ["hayabusa_log", "hayabusa_log_#{log.id}"]
    classes << "hayabusa_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 count <= 0
    html << "<tr>"
    html << "<td colspan=\"2\" class=\"error\">No logs were found for that object.</td>"
    html << "</tr>"
  end
  
  html << "</tbody>"
  html << "</table>"
  
  return nil
end

#mail(mail_args) ⇒ Object

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



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/hayabusa_ext/mailing.rb', line 16

def mail(mail_args)
  raise "'smtp_args' has not been given for the Hayabusa." 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 = Hayabusa::Mail.new({:hb => self, :errors => {}, :status => :waiting}.merge(mail_args))
    self.log_puts("Added mail '#{mailobj.__id__}' to the mail-send-queue.") if debug
    @mails_waiting << mailobj
    
    #Try to send right away and raise error instantly if something happens if told to do so.
    if mail_args[:now] or @config[:mailing_instant]
      self.mail_flush
      raise mailobj.args[:error] if mailobj.args[:error]
    end
    
    return mailobj
  end
end

#mail_flushObject

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



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/hayabusa_ext/mailing.rb', line 45

def mail_flush
  @mails_mutex.synchronize do
    self.log_puts("Flushing mails.") if @debug
    
    if @mails_waiting.empty?
      self.log_puts("No mails to flush - skipping.") if @debug
      return false
    end
    
    self.log_puts("Trying to ping Google to figure out if we are online...") if @debug
    status = Ping.pingecho("google.dk", 10, 80)
    if !status
      self.log_puts("We are not online - skipping mail flush.")
      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
    
    #Use subprocessing to avoid the mail-framework (activesupport and so on, also possible memory leaks in those large frameworks).
    self.log_puts("Starting subprocess for mailing.") if @debug
    require "ruby_process"
    ::Ruby_process::Cproxy.run do |data|
      subproc = data[:subproc]
      subproc.static(:Object, :require, "rubygems")
      subproc.static(:Object, :require, "mail")
      subproc.static(:Object, :require, "#{Knj.knjrbfw_path}/../knjrbfw.rb")
      
      self.log_puts("Flushing emails.") if @debug
      @mails_waiting.each do |mail|
        begin
          self.log_puts("Sending email: #{mail.__id__}") if @debug
          if mail.send(:proc => subproc)
            self.log_puts("Email sent: #{mail.__id__}") 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
    end
    
    return nil
  end
end

#no_date(event, classname) ⇒ Object



370
371
372
# File 'lib/hayabusa.rb', line 370

def no_date(event, classname)
  return "[no date]"
end

#num(*args) ⇒ Object

Returns a number localized as a string.



114
115
116
# File 'lib/hayabusa_ext/web.rb', line 114

def num(*args)
  return Knj::Locales.number_out(*args)
end

#on_error_go_back(&block) ⇒ Object

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



115
116
117
118
119
120
121
# File 'lib/hayabusa_ext/errors.rb', line 115

def on_error_go_back(&block)
  begin
    block.call
  rescue => e
    Knj::Web.alert(e.message).back
  end
end

#on_event_filemod(event, path) ⇒ Object



374
375
376
377
378
# File 'lib/hayabusa.rb', line 374

def on_event_filemod(event, path)
  self.log_puts "File changed - restart server: #{path}"
  @should_restart = true
  @mod_event.destroy if @mod_event
end

#pauseObject

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



446
447
448
# File 'lib/hayabusa.rb', line 446

def pause
  @paused += 1
end

#paused?Boolean

Returns true if paued - otherwise false.

Returns:

  • (Boolean)


456
457
458
459
# File 'lib/hayabusa.rb', line 456

def paused?
  return true if @paused > 0
  return false
end

#paused_execObject

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



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# File 'lib/hayabusa.rb', line 462

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

#portObject

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



152
153
154
155
# File 'lib/hayabusa_ext/web.rb', line 152

def port
  raise "Http-server not spawned yet. Call Hayabusa#start to spawn it." if !@httpserv
  return @httpserv.server.addr[1]
end

#serve_file(filepath) ⇒ Object

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

Examples

_hb.header("Content-Type", "text/javascript")
_hb.serve_file("somefile.js")


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/hayabusa_ext/web.rb', line 64

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_dont_rememberObject

Will make the session run out as soon as the user closes his browser.



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/hayabusa_ext/sessions.rb', line 61

def session_dont_remember
  httpsession = Thread.current[:hayabusa][:httpsession]
  raise "Could not figure out HTTP-session." if !httpsession
  session = httpsession.session
  raise "Could not get session-variable from HTTP-session." if !session
  session[:remember] = 0
  
  self.cookie(
    "name" => "HayabusaSession",
    "value" => _httpsession.session_id,
    "path" => "/"
  )
end

#session_fromid(ip, idhash, meta) ⇒ Object

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

Raises:

  • (ArgumentError)


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/hayabusa_ext/sessions.rb', line 9

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,
        :date_lastused => Time.now
      })
    else
      session[:date_lastused] = Time.now
    end
    
    hash = {}
    @sessions[idhash] = {
      :dbobj => session,
      :hash => hash
    }
  else
    session = @sessions[idhash][:dbobj]
    hash = @sessions[idhash][:hash]
  end
  
  raise ArgumentError, "Invalid IP." if ip != "bot" and !session.remember? and ip.to_s != session[:ip].to_s
  return [session, hash]
end

#session_generate_id(meta) ⇒ Object

Generates a new session-ID by the meta data.



40
41
42
# File 'lib/hayabusa_ext/sessions.rb', line 40

def session_generate_id(meta)
  return Digest::MD5.hexdigest("#{Time.now.to_f}_#{meta["HTTP_HOST"]}_#{self.ip(:meta => meta)}_#{meta["HTTP_USER_AGENT"]}")
end

#session_rememberObject

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



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/hayabusa_ext/sessions.rb', line 45

def session_remember
  httpsession = Thread.current[:hayabusa][:httpsession]
  raise "Could not figure out HTTP-session." if !httpsession
  session = httpsession.session
  raise "Could not get session-variable from HTTP-session." if !session
  session[:remember] = 1
  
  self.cookie(
    "name" => "HayabusaSession",
    "value" => _httpsession.session_id,
    "path" => "/",
    "expires" => Time.now + 32140800 #add around 12 months
  )
end

#sessions_flushObject

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



76
77
78
79
80
81
82
83
# File 'lib/hayabusa_ext/sessions.rb', line 76

def sessions_flush
  if @sessions
    @sessions.each do |session_hash, session_data|
      self.log_puts("Flushing session: #{session_data[:dbobj].id}") if @debug
      session_data[:dbobj].flush
    end
  end
end

#sessions_resetObject

Writes all session-data and resets the hash.



86
87
88
89
# File 'lib/hayabusa_ext/sessions.rb', line 86

def sessions_reset
  self.sessions_flush
  @sessions = {}
end

#startObject

Starts the HTTP-server and threadpool.



405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/hayabusa.rb', line 405

def start
  #Start the appserver.
  self.log_puts "Spawning appserver." if @debug
  @httpserv = Hayabusa::Http_server.new(self)
  @httpserv.start
  
  
  self.log_puts "Starting appserver." if @debug
  Thread.current[:hayabusa] = {:hb => self} if !Thread.current[:hayabusa]
  
  if @config[:autoload]
    self.log_puts "Autoloading #{@config[:autoload]}" if @debug
    require @config[:autoload]
  end
  
  self.log_puts "Appserver startet." if @debug
end

#start_cgi_requestObject

Start a new CGI-request.



400
401
402
# File 'lib/hayabusa.rb', line 400

def start_cgi_request
  @cgi_http_session = Hayabusa::Cgi_session.new(:hb => self)
end

#stopObject

Stops the entire app and releases join.



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/hayabusa.rb', line 424

def stop
  return nil if @stop_called
  @stop_called = true
  
  self.log_puts "Stopping appserver." if @debug
  @httpserv.stop if @httpserv and @httpserv.respond_to?(:stop)
  
  self.log_puts "Stopping threadpool." if @debug
  @threadpool.stop if @threadpool
  
  #This should be done first to be sure it finishes (else we have a serious bug).
  self.log_puts "Flush out loaded sessions." if @debug
  
  #Flush sessions and mails (only if the modules are loaded).
  self.flush_error_emails(:ignore_time => true) if self.respond_to?(:flush_error_emails)
  self.sessions_flush if self.respond_to?(:sessions_flush)
  self.mail_flush if self.respond_to?(:mail_flush)
  
  self.log_puts "Stopping done..." if @debug
end

#thread(args = {}) ⇒ Object

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



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/hayabusa_ext/threadding.rb', line 26

def thread(args = {})
  raise "No block given." if !block_given?
  args[:args] = [] if !args[:args]
  
  thread_obj = Hayabusa::Thread_instance.new(
    :running => false,
    :error => false,
    :done => false,
    :args => args
  )
  
  @threadpool.run_async do
    begin
      @ob.db.get_and_register_thread if @ob.db.opts[:threadsafe]
      @db_handler.get_and_register_thread if @db_handler.opts[:threadsafe]
      
      Thread.current[:hayabusa] = {
        :hb => self,
        :db => @db_handler
      }
      
      thread_obj.args[:running] = true
      yield(*args[:args])
    rescue => e
      thread_obj.args[:error] = true
      thread_obj.args[:error_obj] = e
      
      self.handle_error(e)
    ensure
      thread_obj.args[:running] = false
      thread_obj.args[:done] = true
      @ob.db.free_thread if @ob.db.opts[:threadsafe]
      @db_handler.free_thread if @db_handler.opts[:threadsafe]
    end
  end
  
  return thread_obj
end

#thread_block(&block) ⇒ Object

If a custom thread is spawned, you can run whatever code within this block, and it will have its own database-connection and so on like normal Hayabusa-threads.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/hayabusa_ext/threadding.rb', line 66

def thread_block(&block)
  @ob.db.get_and_register_thread if @ob.db.opts[:threadsafe]
  @db_handler.get_and_register_thread if @db_handler.opts[:threadsafe]
  
  thread = Thread.current
  thread[:hayabusa] = {} if !thread[:hayabusa]
  thread[:hayabusa][:hb] = self
  
  begin
    return block.call
  ensure
    @ob.db.free_thread if @ob.db.opts[:threadsafe]
    @db_handler.free_thread if @db_handler.opts[:threadsafe]
  end
end

#thread_init(thread = Thread.current) ⇒ Object

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



19
20
21
22
23
# File 'lib/hayabusa_ext/threadding.rb', line 19

def thread_init(thread = Thread.current)
  thread[:hayabusa] = {} if !thread[:hayabusa]
  thread[:hayabusa][:hb] = self
  return nil
end

#threadded_content(&block) ⇒ Object

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



92
93
94
95
# File 'lib/hayabusa_ext/threadding.rb', line 92

def threadded_content(&block)
  _httpsession.threadded_content(block)
  return nil
end

#threadpool_on_error(args) ⇒ Object

Callback for when an error occurs in the threadpool.



14
15
16
# File 'lib/hayabusa_ext/threadding.rb', line 14

def threadpool_on_error(args)
  self.handle_error(args[:error])
end

#timeout(args = {}, &block) ⇒ Object

Runs a proc every number of seconds.



83
84
85
86
87
88
89
# File 'lib/hayabusa_ext/threadding.rb', line 83

def timeout(args = {}, &block)
  return Hayabusa::Threadding_timeout.new({
    :hb => self,
    :block => block,
    :args => []
  }.merge(args)).start
end

#trans(obj, key, args = {}) ⇒ Object

Translates a given key for a given object.

Examples

print _hb.trans(obj, :title) #=> “Trala”



5
6
7
8
9
10
# File 'lib/hayabusa_ext/translations.rb', line 5

def trans(obj, key, args = {})
  args[:locale] = self.trans_locale if !args[:locale]
  trans_val = self.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) ⇒ Object

Deletes all translations for the given object.

Examples

_hb.trans_del(obj)



41
42
43
44
# File 'lib/hayabusa_ext/translations.rb', line 41

def trans_del(obj)
  raise "Translations-object now spawned." if !self.translations
  self.translations.delete(obj)
end

#trans_locale(args = {}) ⇒ Object

Returns the locale for the current thread.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/hayabusa_ext/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 = {}) ⇒ Object

Sets new translations for the given object.

Examples

_hb.trans_set(obj, => “Trala”)



32
33
34
35
36
# File 'lib/hayabusa_ext/translations.rb', line 32

def trans_set(obj, values, args = {})
  raise "Translations-object now spawned." if !self.translations
  args[:locale] = self.trans_locale if !args[:locale]
  self.translations.set(obj, values, args)
end

#unpauseObject

Unpause - start handeling HTTP-requests again.



451
452
453
# File 'lib/hayabusa.rb', line 451

def unpause
  @paused -= 1
end

#urldec(str) ⇒ Object

Urldecodes a string.



109
110
111
# File 'lib/hayabusa_ext/web.rb', line 109

def urldec(str)
  return Knj::Web.urldec(str)
end

#urlenc(str) ⇒ Object

Urlencodes a string.

Examples

Knj::Web.redirect("mypage.rhtml?arg=#{_hb.urlenc(value_variable)}")


104
105
106
# File 'lib/hayabusa_ext/web.rb', line 104

def urlenc(str)
  return Knj::Web.urlenc(str)
end

#working?Boolean

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

Returns:

  • (Boolean)


479
480
481
482
# File 'lib/hayabusa.rb', line 479

def working?
  return true if @httpserv and @httpserv.working_count > 0
  return false
end