hayabusa 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.13
1
+ 0.0.14
@@ -17,10 +17,9 @@ begin
17
17
  require "fileutils"
18
18
 
19
19
  #Try to load development-version to enable debugging without doing constant gem-installations.
20
- path = "/home/kaspernj/Ruby/knjrbfw/lib/knjrbfw.rb"
21
- if File.exists?(path)
22
- require path
23
- else
20
+ begin
21
+ require "#{File.realpath(File.dirname(__FILE__))}/../../knjrbfw/lib/knjrbfw.rb"
22
+ rescue LoadError
24
23
  require "knjrbfw"
25
24
  end
26
25
 
data/bin/hayabusa_fcgi.rb CHANGED
@@ -17,10 +17,9 @@ begin
17
17
  require "fileutils"
18
18
 
19
19
  #Try to load development-version to enable debugging without doing constant gem-installations.
20
- path = "/home/kaspernj/Ruby/knjrbfw/lib/knjrbfw.rb"
21
- if File.exists?(path)
22
- require path
23
- else
20
+ begin
21
+ require "#{File.realpath(File.dirname(__FILE__))}/../../knjrbfw/lib/knjrbfw.rb"
22
+ rescue LoadError
24
23
  require "knjrbfw"
25
24
  end
26
25
 
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #This script starts the Hayabusa-server that the FCGI-instances will connect- and proxy requests to.
4
+
5
+
6
+ #Make stuff instantly printed to speed up.
7
+ $stdout.sync = true
8
+
9
+ require "rubygems"
10
+ require "#{File.realpath(File.dirname(__FILE__))}/../lib/hayabusa.rb"
11
+ require "base64"
12
+
13
+
14
+ #Try to load parent path knjrbfw to allowed developing without updating Gem-installation constantly.
15
+ begin
16
+ require "#{File.realpath(File.dirname(__FILE__))}/../../knjrbfw/lib/knjrbfw.rb"
17
+ rescue LoadError
18
+ require "knjrbfw"
19
+ end
20
+
21
+
22
+ #Parse given arguments.
23
+ opts = {}
24
+
25
+ ARGV.each do |val|
26
+ if match = val.match(/^--(conf_path|fcgi_data_path|title)=(.+)$/)
27
+ opts[match[1].to_sym] = match[2]
28
+ else
29
+ raise "Unknown argument: '#{val}'."
30
+ end
31
+ end
32
+
33
+ raise "'conf-path' from arguments could not be found: '#{opts[:conf_path]}'." if !opts[:conf_path] or !File.exists?(opts[:conf_path])
34
+ require opts[:conf_path]
35
+
36
+
37
+ #Gemerate Hayabusa-config-hash.
38
+ hayabusa_conf = Hayabusa::FCGI_CONF[:hayabusa]
39
+ hayabusa_conf.merge!(
40
+ :debug => false,
41
+ :debug_log => true,
42
+ :debug_print => false,
43
+ :debug_print_err => false,
44
+ :cmdline => false,
45
+ :mailing_timeout => 1, #Since FCGI might terminate at any time, try to send out mails almost instantly in the background.
46
+ :port => 0 #Ruby picks random port and we get the actual port after starting the appserver.
47
+ )
48
+ fcgi_server = Hayabusa::Fcgi_server.new(:hayabusa_conf => hayabusa_conf)
49
+
50
+
51
+ #Give information about this process to the FCGI-process that spawned us.
52
+ puts Base64.strict_encode64(Marshal.dump(
53
+ :pid => Process.pid,
54
+ :port => fcgi_server.hayabusa.port
55
+ ))
56
+
57
+
58
+ #Join the server and unlink the config-file when it terminates.
59
+ begin
60
+ fcgi_server.hayabusa.join
61
+ ensure
62
+ File.unlink(opts[:fcgi_data_path]) if opts[:fcgi_data_path] and File.exists?(opts[:fcgi_data_path])
63
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #This script is used to restart the FCGI-spec-test servers.
4
+
5
+ require "rubygems"
6
+ require "php4r"
7
+
8
+ #Close down the running instance of FCGI-server-process.
9
+ fpath = "/tmp/hayabusa_fcgi_Fcgi_test_fcgi.conf"
10
+
11
+ if File.exists?(fpath) and fcont = File.read(fpath) and !fcont.empty?
12
+ cont = Marshal.load(fcont)
13
+ Process.kill("HUP", cont[:pid])
14
+ end
15
+
16
+ #Restart apache to restart any FCGI instances.
17
+ Php4r.passthru("apache2ctl restart")
data/hayabusa.gemspec CHANGED
@@ -5,14 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "hayabusa"
8
- s.version = "0.0.13"
8
+ s.version = "0.0.14"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kasper Johansen"]
12
- s.date = "2012-09-20"
12
+ s.date = "2012-09-30"
13
13
  s.description = "A threadded web/app-server that focuses on threadding, shared ressources, speed and more."
14
14
  s.email = "k@spernj.org"
