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.
- data/VERSION +1 -1
- data/bin/knjappserver_start.rb +17 -25
- data/knjappserver.gemspec +17 -6
- data/lib/conf/conf_example.rb +3 -3
- data/lib/files/database_schema.rb +1 -2
- data/lib/include/class_customio.rb +8 -25
- data/lib/include/class_erbhandler.rb +7 -1
- data/lib/include/class_httpserver.rb +50 -34
- data/lib/include/class_httpsession.rb +138 -101
- data/lib/include/class_httpsession_contentgroup.rb +52 -24
- data/lib/include/class_httpsession_http_request.rb +203 -0
- data/lib/include/{class_httpresp.rb → class_httpsession_http_response.rb} +27 -28
- data/lib/include/class_httpsession_post_multipart.rb +117 -0
- data/lib/include/class_knjappserver.rb +146 -96
- data/lib/include/class_knjappserver_cleaner.rb +74 -68
- data/lib/include/class_knjappserver_cmdline.rb +23 -17
- data/lib/include/class_knjappserver_errors.rb +121 -104
- data/lib/include/class_knjappserver_leakproxy_client.rb +6 -0
- data/lib/include/class_knjappserver_leakproxy_server.rb +56 -0
- data/lib/include/class_knjappserver_logging.rb +25 -25
- data/lib/include/class_knjappserver_mailing.rb +84 -56
- data/lib/include/class_knjappserver_sessions.rb +15 -22
- data/lib/include/class_knjappserver_threadding.rb +70 -43
- data/lib/include/class_knjappserver_threadding_timeout.rb +20 -4
- data/lib/include/class_knjappserver_translations.rb +6 -4
- data/lib/include/class_knjappserver_web.rb +87 -35
- data/lib/include/class_log.rb +9 -9
- data/lib/include/class_log_link.rb +4 -4
- data/lib/include/class_session.rb +8 -4
- data/lib/include/gettext_funcs.rb +8 -6
- data/lib/include/magic_methods.rb +4 -0
- data/lib/pages/debug_database_connections.rhtml +46 -0
- data/lib/pages/debug_http_sessions.rhtml +40 -0
- data/lib/pages/error_notfound.rhtml +12 -0
- data/lib/pages/spec.rhtml +1 -1
- data/lib/pages/spec_post.rhtml +3 -0
- data/lib/pages/spec_thread_joins.rhtml +21 -0
- data/lib/pages/spec_threadded_content.rhtml +2 -0
- data/lib/pages/tests.rhtml +14 -0
- data/lib/scripts/benchmark.rb +25 -8
- data/lib/scripts/knjappserver_cgi.rb +60 -0
- data/lib/scripts/knjappserver_fcgi.rb +135 -0
- data/lib/scripts/leakproxy.rb +27 -0
- data/spec/knjappserver_spec.rb +16 -5
- data/spec/leakproxy_spec.rb +56 -0
- metadata +38 -27
- data/lib/include/class_httpsession_knjengine.rb +0 -154
- data/lib/include/class_httpsession_mongrel.rb +0 -75
- 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
|
-
|
11
|
-
|
12
|
-
|
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
|
20
|
-
|
21
|
-
@
|
22
|
-
|
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
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
74
|
-
|
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(
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
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
|