knjappserver 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/VERSION +1 -1
  2. data/bin/knjappserver_start.rb +17 -25
  3. data/knjappserver.gemspec +17 -6
  4. data/lib/conf/conf_example.rb +3 -3
  5. data/lib/files/database_schema.rb +1 -2
  6. data/lib/include/class_customio.rb +8 -25
  7. data/lib/include/class_erbhandler.rb +7 -1
  8. data/lib/include/class_httpserver.rb +50 -34
  9. data/lib/include/class_httpsession.rb +138 -101
  10. data/lib/include/class_httpsession_contentgroup.rb +52 -24
  11. data/lib/include/class_httpsession_http_request.rb +203 -0
  12. data/lib/include/{class_httpresp.rb → class_httpsession_http_response.rb} +27 -28
  13. data/lib/include/class_httpsession_post_multipart.rb +117 -0
  14. data/lib/include/class_knjappserver.rb +146 -96
  15. data/lib/include/class_knjappserver_cleaner.rb +74 -68
  16. data/lib/include/class_knjappserver_cmdline.rb +23 -17
  17. data/lib/include/class_knjappserver_errors.rb +121 -104
  18. data/lib/include/class_knjappserver_leakproxy_client.rb +6 -0
  19. data/lib/include/class_knjappserver_leakproxy_server.rb +56 -0
  20. data/lib/include/class_knjappserver_logging.rb +25 -25
  21. data/lib/include/class_knjappserver_mailing.rb +84 -56
  22. data/lib/include/class_knjappserver_sessions.rb +15 -22
  23. data/lib/include/class_knjappserver_threadding.rb +70 -43
  24. data/lib/include/class_knjappserver_threadding_timeout.rb +20 -4
  25. data/lib/include/class_knjappserver_translations.rb +6 -4
  26. data/lib/include/class_knjappserver_web.rb +87 -35
  27. data/lib/include/class_log.rb +9 -9
  28. data/lib/include/class_log_link.rb +4 -4
  29. data/lib/include/class_session.rb +8 -4
  30. data/lib/include/gettext_funcs.rb +8 -6
  31. data/lib/include/magic_methods.rb +4 -0
  32. data/lib/pages/debug_database_connections.rhtml +46 -0
  33. data/lib/pages/debug_http_sessions.rhtml +40 -0
  34. data/lib/pages/error_notfound.rhtml +12 -0
  35. data/lib/pages/spec.rhtml +1 -1
  36. data/lib/pages/spec_post.rhtml +3 -0
  37. data/lib/pages/spec_thread_joins.rhtml +21 -0
  38. data/lib/pages/spec_threadded_content.rhtml +2 -0
  39. data/lib/pages/tests.rhtml +14 -0
  40. data/lib/scripts/benchmark.rb +25 -8
  41. data/lib/scripts/knjappserver_cgi.rb +60 -0
  42. data/lib/scripts/knjappserver_fcgi.rb +135 -0
  43. data/lib/scripts/leakproxy.rb +27 -0
  44. data/spec/knjappserver_spec.rb +16 -5
  45. data/spec/leakproxy_spec.rb +56 -0
  46. metadata +38 -27
  47. data/lib/include/class_httpsession_knjengine.rb +0 -154
  48. data/lib/include/class_httpsession_mongrel.rb +0 -75
  49. data/lib/include/class_httpsession_webrick.rb +0 -75
@@ -7,85 +7,90 @@ class Knjappserver
7
7
  end
8
8
 
9
9
  #This should not be runned via _kas.timeout because timeout wont run when @should_restart is true! - knj
10
- Knj::Thread.new do
11
- loop do
12
- sleep time
13
-
14
- if @config.has_key?(:restart_when_used_memory) and !@should_restart
15
- mbs_used = (Knj::Php.memory_get_usage / 1024) / 1024
16
- STDOUT.print "Restart when over #{@config[:restart_when_used_memory]}mb\n" if @config[:debug]
17
- STDOUT.print "Used: #{mbs_used}mb\n" if @config[:debug]
10
+ Thread.new do
11
+ begin
12
+ loop do
13
+ sleep time
18
14
 
19
- if mbs_used.to_i >= @config[:restart_when_used_memory].to_i
20
- STDOUT.print "Memory is over #{@config[:restart_when_used_memory]} - restarting.\n"
21
- @should_restart = true
22
- end
23
- end
24
-
25
- if @should_restart and !@should_restart_done and !@should_restart_runnning
26
- begin
27
- @should_restart_runnning = true
28
-
29
- #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...
30
- STDOUT.print "Flushing mails.\n"
31
- self.mail_flush
15
+ if @config.has_key?(:restart_when_used_memory) and !@should_restart
16
+ mbs_used = (Knj::Php.memory_get_usage / 1024) / 1024
17
+ STDOUT.print "Restart when over #{@config[:restart_when_used_memory]}mb\n" if @config[:debug]
18
+ STDOUT.print "Used: #{mbs_used}mb\n" if @config[:debug]
32
19
 
