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,190 @@
1
+ class Hayabusa
2
+ #Imports a .rhtml-file and executes it.
3
+ #===Examples
4
+ # _hb.import("/some/path/page.rhtml")
5
+ def import(filepath)
6
+ if filepath.to_s.index("../proc/self") != nil
7
+ raise Errno::EACCES, "Possible attempt to hack the appserver."
8
+ end
9
+
10
+ _httpsession.eruby.import(filepath)
11
+ end
12
+
13
+ #Redirects to another URL.
14
+ #===Examples
15
+ # _hb.redirect("someotherpage.rhtml")
16
+ # _hb.redirect("newpage.rhtml", :perm => true)
17
+ def redirect(url, args = {})
18
+ #Header way
19
+ if !_httpsession.alert_sent and !self.headers_sent?
20
+ if args[:perm]
21
+ _httpsession.resp.status = 301 if !self.headers_sent?
22
+ else
23
+ _httpsession.resp.status = 303 if !self.headers_sent?
24
+ end
25
+
26
+ self.header("Location", url) if !self.headers_sent?
27
+ end
28
+
29
+ print "<script type=\"text/javascript\">location.href=\"#{url}\";</script>"
30
+ exit
31
+ end
32
+
33
+ #Sends a javascript-alert to the HTML.
34
+ #===Examples
35
+ # _hb.alert("Hello world!")
36
+ def alert(msg)
37
+ _httpsession.alert_sent = true
38
+ Knj::Web.alert(msg)
39
+ return self
40
+ end
41
+
42
+ #Define a cookies in the clients browser.
43
+ #===Examples
44
+ # _hb.cookie(:name => "MyCookie", :value => "Trala")
45
+ def cookie(cookie)
46
+ raise "No HTTP-session attached to this thread." if !_httpsession
47
+ raise "HTTP-session not active." if !_httpsession.resp
48
+ raise "Not a hash: '#{cookie.class.name}', '#{cookie}'." unless cookie.is_a?(Hash)
49
+ _httpsession.resp.cookie(cookie)
50
+ return nil
51
+ end
52
+
53
+ #Sends a header to the clients browser.
54
+ #===Examples
55
+ # _hb.header("Content-Type", "text/javascript")
56
+ def header(key, val)
57
+ raise "No HTTP-session attached to this thread." if !_httpsession
58
+ raise "HTTP-session not active." if !_httpsession.resp
59
+ _httpsession.resp.header(key, val)
60
+ return nil
61
+ end
62
+
63
+ #Sends a raw header-line to the clients browser.
64
+ def header_raw(str)
65
+ raise "No HTTP-session attached to this thread." if !_httpsession
66
+ raise "HTTP-session not active." if !_httpsession.resp
67
+ Php4r.header(str)
68
+ return nil
69
+ end
70
+
71
+ #Returns true if the headers are already sent.
72
+ #===Examples
73
+ # _hb.headers_sent? #=> true
74
+ def headers_sent?
75
+ return true if _httpsession.resp.headers_sent
76
+ return false
77
+ end
78
+
79
+ #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.
80
+ #===Examples
81
+ #Set the size to 200 kb.
82
+ # _hb.headers_send_size = (1024 * 200)
83
+ def headers_send_size=(newsize)
84
+ raise "The headers are already sent and you cannot modify the send-size any more." if self.headers_sent?
85
+ _httpsession.size_send = newsize.to_i
86
+ return nil
87
+ end
88
+
89
+ #Serves the given filepath and enables caching for it. No other content should be written to the page when using this method.
90
+ #===Examples
91
+ # _hb.header("Content-Type", "text/javascript")
92
+ # _hb.serve_file("somefile.js")
93
+ def serve_file(filepath)
94
+ raise "File doesnt exist: '#{filepath}'." if !File.exists?(filepath)
95
+ httpsess = _httpsession
96
+ headers = httpsess.headers
97
+ resp = httpsess.resp
98
+
99
+ if headers["cache-control"] and headers["cache-control"][0]
100
+ cache_control = {}
101
+ headers["cache-control"][0].scan(/(.+)=(.+)/) do |match|
102
+ cache_control[match[1]] = match[2]
103
+ end
104
+ end
105
+
106
+ cache_dont = true if cache_control and cache_control.key?("max-age") and cache_control["max-age"].to_i <= 0
107
+ lastmod = File.mtime(filepath)
108
+
109
+ self.header("Last-Modified", lastmod.httpdate)
110
+ self.header("Expires", (Time.now + 86400).httpdate) #next day.
111
+
112
+ if !cache_dont and headers["if-modified-since"] and headers["if-modified-since"][0]
113
+ request_mod = Datet.in(headers["if-modified-since"].first).time
114
+
115
+ if request_mod == lastmod
116
+ resp.status = 304
117
+ return nil
118
+ end
119
+ end
120
+
121
+ httpsess.force_content(:type => :file, :path => filepath)
122
+ return nil
123
+ end
124
+
125
+ #Sends a javascript back to the browser and exits.
126
+ def back
127
+ Knj::Web.back
128
+ end
129
+
130
+ #Draw a input in a table.
131
+ def inputs(*args)
132
+ return Knj::Web.inputs(args)
133
+ end
134
+
135
+ #Urlencodes a string.
136
+ #===Examples
137
+ # _hb.redirect("mypage.rhtml?arg=#{_hb.urlenc(value_variable)}")
138
+ def urlenc(str)
139
+ return Knj::Web.urlenc(str)
140
+ end
141
+
142
+ #Urldecodes a string.
143
+ def urldec(str)
144
+ return Knj::Web.urldec(str)
145
+ end
146
+
147
+ #Returns a number localized as a string.
148
+ def num(*args)
149
+ return Knj::Locales.number_out(*args)
150
+ end
151
+
152
+ #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.
153
+ def get_parse_arrays(arg = nil, ob = nil)
154
+ arg = _get.clone if !arg
155
+
156
+ #Parses key-numeric-hashes into arrays and converts special model-strings into actual models.
157
+ if arg.is_a?(Hash) and Knj::ArrayExt.hash_numeric_keys?(arg)
158
+ arr = []
159
+
160
+ arg.each do |key, val|
161
+ arr << val
162
+ end
163
+
164
+ return self.get_parse_arrays(arr, ob)
165
+ elsif arg.is_a?(Hash)
166
+ arg.each do |key, val|
167
+ arg[key] = self.get_parse_arrays(val, ob)
168
+ end
169
+
170
+ return arg
171
+ elsif arg.is_a?(Array)
172
+ arg.each_index do |key|
173
+ arg[key] = self.get_parse_arrays(arg[key], ob)
174
+ end
175
+
176
+ return arg
177
+ elsif arg.is_a?(String) and match = arg.match(/^#<Model::(.+?)::(\d+)>$/)
178
+ ob = @ob if !ob
179
+ return ob.get(match[1], match[2])
180
+ else
181
+ return arg
182
+ end
183
+ end
184
+
185
+ #Returns the socket-port the appserver is currently running on.
186
+ def port
187
+ raise "Http-server not spawned yet. Call Hayabusa#start to spawn it." if !@httpserv
188
+ return @httpserv.server.addr[1]
189
+ end
190
+ end
@@ -0,0 +1,102 @@
1
+ require "socket"
2
+
3
+ class Hayabusa::Http_server
4
+ attr_accessor :working_count
5
+ attr_reader :hb, :http_sessions, :thread_accept, :thread_restart, :server
6
+
7
+ def initialize(hb)
8
+ @hb = hb
9
+ @debug = @hb.config[:debug]
10
+ @mutex_count = Mutex.new
11
+ end
12
+
13
+ def start
14
+ @http_sessions = []
15
+ @working_count = 0
16
+
17
+ raise "No host was given." if @hb and !@hb.config.has_key?(:host)
18
+ raise "No port was given." if @hb and !@hb.config.has_key?(:port)
19
+
20
+ @server = TCPServer.new(@hb.config[:host], @hb.config[:port])
21
+
22
+ @thread_accept = Thread.new do
23
+ loop do
24
+ if !@server or @server.closed?
25
+ STDOUT.puts "Starting TCPServer." if @debug
26
+ @server = TCPServer.new(@hb.config[:host], @hb.config[:port])
27
+ end
28
+
29
+ begin
30
+ STDOUT.puts "Trying to spawn new HTTP-session from socket-accept." if @debug
31
+ self.spawn_httpsession(@server.accept)
32
+ STDOUT.puts "Starting new HTTP-request." if @debug
33
+ rescue Exception => e
34
+ if @debug
35
+ STDOUT.puts Knj::Errors.error_str(e)
36
+ STDOUT.print "Could not accept HTTP-request - waiting 1 sec and then trying again.\n"
37
+ end
38
+
39
+ raise e if e.is_a?(SystemExit) or e.is_a?(Interrupt)
40
+ sleep 1
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def stop
47
+ STDOUT.print "Stopping accept-thread.\n" if @debug
48
+ @thread_accept.kill if @thread_accept and @thread_accept.alive?
49
+ @thread_restart.kill if @thread_restart and @thread_restart.alive?
50
+
51
+ STDOUT.print "Stopping all HTTP sessions.\n" if @debug
52
+ if @http_sessions
53
+ @http_sessions.each do |httpsession|
54
+ httpsession.destruct
55
+ end
56
+ end
57
+
58
+ begin
59
+ STDOUT.print "Stopping TCPServer.\n" if @debug
60
+ @server.close if @server and !@server.closed?
61
+ STDOUT.print "TCPServer was closed.\n" if @debug
62
+ rescue Timeout::Error
63
+ raise "Could not close TCPserver.\n"
64
+ rescue IOError => e
65
+ if e.message == "closed stream"
66
+ #ignore - it should be closed.
67
+ else
68
+ raise e
69
+ end
70
+ end
71
+
72
+ @http_sessions = nil
73
+ @thread_accept = nil
74
+ @thread_restart = nil
75
+ @server = nil
76
+ @working_count = nil
77
+ @hb = nil
78
+ end
79
+
80
+ def spawn_httpsession(socket)
81
+ STDOUT.puts "Starting new HTTP-session." if @debug
82
+ @http_sessions << Hayabusa::Http_session.new(self, socket)
83
+ end
84
+
85
+ def count_block
86
+ begin
87
+ added = false
88
+ @mutex_count.synchronize do
89
+ @working_count += 1 if @working_count != nil
90
+ added = true
91
+ end
92
+
93
+ yield
94
+ ensure
95
+ @hb.served += 1 if @hb
96
+
97
+ @mutex_count.synchronize do
98
+ @working_count -= 1 if @working_count != nil and added
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,361 @@
1
+ #This class handels the HTTP-sessions.
2
+ class Hayabusa::Http_session
3
+ attr_accessor :data, :alert_sent
4
+ attr_reader :cookie, :get, :headers, :session, :session_id, :session_hash, :hb, :active, :out, :eruby, :browser, :debug, :resp, :page_path, :post, :cgroup, :meta, :httpsession_var, :handler, :working
5
+
6
+ #Autoloader for subclasses.
7
+ def self.const_missing(name)
8
+ require "#{File.dirname(__FILE__)}/hayabusa_http_session_#{name.to_s.downcase}.rb"
9
+ raise "Still not defined: '#{name}'." if !Hayabusa::Http_session.const_defined?(name)
10
+ return Hayabusa::Http_session.const_get(name.to_s.to_sym)
11
+ end
12
+
13
+ def initialize(httpserver, socket)
14
+ @data = {}
15
+ @socket = socket
16
+ @httpserver = httpserver
17
+ @hb = httpserver.hb
18
+ @types = @hb.types
19
+ @config = @hb.config
20
+ @active = true
21
+ @debug = @hb.debug
22
+ @handlers_cache = @config[:handlers_cache]
23
+ @httpsession_var = {}
24
+
25
+ @eruby = Knj::Eruby.new(
26
+ :cache_hash => @hb.eruby_cache,
27
+ :binding_callback => self.method(:create_binding)
28
+ )
29
+
30
+ #Set socket stuff.
31
+ if RUBY_PLATFORM == "java" or RUBY_ENGINE == "rbx"
32
+ if @hb.config[:peeraddr_static]
33
+ addr_peer = [0, 0, @hb.config[:peeraddr_static]]
34
+ else
35
+ addr_peer = @socket.peeraddr
36
+ end
37
+
38
+ addr = @socket.addr
39
+ else
40
+ addr = @socket.addr(false)
41
+ addr_peer = @socket.peeraddr(false)
42
+ end
43
+
44
+ @socket_meta = {
45
+ "REMOTE_ADDR" => addr[2],
46
+ "REMOTE_PORT" => addr[1],
47
+ "SERVER_ADDR" => addr_peer[2],
48
+ "SERVER_PORT" => addr_peer[1]
49
+ }
50
+
51
+ @resp = Hayabusa::Http_session::Response.new(:socket => @socket)
52
+ @handler = Hayabusa::Http_session::Request.new(:hb => @hb, :httpsession => self)
53
+ @cgroup = Hayabusa::Http_session::Contentgroup.new(:socket => @socket, :hb => @hb, :resp => @resp, :httpsession => self)
54
+ @resp.cgroup = @cgroup
55
+
56
+ Dir.chdir(@config[:doc_root])
57
+ ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) if @debug
58
+ STDOUT.print "New httpsession #{self.__id__} (total: #{@httpserver.http_sessions.count}).\n" if @debug
59
+
60
+ @thread_request = Thread.new(&self.method(:thread_request_run))
61
+ end
62
+
63
+ def thread_request_run
64
+ Thread.current[:hayabusa] = {} if !Thread.current[:hayabusa]
65
+ Thread.current[:type] = :httpsession
66
+
67
+ if @config.key?(:max_requests_working)
68
+ max_requests_working = @config[:max_requests_working].to_i
69
+ else
70
+ max_requests_working = false
71
+ end
72
+
73
+ begin
74
+ while @active
75
+ begin
76
+ @cgroup.reset
77
+ @written_size = 0
78
+ @size_send = @config[:size_send]
79
+ @alert_sent = false
80
+ @working = false
81
+ break if @hb.should_restart
82
+
83
+ STDOUT.print "#{__id__} - Waiting to parse from socket.\n" if @debug
84
+ Timeout.timeout(1800) do
85
+ @handler.socket_parse(@socket)
86
+ end
87
+
88
+ STDOUT.print "#{__id__} - Done parsing from socket.\n" if @debug
89
+
90
+ while @hb.paused? #Check if we should be waiting with executing the pending request.
91
+ STDOUT.print "#{__id__} - Paused! (#{@hb.paused}) - sleeping.\n" if @debug
92
+ sleep 0.1
93
+ end
94
+
95
+ break if @hb.should_restart
96
+
97
+ if max_requests_working and @httpserver
98
+ while @httpserver.working_count.to_i >= max_requests_working
99
+ STDOUT.print "#{__id__} - Maximum amounts of requests are working (#{@httpserver.working_count}, #{max_requests_working}) - sleeping.\n" if @debug
100
+ sleep 0.1
101
+ end
102
+ end
103
+
104
+ #Reserve database connections.
105
+ @hb.db_handler.get_and_register_thread if @hb.db_handler.opts[:threadsafe]
106
+ @hb.ob.db.get_and_register_thread if @hb.ob.db.opts[:threadsafe]
107
+
108
+ @working = true
109
+ STDOUT.print "#{__id__} - Serving.\n" if @debug
110
+
111
+ @httpserver.count_block do
112
+ self.serve
113
+ end
114
+ ensure
115
+ STDOUT.print "#{__id__} - Closing request.\n" if @debug
116
+ @working = false
117
+
118
+ #Free reserved database-connections.
119
+ @hb.db_handler.free_thread if @hb and @hb.db_handler.opts[:threadsafe]
120
+ @hb.ob.db.free_thread if @hb and @hb.ob.db.opts[:threadsafe]
121
+ end
122
+ end
123
+ rescue Timeout::Error
124
+ STDOUT.print "#{__id__} - Closing httpsession because of timeout.\n" if @debug
125
+ rescue Errno::ECONNRESET, Errno::ENOTCONN, Errno::EPIPE => e
126
+ STDOUT.print "#{__id__} - Connection error (#{e.inspect})...\n" if @debug
127
+ rescue Interrupt => e
128
+ raise e
129
+ rescue Exception => e
130
+ STDOUT.puts Knj::Errors.error_str(e)
131
+ ensure
132
+ self.destruct
133
+ end
134
+ end
135
+
136
+ #Creates a new Hayabusa::Binding-object and returns the binding for that object.
137
+ def create_binding
138
+ binding_obj = Hayabusa::Http_session::Page_environment.new(:httpsession => self, :hb => @hb)
139
+ return binding_obj.get_binding
140
+ end
141
+
142
+ #Is called when content is added and begings to write the output if it goes above the limit.
143
+ def add_size(size)
144
+ @written_size += size
145
+ @cgroup.write_output if @written_size >= @size_send
146
+ end
147
+
148
+ def threadded_content(block)
149
+ raise "No block was given." if !block
150
+ cgroup = Thread.current[:hayabusa][:contentgroup].new_thread
151
+
152
+ Thread.new do
153
+ begin
154
+ self.init_thread
155
+ cgroup.register_thread
156
+
157
+ @hb.db_handler.get_and_register_thread if @hb and @hb.db_handler.opts[:threadsafe]
158
+ @hb.ob.db.get_and_register_thread if @hb and @hb.ob.db.opts[:threadsafe]
159
+
160
+ block.call
161
+ rescue Exception => e
162
+ Thread.current[:hayabusa][:contentgroup].write Knj::Errors.error_str(e, {:html => true})
163
+ _hb.handle_error(e)
164
+ ensure
165
+ Thread.current[:hayabusa][:contentgroup].mark_done
166
+ @hb.ob.db.free_thread if @hb and @hb.ob.db.opts[:threadsafe]
167
+ @hb.db_handler.free_thread if @hb and @hb.db_handler.opts[:threadsafe]
168
+ end
169
+ end
170
+ end
171
+
172
+ def init_thread
173
+ Thread.current[:hayabusa] = {} if !Thread.current[:hayabusa]
174
+ Thread.current[:hayabusa][:hb] = @hb
175
+ Thread.current[:hayabusa][:httpsession] = self
176
+ Thread.current[:hayabusa][:session] = @session
177
+ Thread.current[:hayabusa][:get] = @get
178
+ Thread.current[:hayabusa][:post] = @post
179
+ Thread.current[:hayabusa][:meta] = @meta
180
+ Thread.current[:hayabusa][:cookie] = @cookie
181
+ end
182
+
183
+ def self.finalize(id)
184
+ STDOUT.print "Http_session finalize #{id}.\n" if @debug
185
+ end
186
+
187
+ def destruct
188
+ STDOUT.print "Http_session destruct (#{@httpserver.http_sessions.length})\n" if @debug and @httpserver and @httpserver.http_sessions
189
+
190
+ begin
191
+ @socket.close if !@socket.closed?
192
+ rescue => e
193
+ STDOUT.puts e.inspect
194
+ STDOUT.puts e.backtrace
195
+ #ignore if it fails...
196
+ end
197
+
198
+ @httpserver.http_sessions.delete(self) if @httpserver and @httpserver.http_sessions
199
+
200
+ @eruby.destroy if @eruby
201
+ @thread_request.kill if @thread_request.alive?
202
+ end
203
+
204
+ #Forces the content to be the input - nothing else can be added after calling this.
205
+ def force_content(newcont)
206
+ @cgroup.force_content(newcont)
207
+ end
208
+
209
+ def serve
210
+ STDOUT.print "Generating meta, cookie, get, post and headers.\n" if @debug
211
+ @meta = @handler.meta.merge(@socket_meta)
212
+ @cookie = @handler.cookie
213
+ @get = @handler.get
214
+ @post = @handler.post
215
+ @headers = @handler.headers
216
+
217
+ close = true if @meta["HTTP_CONNECTION"] == "close"
218
+ @resp.reset(
219
+ :http_version => @handler.http_version,
220
+ :close => close
221
+ )
222
+ if @handler.http_version == "1.1"
223
+ @cgroup.chunked = true
224
+ @resp.chunked = true
225
+ else
226
+ @cgroup.chunked = false
227
+ @resp.chunked = false
228
+ end
229
+
230
+ @page_path = @handler.page_path
231
+ @ext = File.extname(@page_path).downcase[1..-1].to_s
232
+
233
+ @ctype = @types[@ext.to_sym] if @ext.length > 0 and @types.key?(@ext.to_sym)
234
+ @ctype = @config[:default_filetype] if !@ctype and @config.key?(:default_filetype)
235
+ @resp.header("Content-Type", @ctype)
236
+
237
+ @browser = Knj::Web.browser(@meta)
238
+
239
+ if @meta["HTTP_X_FORWARDED_FOR"]
240
+ @ip = @meta["HTTP_X_FORWARDED_FOR"].split(",")[0].strip
241
+ elsif @meta["REMOTE_ADDR"]
242
+ @ip = @meta["REMOTE_ADDR"]
243
+ else
244
+ raise "Could not figure out the IP of the session."
245
+ end
246
+
247
+ STDOUT.print "Figuring out session-ID, session-object and more.\n" if @debug
248
+ if @cookie["HayabusaSession"].to_s.length > 0
249
+ @session_id = @cookie["HayabusaSession"]
250
+ elsif @browser["browser"] == "bot"
251
+ @session_id = "bot"
252
+ else
253
+ @session_id = @hb.session_generate_id(@meta)
254
+ send_cookie = true
255
+ end
256
+
257
+ begin
258
+ @session, @session_hash = @hb.session_fromid(@ip, @session_id, @meta)
259
+ rescue ArgumentError => e
260
+ #User should not have the session he asked for because of invalid user-agent or invalid IP.
261
+ @session_id = @hb.session_generate_id(@meta)
262
+ @session, @session_hash = @hb.session_fromid(@ip, @session_id, @meta)
263
+ send_cookie = true
264
+ end
265
+
266
+ if send_cookie
267
+ @resp.cookie(
268
+ "name" => "HayabusaSession",
269
+ "value" => @session_id,
270
+ "path" => "/",
271
+ "expires" => Time.now + 32140800 #add around 12 months
272
+ )
273
+ end
274
+
275
+ if @config.key?(:logging) and @config[:logging][:access_db]
276
+ STDOUT.print "Doing access-logging.\n" if @debug
277
+ @ips = [@meta["REMOTE_ADDR"]]
278
+ @ips << @meta["HTTP_X_FORWARDED_FOR"].split(",")[0].strip if @meta["HTTP_X_FORWARDED_FOR"]
279
+ @hb.logs_access_pending << {
280
+ :session_id => @session.id,
281
+ :date_request => Time.now,
282
+ :ips => @ips,
283
+ :get => @get,
284
+ :post => @post,
285
+ :meta => @meta,
286
+ :cookie => @cookie
287
+ }
288
+ end
289
+
290
+ STDOUT.print "Initializing thread and content-group.\n" if @debug
291
+ self.init_thread
292
+ Thread.current[:hayabusa][:contentgroup] = @cgroup
293
+ time_start = Time.now.to_f if @debug
294
+
295
+ begin
296
+ @hb.events.call(:request_begin, :httpsession => self) if @hb.events
297
+
298
+ Timeout.timeout(@hb.config[:timeout]) do
299
+ if @handlers_cache.key?(@ext)
300
+ STDOUT.print "Calling handler.\n" if @debug
301
+ @handlers_cache[@ext].call(self)
302
+ else
303
+ #check if we should use a handler for this request.
304
+ @config[:handlers].each do |handler_info|
305
+ if handler_info.key?(:file_ext) and handler_info[:file_ext] == @ext
306
+ return handler_info[:callback].call(self)
307
+ elsif handler_info.key?(:path) and handler_info[:mount] and @meta["SCRIPT_NAME"].slice(0, handler_info[:path].length) == handler_info[:path]
308
+ @page_path = "#{handler_info[:mount]}#{@meta["SCRIPT_NAME"].slice(handler_info[:path].length, @meta["SCRIPT_NAME"].length)}"
309
+ break
310
+ end
311
+ end
312
+
313
+ if !File.exists?(@page_path)
314
+ @resp.status = 404
315
+ @resp.header("Content-Type", "text/html")
316
+ @cgroup.write("File you are looking for was not found: '#{@meta["REQUEST_URI"]}'.")
317
+ else
318
+ if @headers["cache-control"] and @headers["cache-control"][0]
319
+ cache_control = {}
320
+ @headers["cache-control"][0].scan(/(.+)=(.+)/) do |match|
321
+ cache_control[match[1]] = match[2]
322
+ end
323
+ end
324
+
325
+ cache_dont = true if cache_control and cache_control.key?("max-age") and cache_control["max-age"].to_i <= 0
326
+ lastmod = File.mtime(@page_path)
327
+
328
+ @resp.header("Last-Modified", lastmod.httpdate)
329
+ @resp.header("Expires", (Time.now + 86400).httpdate) #next day.
330
+
331
+ if !cache_dont and @headers["if-modified-since"] and @headers["if-modified-since"][0]
332
+ request_mod = Datet.in(@headers["if-modified-since"].first).time
333
+
334
+ if request_mod == lastmod
335
+ @resp.status = 304
336
+ return nil
337
+ end
338
+ end
339
+
340
+ @cgroup.new_io(:type => :file, :path => @page_path)
341
+ end
342
+ end
343
+ end
344
+ rescue SystemExit
345
+ #do nothing - ignore.
346
+ rescue Timeout::Error
347
+ @resp.status = 500
348
+ print "The request timed out."
349
+ end
350
+
351
+ @cgroup.mark_done
352
+ @cgroup.write_output
353
+ STDOUT.print "#{__id__} - Served '#{@meta["REQUEST_URI"]}' in #{Time.now.to_f - time_start} secs (#{@resp.status}).\n" if @debug
354
+ @cgroup.join
355
+
356
+ @hb.events.call(:request_done, {
357
+ :httpsession => self
358
+ }) if @hb.events
359
+ @httpsession_var = {}
360
+ end
361
+ end