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
@@ -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