33
- #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.
20
+ if mbs_used.to_i >= @config[:restart_when_used_memory].to_i
21
+ STDOUT.print "Memory is over #{@config[:restart_when_used_memory]} - restarting.\n"
22
+ @should_restart = true
23
+ end
24
+ end
25
+
26
+ if @should_restart and !@should_restart_done and !@should_restart_runnning
34
27
  begin
35
- Timeout.timeout(30) do
36
- loop do
37
- working_count = self.httpserv.working_count
38
- working = false
39
-
40
- if working_count > 0
41
- working = true
42
- STDOUT.print "Someone is working - wait two sec and try to restart again!\n"
43
- end
44
-
45
- if !working
46
- STDOUT.print "Found window where no sessions were active - restarting!\n"
47
- break
48
- else
49
- sleep 0.2
28
+ @should_restart_runnning = true
29
+
30
+ #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...
31
+ STDOUT.print "Flushing mails.\n"
32
+ self.mail_flush
33
+
34
+ #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.
35
+ begin
36
+ Timeout.timeout(30) do
37
+ loop do
38
+ working_count = self.httpserv.working_count
39
+ working = false
40
+
41
+ if working_count and working_count > 0
42
+ working = true
43
+ STDOUT.print "Someone is working - wait two sec and try to restart again!\n"
44
+ end
45
+
46
+ if !working
47
+ STDOUT.print "Found window where no sessions were active - restarting!\n"
48
+ break
49
+ else
50
+ sleep 0.2
51
+ end
52
+
53
+ STDOUT.print "Trying to find window with no active sessions to restart...\n"
50
54
  end
51
-
52
- STDOUT.print "Trying to find window with no active sessions to restart...\n"
53
55
  end
56
+ rescue Timeout::Error
57
+ STDOUT.print "Could not find a timing window for restarting... Forcing restart!\n"
54
58
  end
55
- rescue Timeout::Error
56
- STDOUT.print "Could not find a timing window for restarting... Forcing restart!\n"
57
- end
58
-
59
- #Flush emails again if any are pending (while we tried to find a window to restart)...
60
- STDOUT.print "Flushing mails.\n"
61
- self.mail_flush
62
-
63
- STDOUT.print "Stopping appserver.\n"
64
- self.stop
65
-
66
- STDOUT.print "Figuring out restart-command.\n"
67
- mycmd = @config[:restart_cmd]
68
-
69
- if !mycmd or mycmd.to_s.strip.length <= 0
70
- fpath = Knj::Php.realpath(File.dirname(__FILE__) + "/../knjappserver.rb")
71
- mycmd = Knj::Os.executed_cmd
72
59
 
73
- STDOUT.print "Previous cmd: #{mycmd}\n"
74
- mycmd = mycmd.gsub(/\s+knjappserver.rb/, " #{Knj::Strings.unixsafe(fpath)}")
60
+ #Flush emails again if any are pending (while we tried to find a window to restart)...
61
+ STDOUT.print "Flushing mails.\n"
62
+ self.mail_flush
63
+
64
+ STDOUT.print "Stopping appserver.\n"
65
+ self.stop
66
+
67
+ STDOUT.print "Figuring out restart-command.\n"
68
+ mycmd = @config[:restart_cmd]
69
+
70
+ if !mycmd or mycmd.to_s.strip.length <= 0
71
+ fpath = File.realpath("#{File.dirname(__FILE__)}/../knjappserver.rb")
72
+ mycmd = Knj::Os.executed_cmd
73
+
74
+ STDOUT.print "Previous cmd: #{mycmd}\n"
75
+ mycmd = mycmd.gsub(/\s+knjappserver.rb/, " #{Knj::Strings.unixsafe(fpath)}")
76
+ end
77
+
78
+ STDOUT.print "Restarting knjAppServer with command: #{mycmd}\n"
79
+ @should_restart_done = true
80
+ print exec(mycmd)
81
+ exit
82
+ rescue Exception => e
83
+ STDOUT.puts e.inspect
84
+ STDOUT.puts e.backtrace
75
85
  end
