hayabusa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +20 -0
  4. data/Gemfile.lock +59 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/bin/check_running.rb +69 -0
  10. data/bin/hayabusa_benchmark.rb +82 -0
  11. data/bin/hayabusa_cgi.rb +84 -0
  12. data/bin/hayabusa_fcgi.fcgi +159 -0
  13. data/bin/hayabusa_fcgi.rb +159 -0
  14. data/bin/knjappserver_start.rb +42 -0
  15. data/conf/apache2_cgi_rhtml_conf.conf +10 -0
  16. data/conf/apache2_fcgi_rhtml_conf.conf +22 -0
  17. data/hayabusa.gemspec +151 -0
  18. data/lib/hayabusa.rb +518 -0
  19. data/lib/hayabusa_cgi_session.rb +128 -0
  20. data/lib/hayabusa_cgi_tools.rb +102 -0
  21. data/lib/hayabusa_custom_io.rb +22 -0
  22. data/lib/hayabusa_database.rb +125 -0
  23. data/lib/hayabusa_erb_handler.rb +27 -0
  24. data/lib/hayabusa_ext/cleaner.rb +140 -0
  25. data/lib/hayabusa_ext/cmdline.rb +52 -0
  26. data/lib/hayabusa_ext/errors.rb +135 -0
  27. data/lib/hayabusa_ext/logging.rb +404 -0
  28. data/lib/hayabusa_ext/mailing.rb +158 -0
  29. data/lib/hayabusa_ext/sessions.rb +71 -0
  30. data/lib/hayabusa_ext/threadding.rb +96 -0
  31. data/lib/hayabusa_ext/threadding_timeout.rb +101 -0
  32. data/lib/hayabusa_ext/translations.rb +43 -0
  33. data/lib/hayabusa_ext/web.rb +190 -0
  34. data/lib/hayabusa_http_server.rb +102 -0
  35. data/lib/hayabusa_http_session.rb +361 -0
  36. data/lib/hayabusa_http_session_contentgroup.rb +176 -0
  37. data/lib/hayabusa_http_session_page_environment.rb +66 -0
  38. data/lib/hayabusa_http_session_post_multipart.rb +135 -0
  39. data/lib/hayabusa_http_session_request.rb +219 -0
  40. data/lib/hayabusa_http_session_response.rb +144 -0
  41. data/lib/hayabusa_models.rb +8 -0
  42. data/lib/kernel_ext/gettext_methods.rb +22 -0
  43. data/lib/kernel_ext/magic_methods.rb +61 -0
  44. data/lib/models/log.rb +130 -0
  45. data/lib/models/log_access.rb +88 -0
  46. data/lib/models/log_data.rb +27 -0
  47. data/lib/models/log_data_link.rb +3 -0
  48. data/lib/models/log_data_value.rb +21 -0
  49. data/lib/models/log_link.rb +65 -0
  50. data/lib/models/session.rb +35 -0
  51. data/pages/benchmark.rhtml +0 -0
  52. data/pages/benchmark_print.rhtml +14 -0
  53. data/pages/benchmark_simple.rhtml +3 -0
  54. data/pages/benchmark_threadded_content.rhtml +21 -0
  55. data/pages/debug_database_connections.rhtml +46 -0
  56. data/pages/debug_http_sessions.rhtml +40 -0
  57. data/pages/debug_memory_usage.rhtml +16 -0
  58. data/pages/error_notfound.rhtml +12 -0
  59. data/pages/logs_latest.rhtml +57 -0
  60. data/pages/logs_show.rhtml +32 -0
  61. data/pages/spec.rhtml +41 -0
  62. data/pages/spec_post.rhtml +3 -0
  63. data/pages/spec_test_multiple_clients.rhtml +3 -0
  64. data/pages/spec_thread_joins.rhtml +21 -0
  65. data/pages/spec_threadded_content.rhtml +40 -0
  66. data/pages/tests.rhtml +14 -0
  67. data/spec/cgi_spec.rb +47 -0
  68. data/spec/custom_urls_spec.rb +35 -0
  69. data/spec/fcgi_multiple_processes_spec.rb +32 -0
  70. data/spec/fcgi_spec.rb +69 -0
  71. data/spec/hayabusa_spec.rb +194 -0
  72. data/spec/spec_helper.rb +12 -0
  73. data/tests/cgi_test/config_cgi.rb +6 -0
  74. data/tests/cgi_test/threadded_content_test.rhtml +23 -0
  75. data/tests/cgi_test/vars_get_test.rhtml +4 -0
  76. data/tests/cgi_test/vars_header_test.rhtml +3 -0
  77. data/tests/cgi_test/vars_post_test.rhtml +4 -0
  78. data/tests/fcgi_test/config_fcgi.rb +6 -0
  79. data/tests/fcgi_test/index.rhtml +3 -0
  80. data/tests/fcgi_test/sleeper.rhtml +4 -0
  81. data/tests/fcgi_test/threadded_content_test.rhtml +23 -0
  82. data/tests/fcgi_test/vars_get_test.rhtml +4 -0
  83. data/tests/fcgi_test/vars_header_test.rhtml +3 -0
  84. data/tests/fcgi_test/vars_post_test.rhtml +4 -0
  85. metadata +257 -0
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/ruby
2
+
3
+ #This scripts start an appserver, executes a HTTP-request for every FCGI-request and terminates when FCGI terminates.
4
+ #Good for programming appserver-supported projects that doesnt need threadding without running an appserver all the time.
5
+
6
+ #It doesnt support shared threadding or objects because multiple instances in form of processes will be executed.
7
+
8
+ #Its a bit slower because it needs to flush writes to the database at end of every request and re-read them on spawn, because multiple instances might be present.
9
+
10
+ error_log_file = "/tmp/hayabusa_fcgi.log"
11
+
12
+ begin
13
+ File.unlink(error_log_file) if File.exists?(error_log_file)
14
+ rescue Errno::ENOENT
15
+ #ignore.
16
+ end
17
+
18
+ begin
19
+ require "rubygems"
20
+ require "knjrbfw"
21
+ require "fcgi"
22
+ require "fileutils"
23
+ require "#{File.dirname(Knj::Os.realpath(__FILE__))}/../lib/hayabusa.rb"
24
+
25
+ #Spawn CGI-variable to emulate FCGI part.
26
+ cgi_tools = Hayabusa::Cgi_tools.new
27
+
28
+ #We cant define the Hayabusa-server untuil we receive the first headers, so wait for the first request.
29
+ hayabusa = nil
30
+ fcgi_proxy = nil
31
+
32
+ FCGI.each_cgi do |cgi|
33
+ begin
34
+ #cgi.print "Content-Type: text/html\r\n"
35
+ #cgi.print "\r\n"
36
+
37
+ cgi_tools.cgi = cgi
38
+
39
+ if !hayabusa
40
+ raise "No HTTP_HAYABUSA_FCGI_CONFIG-header was given." if !cgi.env_table["HTTP_HAYABUSA_FCGI_CONFIG"]
41
+ require cgi.env_table["HTTP_HAYABUSA_FCGI_CONFIG"]
42
+
43
+ begin
44
+ conf = Hayabusa::FCGI_CONF
45
+ rescue NameError
46
+ raise "No 'Hayabusa::FCGI_CONF'-constant was spawned by '#{cgi.env_table["HTTP_HAYABUSA_FCGI_CONFIG"]}'."
47
+ end
48
+
49
+ hayabusa_conf = Hayabusa::FCGI_CONF[:hayabusa]
50
+ hayabusa_conf.merge!(
51
+ :cmdline => false,
52
+ :port => 0 #Ruby picks random port and we get the actual port after starting the appserver.
53
+ )
54
+
55
+ #Figure out if this should be a host-FCGI-process or a proxy-FCGI-process.
56
+ fcgi_config_fp = "#{Knj::Os.tmpdir}/hayabusa_fcgi_#{hayabusa_conf[:title]}_fcgi.conf"
57
+ FileUtils.touch(fcgi_config_fp) if !File.exists?(fcgi_config_fp)
58
+
59
+ begin
60
+ File.open(fcgi_config_fp) do |fp|
61
+ fp.flock(File::LOCK_EX)
62
+
63
+ fcgi_config_cont = File.read(fcgi_config_fp)
64
+
65
+ if !fcgi_config_cont.empty?
66
+ fcgi_config = Marshal.load(File.read(fcgi_config_fp))
67
+ pid = fcgi_config[:pid]
68
+
69
+ if Knj::Unix_proc.pid_running?(pid)
70
+ fcgi_proxy = fcgi_config
71
+
72
+ require "http2"
73
+ http = Http2.new(:host => "localhost", :port => fcgi_proxy[:port].to_i)
74
+ fcgi_proxy[:http] = http
75
+ end
76
+ else
77
+ fcgi_config = nil
78
+ end
79
+
80
+ if !fcgi_proxy
81
+ File.open(fcgi_config_fp, "w") do |fp|
82
+ hayabusa = Hayabusa.new(hayabusa_conf)
83
+
84
+ #Start web-server for proxy-requests.
85
+ hayabusa.start
86
+
87
+ fp.write(Marshal.dump(
88
+ :pid => Process.pid,
89
+ :port => hayabusa.port
90
+ ))
91
+ end
92
+ end
93
+ end
94
+ ensure
95
+ File.open(fcgi_config_fp).flock(File::LOCK_UN)
96
+ end
97
+ end
98
+
99
+
100
+
101
+ if fcgi_proxy
102
+ #Proxy request to the host-FCGI-process.
103
+ cgi_tools.proxy_request_to(:cgi => cgi, :http => fcgi_proxy[:http])
104
+ else
105
+ #Host the FCGI-process.
106
+
107
+ #Enforce $stdout variable.
108
+ $stdout = hayabusa.cio
109
+
110
+ #The rest is copied from the FCGI-part.
111
+ headers = {}
112
+ cgi.env_table.each do |key, val|
113
+ if key[0, 5] == "HTTP_" and key != "HTTP_HAYABUSA_FCGI_CONFIG"
114
+ key = key[5, key.length].gsub("_", " ").gsub(" ", "-")
115
+ headers[key] = val
116
+ end
117
+ end
118
+
119
+ meta = cgi.env_table.to_hash
120
+
121
+ uri = Knj::Web.parse_uri(meta["REQUEST_URI"])
122
+ meta["PATH_TRANSLATED"] = File.basename(uri[:path])
123
+
124
+ cgi_data = {
125
+ :cgi => cgi,
126
+ :headers => headers,
127
+ :get => Knj::Web.parse_urlquery(cgi.env_table["QUERY_STRING"], :urldecode => true, :force_utf8 => true),
128
+ :meta => meta
129
+ }
130
+ if cgi.request_method == "POST"
131
+ cgi_data[:post] = cgi_tools.convert_fcgi_post(cgi.params)
132
+ else
133
+ cgi_data[:post] = {}
134
+ end
135
+
136
+ hayabusa.config[:cgi] = cgi_data
137
+
138
+
139
+ #Handle request.
140
+ hayabusa.start_cgi_request
141
+ end
142
+ rescue Exception => e
143
+ cgi.print "Content-Type: text/html\r\n"
144
+ cgi.print "\r\n"
145
+ cgi.print Knj::Errors.error_str(e, :html => true)
146
+ cgi.print Knj.p(cgi_data, true) if cgi_data
147
+ end
148
+ end
149
+ rescue Exception => e
150
+ if !e.is_a?(Interrupt)
151
+ File.open(error_log_file, "w") do |fp|
152
+ fp.puts e.inspect
153
+ fp.puts e.backtrace
154
+ fp.puts ""
155
+ end
156
+ end
157
+
158
+ raise e
159
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "#{File.dirname(__FILE__)}/../lib/hayabusa.rb"
4
+
5
+ knjrbfw_path = ""
6
+
7
+ ARGV.each do |arg|
8
+ if arg == "--active_support"
9
+ ARGV.delete(arg)
10
+ require "active_support"
11
+ require "active_support/core_ext"
12
+ elsif match = arg.match(/--knjrbfw_path=(.+)/)
13
+ knjrbfw_path = match[1]
14
+ ARGV.delete(arg)
15
+ else
16
+ print "Unknown argument: '#{arg}'.\n"
17
+ exit
18
+ end
19
+ end
20
+
21
+ require "#{knjrbfw_path}knjrbfw"
22
+
23
+ filepath = File.dirname(__FILE__) + "/../lib/"
24
+
25
+ if File.exists?($0)
26
+ conf_path = File.dirname($0) + "/../"
27
+ else
28
+ conf_path = File.dirname(__FILE__) + "/../"
29
+ end
30
+
31
+ require "#{conf_path}conf/conf_vars"
32
+ require "#{$hayabusa_config["knjrbfw"]}knj/autoload"
33
+
34
+ $hayabusa = {
35
+ :path => File.realpath(File.dirname(__FILE__))
36
+ }
37
+
38
+ Knj::Os.chdir_file(File.realpath(__FILE__))
39
+ require "#{filepath}include/class_hayabusa.rb"
40
+
41
+ print "Starting knjAppServer.\n"
42
+ require "#{conf_path}conf/conf"
@@ -0,0 +1,10 @@
1
+ Alias /hayabusa_cgi_test "/path/to/hayabusa/tests/cgi_test"
2
+ ScriptAlias /hayabusa/ "/path/to/hayabusa/bin/"
3
+
4
+ <Directory "/path/to/hayabusa/tests/cgi_test">
5
+ Options +FollowSymLinks +ExecCGI
6
+ RequestHeader set HAYABUSA_CGI_CONFIG "/path/to/hayabusa/tests/cgi_test/config_cgi.rb"
7
+ AddHandler application/x-hayabusa-cgi .rhtml
8
+ Action application/x-hayabusa-cgi /hayabusa/hayabusa_cgi.rb
9
+ DirectoryIndex index.cgi
10
+ </Directory>
@@ -0,0 +1,22 @@
1
+ Alias /hayabusa_fcgi_test "/path/to/hayabusa/tests/fcgi_test"
2
+ Alias /hayabusa_fcgi_bin "/path/to/hayabusa/bin"
3
+
4
+ <IfModule mod_fcgid.c>
5
+ SocketPath /tmp/fcgi_socket
6
+ FcgidIdleTimeout 30
7
+ FcgidMinProcessesPerClass 0
8
+ FcgidIdleScanInterval 15
9
+ </IfModule>
10
+
11
+ <Directory "/path/to/hayabusa/tests/fcgi_test">
12
+ RewriteEngine On
13
+ RewriteRule ^(.*)\.rhtml$ /hayabusa_fcgi_bin/hayabusa_fcgi.fcgi
14
+
15
+ Options +FollowSymLinks
16
+
17
+ RequestHeader set HAYABUSA_FCGI_CONFIG "/path/to/hayabusa/tests/fcgi_test/config_fcgi.rb"
18
+
19
+ AddHandler fcgid-script .fgci
20
+
21
+ DirectoryIndex index.rhtml
22
+ </Directory>
@@ -0,0 +1,151 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hayabusa}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kasper Johansen"]
12
+ s.date = %q{2012-08-16}
13
+ s.description = %q{A threadded web/app-server that focuses on threadding, shared ressources, speed and more.}
14
+ s.email = %q{k@spernj.org}
15
+ s.executables = ["check_running.rb", "hayabusa_benchmark.rb", "hayabusa_cgi.rb", "hayabusa_fcgi.fcgi", "hayabusa_fcgi.rb", "knjappserver_start.rb"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/check_running.rb",
30
+ "bin/hayabusa_benchmark.rb",
31
+ "bin/hayabusa_cgi.rb",
32
+ "bin/hayabusa_fcgi.fcgi",
33
+ "bin/hayabusa_fcgi.rb",
34
+ "bin/knjappserver_start.rb",
35
+ "conf/apache2_cgi_rhtml_conf.conf",
36
+ "conf/apache2_fcgi_rhtml_conf.conf",
37
+ "hayabusa.gemspec",
38
+ "lib/hayabusa.rb",
39
+ "lib/hayabusa_cgi_session.rb",
40
+ "lib/hayabusa_cgi_tools.rb",
41
+ "lib/hayabusa_custom_io.rb",
42
+ "lib/hayabusa_database.rb",
43
+ "lib/hayabusa_erb_handler.rb",
44
+ "lib/hayabusa_ext/cleaner.rb",
45
+ "lib/hayabusa_ext/cmdline.rb",
46
+ "lib/hayabusa_ext/errors.rb",
47
+ "lib/hayabusa_ext/logging.rb",
48
+ "lib/hayabusa_ext/mailing.rb",
49
+ "lib/hayabusa_ext/sessions.rb",
50
+ "lib/hayabusa_ext/threadding.rb",
51
+ "lib/hayabusa_ext/threadding_timeout.rb",
52
+ "lib/hayabusa_ext/translations.rb",
53
+ "lib/hayabusa_ext/web.rb",
54
+ "lib/hayabusa_http_server.rb",
55
+ "lib/hayabusa_http_session.rb",
56
+ "lib/hayabusa_http_session_contentgroup.rb",
57
+ "lib/hayabusa_http_session_page_environment.rb",
58
+ "lib/hayabusa_http_session_post_multipart.rb",
59
+ "lib/hayabusa_http_session_request.rb",
60
+ "lib/hayabusa_http_session_response.rb",
61
+ "lib/hayabusa_models.rb",
62
+ "lib/kernel_ext/gettext_methods.rb",
63
+ "lib/kernel_ext/magic_methods.rb",
64
+ "lib/models/log.rb",
65
+ "lib/models/log_access.rb",
66
+ "lib/models/log_data.rb",
67
+ "lib/models/log_data_link.rb",
68
+ "lib/models/log_data_value.rb",
69
+ "lib/models/log_link.rb",
70
+ "lib/models/session.rb",
71
+ "pages/benchmark.rhtml",
72
+ "pages/benchmark_print.rhtml",
73
+ "pages/benchmark_simple.rhtml",
74
+ "pages/benchmark_threadded_content.rhtml",
75
+ "pages/debug_database_connections.rhtml",
76
+ "pages/debug_http_sessions.rhtml",
77
+ "pages/debug_memory_usage.rhtml",
78
+ "pages/error_notfound.rhtml",
79
+ "pages/logs_latest.rhtml",
80
+ "pages/logs_show.rhtml",
81
+ "pages/spec.rhtml",
82
+ "pages/spec_post.rhtml",
83
+ "pages/spec_test_multiple_clients.rhtml",
84
+ "pages/spec_thread_joins.rhtml",
85
+ "pages/spec_threadded_content.rhtml",
86
+ "pages/tests.rhtml",
87
+ "spec/cgi_spec.rb",
88
+ "spec/custom_urls_spec.rb",
89
+ "spec/fcgi_multiple_processes_spec.rb",
90
+ "spec/fcgi_spec.rb",
91
+ "spec/hayabusa_spec.rb",
92
+ "spec/spec_helper.rb",
93
+ "tests/cgi_test/config_cgi.rb",
94
+ "tests/cgi_test/threadded_content_test.rhtml",
95
+ "tests/cgi_test/vars_get_test.rhtml",
96
+ "tests/cgi_test/vars_header_test.rhtml",
97
+ "tests/cgi_test/vars_post_test.rhtml",
98
+ "tests/fcgi_test/config_fcgi.rb",
99
+ "tests/fcgi_test/index.rhtml",
100
+ "tests/fcgi_test/sleeper.rhtml",
101
+ "tests/fcgi_test/threadded_content_test.rhtml",
102
+ "tests/fcgi_test/vars_get_test.rhtml",
103
+ "tests/fcgi_test/vars_header_test.rhtml",
104
+ "tests/fcgi_test/vars_post_test.rhtml"
105
+ ]
106
+ s.homepage = %q{http://github.com/kaspernj/hayabusa}
107
+ s.licenses = ["MIT"]
108
+ s.require_paths = ["lib"]
109
+ s.rubygems_version = %q{1.6.2}
110
+ s.summary = %q{A threadded web/app-server that supports stand-alone, CGI and FCGI-modes.}
111
+
112
+ if s.respond_to? :specification_version then
113
+ s.specification_version = 3
114
+
115
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
116
+ s.add_runtime_dependency(%q<knjrbfw>, [">= 0"])
117
+ s.add_runtime_dependency(%q<erubis>, [">= 0"])
118
+ s.add_runtime_dependency(%q<mail>, [">= 0"])
119
+ s.add_runtime_dependency(%q<datet>, [">= 0"])
120
+ s.add_runtime_dependency(%q<http2>, [">= 0"])
121
+ s.add_development_dependency(%q<json>, [">= 0"])
122
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
123
+ s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
124
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.3"])
125
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
126
+ else
127
+ s.add_dependency(%q<knjrbfw>, [">= 0"])
128
+ s.add_dependency(%q<erubis>, [">= 0"])
129
+ s.add_dependency(%q<mail>, [">= 0"])
130
+ s.add_dependency(%q<datet>, [">= 0"])
131
+ s.add_dependency(%q<http2>, [">= 0"])
132
+ s.add_dependency(%q<json>, [">= 0"])
133
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
134
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
135
+ s.add_dependency(%q<jeweler>, ["~> 1.6.3"])
136
+ s.add_dependency(%q<sqlite3>, [">= 0"])
137
+ end
138
+ else
139
+ s.add_dependency(%q<knjrbfw>, [">= 0"])
140
+ s.add_dependency(%q<erubis>, [">= 0"])
141
+ s.add_dependency(%q<mail>, [">= 0"])
142
+ s.add_dependency(%q<datet>, [">= 0"])
143
+ s.add_dependency(%q<http2>, [">= 0"])
144
+ s.add_dependency(%q<json>, [">= 0"])
145
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
146
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
147
+ s.add_dependency(%q<jeweler>, ["~> 1.6.3"])
148
+ s.add_dependency(%q<sqlite3>, [">= 0"])
149
+ end
150
+ end
151
+
@@ -0,0 +1,518 @@
1
+ require "base64"
2
+ require "digest"
3
+ require "stringio"
4
+ require "timeout"
5
+
6
+ #The class that stands for the whole appserver / webserver.
7
+ #===Examples
8
+ # appsrv = Hayabusa.new(
9
+ # :locales_root => "/some/path/locales",
10
+ # :locales_gettext_funcs => true,
11
+ # :magic_methods => true
12
+ # )
13
+ # appsrv.start
14
+ # appsrv.join
15
+ class Hayabusa
16
+ @@path = File.dirname(__FILE__)
17
+
18
+ attr_reader :cio, :config, :httpserv, :debug, :db, :db_handler, :ob, :translations, :paused, :should_restart, :events, :mod_event, :db_handler, :gettext, :sessions, :logs_access_pending, :threadpool, :vars, :magic_procs, :magic_vars, :types, :eruby_cache, :httpsessions_ids
19
+ attr_accessor :served, :should_restart, :should_restart_done
20
+
21
+ #Autoloader for subclasses.
22
+ def self.const_missing(name)
23
+ require "#{File.dirname(__FILE__)}/hayabusa_#{name.to_s.downcase}.rb"
24
+ return Hayabusa.const_get(name)
25
+ end
26
+
27
+ def initialize(config)
28
+ raise "No arguments given." if !config.is_a?(Hash)
29
+
30
+ @config = {
31
+ :host => "0.0.0.0",
32
+ :timeout => 30,
33
+ :default_page => "index.rhtml",
34
+ :default_filetype => "text/html",
35
+ :max_requests_working => 20,
36
+ :size_send => 1024,
37
+ :cleaner_timeout => 300
38
+ }.merge(config)
39
+
40
+ @config[:smtp_args] = {"smtp_host" => "localhost", "smtp_port" => 25} if !@config[:smtp_args]
41
+ @config[:timeout] = 30 if !@config.has_key?(:timeout)
42
+ raise "No ':doc_root' was given in arguments." if !@config.has_key?(:doc_root)
43
+
44
+
45
+ #Require gems.
46
+ require "rubygems"
47
+ gems = [
48
+ [:Erubis, "erubis"],
49
+ [:Knj, "knjrbfw"],
50
+ [:Tsafe, "tsafe"]
51
+ ]
52
+
53
+ gems.each do |gem|
54
+ if Kernel.const_defined?(gem[0])
55
+ puts "Gem already loaded: '#{gem[1]}'." if @debug
56
+ next
57
+ end
58
+
59
+ fpath = "#{@@path}/../../#{gem[1]}/lib/#{gem[1]}.rb"
60
+
61
+ if File.exists?(fpath)
62
+ puts "Loading custom gem-path: '#{fpath}'." if @debug
63
+ require fpath
64
+ else
65
+ puts "Loading gem: '#{gem[1]}'." if @debug
66
+ require gem[1]
67
+ end
68
+ end
69
+
70
+
71
+ #Setup default handlers if none are given.
72
+ if !@config.has_key?(:handlers)
73
+ @erb_handler = Hayabusa::Erb_handler.new
74
+ @config[:handlers] = [
75
+ {
76
+ :file_ext => "rhtml",
77
+ :callback => @erb_handler.method(:erb_handler)
78
+ },{
79
+ :path => "/fckeditor",
80
+ :mount => "/usr/share/fckeditor"
81
+ }
82
+ ]
83
+ end
84
+
85
+
86
+ #Add extra handlers if given.
87
+ @config[:handlers] += @config[:handlers_extra] if @config[:handlers_extra]
88
+
89
+
90
+ #Setup cache to make .rhtml-calls faster.
91
+ @config[:handlers_cache] = {}
92
+ @config[:handlers].each do |handler_info|
93
+ next if !handler_info[:file_ext] or !handler_info[:callback]
94
+ @config[:handlers_cache][handler_info[:file_ext]] = handler_info[:callback]
95
+ end
96
+
97
+
98
+ @debug = @config[:debug]
99
+ @paused = 0
100
+ @paused_mutex = Mutex.new
101
+ @should_restart = false
102
+ @mod_events = {}
103
+ @served = 0
104
+ @mod_files = {}
105
+ @sessions = {}
106
+ @eruby_cache = {}
107
+ @httpsessions_ids = {}
108
+
109
+ @path_hayabusa = File.dirname(__FILE__)
110
+
111
+
112
+ #If auto-restarting is enabled - start the modified events-module.
113
+ if @config[:autorestart]
114
+ paths = [
115
+ "#{@path_hayabusa}/class_customio.rb"
116
+ ]
117
+
118
+ print "Auto restarting.\n" if @debug
119
+ @mod_event = Knj::Event_filemod.new(:wait => 2, :paths => paths, &self.method(:on_event_filemod))
120
+ end
121
+
122
+
123
+ #Set up default file-types and merge given filetypes into it.
124
+ @types = {
125
+ :ico => "image/x-icon",
126
+ :jpeg => "image/jpeg",
127
+ :jpg => "image/jpeg",
128
+ :gif => "image/gif",
129
+ :png => "image/png",
130
+ :html => "text/html",
131
+ :htm => "text/html",
132
+ :rhtml => "text/html",
133
+ :css => "text/css",
134
+ :xml => "text/xml",
135
+ :js => "text/javascript"
136
+ }
137
+ @types.merge!(@config[:filetypes]) if @config.key?(:filetypes)
138
+
139
+
140
+
141
+ #Load various required files in the hayabusa-framework.
142
+ files = [
143
+ "#{@path_hayabusa}/hayabusa_ext/errors.rb",
144
+ "#{@path_hayabusa}/hayabusa_ext/logging.rb",
145
+ "#{@path_hayabusa}/hayabusa_ext/mailing.rb",
146
+ "#{@path_hayabusa}/hayabusa_ext/sessions.rb",
147
+ "#{@path_hayabusa}/hayabusa_ext/translations.rb",
148
+ "#{@path_hayabusa}/hayabusa_ext/web.rb"
149
+ ]
150
+
151
+ files.each do |file|
152
+ STDOUT.print "Loading: '#{file}'.\n" if @debug
153
+ self.loadfile(file)
154
+ end
155
+
156
+
157
+ print "Setting up database.\n" if @debug
158
+ if @config[:db].is_a?(Knj::Db)
159
+ @db = @config[:db]
160
+ elsif @config[:db].is_a?(Hash)
161
+ @db = Knj::Db.new(@config[:db])
162
+ elsif !@config[:db] and @config[:db_args]
163
+ @db = Knj::Db.new(@config[:db_args])
164
+ else
165
+ if @config[:title]
166
+ db_title = @config[:title]
167
+ else
168
+ db_title = Time.now.to_f.to_s.hash
169
+ end
170
+
171
+ db_path = "#{Knj::Os.tmpdir}/hayabusa_fallback_db_#{db_title}.sqlite3"
172
+ @config[:dbrev] = true
173
+
174
+ require "sqlite3" if RUBY_ENGINE != "jruby"
175
+ @db = Knj::Db.new(
176
+ :type => "sqlite3",
177
+ :path => db_path,
178
+ :return_keys => "symbols",
179
+ :index_append_table_name => true
180
+ )
181
+ end
182
+
183
+
184
+ if !@config.key?(:dbrev) or @config[:dbrev]
185
+ print "Updating database.\n" if @debug
186
+ dbrev_args = {"schema" => Hayabusa::Database::SCHEMA, "db" => @db}
187
+ dbrev_args.merge!(@config[:dbrev_args]) if @config.key?(:dbrev_args)
188
+ Knj::Db::Revision.new.init_db(dbrev_args)
189
+ dbrev_args = nil
190
+ end
191
+
192
+
193
+ print "Spawning objects.\n" if @debug
194
+ @ob = Knj::Objects.new(
195
+ :db => db,
196
+ :class_path => @path_hayabusa,
197
+ :module => Hayabusa::Models,
198
+ :datarow => true,
199
+ :hayabusa => self,
200
+ :require => false
201
+ )
202
+ @ob.events.connect(:no_date, &self.method(:no_date))
203
+
204
+
205
+ if @config[:httpsession_db_args]
206
+ @db_handler = Knj::Db.new(@config[:httpsession_db_args])
207
+ else
208
+ @db_handler = @db
209
+ end
210
+
211
+
212
+ if @config[:locales_root]
213
+ @gettext = Knj::Gettext_threadded.new("dir" => config[:locales_root])
214
+ end
215
+
216
+ require "#{@path_hayabusa}/kernel_ext/gettext_methods" if @config[:locales_gettext_funcs]
217
+
218
+ if @config[:magic_methods] or !@config.has_key?(:magic_methods)
219
+ print "Loading magic-methods.\n" if @debug
220
+ require "#{@path_hayabusa}/kernel_ext/magic_methods"
221
+ end
222
+
223
+ if @config[:customio] or !@config.has_key?(:customio)
224
+ print "Loading custom-io.\n" if @debug
225
+
226
+ if $stdout.class.name != "Hayabusa::Custom_io"
227
+ @cio = Hayabusa::Custom_io.new
228
+ $stdout = @cio
229
+ end
230
+ end
231
+
232
+
233
+ #Save the PID to the run-file.
234
+ print "Setting run-file.\n" if @debug
235
+ tmpdir = "#{Knj::Os.tmpdir}/hayabusa"
236
+ tmppath = "#{tmpdir}/run_#{@config[:title]}"
237
+
238
+ if !File.exists?(tmpdir)
239
+ Dir.mkdir(tmpdir)
240
+ File.chmod(0777, tmpdir)
241
+ end
242
+
243
+ File.open(tmppath, "w") do |fp|
244
+ fp.write(Process.pid)
245
+ end
246
+ File.chmod(0777, tmppath)
247
+
248
+
249
+ #Set up various events for the appserver.
250
+ if !@config.key?(:events) or @config[:events]
251
+ print "Loading events.\n" if @debug
252
+ @events = Knj::Event_handler.new
253
+ @events.add_event(
254
+ :name => :check_page_access,
255
+ :connections_max => 1
256
+ )
257
+ @events.add_event(
258
+ :name => :ob,
259
+ :connections_max => 1
260
+ )
261
+ @events.add_event(
262
+ :name => :trans_no_str,
263
+ :connections_max => 1
264
+ )
265
+ @events.add_event(
266
+ :name => :request_done,
267
+ :connections_max => 1
268
+ )
269
+ @events.add_event(
270
+ :name => :request_begin,
271
+ :connections_max => 1
272
+ )
273
+
274
+ #This event is used if the user himself wants stuff to be cleaned up when the appserver is cleaning up stuff.
275
+ @events.add_event(
276
+ :name => :on_clean
277
+ )
278
+ end
279
+
280
+ #Set up the 'vars'-variable that can be used to set custom global variables for web-requests.
281
+ @vars = Knj::Hash_methods.new
282
+ @magic_vars = {}
283
+ @magic_procs = {}
284
+
285
+
286
+ #Initialize the various feature-modules.
287
+ print "Init sessions.\n" if @debug
288
+ self.initialize_sessions
289
+
290
+ if !@config.key?(:threadding) or @config[:threadding]
291
+ self.loadfile("#{@path_hayabusa}/hayabusa_ext/threadding.rb")
292
+ self.loadfile("#{@path_hayabusa}/hayabusa_ext/threadding_timeout.rb")
293
+ print "Init threadding.\n" if @debug
294
+ self.initialize_threadding
295
+ end
296
+
297
+ print "Init mailing.\n" if @debug
298
+ self.initialize_mailing
299
+
300
+ print "Init errors.\n" if @debug
301
+ self.initialize_errors
302
+
303
+ print "Init logging.\n" if @debug
304
+ self.initialize_logging
305
+
306
+ if !@config.key?(:cleaner) or @config[:cleaner]
307
+ self.loadfile("#{@path_hayabusa}/hayabusa_ext/cleaner.rb")
308
+ print "Init cleaner.\n" if @debug
309
+ self.initialize_cleaner
310
+ end
311
+
312
+ if !@config.key?(:cmdline) or @config[:cmdline]
313
+ self.loadfile("#{@path_hayabusa}/hayabusa_ext/cmdline.rb")
314
+ print "Init cmdline.\n" if @debug
315
+ self.initialize_cmdline
316
+ end
317
+
318
+
319
+ #Clear memory at exit.
320
+ Kernel.at_exit(&self.method(:stop))
321
+
322
+
323
+ print "Appserver spawned.\n" if @debug
324
+ end
325
+
326
+ def no_date(event, classname)
327
+ return "[no date]"
328
+ end
329
+
330
+ def on_event_filemod(event, path)
331
+ print "File changed - restart server: #{path}\n"
332
+ @should_restart = true
333
+ @mod_event.destroy if @mod_event
334
+ end
335
+
336
+ #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.
337
+ def loadfile(fpath)
338
+ if !@config[:autorestart]
339
+ require fpath
340
+ return nil
341
+ end
342
+
343
+ rpath = File.realpath(fpath)
344
+ raise "No such filepath: #{fpath}" if !rpath or !File.exists?(rpath)
345
+
346
+ return true if @mod_files[rpath]
347
+
348
+ @mod_event.args[:paths] << rpath
349
+ @mod_files = rpath
350
+
351
+ require rpath
352
+ return false
353
+ end
354
+
355
+ #Start a new CGI-request.
356
+ def start_cgi_request
357
+ @cgi_http_session = Hayabusa::Cgi_session.new(:hb => self)
358
+ end
359
+
360
+ #Starts the HTTP-server and threadpool.
361
+ def start
362
+ #Start the appserver.
363
+ print "Spawning appserver.\n" if @debug
364
+ @httpserv = Hayabusa::Http_server.new(self)
365
+
366
+
367
+ STDOUT.print "Starting appserver.\n" if @debug
368
+ Thread.current[:hayabusa] = {:hb => self} if !Thread.current[:hayabusa]
369
+
370
+ if @config[:autoload]
371
+ STDOUT.print "Autoloading #{@config[:autoload]}\n" if @debug
372
+ require @config[:autoload]
373
+ end
374
+
375
+ begin
376
+ @threadpool.start if @threadpool
377
+ STDOUT.print "Threadpool startet.\n" if @debug
378
+ @httpserv.start
379
+ STDOUT.print "Appserver startet.\n" if @debug
380
+ rescue Interrupt => e
381
+ STDOUT.print "Got interrupt - trying to stop appserver.\n" if @debug
382
+ self.stop
383
+ raise e
384
+ end
385
+ end
386
+
387
+ #Stops the entire app and releases join.
388
+ def stop
389
+ return nil if @stop_called
390
+ @stop_called = true
391
+
392
+ proc_stop = proc{
393
+ STDOUT.print "Stopping appserver.\n" if @debug
394
+ @httpserv.stop if @httpserv and @httpserv.respond_to?(:stop)
395
+
396
+ STDOUT.print "Stopping threadpool.\n" if @debug
397
+ @threadpool.stop if @threadpool
398
+
399
+ #This should be done first to be sure it finishes (else we have a serious bug).
400
+ STDOUT.print "Flush out loaded sessions.\n" if @debug
401
+ self.sessions_flush
402
+
403
+ STDOUT.print "Stopping done...\n" if @debug
404
+ }
405
+
406
+ #If we cant get a paused-execution in 5 secs - we just force the stop.
407
+ begin
408
+ Timeout.timeout(5) do
409
+ self.paused_exec(&proc_stop)
410
+ end
411
+ rescue Timeout::Error, SystemExit, Interrupt
412
+ STDOUT.print "Forcing stop-appserver - couldnt get timing window.\n" if @debug
413
+ proc_stop.call
414
+ end
415
+ end
416
+
417
+ #Stop running any more HTTP-requests - make them wait.
418
+ def pause
419
+ @paused += 1
420
+ end
421
+
422
+ #Unpause - start handeling HTTP-requests again.
423
+ def unpause
424
+ @paused -= 1
425
+ end
426
+
427
+ #Returns true if paued - otherwise false.
428
+ def paused?
429
+ return true if @paused > 0
430
+ return false
431
+ end
432
+
433
+ #Will stop handeling any more HTTP-requests, run the proc given and return handeling HTTP-requests.
434
+ def paused_exec
435
+ raise "No block given." if !block_given?
436
+ self.pause
437
+
438
+ begin
439
+ sleep 0.2 while @httpserv and @httpserv.working_count and @httpserv.working_count > 0
440
+ @paused_mutex.synchronize do
441
+ Timeout.timeout(15) do
442
+ yield
443
+ end
444
+ end
445
+ ensure
446
+ self.unpause
447
+ end
448
+ end
449
+
450
+ #Returns true if a HTTP-request is working. Otherwise false.
451
+ def working?
452
+ return true if @httpserv and @httpserv.working_count > 0
453
+ return false
454
+ end
455
+
456
+ def self.data
457
+ raise "Could not register current thread." if !Thread.current[:hayabusa]
458
+ return Thread.current[:hayabusa]
459
+ end
460
+
461
+ #Sleeps until the server is stopped.
462
+ def join
463
+ raise "No http-server or http-server not running." if !@httpserv or !@httpserv.thread_accept
464
+
465
+ begin
466
+ @httpserv.thread_accept.join
467
+ @httpserv.thread_restart.join if @httpserv and @httpserv.thread_restart
468
+ rescue Interrupt => e
469
+ self.stop
470
+ end
471
+
472
+ if @should_restart
473
+ loop do
474
+ if @should_restart_done
475
+ STDOUT.print "Ending join because the restart is done.\n"
476
+ break
477
+ end
478
+
479
+ sleep 1
480
+ end
481
+ end
482
+ end
483
+
484
+ #Defines a variable as a method bound to the threads spawned by this instance of Hayabusa.
485
+ def define_magic_var(method_name, var)
486
+ @magic_vars[method_name] = var
487
+
488
+ if !Object.respond_to?(method_name)
489
+ Object.send(:define_method, method_name) do
490
+ return Thread.current[:hayabusa][:hb].magic_vars[method_name] if Thread.current[:hayabusa] and Thread.current[:hayabusa][:hb]
491
+ raise "Could not figure out the object: '#{method_name}'."
492
+ end
493
+ end
494
+ end
495
+
496
+ def define_magic_proc(method_name, &block)
497
+ raise "No block given." if !block_given?
498
+ @magic_procs[method_name] = block
499
+
500
+ if !Object.respond_to?(method_name)
501
+ Object.send(:define_method, method_name) do
502
+ return Thread.current[:hayabusa][:hb].magic_procs[method_name].call(:hb => self) if Thread.current[:hayabusa] and Thread.current[:hayabusa][:hb]
503
+ raise "Could not figure out the object: '#{method_name}'."
504
+ end
505
+ end
506
+ end
507
+
508
+ def translations
509
+ if !@translations
510
+ #Start the Knj::Gettext_threadded- and Knj::Translations modules for translations.
511
+ print "Loading Gettext and translations.\n" if @debug
512
+ @translations = Knj::Translations.new(:db => @db)
513
+ @ob.requireclass(:Translation, :require => false, :class => Knj::Translations::Translation)
514
+ end
515
+
516
+ return @translations
517
+ end
518
+ end