nitro 0.1.2

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 (119) hide show
  1. data/AUTHORS +8 -0
  2. data/ChangeLog +1546 -0
  3. data/LICENCE +32 -0
  4. data/README +278 -0
  5. data/RELEASES +7 -0
  6. data/Rakefile +79 -0
  7. data/bin/cluster.rb +219 -0
  8. data/doc/architecture.txt +28 -0
  9. data/doc/bugs.txt +7 -0
  10. data/doc/css.txt +20 -0
  11. data/doc/ideas.txt +120 -0
  12. data/doc/pg.txt +47 -0
  13. data/doc/svn.txt +82 -0
  14. data/doc/todo.txt +30 -0
  15. data/etc/new-project.rb +18 -0
  16. data/examples/simple/README +15 -0
  17. data/examples/simple/app.rb +31 -0
  18. data/examples/simple/conf/apache.conf +100 -0
  19. data/examples/simple/conf/config.rb +89 -0
  20. data/examples/simple/conf/debug-config.rb +53 -0
  21. data/examples/simple/conf/live-config.rb +48 -0
  22. data/examples/simple/conf/overrides.rb +9 -0
  23. data/examples/simple/conf/requires.rb +51 -0
  24. data/examples/simple/ctl +32 -0
  25. data/examples/simple/env.rb +33 -0
  26. data/examples/simple/install.rb +12 -0
  27. data/examples/simple/lib/articles/entities.rb +35 -0
  28. data/examples/simple/lib/articles/lc-en.rb +36 -0
  29. data/examples/simple/lib/articles/methods.rb +55 -0
  30. data/examples/simple/lib/articles/part.rb +58 -0
  31. data/examples/simple/logs/access_log +2 -0
  32. data/examples/simple/logs/apache.log +3 -0
  33. data/examples/simple/logs/app.log +1 -0
  34. data/examples/simple/logs/events.log +1 -0
  35. data/examples/simple/root/add-article.sx +15 -0
  36. data/examples/simple/root/article-form.ss +20 -0
  37. data/examples/simple/root/comments-form.ss +16 -0
  38. data/examples/simple/root/comments.si +30 -0
  39. data/examples/simple/root/index.sx +44 -0
  40. data/examples/simple/root/shader/shader.xsl +100 -0
  41. data/examples/simple/root/shader/style.css +9 -0
  42. data/examples/simple/root/view-article.sx +30 -0
  43. data/examples/tiny/app.rb +30 -0
  44. data/examples/tiny/conf/apache.conf +100 -0
  45. data/examples/tiny/conf/config.rb +67 -0
  46. data/examples/tiny/conf/requires.rb +40 -0
  47. data/examples/tiny/ctl +31 -0
  48. data/examples/tiny/logs/access_log +9 -0
  49. data/examples/tiny/logs/apache.log +9 -0
  50. data/examples/tiny/root/index.sx +35 -0
  51. data/lib/n/app/cluster.rb +219 -0
  52. data/lib/n/app/cookie.rb +86 -0
  53. data/lib/n/app/filters/autologin.rb +50 -0
  54. data/lib/n/app/fragment.rb +67 -0
  55. data/lib/n/app/handlers.rb +120 -0
  56. data/lib/n/app/handlers/code-handler.rb +184 -0
  57. data/lib/n/app/handlers/page-handler.rb +612 -0
  58. data/lib/n/app/request-part.rb +59 -0
  59. data/lib/n/app/request.rb +653 -0
  60. data/lib/n/app/script.rb +398 -0
  61. data/lib/n/app/server.rb +53 -0
  62. data/lib/n/app/session.rb +224 -0
  63. data/lib/n/app/user.rb +47 -0
  64. data/lib/n/app/webrick-servlet.rb +213 -0
  65. data/lib/n/app/webrick.rb +70 -0
  66. data/lib/n/application.rb +187 -0
  67. data/lib/n/config.rb +31 -0
  68. data/lib/n/db.rb +217 -0
  69. data/lib/n/db/README +232 -0
  70. data/lib/n/db/connection.rb +369 -0
  71. data/lib/n/db/make-release.sh +26 -0
  72. data/lib/n/db/managed.rb +235 -0
  73. data/lib/n/db/mixins.rb +282 -0
  74. data/lib/n/db/mysql.rb +342 -0
  75. data/lib/n/db/psql.rb +378 -0
  76. data/lib/n/db/tools.rb +110 -0
  77. data/lib/n/db/utils.rb +99 -0
  78. data/lib/n/events.rb +118 -0
  79. data/lib/n/l10n.rb +22 -0
  80. data/lib/n/logger.rb +33 -0
  81. data/lib/n/macros.rb +53 -0
  82. data/lib/n/mixins.rb +46 -0
  83. data/lib/n/parts.rb +154 -0
  84. data/lib/n/properties.rb +194 -0
  85. data/lib/n/server.rb +61 -0
  86. data/lib/n/server/PLAYBACK.txt +8 -0
  87. data/lib/n/server/RESEARCH.txt +13 -0
  88. data/lib/n/server/filter.rb +77 -0
  89. data/lib/n/shaders.rb +167 -0
  90. data/lib/n/sitemap.rb +188 -0
  91. data/lib/n/std.rb +69 -0
  92. data/lib/n/sync/clc.rb +108 -0
  93. data/lib/n/sync/handler.rb +221 -0
  94. data/lib/n/sync/server.rb +170 -0
  95. data/lib/n/tools/README +11 -0
  96. data/lib/n/ui/date-select.rb +74 -0
  97. data/lib/n/ui/pager.rb +187 -0
  98. data/lib/n/ui/popup.rb +45 -0
  99. data/lib/n/ui/select.rb +41 -0
  100. data/lib/n/ui/tabs.rb +34 -0
  101. data/lib/n/utils/array.rb +92 -0
  102. data/lib/n/utils/cache.rb +144 -0
  103. data/lib/n/utils/gfx.rb +108 -0
  104. data/lib/n/utils/hash.rb +148 -0
  105. data/lib/n/utils/html.rb +147 -0
  106. data/lib/n/utils/http.rb +98 -0
  107. data/lib/n/utils/mail.rb +28 -0
  108. data/lib/n/utils/number.rb +31 -0
  109. data/lib/n/utils/pool.rb +66 -0
  110. data/lib/n/utils/string.rb +297 -0
  111. data/lib/n/utils/template.rb +38 -0
  112. data/lib/n/utils/time.rb +91 -0
  113. data/lib/n/utils/uri.rb +193 -0
  114. data/lib/xsl/base.xsl +205 -0
  115. data/lib/xsl/ce.xsl +30 -0
  116. data/lib/xsl/localization.xsl +23 -0
  117. data/lib/xsl/xforms.xsl +26 -0
  118. data/test/run.rb +95 -0
  119. metadata +187 -0
