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
@@ -0,0 +1,117 @@
1
+ #This class parses and handels post-multipart requests.
2
+ class Knjappserver::Httpsession::Post_multipart
3
+ attr_reader :return
4
+
5
+ def initialize(args)
6
+ @args = args
7
+ boundary_regexp = /\A--#{@args["boundary"]}(--)?#{@args["crlf"]}\z/
8
+ @return = {}
9
+ @data = nil
10
+ @mode = nil
11
+ @headers = {}
12
+
13
+ @args["io"].each do |line|
14
+ if boundary_regexp =~ line
15
+ #Finish the data we were writing.
16
+ self.finish_data if @data
17
+
18
+ @data = ""
19
+ @mode = "headers"
20
+ elsif @mode == "headers"
21
+ if match = line.match(/^(.+?):\s+(.+)#{@args["crlf"]}$/)
22
+ @headers[match[1].to_s.downcase] = match[2]
23
+ elsif line == @args["crlf"]
24
+ @mode = "body"
25
+ else
26
+ raise "Could not match header from: '#{line}'."
27
+ end
28
+ elsif @mode == "body"
29
+ @data << line
30
+ else
31
+ raise "Invalid mode: '#{@mode}'."
32
+ end
33
+ end
34
+
35
+ self.finish_data if @data and @data.to_s.length > 0
36
+
37
+ @data = nil
38
+ @headers = nil
39
+ @mode = nil
40
+ @args = nil
41
+ end
42
+
43
+ #Add the current treated data to the return-hash.
44
+ def finish_data
45
+ @data.chop!
46
+ name = nil
47
+
48
+ disp = @headers["content-disposition"]
49
+ raise "No 'content-disposition' was given." if !disp
50
+
51
+ match_name = disp.match(/name=\"(.+?)\"/)
52
+ raise "Could not match name." if !match_name
53
+
54
+ match_fname = disp.match(/filename=\"(.+?)\"/)
55
+
56
+ if match_fname
57
+ obj = Knjappserver::Httpsession::Post_multipart::File_upload.new(
58
+ "fname" => match_fname[1],
59
+ "headers" => @headers,
60
+ "data" => @data
61
+ )
62
+ @return[match_name[1]] = obj
63
+ @data = nil
64
+ @headers = {}
65
+ @mode = nil
66
+ else
67
+ @return[match_name[1]] = @data
68
+ @data = nil
69
+ @headers = {}
70
+ @mode = nil
71
+ end
72
+ end
73
+ end
74
+
75
+ #This is the actual returned object for fileuploads. It is able to do various user-friendly things like save the content to a given path, return the filename, returns the content to a string and more.
76
+ class Knjappserver::Httpsession::Post_multipart::File_upload
77
+ def initialize(args)
78
+ @args = args
79
+ end
80
+
81
+ #Returns the size of the upload.
82
+ def size
83
+ return @args["data"].length
84
+ end
85
+
86
+ #Returns the size of the fileupload.
87
+ def length
88
+ return @args["data"].length
89
+ end
90
+
91
+ #Returns the filename given for the fileupload.
92
+ def filename
93
+ return @args["fname"]
94
+ end
95
+
96
+ #Returns the headers given for the fileupload. Type and more should be here.
97
+ def headers
98
+ return @args["headers"]
99
+ end
100
+
101
+ #Returns the content of the file-upload as a string.
102
+ def to_s
103
+ return @args["data"]
104
+ end
105
+
106
+ #Saves the content of the fileupload to a given path.
107
+ def save_to(filepath)
108
+ File.open(filepath, "w") do |fp|
109
+ fp.write(self.to_s)
110
+ end
111
+ end
112
+
113
+ #This methods prevents the object from being converted to JSON. This can make some serious bugs.
114
+ def to_json(*args)
115
+ raise "File_upload-objects should not be converted to json."
116
+ end
117
+ end
@@ -1,26 +1,10 @@
1
- require "#{File.dirname(__FILE__)}/class_knjappserver_cleaner"
2
- require "#{File.dirname(__FILE__)}/class_knjappserver_cmdline"
3
- require "#{File.dirname(__FILE__)}/class_knjappserver_errors"
4
- require "#{File.dirname(__FILE__)}/class_knjappserver_logging"
5
- require "#{File.dirname(__FILE__)}/class_knjappserver_mailing"
6
- require "#{File.dirname(__FILE__)}/class_knjappserver_sessions"
7
- require "#{File.dirname(__FILE__)}/class_knjappserver_threadding"
8
- require "#{File.dirname(__FILE__)}/class_knjappserver_threadding_timeout"
9
- require "#{File.dirname(__FILE__)}/class_knjappserver_translations"
10
- require "#{File.dirname(__FILE__)}/class_knjappserver_web"
11
-
12
- require "timeout"
13
- require "digest"
14
- require "erubis"
15
- require "base64"
16
- require "stringio"
17
- require "socket"
18
-
19
1
  class Knjappserver
20
- attr_reader :config, :httpserv, :debug, :db, :db_handler, :ob, :translations, :paused, :should_restart, :events, :mod_event, :db_handler, :gettext, :sessions, :logs_access_pending, :threadpool, :vars, :magic_vars, :types, :eruby_cache
2
+ 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
21
3
  attr_accessor :served, :should_restart, :should_restart_done
22
4
 
23
- autoload :ERBHandler, "#{File.dirname(__FILE__)}/class_erbhandler"
5
+ appsrv_dir = File.dirname(__FILE__)
6
+ autoload :ERBHandler, "#{appsrv_dir}/class_erbhandler"
7
+ autoload :Log, "#{appsrv_dir}/class_log.rb"
24
8
 
25
9
  def initialize(config)
26
10
  raise "No arguments given." if !config.is_a?(Hash)
@@ -36,7 +20,6 @@ class Knjappserver
36
20
 
37
21
  @config[:smtp_args] = {"smtp_host" => "localhost", "smtp_port" => 25} if !@config[:smtp_args]
38
22
  @config[:timeout] = 30 if !@config.has_key?(:timeout)
39
- @config[:engine_knjengine] = true if !@config[:engine_knjengine] and !@config[:engine_webrick] and !@config[:engine_mongrel]
40
23
  raise "No ':doc_root' was given in arguments." if !@config.has_key?(:doc_root)
41
24
 
42
25
 
@@ -54,6 +37,21 @@ class Knjappserver
54
37
  ]
55
38
  end
56
39
 
40
+
41
+ #Add extra handlers if given.
42
+ if @config[:handlers_extra]
43
+ @config[:handlers] += @config[:handlers_extra]
44
+ end
45
+
46
+
47
+ #Setup cache to make .rhtml-calls faster.
48
+ @config[:handlers_cache] = {}
49
+ @config[:handlers].each do |handler_info|
50
+ next if !handler_info[:file_ext] or !handler_info[:callback]
51
+ @config[:handlers_cache][handler_info[:file_ext]] = handler_info[:callback]
52
+ end
53
+
54
+
57
55
  @debug = @config[:debug]
58
56
  @paused = 0
59
57
  @paused_mutex = Mutex.new
@@ -63,6 +61,7 @@ class Knjappserver
63
61
  @mod_files = {}
64
62
  @sessions = {}
65
63
  @eruby_cache = {}
64
+ @httpsessions_ids = {}
66
65
 
67
66
  @path_knjappserver = File.dirname(__FILE__)
68
67
  if @config[:knjrbfw_path]
@@ -105,37 +104,51 @@ class Knjappserver
105
104
  :xml => "text/xml",
106
105
  :js => "text/javascript"
107
106
  }
108
- @types = @types.merge(@config[:filetypes]) if @config.has_key?(:filetypes)
107
+ @types.merge!(@config[:filetypes]) if @config.has_key?(:filetypes)
109
108
 
110
109
 
111
110
 
112
111
  #Load various required files from knjrbfw and stuff in the knjappserver-framework.
113
112
  files = [
114
113
  "#{@path_knjrbfw}knjrbfw.rb",
115
- "#{@path_knjrbfw}knj/arrayext.rb",
116
- "#{@path_knjrbfw}knj/event_handler.rb",
117
- "#{@path_knjrbfw}knj/errors.rb",
118
- "#{@path_knjrbfw}knj/eruby.rb",
119
- "#{@path_knjrbfw}knj/hash_methods.rb",
120
- "#{@path_knjrbfw}knj/objects.rb",
121
- "#{@path_knjrbfw}knj/web.rb",
122
- "#{@path_knjrbfw}knj/datarow.rb",
123
- "#{@path_knjrbfw}knj/datet.rb",
124
- "#{@path_knjrbfw}knj/php.rb",
125
- "#{@path_knjrbfw}knj/thread.rb",
126
- "#{@path_knjrbfw}knj/threadhandler.rb",
127
- "#{@path_knjrbfw}knj/threadpool.rb",
128
- "#{@path_knjrbfw}knj/translations.rb",
129
- "#{@path_knjrbfw}knj/knjdb/libknjdb.rb",
130
- "#{@path_knjappserver}/class_httpresp.rb",
131
114
  "#{@path_knjappserver}/class_httpserver.rb",
132
115
  "#{@path_knjappserver}/class_httpsession.rb",
133
- "#{@path_knjappserver}/class_httpsession_contentgroup.rb",
134
- "#{@path_knjappserver}/class_session.rb",
135
- "#{@path_knjappserver}/class_log.rb",
136
- "#{@path_knjappserver}/class_log_access.rb",
137
- "#{@path_knjappserver}/class_log_data_value.rb"
116
+ "#{@path_knjappserver}/class_knjappserver_errors.rb",
117
+ "#{@path_knjappserver}/class_knjappserver_logging.rb",
118
+ "#{@path_knjappserver}/class_knjappserver_mailing.rb",
119
+ "#{@path_knjappserver}/class_knjappserver_sessions.rb",
120
+ "#{@path_knjappserver}/class_knjappserver_translations.rb",
121
+ "#{@path_knjappserver}/class_knjappserver_web.rb"
138
122
  ]
123
+
124
+ if @config[:preload]
125
+ require "timeout"
126
+ require "digest"
127
+ require "erubis"
128
+ require "base64"
129
+ require "stringio"
130
+ require "socket"
131
+
132
+ files += [
133
+ "#{@path_knjrbfw}knj/event_handler.rb",
134
+ "#{@path_knjrbfw}knj/errors.rb",
135
+ "#{@path_knjrbfw}knj/eruby.rb",
136
+ "#{@path_knjrbfw}knj/hash_methods.rb",
137
+ "#{@path_knjrbfw}knj/objects.rb",
138
+ "#{@path_knjrbfw}knj/web.rb",
139
+ "#{@path_knjrbfw}knj/datarow.rb",
140
+ "#{@path_knjrbfw}knj/datet.rb",
141
+ "#{@path_knjrbfw}knj/php.rb",
142
+ "#{@path_knjrbfw}knj/thread.rb",
143
+ "#{@path_knjrbfw}knj/threadhandler.rb",
144
+ "#{@path_knjrbfw}knj/threadpool.rb",
145
+ "#{@path_knjrbfw}knj/translations.rb",
146
+ "#{@path_knjrbfw}knj/knjdb/libknjdb.rb",
147
+ ]
148
+ else
149
+ files << "#{@path_knjrbfw}knj/autoload.rb"
150
+ end
151
+
139
152
  files << "#{@path_knjrbfw}knj/gettext_threadded.rb" if @config[:locales_root]
140
153
  files.each do |file|
141
154
  STDOUT.print "Loading: '#{file}'.\n" if @debug
@@ -155,18 +168,18 @@ class Knjappserver
155
168
  end
156
169
 
157
170
 
158
- print "Updating database.\n" if @debug
159
- require "rubygems" if !@config.key?(:knjdbrevision_path)
160
- require "#{@config[:knjdbrevision_path]}knjdbrevision"
161
-
162
- dbschemapath = "#{File.dirname(__FILE__)}/../files/database_schema.rb"
163
- raise "'#{dbschemapath}' did not exist." if !File.exists?(dbschemapath)
164
- require dbschemapath
165
- raise "No schema-variable was spawned." if !DATABASE_SCHEMA
166
-
167
- dbpath = "#{File.dirname(__FILE__)}/../files/database.sqlite3"
168
- dbrev = Knjdbrevision.new
169
- dbrev.init_db(DATABASE_SCHEMA, @db)
171
+ if !@config.key?(:dbrev) or @config[:dbrev]
172
+ print "Updating database.\n" if @debug
173
+ require "knj/knjdb/revision.rb"
174
+
175
+ dbschemapath = "#{File.dirname(__FILE__)}/../files/database_schema.rb"
176
+ raise "'#{dbschemapath}' did not exist." if !File.exists?(dbschemapath)
177
+ require dbschemapath
178
+ raise "No schema-variable was spawned." if !Knjappserver::DATABASE_SCHEMA
179
+
180
+ dbrev = Knj::Db::Revision.new
181
+ dbrev.init_db("schema" => Knjappserver::DATABASE_SCHEMA, "db" => @db)
182
+ end
170
183
 
171
184
 
172
185
  print "Spawning objects.\n" if @debug
@@ -198,9 +211,7 @@ class Knjappserver
198
211
  @gettext = Knj::Gettext_threadded.new("dir" => config[:locales_root])
199
212
  end
200
213
 
201
- if @config[:locales_gettext_funcs]
202
- require "#{@path_knjappserver}/gettext_funcs"
203
- end
214
+ require "#{@path_knjappserver}/gettext_funcs" if @config[:locales_gettext_funcs]
204
215
 
205
216
  if @config[:magic_methods] or !@config.has_key?(:magic_methods)
206
217
  print "Loading magic-methods.\n" if @debug
@@ -212,8 +223,8 @@ class Knjappserver
212
223
 
213
224
  if $stdout.class.name != "Knjappserver::CustomIO"
214
225
  require "#{@path_knjappserver}/class_customio.rb"
215
- cio = Knjappserver::CustomIO.new
216
- $stdout = cio
226
+ @cio = Knjappserver::CustomIO.new
227
+ $stdout = @cio
217
228
  end
218
229
  end
219
230
 
@@ -236,56 +247,72 @@ class Knjappserver
236
247
 
237
248
 
238
249
  #Set up various events for the appserver.
239
- print "Loading events.\n" if @debug
240
- @events = Knj::Event_handler.new
241
- @events.add_event(
242
- :name => :check_page_access,
243
- :connections_max => 1
244
- )
245
- @events.add_event(
246
- :name => :ob,
247
- :connections_max => 1
248
- )
249
- @events.add_event(
250
- :name => :trans_no_str,
251
- :connections_max => 1
252
- )
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
+ end
253
274
 
254
275
  #Set up the 'vars'-variable that can be used to set custom global variables for web-requests.
255
276
  @vars = Knj::Hash_methods.new
256
277
  @magic_vars = {}
278
+ @magic_procs = {}
257
279
 
258
280
 
259
281
  #Initialize the various feature-modules.
260
282
  print "Init sessions.\n" if @debug
261
- initialize_sessions
283
+ self.initialize_sessions
262
284
 
263
- print "Init threadding.\n" if @debug
264
- initialize_threadding
285
+ if !@config.key?(:threadding) or @config[:threadding]
286
+ self.loadfile("#{@path_knjappserver}/class_knjappserver_threadding.rb")
287
+ self.loadfile("#{@path_knjappserver}/class_knjappserver_threadding_timeout.rb")
288
+ print "Init threadding.\n" if @debug
289
+ self.initialize_threadding
290
+ end
265
291
 
266
292
  print "Init mailing.\n" if @debug
267
- initialize_mailing
293
+ self.initialize_mailing
268
294
 
269
295
  print "Init errors.\n" if @debug
270
- initialize_errors
296
+ self.initialize_errors
271
297
 
272
298
  print "Init logging.\n" if @debug
273
- initialize_logging
274
-
275
- print "Init cleaner.\n" if @debug
276
- initialize_cleaner
277
-
278
- print "Init cmdline.\n" if @debug
279
- initialize_cmdline
299
+ self.initialize_logging
280
300
 
301
+ if !@config.key?(:cleaner) or @config[:cleaner]
302
+ self.loadfile("#{@path_knjappserver}/class_knjappserver_cleaner.rb")
303
+ print "Init cleaner.\n" if @debug
304
+ self.initialize_cleaner
305
+ end
281
306
 
282
- #Start the appserver.
283
- print "Spawning appserver.\n" if @debug
284
- @httpserv = Knjappserver::Httpserver.new(self)
307
+ if !@config.key?(:cmdline) or @config[:cmdline]
308
+ self.loadfile("#{@path_knjappserver}/class_knjappserver_cmdline.rb")
309
+ print "Init cmdline.\n" if @debug
310
+ self.initialize_cmdline
311
+ end
285
312
 
286
313
 
287
314
  #Clear memory at exit.
288
- at_exit do
315
+ Kernel.at_exit do
289
316
  self.stop
290
317
  end
291
318
 
@@ -300,7 +327,7 @@ class Knjappserver
300
327
  return nil
301
328
  end
302
329
 
303
- rpath = Knj::Php.realpath(fpath)
330
+ rpath = File.realpath(fpath)
304
331
  raise "No such filepath: #{fpath}" if !rpath or !File.exists?(rpath)
305
332
 
306
333
  return true if @mod_files[rpath]
@@ -314,6 +341,18 @@ class Knjappserver
314
341
 
315
342
  #Starts the HTTP-server and threadpool.
316
343
  def start
344
+ #Start the appserver.
345
+ print "Spawning appserver.\n" if @debug
346
+ @httpserv = Knjappserver::Httpserver.new(self)
347
+
348
+
349
+ #Start Leakproxy-module if defined in config.
350
+ if @config[:leakproxy]
351
+ require "#{File.dirname(__FILE__)}/class_knjappserver_leakproxy_server.rb"
352
+ @leakproxy_server = Knjappserver::Leakproxy_server.new(:kas => self)
353
+ end
354
+
355
+
317
356
  STDOUT.print "Starting appserver.\n" if @debug
318
357
  Thread.current[:knjappserver] = {:kas => self} if !Thread.current[:knjappserver]
319
358
 
@@ -336,15 +375,15 @@ class Knjappserver
336
375
  #Stops the entire app and releases join.
337
376
  def stop
338
377
  proc_stop = proc{
339
- #This should be done first to be sure it finishes (else we have a serious bug).
340
- STDOUT.print "Flush out loaded sessions.\n" if @debug
341
- self.sessions_flush
342
-
343
378
  STDOUT.print "Stopping appserver for real.\n" if @debug
344
379
  @httpserv.stop if @httpserv and @httpserv.respond_to?(:stop)
345
380
 
346
381
  STDOUT.print "Stopping threadpool.\n" if @debug
347
382
  @threadpool.stop if @threadpool
383
+
384
+ #This should be done first to be sure it finishes (else we have a serious bug).
385
+ STDOUT.print "Flush out loaded sessions.\n" if @debug
386
+ self.sessions_flush
348
387
  }
349
388
 
350
389
  #If we cant get a paused-execution in 10 secs - we just force the stop.
@@ -431,7 +470,18 @@ class Knjappserver
431
470
  if !Object.respond_to?(method_name)
432
471
  Object.send(:define_method, method_name) do
433
472
  return Thread.current[:knjappserver][:kas].magic_vars[method_name] if Thread.current[:knjappserver] and Thread.current[:knjappserver][:kas]
434
- return $knjappserver[:knjappserver].magic_vars[method_name] if $knjappserver and $knjappserver[:knjappserver]
473
+ raise "Could not figure out the object: '#{method_name}'."
474
+ end
475
+ end
476
+ end
477
+
478
+ def define_magic_proc(method_name, &block)
479
+ raise "No block given." if !block_given?
480
+ @magic_procs[method_name] = block
481
+
482
+ if !Object.respond_to?(method_name)
483
+ Object.send(:define_method, method_name) do
484
+ return Thread.current[:knjappserver][:kas].magic_procs[method_name].call(:kas => self) if Thread.current[:knjappserver] and Thread.current[:knjappserver][:kas]
435
485
  raise "Could not figure out the object: '#{method_name}'."
436
486
  end
437
487
  end