76
-
77
- STDOUT.print "Restarting knjAppServer with command: #{mycmd}\n"
78
- @should_restart_done = true
79
- print exec(mycmd)
80
- exit
81
- rescue Exception => e
82
- STDOUT.puts e.inspect
83
- STDOUT.puts e.backtrace
84
86
  end
85
87
  end
88
+ rescue => e
89
+ self.handle_error(e)
86
90
  end
87
91
  end
88
92
 
93
+ #This flushes (writes) all session-data to the server and deletes old unused sessions from the database.
89
94
  self.timeout(:time => 300) do
90
95
  STDOUT.print "Cleaning sessions on appserver.\n" if @config[:debug]
91
96
 
@@ -106,9 +111,10 @@ class Knjappserver
106
111
 
107
112
  STDOUT.print "Delete sessions...\n" if @config[:debug]
108
113
  @ob.list(:Session, {"id_not" => session_not_ids, "date_lastused_below" => (Time.now - 5356800)}) do |session|
114
+ idhash = session[:idhash]
115
+ STDOUT.print "Deleting session: '#{session.id}'.\n" if @config[:debug]
109
116
  @ob.delete(session)
110
- @sessions.delete(session[:idhash])
111
- STDOUT.print "Deleted session: #{session.id}\n" if @config[:debug]
117
+ @sessions.delete(idhash)
112
118
  end
113
119
  end
114
120
  end
@@ -2,27 +2,32 @@ class Knjappserver
2
2
  def initialize_cmdline
3
3
  @cmds = {}
4
4
 
5
- Knj::Thread.new do
6
- line = $stdin.gets
7
-
8
- called = 0
9
- @cmds.each do |key, connects|
10
- data = {}
5
+ Thread.new do
6
+ begin
7
+ line = $stdin.gets
8
+ next if line == "\n"
11
9
 
12
- if key.is_a?(Regexp)
13
- if line.match(key)
14
- connects.each do |conn|
15
- called += 1
16
- conn[:block].call(data)
10
+ called = 0
11
+ @cmds.each do |key, connects|
12
+ data = {}
13
+
14
+ if key.is_a?(Regexp)
15
+ if line.match(key)
16
+ connects.each do |conn|
17
+ called += 1
18
+ conn[:block].call(data)
19
+ end
17
20
  end
21
+ else
22
+ raise "Unknown class for 'cmd_connect': '#{key.class.name}'."
18
23
  end
19
- else
20
- raise "Unknown class for 'cmd_connect': '#{key.class.name}'."
21
24
  end
22
- end
23
-
24
- if called == 0
25
- print "Unknown command: '#{line.strip}'.\n"
25
+
26
+ if called == 0
27
+ print "Unknown command: '#{line.strip}'.\n"
28
+ end
29
+ rescue => e
30
+ self.handle_error(e)
26
31
  end
27
32
  end
28
33
 
@@ -32,6 +37,7 @@ class Knjappserver
32
37
  end
33
38
 
34
39
  self.cmd_connect(/^\s*stop\s*$/i) do |data|
40
+ print "Stopping appserver.\n"
35
41
  self.stop
36
42
  end
37
43
  end
@@ -1,120 +1,137 @@
1
1
  class Knjappserver
2
2
  attr_reader :error_emails_pending
3
3
 
4
- def initialize_errors
5
- @error_emails_pending = {}
6
- @error_emails_pending_mutex = Mutex.new
7
-
8
- if @config[:error_emails_time]
4
+ def initialize_errors
5
+ @error_emails_pending = {}
6
+ @error_emails_pending_mutex = Mutex.new
7
+
8
+ if @config[:error_emails_time]
9
9
  @error_emails_time = @config[:error_emails_time]
10
10
  elsif @config[:debug]
11
11
  @error_emails_time = 5
12
12
  else
13
13
  @error_emails_time = 180
14
14
  end