15
- s.executables = ["check_running.rb", "hayabusa_benchmark.rb", "hayabusa_cgi.rb", "hayabusa_fcgi.fcgi", "hayabusa_fcgi.rb", "knjappserver_start.rb"]
15
+ s.executables = ["check_running.rb", "hayabusa_benchmark.rb", "hayabusa_cgi.rb", "hayabusa_fcgi.fcgi", "hayabusa_fcgi.rb", "hayabusa_fcgi_server.rb", "hayabusa_spec_restart.rb", "knjappserver_start.rb"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE.txt",
18
18
  "README.rdoc"
@@ -31,6 +31,8 @@ Gem::Specification.new do |s|
31
31
  "bin/hayabusa_cgi.rb",
32
32
  "bin/hayabusa_fcgi.fcgi",
33
33
  "bin/hayabusa_fcgi.rb",
34
+ "bin/hayabusa_fcgi_server.rb",
35
+ "bin/hayabusa_spec_restart.rb",
34
36
  "bin/knjappserver_start.rb",
35
37
  "conf/apache2_cgi_rhtml_conf.conf",
36
38
  "conf/apache2_fcgi_rhtml_conf.conf",
@@ -54,6 +56,7 @@ Gem::Specification.new do |s|
54
56
  "lib/hayabusa_ext/translations.rb",
55
57
  "lib/hayabusa_ext/web.rb",
56
58
  "lib/hayabusa_fcgi.rb",
59
+ "lib/hayabusa_fcgi_server.rb",
57
60
  "lib/hayabusa_http_server.rb",
58
61
  "lib/hayabusa_http_session.rb",
59
62
  "lib/hayabusa_http_session_contentgroup.rb",
data/lib/hayabusa.rb CHANGED
@@ -46,8 +46,8 @@ class Hayabusa
46
46
  #Require gems.
47
47
  require "rubygems"
48
48
  gems = [
49
- [:Erubis, "erubis"],
50
49
  [:Knj, "knjrbfw"],
50
+ [:Erubis, "erubis"],
51
51
  [:Tsafe, "tsafe"],
52
52
  [:Tpool, "tpool"]
53
53
  ]
@@ -102,22 +102,20 @@ class Hayabusa
102
102
 
103
103
 
104
104
  @debug = @config[:debug]
105
- if @debug
106
- if !@config.key?(:debug_log) or @config[:debug_log]
107
- @debug_log = true
108
- else
109
- @debug_log = false
110
- end
111
-
112
- if @config[:debug_print]
113
- @debug_print = true
114
- else
115
- @debug_print = false
116
- end
117
-
118
- if !@config.key?(:debug_print_err) or @config[:debug_print_err]
119
- @debug_print_err = true
120
- end
105
+ if !@config.key?(:debug_log) or @config[:debug_log]
106
+ @debug_log = true
107
+ else
108
+ @debug_log = false
109
+ end
110
+
111
+ if @config[:debug_print]
112
+ @debug_print = true
113
+ else
114
+ @debug_print = false
115
+ end
116
+
117
+ if !@config.key?(:debug_print_err) or @config[:debug_print_err]
118
+ @debug_print_err = true
121
119
  end
122
120
 
123
121
  @paused = 0
@@ -299,11 +297,10 @@ class Hayabusa
299
297
  :name => :request_begin,
300
298
  :connections_max => 1
301
299
  )
300
+ @events.add_event(:name => :http_session_destruct)
302
301
 
303
302
  #This event is used if the user himself wants stuff to be cleaned up when the appserver is cleaning up stuff.
304
- @events.add_event(
305
- :name => :on_clean
306
- )
303
+ @events.add_event(:name => :on_clean)
307
304
  end
308
305
 
309
306
  #Set up the 'vars'-variable that can be used to set custom global variables for web-requests.
@@ -345,7 +342,7 @@ class Hayabusa
345
342
  end
346
343
 
347
344
 
348
- #Clear memory at exit.
345
+ #Clear memory, flush emails, flush sessions and more at exit.
349
346
  Kernel.at_exit(&self.method(:stop))
350
347
 
351
348
 
@@ -497,7 +494,7 @@ class Hayabusa
497
494
  @httpserv.thread_accept.join
498
495
  @httpserv.thread_restart.join if @httpserv and @httpserv.thread_restart
499
496
  rescue Interrupt => e
500
- STDOUT.puts "Trying to stop because of interrupt - please wait while various data is beging flushed."
497
+ self.log_puts("Trying to stop because of interrupt - please wait while various data is beging flushed.")
501
498
  self.stop
502
499
  end
503
500
 
@@ -43,11 +43,17 @@ class Hayabusa::Cgi_tools
43
43
 
44
44
  headers = {"Hayabusa_mode" => "proxy"}
45
45
  cgi.env_table.each do |key, val|
46
- if key[0, 5] == "HTTP_" and key != "HTTP_HAYABUSA_CGI_CONFIG"
46
+ keyl = key.to_s.downcase
47
+
48
+ if key[0, 5] == "HTTP_"
47
49
  key = key[5, key.length].gsub("_", " ")
48
50
  key = key.to_s.split(" ").select{|w| w.capitalize! or w }.join(" ")
49
51
  key = key.gsub(" ", "-")
50
- headers[key] = val
52
+ keyl = key.downcase
53
+
54
+ if keyl != "connection" and keyl != "accept-encoding" and keyl != "hayabusa-fcgi-config" and key != "hayabusa-cgi-config"
55
+ headers[key] = val
56
+ end
51
57
  end
52
58
  end
53
59
 
@@ -75,6 +75,7 @@ class Hayabusa::Client_session
75
75
 
76
76
  def execute_page
77
77
  begin
78
+ @time_start = Time.now.to_f if @debug
78
79
  @hb.events.call(:request_begin, :httpsession => self) if @hb.events