@@ -0,0 +1,59 @@
1
+ # = RequestPart
2
+ #
3
+ # Encapsulates a multipart part.
4
+ #
5
+ # code:
6
+ # George Moschovitis <gm@navel.gr>
7
+ #
8
+ # (c) 2004 Navel, all rights reserved.
9
+ # $Id: request-part.rb 71 2004-10-18 10:50:22Z gmosx $
10
+
11
+ require "cgi"
12
+ require "ftools"
13
+
14
+ require "n/utils/string"
15
+ require "n/utils/uri"
16
+ require "n/utils/http"
17
+ require "n/app/cookie"
18
+
19
+ module N; module App
20
+
21
+ # RequestPart
22
+ #
23
+ # This class encapsulates a part in a multipart request.
24
+ # A part is typically an uploaded files.
25
+ #
26
+ class RequestPart
27
+ # the filename of the part
28
+ attr_accessor :filename
29
+
30
+ # the original filename path
31
+ attr_accessor :original_path
32
+
33
+ # the temp filename path
34
+ attr_accessor :body
35
+
36
+ # the content-type
37
+ attr_accessor :content_type
38
+
39
+ def initialize(original_path, body, content_type = nil)
40
+ @original_path = original_path
41
+ @body = body
42
+ @content_type = content_type
43
+ # handle dos + unix separators.
44
+ @filename = @original_path.split(/\/|\\/).last
45
+ end
46
+
47
+ # gmosx:
48
+ # Hack fixed save for webrick!
49
+ #
50
+ def save(prefix, forced_filename = nil, forced_extension = nil)
51
+ # ARGH!! local path is used for something else, FIXME!!
52
+ ::FileUtils.mkdir_p(prefix)
53
+ save_path = "#{prefix}/#{@filename}"
54
+ ::File.open(save_path, "wb") {|f| f.write @body }
55
+ return save_path
56
+ end
57
+ end
58
+
59
+ end; end # module
@@ -0,0 +1,653 @@
1
+ # = Request
2
+ #
3
+ # Will use params with symbols for args. use uppercase for system
4
+ # 'args'.
5
+ #
6
+ # === INVESTIGATE:
7
+ # extend request from hash? to make more compatible with irb?
8
+ #
9
+ # code: gmosx, drak
10
+ #
11
+ # (c) 2004 Navel, all rights reserved.
12
+ # $Id: request.rb 71 2004-10-18 10:50:22Z gmosx $
13
+
14
+ require "cgi"
15
+ require "ftools"
16
+
17
+ require "n/utils/string"
18
+ require "n/utils/uri"
19
+ require "n/utils/http"
20
+ require "n/app/cookie"
21
+ require "n/app/request-part"
22
+
23
+ module N; module App
24
+
25
+ # = RequestUtils
26
+ #
27
+ # A collection of Request utility methods. Factored out from the
28
+ # Request object in a separate module to allow for inclusion in generic
29
+ # request objects (for example WEBrick).
30
+ #
31
+ module RequestUtils
32
+
33
+ # Full URI
34
+ #
35
+ def full_uri
36
+ if @query_string
37
+ return "#{@translated_uri}?#{@query_string}"
38
+ else
39
+ return @translated_uri
40
+ end
41
+ end
42
+
43
+ # Expand URI
44
+ # Calculates a new uri based on the request.uri that includes
45
+ # the additional parameters suplied.
46
+ #
47
+ def expand_uri(params)
48
+ hash = @parameters.dup()
49
+ hash.update(params)
50
+
51
+ pairs = []
52
+ hash.each { |param, value|
53
+ pairs << "#{param}=#{value}"
54
+ }
55
+
56
+ # gmosx: hash is ALWAYS non empty!
57
+ return "#{@translated_uri}?#{pairs.join(';')}"
58
+ end
59
+
60
+ # Uses the passed oid_param to load a managed object (entity).
61
+ # Enforces some form of security by checking the klass of the
62
+ # requested entity.
63
+ #
64
+ def get_entity(oid_param, klass = nil)
65
+ if oid = self[oid_param]
66
+ obj = $db.get(oid, klass)
67
+
68
+ if klass
69
+ if obj.is_a?(klass)
70
+ return obj
71
+ else
72
+ return nil
73
+ end
74
+ else
75
+ return obj
76
+ end
77
+ else
78
+ # $log.error "request.get_object('#{oid_param}') failed!"
79
+ return nil
80
+ end
81
+ end
82
+
83
+ # Uses the passed name_param to load a managed object (entity)
84
+ # by name.
85
+ # Enforces some form of security by checking the klass of the
86
+ # requested entity.
87
+ #
88
+ def get_entity_by_name(name_param, klass)
89
+ if name = self[name_param]
90
+ obj = $db.get_by_name(name, klass)
91
+
92
+ if klass
93
+ if obj.is_a?(klass)
94
+ return obj
95
+ else
96
+ return nil
97
+ end
98
+ else
99
+ # $log.error "request.get_object_by_name('#{name_param}') failed!"
100
+ return obj
101
+ end
102
+ else
103
+ return nil
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ # = Request
110
+ #
111
+ # Encapsulates an http protocol request. Generally clones
112
+ # the Apache Request passed by mod_ruby.
113
+ #
114
+ # === Todo:
115
+ #
116
+ # - USE libapreq!
117
+ # - Dont use env_table (arghhhh!)
118
+ # - dont use a separate path and real path
119
+ # - MEGA: unify request and request (like a socket, io
120
+ # stream,etc). Evan subclass IO!
121
+ #
122
+ # === Design:
123
+ #
124
+ # - uri: the original uri entered to the browser (INCLUDES qs)
125
+ # gmosx: i included the qs and removed full_uri (always forgot
126
+ # to use it anyway, was very error prone)
127
+ # - translated_uri: as translated by the web server (no query string)
128
+ # - path: the path to the actual script (or object)
129
+ #
130
+ # Example:
131
+ #
132
+ # http://www.site.com/faq/?id=1
133
+ # ->
134
+ # uri: /faq/?id=1
135
+ # translated_uri: /faq/index.sx
136
+ # path: base/site/root/faq/index.sx
137
+ # query_string: id=1
138
+ #
139
+ # - querystring should probably include the ?
140
+ # - Encapsulate ModRuby/Apache requests
141
+ # - Based on resin3.0 excellent code:
142
+ # com/caucho/server/http/HttpRequest.java
143
+ # - use as much of ruby's default cgi/http code as
144
+ # possible (why reinvent the wheel?)
145
+ #
146
+ #
147
+ class Request
148
+ # include usefull query parsing code from the standard
149
+ # lib. ARGHHH cgi.rb sucks!
150
+ include CGI::QueryExtension
151
+ include RequestUtils
152
+
153
+ # request methods enumeration
154
+ METHOD_GET = 0
155
+ METHOD_POST = 1
156
+ METHOD_HEAD = 2
157
+
158
+ # request method
159
+ attr_accessor :method
160
+
161
+ # content type
162
+ attr_accessor :content_type
163
+
164
+ # the uri for the request. For sub-requests keeps the
165
+ # uri of the top level request.
166
+ # gmosx: the writer is needed for injects.
167
+ attr_accessor :uri
168
+
169
+ # the uri as translated by the web server. For sub-requests keeps
170
+ # the usri of the top level request.
171
+ attr_accessor :translated_uri
172
+
173
+ # the path to the actual object (script)
174
+ # gets overriden by sub-requests, is not needed in scripts.
175
+ attr_accessor :path
176
+
177
+ # path info (extra parameters in the uri)
178
+ #
179
+ attr_accessor :path_info
180
+
181
+ # the query string part of the uri
182
+ attr_accessor :query_string
183
+
184
+ # The query string is parsed to the parameters
185
+ # hash.
186
+ attr_accessor :parameters
187
+ alias_method :query, :parameters
188
+
189
+ # the session this request is part-of
190
+ attr_accessor :session
191
+
192
+ # The parts attached to this request.
193
+ # A part is typically an uploaded file. By using
194
+ # a separate hash instead of the parameters hash one
195
+ # can easily enumerate parts.
196
+ attr_accessor :parts
197
+
198
+ # the level of the request (0 = toplevel)
199
+ attr_accessor :level
200
+
201
+ # the remote address for this request
202
+ attr_accessor :remote_addr
203
+
204
+ # Is the request cacheable?
205
+ # Set this attribute to 'true' to avoid caching the request
206
+ # fragment. Used to avoid caching 'action' requests.
207
+ attr_accessor :uncacheable
208
+
209
+ # the locale hash for this request.
210
+ attr_accessor :locale
211
+
212
+ # the shader for this request.
213
+ attr_accessor :shader
214
+
215
+ # the script hash for this request
216
+ attr_accessor :tag
217
+
218
+ # keep the original handler process uri, usefull as a cache key.
219
+ # Typically updated in subrequests only. Investigate if we could
220
+ # use a hash here!
221
+ attr_accessor :fragment_hash
222
+
223
+ # Keep all errors to present them in-page when in admin mode.
224
+ attr_accessor :error_log
225
+
226
+ # The top level script for this request.
227
+ attr_accessor :top_script
228
+
229
+ # last modified cache
230
+ attr_accessor :lm
231
+
232
+ # request:
233
+
234
+ # the incoming headers
235
+ # gmosx: writer needed for inject (sub-req cloning)
236
+ attr_accessor :in
237
+
238
+ # the incoming cookies
239
+ attr_accessor :in_cookies
240
+
241
+ # request:
242
+
243
+ # the outgoing headers
244
+ attr_accessor :out
245
+
246
+ # the outgoing cookies
247
+ attr_accessor :out_cookies
248
+
249
+ # the outgoing buffer
250
+ attr_accessor :out_buffer
251
+
252
+ # request content type, default: text/html
253
+ attr_accessor :content_type
254
+
255
+ # HTTP request status
256
+ attr_accessor :status
257
+
258
+ # HTTP request message
259
+ attr_accessor :message
260
+
261
+ def initialize
262
+ # set to 0 (== top level). When including fragments
263
+ # the level is incremented.
264
+ @level = 0
265
+
266
+ # gmosx: it would be good to defere the hash creation,
267
+ # but having the hash always created saves as a LOT of
268
+ # checks in client code, so we create it here.
269
+ #
270
+ # FIXME: WE SHOULD NOT CREATE unneeded hash objects.
271
+ #
272
+ # @parts = {}
273
+
274
+ # request:
275
+ # provide some fair initialization values.
276
+ set_status(200)
277
+ @content_type = "text/html; charset=iso-8859-7"
278
+ @out = {}
279
+ @out_cookies = {}
280
+ end
281
+
282
+ #-----------------------------------------------------------------------
283
+ # Headers
284
+
285
+ # Return the referer to this resource. For the initial page in the
286
+ # clickstream there is no referer, set "/" by default.
287
+
288
+ def referer
289
+ return @in["REFERER"] || "/"
290
+ end
291
+ alias_method :referrer, :referer
292
+
293
+ #-----------------------------------------------------------------------
294
+ # Cookies
295
+
296
+ # this method is also usefull for probing (testing) the request class.
297
+ # FIXME: optimize this (libapreq)
298
+
299
+ def parse_cookies(cookie_string)
300
+ @cookies = Cookie.parse(cookie_string)
301
+ end
302
+ alias_method :parse_cookie_string, :parse_cookies
303
+
304
+ # === Input:
305
+ # the cookie name
306
+ #
307
+ # === Output:
308
+ # - the cookie value, or an array of values for multivalued cookies.
309
+ # - nil if the cookie doesnt exist.
310
+ #
311
+ # === Example:
312
+ # nsid = request.get_cookie("nsid")
313
+ #
314
+ def get_cookie(cookie_name)
315
+ return nil unless @in_cookies
316
+ cookie = @in_cookies[cookie_name]
317
+ return nil unless cookie
318
+ return CGI.unescape(cookie.value)
319
+ end
320
+
321
+ # Removes a cookie from the client by seting the expire
322
+ # time to the past (epoch).
323
+ #
324
+ def del_cookie(name)
325
+ cookie = N::App::Cookie.new(name, "nil")
326
+ cookie.path = "/"
327
+ cookie.expires = Time.at(0)
328
+ @out_cookies[name] = cookie
329
+ end
330
+
331
+ #-------------------------------------------------------------------------------
332
+ # Query
333
+
334
+ def parse_query_string
335
+ return N::UriUtils.query_string_to_hash(@query_string)
336
+ end
337
+
338
+ # Return the value of a query parameter
339
+
340
+ def [](name)
341
+ return @parameters[name]
342
+ end
343
+
344
+ # Same as [] but enforces a default value to!
345
+ # Also tries to guess the parameters type from the default
346
+ # value. It works like delete (ie returns nil for 'empty'
347
+ # String parameters).
348
+ #
349
+ def get(key, default=nil)
350
+ val = @parameters[key]
351
+
352
+ if !val or (val.is_a?(String) and (not N::StringUtils.valid?(val)))
353
+ @parameters[key] = default
354
+ return default
355
+ elsif default.is_a?(Integer)
356
+ return val.to_i
357
+ else
358
+ return val
359
+ end
360
+ end
361
+
362
+ # Set the value of a query parameter
363
+ #
364
+ # === FIXME:
365
+ # - handle multivalued parameters!
366
+
367
+ def []=(name, value)
368
+ @parameters[name] = value
369
+ end
370
+
371
+ #-----------------------------------------------------------------------
372
+ # Utilities
373
+
374
+ # Returns true for the top-level request, but false for
375
+ # any inject or forward
376
+ #
377
+ def is_top?
378
+ return 0 == @level
379
+ end
380
+
381
+ # Is this an admin request?
382
+ # FIXME: no longer valid, recode.
383
+ #
384
+ def admin?
385
+ return @parameters.include?("*admin")
386
+ end
387
+
388
+ # Shorthand for request.session.user
389
+ #
390
+ def user
391
+ return @session.user
392
+ end
393
+
394
+ # Shorthand for request.session.user.anonymous?
395
+ #
396
+ def anonymous?
397
+ return @session.user.anonymous?
398
+ end
399
+
400
+ # Set errors as a transaction entity.
401
+ # Returns the txid for the errors
402
+ def set_errors(errors)
403
+ new_tx_entity!(errors, "errid") unless errors.empty?
404
+ end
405
+
406
+ # Shorthand
407
+ #
408
+ # Output:
409
+ # nil if no errors.
410
+ #
411
+ def errors
412
+ return del_tx_entity!("errid")
413
+ end
414
+
415
+ # Returns the errors as an array.
416
+ #
417
+ def errors_to_a
418
+ if errors = del_tx_entity!("errid")
419
+ return errors.values
420
+ end
421
+ return nil
422
+ end
423
+ alias_method :errors_list, :errors_to_a
424
+
425
+ # Check if a parameter is valid
426
+ #
427
+ def param?(param)
428
+ return N::StringUtils.valid?(self[param])
429
+ end
430
+ alias_method :action?, :param?
431
+
432
+ # Check if a parameter exists!
433
+ # Example:
434
+ # url:www.mysite.com/page.sx?admin
435
+ # request.include?(admin) => true
436
+ #
437
+ def include?(param)
438
+ return @parameters.include?(param)
439
+ end
440
+
441
+ def update(*params)
442
+ @parameters.update(*params)
443
+ end
444
+
445
+ # Use the delete name to make the request compatible with
446
+ # hashes.
447
+ # Tests is a parameter is passed to the request and removes it!
448
+ # Used in action handlers.
449
+ #
450
+ def delete(param)
451
+ oparam = param
452
+ if param = @parameters.delete(param)
453
+ # gmosx: remove from querystring too! NEEDED.
454
+ # perhaps kinda slow but happens seldom and optimizes
455
+ # another frequent case
456
+ @query_string = @parameters.collect { |k, v| (v && k.is_a?(String)) ? "#{k}=#{v}" : k }.join(";")
457
+
458
+ # If the parameter exist this is an action request, so
459
+ # do NOT cache the fragment.
460
+ @uncacheable = true
461
+ end
462
+
463
+ # gmosx: to avoid using param?
464
+ if param.is_a?(String) and (not N::StringUtils.valid?(param))
465
+ return nil
466
+ else
467
+ return param
468
+ end
469
+ end
470
+
471
+ # exclude those parameters for security
472
+ EXCLUDED_PARAMETERS = %w{ oid pid name }
473
+
474
+ # gmosx: hmm this is a really dangerous method, the EXCLUDED params
475
+ # above dont seem enough :(
476
+ #
477
+ def update_entity(entity)
478
+ @parameters.each { |param, val|
479
+ begin
480
+ # gmosx: DO NOT escape by default !!!
481
+ # gmosx: We need to get non valid params
482
+ if (not EXCLUDED_PARAMETERS.include?(param))
483
+ entity.send("__force_#{param}", val)
484
+ end
485
+ rescue NameError
486
+ next
487
+ end
488
+ }
489
+
490
+ return entity
491
+ end
492
+
493
+ # --------------------------------------------------------------------
494
+
495
+ # The tx sequence increases, the count of transactions / session
496
+ # is usually bounded.
497
+ #
498
+ def new_tx_entity!(entity, txparam = "txid")
499
+ unless seq = @session["TXSEQ"]
500
+ seq = 0
501
+ end
502
+ seq += 1
503
+ @session["TXSEQ"] = seq
504
+ txid = "TX#{seq}"
505
+ @session[txid] = entity
506
+ @parameters[txparam] = txid
507
+ return txid
508
+ end
509
+
510
+ # Set (update) an existing tx entity. Does NOT increase the
511
+ # tx sequence.
512
+ #
513
+ def set_tx_entity!(entity, txparam = "txid")
514
+ if txid = @parameters[txparam]
515
+ @session[txid] = entity
516
+ end
517
+ end
518
+ alias_method :update_tx_entity!, :set_tx_entity!
519
+
520
+ #
521
+ #
522
+ def get_tx_entity(txparam = "txid")
523
+ if txid = @parameters[txparam]
524
+ return @session[txid]
525
+ end
526
+ return nil
527
+ end
528
+
529
+ #
530
+ #
531
+ def del_tx_entity!(txparam = "txid")
532
+ if txid = @parameters[txparam]
533
+ @session.delete(txid)
534
+ end
535
+ end
536
+
537
+ # --------------------------------------------------------------------
538
+ # Debugging helper
539
+
540
+ #
541
+ #
542
+ def log_error(str)
543
+ @error_log = [] unless @error_log
544
+ @error_log << str if @error_log.size < 200 # gmosx: dod attack!
545
+ $log.error str
546
+ end
547
+
548
+ # ====================================================================
549
+ # Request
550
+
551
+ # TODO: add status codes, messages
552
+
553
+ # Set the request HTTP status and lookup the
554
+ # corresponding request status message.
555
+
556
+ def set_status(status = 200)
557
+ @status = status
558
+ @message = HTTP::STATUS_STRINGS[status]
559
+ end
560
+
561
+ # Set the HTTP NOT_MODIFIED status code.
562
+ # Usefull for HTTP Caching.
563
+ #
564
+ def set_not_modified!
565
+ @status = 304
566
+ @message = N::HTTP::STATUS_STRINGS[status]
567
+ end
568
+
569
+ # 302 is the redirect status!
570
+ #
571
+ # === Status 303:
572
+ #
573
+ # The request to the request can be found
574
+ # under a different URI and SHOULD be retrieved using
575
+ # a GET method on that resource. This method exists
576
+ # primarily to allow the output of a POST-activated script
577
+ # to redirect the user agent to a selected resource. The new
578
+ # URI is not a substitute reference for the originally
579
+ # requested resource. The 303 request MUST NOT be cached,
580
+ # but the request to the second (redirected) request might
581
+ # be cacheable.
582
+ #
583
+ # Note: Many pre-HTTP/1.1 user agents do not understand the
584
+ # 303 status. When interoperability with such clients is a
585
+ # concern, the 302 status code may be used instead, since
586
+ # most user agents react to a 302 request as described
587
+ # here for 303.
588
+ #
589
+ # === WARNING:
590
+ #
591
+ # Konqueror always performs a 307 redirect ARGH!
592
+ #
593
+ # === Redesign:
594
+ #
595
+ # Use one redirect method with an optional status
596
+ # parameter, that reads messages from the status
597
+ # constants.
598
+ #
599
+ # === Input:
600
+ #
601
+ # - url to redirect to
602
+ # - if force_exit == true raises a ScriptExitException
603
+ # - status (303 or 307) default = 303
604
+ #
605
+ def redirect(url = nil, force_exit = false, status = 302)
606
+ # FIXME: normalize the url
607
+ # url = $srv_url + url if url =~ /^\//om
608
+ # FIXME: check arguments
609
+
610
+ # enforce a meaningfull default
611
+ url ||= self["_go"] || referer()
612
+
613
+ # the url should have a leading "/"
614
+ # enforce it to be sure. FIXME: optimize this!
615
+ url = "/#{url}".squeeze("/") unless url =~ /^http/
616
+
617
+ @out["Location"] = url
618
+ # gmosx: NOT needed? see the exceprt from the spec.
619
+ # @out['Cache-Control'] = "max-age=1"
620
+
621
+ set_status(status)
622
+ @out_buffer = "The URL has moved <a href='#{url}'>here</a>"
623
+
624
+ if force_exit
625
+ # Stop rendering the script immediately!
626
+ # This is the default behaviour!
627
+ raise N::ScriptExitException
628
+ end
629
+
630
+ # for unit testing
631
+ return url
632
+ end
633
+
634
+ # Internal redirect
635
+ #
636
+ # FIXME: implement me
637
+ #
638
+ def internal_redirect(url)
639
+ end
640
+
641
+ # Utility method to set the expires header.
642
+ #
643
+ def expires!(exp_time)
644
+ @out["Expires"] = N::HttpUtils.time_to_string(exp_time)
645
+ end
646
+
647
+ def expires?
648
+ return @out["Expires"]
649
+ end
650
+
651
+ end
652
+
653
+ end; end # module