15
-
16
- self.timeout(:time => @error_emails_time) do
17
- self.flush_error_emails
18
- end
19
- end
20
-
21
- #Send error-emails based on error-emails-cache (cached so the same error isnt send out every time it occurrs to prevent spamming).
22
- def flush_error_emails
23
- @error_emails_pending_mutex.synchronize do
24
- send_time_older_than = Time.new.to_i - @error_emails_time
25
-
26
- @error_emails_pending.each do |backtrace_hash, error_email|
27
- if send_time_older_than < error_email[:last_time].to_i and error_email[:messages].length < 1000
28
- next
29
- end
30
-
31
- @config[:error_report_emails].each do |email|
32
- next if !email
15
+
16
+ self.timeout(:time => @error_emails_time) do
17
+ self.flush_error_emails
18
+ end
19
+ end
20
+
21
+ #Send error-emails based on error-emails-cache (cached so the same error isnt send out every time it occurrs to prevent spamming).
22
+ def flush_error_emails
23
+ @error_emails_pending_mutex.synchronize do
24
+ send_time_older_than = Time.new.to_i - @error_emails_time
25
+
26
+ @error_emails_pending.each do |backtrace_hash, error_email|
27
+ if send_time_older_than < error_email[:last_time].to_i and error_email[:messages].length < 1000
28
+ next
29
+ end
30
+
31
+ @config[:error_report_emails].each do |email|
32
+ next if !email or error_email[:messages].length <= 0
33
+
34
+ if error_email[:messages].length == 1
35
+ html = error_email[:messages].first
36
+ else
37
+ html = "<b>First time:</b> #{Knj::Datet.in(error_email[:first_time]).out}<br />"
38
+ html << "<b>Last time:</b> #{Knj::Datet.in(error_email[:last_time]).out}<br />"
39
+ html << "<b>Number of errors:</b> #{error_email[:messages].length}<br />"
40
+ count = 0
41
+
42
+ error_email[:messages].each do |error_msg|
43
+ count += 1
44
+
45
+ if count > 10
46
+ html << "<br /><br /><b><i>Limiting to showing 10 out of #{error_email[:messages].length} messages.</i></b>"
47
+ break
48
+ end
49
+
50
+ html << "<br /><br />"
51
+ html << "<b>Message #{count}</b><br />"
52
+ html << error_msg
53
+ end
54
+ end
33
55
 