79
80
 
80
81
  Timeout.timeout(@hb.config[:timeout]) do
@@ -140,7 +141,8 @@ class Hayabusa::Client_session
140
141
  def execute_done
141
142
  @cgroup.mark_done
142
143
  @cgroup.write_output
143
- @hb.log_puts "#{__id__} - Served '#{@meta["REQUEST_URI"]}' in #{Time.now.to_f - time_start} secs (#{@resp.status})." if @debug
144
+ @hb.log_puts "#{__id__} - Served '#{@meta["REQUEST_URI"]}' in #{Time.now.to_f - @time_start} secs (#{@resp.status})." if @debug
145
+ @time_start = nil
144
146
  @cgroup.join
145
147
 
146
148
  @hb.events.call(:request_done, {
@@ -25,11 +25,11 @@ class Hayabusa
25
25
 
26
26
  if @config.has_key?(:restart_when_used_memory) and !@should_restart
27
27
  mbs_used = (Php4r.memory_get_usage / 1024) / 1024
28
- STDOUT.print "Restart when over #{@config[:restart_when_used_memory]}mb\n" if @config[:debug]
29
- STDOUT.print "Used: #{mbs_used}mb\n" if @config[:debug]
28
+ self.log_puts("Restart when over #{@config[:restart_when_used_memory]}mb") if @debug
29
+ self.log_puts("Used: #{mbs_used}mb") if @debug
30
30
 
31
31
  if mbs_used.to_i >= @config[:restart_when_used_memory].to_i
32
- STDOUT.print "Memory is over #{@config[:restart_when_used_memory]} - restarting.\n"
32
+ self.log_puts("Memory is over #{@config[:restart_when_used_memory]} - restarting.") if @debug
33
33
  @should_restart = true
34
34
  end
35
35
  end
@@ -39,7 +39,7 @@ class Hayabusa
39
39
  @should_restart_runnning = true
40
40
 
41
41
  #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...
42
- STDOUT.print "Flushing mails.\n"
42
+ self.log_puts("Flushing mails.") if @debug
43
43
  self.mail_flush
44
44
 
45
45
  #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.
@@ -51,48 +51,48 @@ class Hayabusa
51
51
 
52
52
  if working_count and working_count > 0
53
53
  working = true
54
- STDOUT.print "Someone is working - wait two sec and try to restart again!\n"
54
+ self.log_puts("Someone is working - wait two sec and try to restart again!") if @debug
55
55
  end
56
56
 
57
57
  if !working
58
- STDOUT.print "Found window where no sessions were active - restarting!\n"
58
+ self.log_puts("Found window where no sessions were active - restarting!") if @debug
59
59
  break
60
60
  else
61
61
  sleep 0.2
62
62
  end
63
63
 
64
- STDOUT.print "Trying to find window with no active sessions to restart...\n"
64
+ self.log_puts("Trying to find window with no active sessions to restart...") if @debug
65
65
  end
66
66
  end
67
67
  rescue Timeout::Error
68
- STDOUT.print "Could not find a timing window for restarting... Forcing restart!\n"
68
+ self.log_puts("Could not find a timing window for restarting... Forcing restart!") if @debug
69
69
  end
70
70
 
71
71
  #Flush emails again if any are pending (while we tried to find a window to restart)...
72
- STDOUT.print "Flushing mails.\n"
72
+ self.log_puts("Flushing mails.") if @debug
73
73
  self.mail_flush
74
74
 
75
- STDOUT.print "Stopping appserver.\n"
75
+ self.log_puts("Stopping appserver.") if @debug
76
76
  self.stop
77
77
 
78
- STDOUT.print "Figuring out restart-command.\n"
78
+ self.log_puts("Figuring out restart-command.") if @debug
79
79
  mycmd = @config[:restart_cmd]
80
80
 
81
81
  if !mycmd or mycmd.to_s.strip.length <= 0
82
82
  fpath = File.realpath("#{File.dirname(__FILE__)}/../hayabusa.rb")
83
83
  mycmd = Knj::Os.executed_cmd
84
84
 
85
- STDOUT.print "Previous cmd: #{mycmd}\n"
85
+ self.log_puts("Previous cmd: #{mycmd}") if @debug
86
86
  mycmd = mycmd.gsub(/\s+hayabusa.rb/, " #{Knj::Strings.unixsafe(fpath)}")
87
87
  end
88
88
 
89
- STDOUT.print "Restarting knjAppServer with command: #{mycmd}\n"
89
+ self.log_puts("Restarting knjAppServer with command: #{mycmd}") if @debug
90
90
  @should_restart_done = true
91
91
  print exec(mycmd)
92
92
  exit
93
93
  rescue => e
94
- STDOUT.puts e.inspect
95
- STDOUT.puts e.backtrace
94
+ self.log_puts(e.inspect)
95
+ self.log_puts(e.backtrace)
96
96
  end
97
97
  end
98
98
  end
@@ -103,7 +103,7 @@ class Hayabusa
103
103
 
104
104
  #This method can be used to clean the appserver. Dont call this from a HTTP-request.
105
105
  def clean_sessions
106
- STDOUT.print "Cleaning sessions on appserver.\n" if @config[:debug]
106
+ self.log_puts("Cleaning sessions on appserver.") if @debug
107
107
 
108
108
  #Clean up various inactive sessions.
109
109
  session_not_ids = []
@@ -120,10 +120,10 @@ class Hayabusa
120
120
 
121
121
  @sessions = newsessions
122
122
 
123
- STDOUT.print "Delete sessions...\n" if @config[:debug]
123
+ self.log_puts("Delete sessions...") if @debug
124
124
  @ob.list(:Session, {"id_not" => session_not_ids, "date_lastused_below" => (Time.now - 5356800)}) do |session|
125
125
  idhash = session[:idhash]
126
- STDOUT.print "Deleting session: '#{session.id}'.\n" if @config[:debug]
126
+ self.log_puts("Deleting session: '#{session.id}'.") if @debug
127
127
  @ob.delete(session)
128
128
  @sessions.delete(idhash)
129
129
  end
@@ -68,7 +68,7 @@ class Hayabusa
68
68
  def handle_error(e, args = {})
69
69
  @error_emails_pending_mutex.synchronize do
70
70
  if !Thread.current[:hayabusa] or !Thread.current[:hayabusa][:httpsession]
71
- STDOUT.print "#{Knj::Errors.error_str(e)}\n\n"
71
+ self.log_puts("#{Knj::Errors.error_str(e)}\n")
72
72
  end
73
73
 
74
74
  browser = _httpsession.browser if _httpsession
@@ -122,7 +122,7 @@ class Hayabusa
122
122
 
123
123
  #Prints a detailed overview of the object in the terminal from where the appserver was started. This can be used for debugging.
124
124
  def dprint(obj)
125
- STDOUT.print Php4r.print_r(obj, true)
125
+ self.log_puts(Php4r.print_r(obj, true))
126
126
  end
127
127
 
128
128
  #Prints a string with a single file-line-backtrace prepended which is useful for debugging.
@@ -130,6 +130,6 @@ class Hayabusa
130
130
  #Get backtrace.
131
131
  backtrace_str = caller[0]
132
132
  backtrace_match = backtrace_str.match(/^(.+):(\d+):in /)
133
- STDOUT.print "#{File.basename(backtrace_match[1])}:#{backtrace_match[2]}: #{str}\n"
133
+ self.log_puts("#{File.basename(backtrace_match[1])}:#{backtrace_match[2]}: #{str}")
134
134
  end
135
135
  end
@@ -372,7 +372,7 @@ class Hayabusa
372
372
  raise "No :ob-argument given." if !args[:ob]
373
373
 
374
374
  @db.q_buffer do |db_buffer|
375
- STDOUT.puts "Starting to look for dead log-links." if @debug or args[:debug]
375
+ self.log_puts("Starting to look for dead log-links.") if @debug or args[:debug]
376
376
  @ob.list(:Log_link, :cloned_ubuf => true) do |log_link|
377
377
  classname = log_link.object_class.to_s.split("::").last
378
378
  obj_exists = args[:ob].exists?(classname, log_link[:object_id])
@@ -380,23 +380,23 @@ class Hayabusa
380
380
 
381
381
  log = log_link.log
382
382
 
383
- STDOUT.puts "Deleting log-link #{log_link.id} for #{classname}(#{log_link[:object_id]})." if @debug or args[:debug]
383
+ self.log_puts("Deleting log-link #{log_link.id} for #{classname}(#{log_link[:object_id]}).") if @debug or args[:debug]
384
384
  @ob.delete(log_link, :db_buffer => db_buffer)
385
385
 
386
386
  links_count = log.links("count" => true)
387
387
 
388
388
  if links_count <= 0
389
- STDOUT.puts "Deleting log #{log.id} because it has no more links." if @debug or args[:debug]
389
+ self.log_puts("Deleting log #{log.id} because it has no more links.") if @debug or args[:debug]
390
390
  @ob.delete(log, :db_buffer => db_buffer)
391
391
  end
392
392
  end
393
393
 
394
- STDOUT.puts "Starting to look for logs with no links." if @debug or args[:debug]
394
+ self.log_puts("Starting to look for logs with no links.") if @debug or args[:debug]
395
395
  @ob.list(:Log, {
396
396
  [:Log_link, "id"] => {:type => :sqlval, :val => :null},
397
397
  :cloned_ubuf => true
398
398
  }) do |log|
399
- STDOUT.puts "Deleting log #{log.id} because it has no links: '#{log.text}'." if @debug or args[:debug]
399
+ self.log_puts("Deleting log #{log.id} because it has no links: '#{log.text}'.") if @debug or args[:debug]
400
400
  @ob.delete(log, :db_buffer => db_buffer)
401
401
  end
402
402
  end
@@ -28,7 +28,7 @@ class Hayabusa
28
28
  end
29
29
 
30
30
  mailobj = Hayabusa::Mail.new({:hb => self, :errors => {}, :status => :waiting}.merge(mail_args))
31
- STDOUT.print "Added mail '#{mailobj.__id__}' to the mail-send-queue.\n" if debug
31
+ self.log_puts("Added mail '#{mailobj.__id__}' to the mail-send-queue.") if debug
32
32
  @mails_waiting << mailobj
33
33
 
34
34
  #Try to send right away and raise error instantly if something happens if told to do so.
@@ -44,34 +44,34 @@ class Hayabusa
44
44
  #Sends all queued mails to the respective servers, if we are online.
45
45
  def mail_flush
46
46
  @mails_mutex.synchronize do
47
- STDOUT.print "Flushing mails.\n" if @debug
47
+ self.log_puts("Flushing mails.") if @debug
48
48
 
49
49
  if @mails_waiting.length <= 0
50
- STDOUT.print "No mails to flush - skipping.\n" if @debug
50
+ self.log_puts("No mails to flush - skipping.") if @debug
51
51
  return false
52
52
  end
53
53
 
54
- STDOUT.print "Trying to ping Google to figure out if we are online...\n" if @debug
54
+ self.log_puts("Trying to ping Google to figure out if we are online...") if @debug
55
55
  status = Ping.pingecho("google.dk", 10, 80)
56
56
  if !status
57
- STDOUT.print "We are not online - skipping mail flush.\n"
57
+ self.log_puts("We are not online - skipping mail flush.")
58
58
  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.
59
59
  end
60
60
 
61
61
  #Use subprocessing to avoid the mail-framework (activesupport and so on, also possible memory leaks in those large frameworks).
62
- STDOUT.print "Starting subprocess for mailing.\n" if @debug
62
+ self.log_puts("Starting subprocess for mailing.") if @debug
63
63
  Knj::Process_meta.new("debug" => @debug, "debug_err" => true, "id" => "hayabusa_mailing") do |subproc|
64
64
  subproc.static("Object", "require", "rubygems")
65
65
  subproc.static("Object", "require", "mail")
66
66
  subproc.static("Object", "require", "#{@config[:knjrbfw_path]}knjrbfw")
67
67
  subproc.static("Object", "require", "knj/autoload")
68
68
 
69
- STDOUT.print "Flushing emails." if @debug
69
+ self.log_puts("Flushing emails.") if @debug
70
70
  @mails_waiting.each do |mail|
71
71
  begin
72
- STDOUT.print "Sending email: #{mail.__id__}\n" if @debug
72
+ self.log_puts("Sending email: #{mail.__id__}") if @debug
73
73
  if mail.send("proc" => subproc)
74
- STDOUT.print "Email sent: #{mail.__id__}\n" if @debug
74
+ self.log_puts("Email sent: #{mail.__id__}") if @debug
75
75
  @mails_waiting.delete(mail)
76
76
  end
77
77
  rescue Timeout::Error
@@ -117,7 +117,7 @@ class Hayabusa
117
117
 
118
118
  #Sends the email to the receiver.
119
119
  def send(args = {})
120
- STDOUT.print "Sending mail '#{__id__}'.\n" if @args[:hb].debug
120
+ @args[:hb].log_puts("Sending mail '#{__id__}'.") if @args[:hb].debug
121
121
 
122
122
  if args["proc"]
123
123
  args["proc"].static("Object", "require", "knj/mailobj")
@@ -139,13 +139,13 @@ class Hayabusa
139
139
  end
140
140
 
141
141
  @args[:status] = :sent
142
- STDOUT.print "Sent email #{self.__id__}\n" if @args[:hb].debug
142
+ @args[:hb].log_puts("Sent email #{self.__id__}") if @args[:hb].debug
143
143
  return true
144
144
  rescue => e
145
145
  if @args[:hb].debug
146
- STDOUT.print "Could not send email.\n"
147
- STDOUT.puts e.inspect
148
- STDOUT.puts e.backtrace
146
+ @args[:hb].log_puts("Could not send email.")
147
+ @args[:hb].log_puts(e.inspect)
148
+ @args[:hb].log_puts(e.backtrace)
149
149
  end
150
150
 
151
151
  @args[:errors][e.class.name] = {:count => 0} if !@args[:errors].has_key?(e.class.name)
@@ -73,7 +73,7 @@ class Hayabusa
73
73
  def sessions_flush
74
74
  if @sessions
75
75
  @sessions.each do |session_hash, session_data|
76
- STDOUT.print "Flushing session: #{session_data[:dbobj].id}\n" if @debug
76
+ self.log_puts("Flushing session: #{session_data[:dbobj].id}") if @debug
77
77
  session_data[:dbobj].flush
78
78
  end
79
79
  end
@@ -1,7 +1,7 @@
1
1
  class Hayabusa
2
2
  def initialize_threadding
3
3
  @config[:threadding] = {} if !@config.has_key?(:threadding)
4
- @config[:threadding][:max_running] = 8 if !@config[:threadding].has_key?(:max_running)
4
+ @config[:threadding][:max_running] = 16 if !@config[:threadding].has_key?(:max_running)
5
5
 
6
6
  threadpool_args = {:threads => @config[:threadding][:max_running]}
7
7
  threadpool_args[:priority] = @config[:threadding][:priority] if @config[:threadding].key?(:priority)
@@ -30,35 +30,38 @@ class Hayabusa
30
30
  thread_obj = Hayabusa::Thread_instance.new(
31
31
  :running => false,
32
32
  :error => false,
33
- :done => false
33
+ :done => false,
34
+ :args => args
34
35
  )
35
36
 
36
37
  @threadpool.run_async do
37
- @ob.db.get_and_register_thread if @ob.db.opts[:threadsafe]
38
- @db_handler.get_and_register_thread if @db_handler.opts[:threadsafe]
39
-
40
- Thread.current[:hayabusa] = {
41
- :hb => self,
42
- :db => @db_handler
43
- }
44
-
45
38
  begin
39
+ @ob.db.get_and_register_thread if @ob.db.opts[:threadsafe]
40
+ @db_handler.get_and_register_thread if @db_handler.opts[:threadsafe]
41
+
42
+ Thread.current[:hayabusa] = {
43
+ :hb => self,
44
+ :db => @db_handler
45
+ }
46
+
46
47
  thread_obj.args[:running] = true
47
48
  yield(*args[:args])
49
+ _hb.log_puts("Done yielding for #{args[:id]}")
48
50
  rescue => e
49
- self.handle_error(e)
50
51
  thread_obj.args[:error] = true
51
52
  thread_obj.args[:error_obj] = e
53
+
54
+ self.handle_error(e)
52
55
  ensure
53
- STDOUT.print "Free thread ob-db.\n" if @debug
56
+ _hb.log_puts("Seting arguments on thread.") if @debug
57
+ thread_obj.args[:running] = false
58
+ thread_obj.args[:done] = true
59
+
60
+ _hb.log_puts("Free thread ob-db.") if @debug
54
61
  @ob.db.free_thread if @ob.db.opts[:threadsafe]
55
62
 
56
- STDOUT.print "Free thread db-handler.\n" if @debug
63
+ _hb.log_puts("Free thread db-handler.") if @debug
57
64
  @db_handler.free_thread if @db_handler.opts[:threadsafe]
58
-
59
- STDOUT.print "Seting arguments on thread.\n" if @debug
60
- thread_obj.args[:running] = false
61
- thread_obj.args[:done] = true
62
65
  end
63
66
  end
64
67
 
@@ -105,8 +108,24 @@ class Hayabusa::Thread_instance
105
108
  @args = args
106
109
  end
107
110
 
111
+ def running?
112
+ return @args[:running]
113
+ end
114
+
115
+ def done?
116
+ return @args[:done]
117
+ end
118
+
119
+ def error?
120
+ return true if @args[:error]
121
+ return false
122
+ end
123
+
108
124
  def join
109
- sleep 0.1 while !@args[:done]
125
+ while !@args[:done] and !@args[:error]
126
+ _hb.log_puts(@args)
127
+ sleep 0.1
128
+ end
110
129
  end
111
130
 
112
131
  def join_error
data/lib/hayabusa_fcgi.rb CHANGED
@@ -1,3 +1,4 @@
1
+ #This class is used for FCGI-sessions. It normally starts a Hayabusa-host-process which this (and other) FCGI-processes will proxy requests to. The host-process will automatically kill itself when no more FCGI-sessions are connected to emulate normal FCGI behaviour.
1
2
  class Hayabusa::Fcgi
2
3
  def initialize
3
4
  #Spawn CGI-variable to emulate FCGI part.
@@ -10,6 +11,7 @@ class Hayabusa::Fcgi
10
11
  @debug = false
11
12
  end
12
13
 
14
+ #Evaluates if a new host-process should be started or we should proxy calls to an existing one.
13
15
  def evaluate_mode
14
16
  #If this is a FCGI-proxy-instance then the HTTP-connection should be checked if it is working.
15
17
  if @fcgi_proxy
@@ -52,7 +54,7 @@ class Hayabusa::Fcgi
52
54
  #Set this instance to run in proxy-mode.
53
55
  begin
54
56
  @fcgi_proxy = fcgi_config
55
- Knj.gem_require(:Http2, "http2")
57
+ Knj.gem_require(:Http2)
56
58
 
57
59
  begin
58
60
  @fcgi_proxy[:http] = Http2.new(:host => "localhost", :port => @fcgi_proxy[:port].to_i)
@@ -74,21 +76,46 @@ class Hayabusa::Fcgi
74
76
 
75
77
  #No instance is already running - start new Hayabusa-instance in both CGI- and socket-mode and write that to the config-file so other instances will register this as the main host-instance.
76
78
  if !@fcgi_proxy
79
+ #Spawn sub-process that actually runs the Hayabusa-server.
80
+ require "open3"
81
+ cmd = "#{Knj::Os.executed_executable} #{File.realpath(File.dirname(__FILE__))}/../bin/hayabusa_fcgi_server.rb --conf_path=#{Knj::Strings.unixsafe(@hayabusa_fcgi_conf_path)} --fcgi_data_path=#{Knj::Strings.unixsafe(fcgi_config_fp)}"
82
+
83
+ #Only used to identify the running FCGI-server host-processes with 'ps aux'.
84
+ cmd << "--title=#{Knj::Strings.unixsafe(hayabusa_conf[:title])}" if hayabusa_conf[:title]
85
+
86
+ $stderr.puts("Executing command to start FCGI-server: #{cmd}")
87
+ io_out, io_in, io_err = Open3.popen3(cmd)
88
+
89
+ #Get data that should contain PID and port.
90
+ read = io_in.gets
91
+ raise "Host-process didnt return required data." if !read
92
+
93
+ #Parse data from the host-process (port and PID).
94
+ begin
95
+ data = Marshal.load(Base64.strict_decode64(read.strip))
96
+ rescue
97
+ raise "Invalid data given from host-process: '#{read}'."
98
+ end
99
+
100
+
101
+ #Detach the process where the HTTP-server runs from this process.
102
+ Process.detach(data[:pid])
103
+
104
+ #Write the data about the new HTTP-server in the config-file and re-evaluate mode. The host-process should not write the file itself, since this process has a lock on that file, and another process might try to start another host-process while writing the file from the host-process, which is not desired.
77
105
  File.open(fcgi_config_fp, "w") do |fp|
78
- @hayabusa = Hayabusa.new(hayabusa_conf)
79
-
80
- #Start web-server for proxy-requests.
81
- @hayabusa.start
82
-
83
106
  fp.write(Marshal.dump(
84
- :pid => Process.pid,
85
- :port => @hayabusa.port
107
+ :pid => data[:pid],
108
+ :port => data[:port]
86
109
  ))
110
+
111
+ #Started host-process - evaluate mode again and proxy request to the host-process.
112
+ raise Errno::EAGAIN
87
113
  end
88
114
  end
89
115
  end
90
116
  end
91
117
 
118
+ #Handels the requests comming from the FCGI-object in a loop.
92
119
  def fcgi_loop
93
120
  $stderr.puts "[hayabusa] Starting FCGI." if @debug
94
121
 
@@ -119,8 +146,8 @@ class Hayabusa::Fcgi
119
146
  else
120
147
  self.handle_fcgi_request(:cgi => cgi)
121
148
  end
122
- rescue Errno::ECONNABORTED, Errno::ECONNREFUSED
123
- $stderr.puts "[hayabusa] Connection to server was interrupted - trying again."
149
+ rescue Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET => e
150
+ $stderr.puts "[hayabusa] Connection to server was interrupted - trying again: <#{e.class.name}> #{e.message}"
124
151
  @fcgi_proxy = nil #Force re-evaluate if this process should be host or proxy.
125
152
  retry
126
153
  rescue Exception => e
@@ -146,6 +173,7 @@ class Hayabusa::Fcgi
146
173
  end
147
174
  end
148
175
 
176
+ #Handles the request as a real request on a Hayabusa-host running inside the current process. This is not used any more but kept if we need support for it once again (maybe the developer should be able to decide this in some kind of config?).
149
177
  def handle_fcgi_request(args)
150
178
  #Host the FCGI-process.
151
179
  $stderr.puts "[hayabusa] Running request as CGI." if @debug
@@ -0,0 +1,30 @@
1
+ #This class is used to start Hayabusa in its own process, which FCGI-sessions then connects to. This way only one instance of Hayabusa is actually running to allowed FCGI-sessions to "commuicate" with each other (because they are running in the same process).
2
+ class Hayabusa::Fcgi_server
3
+ attr_reader :hayabusa
4
+
5
+ def initialize(args)
6
+ #Start web-server for proxy-requests.
7
+ @hayabusa = Hayabusa.new(args[:hayabusa_conf])
8
+ @hayabusa.start
9
+
10
+ #In FCGI-mode the host-process should exit when zero FCGI-connections are active.
11
+ @hayabusa.events.connect(:http_session_destruct, &self.method(:on_http_session_destruct))
12
+ end
13
+
14
+ #Called when a HTTP-session destructs (disconnects). Used to stop the Hayabusa-appserver when no connections are active to only be running when FCGI-sessions are running.
15
+ def on_http_session_destruct(*args)
16
+ @hayabusa.log_puts("HTTP-connection destruction - checking if no connections are active any more.")
17
+
18
+ stop = false
19
+ httpserv = @hayabusa.httpserv
20
+
21
+ if !httpserv or !httpserv.http_sessions or httpserv.http_sessions.empty?
22
+ stop = true
23
+ end
24
+
25
+ if stop
26
+ @hayabusa.log_puts("Stopping server because no active connections.")
27
+ @hayabusa.stop
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,6 @@
1
1
  require "socket"
2
2
 
3
+ #This class opens a port to run the HTTP-server on. It then spawns "Hayabusa::Http_session" for each active connection.
3
4
  class Hayabusa::Http_server
4
5
  attr_accessor :working_count
5
6
  attr_reader :hb, :http_sessions, :thread_accept, :thread_restart, :server
@@ -10,6 +11,7 @@ class Hayabusa::Http_server
10
11
  @mutex_count = Mutex.new
11
12
  end
12
13
 
14
+ #Opens a port with TCPServer and spins up a thread to accept connections.
13
15
  def start
14
16
  @http_sessions = []
15
17
  @working_count = 0
@@ -43,6 +45,7 @@ class Hayabusa::Http_server
43
45
  end
44
46
  end
45
47
 
48
+ #Gently stops the HTTP-server. Will wait for various HTTP-sessions to be finish with a page (but wont wait for them to disconnect in regards to keep-alive).
46
49
  def stop
47
50
  while @working_count > 0
48
51
  @hb.log_puts "Waiting until no HTTP-sessions are running." if @debug
@@ -83,11 +86,13 @@ class Hayabusa::Http_server
83
86
  @hb = nil
84
87
  end
85
88
 
89
+ #Spawns a new HTTP-session with the given socket.
86
90
  def spawn_httpsession(socket)
87
- @hb.log_puts "Starting new HTTP-session." if @debug
91
+ @hb.log_puts("Starting new HTTP-session.") if @debug
88
92
  @http_sessions << Hayabusa::Http_session.new(self, socket)
89
93
  end
90
94
 
95
+ #Increases and decreases the 'working_count'-variable to keep track of how many HTTP-sessions are currently processing pages (used for gentle stops).
91
96
  def count_block
92
97
  begin
93
98
  added = false
@@ -141,14 +141,14 @@ class Hayabusa::Http_session < Hayabusa::Client_session
141
141
  begin
142
142
  @socket.close if !@socket.closed?
143
143
  rescue => e
144
- STDOUT.puts e.inspect
145
- STDOUT.puts e.backtrace
144
+ @hb.log_puts(e.inspect)
145
+ @hb.log_puts(e.backtrace)
146
146
  #ignore if it fails...
147
147
  end
148
148
 
149
149
  @httpserver.http_sessions.delete(self) if @httpserver and @httpserver.http_sessions
150
-
151
150
  @eruby.destroy if @eruby
151
+ @hb.events.call(:http_session_destruct, :httpsession => self) if @hb.events
152
152
  @thread_request.kill if @thread_request.alive?
153
153
  end
154
154
 
@@ -208,7 +208,7 @@ class Hayabusa::Http_session < Hayabusa::Client_session
208
208
  "name" => "HayabusaSession",
209
209
  "value" => @session_id,
210
210
  "path" => "/",
211
- "expires" => Time.now + 32140800 #add around 12 months
211
+ "expires" => Time.now + 32140800 #add around 12 months from the current time
212
212
  )
213
213
  end
214
214
 
@@ -230,7 +230,6 @@ class Hayabusa::Http_session < Hayabusa::Client_session
230
230
  @hb.log_puts "Initializing thread and content-group." if @debug
231
231
  self.init_thread
232
232
  Thread.current[:hayabusa][:contentgroup] = @cgroup
233
- time_start = Time.now.to_f if @debug
234
233
 
235
234
  self.execute_page
236
235
  self.execute_done
@@ -159,7 +159,7 @@ class Hayabusa::Http_session::Request
159
159
  if @hb
160
160
  @hb.handle_error(e)
161
161
  else
162
- STDOUT.print Knj::Errors.error_str(e)
162
+ STDOUT.print Knj::Errors.error_str(e) if !STDOUT.closed?
163
163
  end
164
164
  end
165
165
  end
@@ -1,20 +1,35 @@
1
1
  <%
2
+ #Knj.p _hb.config
3
+ #exit
4
+
5
+ _hb.log_puts("1")
2
6
  print "1"
3
7
 
8
+ _hb.log_puts("2")
4
9
  cont = "2"
5
- t1 = _hb.thread do
10
+ t1 = _hb.thread(:id => 4) do
6
11
  sleep 0.5
12
+ _hb.log_puts("4")
7
13
  cont << "4"
14
+ _hb.log_puts("4 done")
8
15
  end
9
16
 
10
- t2 = _hb.thread do
11
- sleep 0.1
17
+ t2 = _hb.thread(:id => 3) do
18
+ sleep 0.3
19
+ _hb.log_puts("3")
12
20
  cont << "3"
21
+ _hb.log_puts("3 done")
13
22
  end
14
23
 
24
+ sleep 0.1
25
+
26
+ raise "Expected thread 1 to be running but it wasnt." if !t1.running?
27
+ raise "Expected thread 2 to be running but it wasnt." if !t2.running?
28
+
15
29
  t1.join
16
30
  t2.join_error
17
31
 
32
+ _hb.log_puts("5")
18
33
  cont << "5"
19
34
 
20
35
  print cont
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  describe "Hayabusa" do
4
4
  it "should handle sessions correctly under stressed conditions" do
5
5
  require "knjrbfw"
6
- Knj.gem_require(:Http2, "http2")
6
+ Knj.gem_require(:Http2)
7
7
  require "json"
8
8
 
9
9
  ts = []
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: hayabusa
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.13
5
+ version: 0.0.14
6
6
  platform: ruby
7
7
  authors:
8
8
  - Kasper Johansen
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-09-20 00:00:00 Z
13
+ date: 2012-09-30 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: knjrbfw
@@ -141,6 +141,8 @@ executables:
141
141
  - hayabusa_cgi.rb
142
142
  - hayabusa_fcgi.fcgi
143
143
  - hayabusa_fcgi.rb
144
+ - hayabusa_fcgi_server.rb
145
+ - hayabusa_spec_restart.rb
144
146
  - knjappserver_start.rb
145
147
  extensions: []
146
148
 
@@ -161,6 +163,8 @@ files:
161
163
  - bin/hayabusa_cgi.rb
162
164
  - bin/hayabusa_fcgi.fcgi
163
165
  - bin/hayabusa_fcgi.rb
166
+ - bin/hayabusa_fcgi_server.rb
167
+ - bin/hayabusa_spec_restart.rb
164
168
  - bin/knjappserver_start.rb
165
169
  - conf/apache2_cgi_rhtml_conf.conf
166
170
  - conf/apache2_fcgi_rhtml_conf.conf
@@ -184,6 +188,7 @@ files:
184
188
  - lib/hayabusa_ext/translations.rb
185
189
  - lib/hayabusa_ext/web.rb
186
190
  - lib/hayabusa_fcgi.rb
191
+ - lib/hayabusa_fcgi_server.rb
187
192
  - lib/hayabusa_http_server.rb
188
193
  - lib/hayabusa_http_session.rb
189
194
  - lib/hayabusa_http_session_contentgroup.rb
@@ -240,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
240
245
  requirements:
241
246
  - - ">="
242
247
  - !ruby/object:Gem::Version
243
- hash: 4196066907538091400
248
+ hash: 888767182680774997
244
249
  segments:
245
250
  - 0
246
251
  version: "0"