knjappserver 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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