34
- if error_email[:messages].length == 1
35
- html = error_email[:messages].first
36
- else
37
- html = "<b>First time:</b> #{Knj::Datet.in(error_email[:first_time]).out}<br />"
38
- html += "<b>Last time:</b> #{Knj::Datet.in(error_email[:last_time]).out}<br />"
39
- html += "<b>Number of errors:</b> #{error_email[:messages].length}<br />"
40
- count = 0
41
-
42
- error_email[:messages].each do |error_msg|
43
- count += 1
44
-
45
- if count > 10
46
- html += "<br /><br /><b><i>Limiting to showing 10 out of #{error_email[:messages].length} messages.</i></b>"
47
- break
48
- end
49
-
50
- html += "<br /><br />"
51
- html += "<b>Message #{count}</b><br />"
52
- html += error_msg
53
- end
54
- end
55
-
56
- self.mail(
57
- :to => email,
58
- :subject => error_email[:subject],
59
- :html => html,
60
- :from => @config[:error_report_from]
61
- )
62
- end
63
-
64
- @error_emails_pending.delete(backtrace_hash)
65
- end
66
- end
67
- end
68
-
69
- #Handels a given error. Sends to the admin-emails.
70
- def handle_error(e, args = {})
71
- @error_emails_pending_mutex.synchronize do
72
- if !Thread.current[:knjappserver] or !Thread.current[:knjappserver][:httpsession]
73
- STDOUT.print "Error: "
74
- STDOUT.puts e.inspect
75
- STDOUT.print "\n"
76
- STDOUT.puts e.backtrace
77
- STDOUT.print "\n\n"
78
- end
79
-
80
- if @config.has_key?(:smtp_args) and @config[:error_report_emails] and (!args.has_key?(:email) or args[:email])
81
- backtrace_hash = Knj::ArrayExt.array_hash(e.backtrace)
82
-
83
- if !@error_emails_pending.has_key?(backtrace_hash)
84
- @error_emails_pending[backtrace_hash] = {
85
- :first_time => Time.new,
86
- :messages => [],
87
- :subject => sprintf("Error @ %s", @config[:title]) + " (#{Knj::Strings.shorten(e.message, 100)})"
88
- }
89
- end
90
-
91
- html = "An error occurred.<br /><br />"
92
- html += "<b>#{e.class.name.html}: #{e.message.html}</b><br /><br />"
93
-
94
- e.backtrace.each do |line|
95
- html += line.html + "<br />"
96
- end
97
-
98
- html += "<br /><b>Post:</b><br /><pre>#{Knj::Php.print_r(_post, true)}</pre>" if _post
99
- html += "<br /><b>Get:</b><br /><pre>#{Knj::Php.print_r(_get, true)}</pre>" if _get
100
- html += "<br /><b>Server:</b><br /><pre>#{Knj::Php.print_r(_server, true).html}</pre>" if _server
101
- html += "<br /><b>Cookie:</b><br /><pre>#{Knj::Php.print_r(_cookie, true).html}</pre>" if _meta
102
- html += "<br /><b>Session:</b><br /><pre>#{Knj::Php.print_r(_session, true).html}</pre>" if _session
103
- html += "<br /><b>Session hash:</b><br /><pre>#{Knj::Php.print_r(_session_hash, true).html}</pre>" if _session_hash
104
-
105
- error_hash = @error_emails_pending[backtrace_hash]
106
- error_hash[:last_time] = Time.new
107
- error_hash[:messages] << html
108
- end
109
- end
110
- end
111
-
112
- #Takes a proc and executes it. On error it alerts the error-message with javascript to the server, sends a javascript back and exits.
113
- def on_error_go_back(&block)
56
+ self.mail(
57
+ :to => email,
58
+ :subject => error_email[:subject],
59
+ :html => html,
60
+ :from => @config[:error_report_from]
61
+ )
62
+ end
63
+
64
+ @error_emails_pending.delete(backtrace_hash)
65
+ end
66
+ end
67
+ end
68
+
69
+ #Handels a given error. Sends to the admin-emails.
70
+ def handle_error(e, args = {})
71
+ @error_emails_pending_mutex.synchronize do
72
+ if !Thread.current[:knjappserver] or !Thread.current[:knjappserver][:httpsession]
73
+ STDOUT.print "#{Knj::Errors.error_str(e)}\n\n"
74
+ end
75
+
76
+ browser = _httpsession.browser if _httpsession
77
+
78
+ send_email = true
79
+ send_email = false if !@config[:smtp_args]
80
+ send_email = false if !@config[:error_report_emails]
81
+ send_email = false if args.has_key?(:email) and !args[:email]
82
+ send_email = false if @config.key?(:error_report_bots) and !@config[:error_report_bots] and browser and browser["browser"] == "bot"
83
+
84
+ if send_email
85
+ backtrace_hash = Knj::ArrayExt.array_hash(e.backtrace)
86
+
87
+ if !@error_emails_pending.has_key?(backtrace_hash)
88
+ @error_emails_pending[backtrace_hash] = {
89
+ :first_time => Time.new,
90
+ :messages => [],
91
+ :subject => sprintf("Error @ %s", @config[:title]) + " (#{Knj::Strings.shorten(e.message, 100)})"
92
+ }
93
+ end
94
+
95
+ html = "An error occurred.<br /><br />"
96
+ html << "<b>#{e.class.name.html}: #{e.message.html}</b><br /><br />"
97
+
98
+ e.backtrace.each do |line|
99
+ html << line.html + "<br />"
100
+ end
101
+
102
+ html << "<br /><b>Post:</b><br /><pre>#{Knj::Php.print_r(_post, true)}</pre>" if _post
103
+ html << "<br /><b>Get:</b><br /><pre>#{Knj::Php.print_r(_get, true)}</pre>" if _get
104
+ html << "<br /><b>Server:</b><br /><pre>#{Knj::Php.print_r(_server, true).html}</pre>" if _server
105
+ html << "<br /><b>Cookie:</b><br /><pre>#{Knj::Php.print_r(_cookie, true).html}</pre>" if _meta
106
+ html << "<br /><b>Session:</b><br /><pre>#{Knj::Php.print_r(_session, true).html}</pre>" if _session
107
+ html << "<br /><b>Session hash:</b><br /><pre>#{Knj::Php.print_r(_session_hash, true).html}</pre>" if _session_hash
108
+
109
+ error_hash = @error_emails_pending[backtrace_hash]
110
+ error_hash[:last_time] = Time.new
111
+ error_hash[:messages] << html
112
+ end
113
+ end
114
+ end
115
+
116
+ #Takes a proc and executes it. On error it alerts the error-message with javascript to the server, sends a javascript back and exits.
117
+ def on_error_go_back(&block)
114
118
  begin
115
119
  block.call
116
120
  rescue => e
117
121
  self.alert(e.message).back
118
122
  end
119
- end
123
+ end
124
+
125
+ #Prints a detailed overview of the object in the terminal from where the appserver was started. This can be used for debugging.
126
+ def dprint(obj)
127
+ STDOUT.print Knj::Php.print_r(obj, true)
128
+ end
129
+
130
+ #Prints a string with a single file-line-backtrace prepended which is useful for debugging.
131
+ def debugs(str)
132
+ #Get backtrace.
133
+ backtrace_str = caller[0]
134
+ backtrace_match = backtrace_str.match(/^(.+):(\d+):in /)
135
+ STDOUT.print "#{File.basename(backtrace_match[1])}:#{backtrace_match[2]}: #{str}\n"
136
+ end
120
137
  end