raw 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/doc/CONTRIBUTORS +106 -0
  2. data/doc/LICENSE +32 -0
  3. data/doc/coding_conventions.txt +11 -0
  4. data/lib/raw.rb +42 -0
  5. data/lib/raw/adapter.rb +113 -0
  6. data/lib/raw/adapter/cgi.rb +41 -0
  7. data/lib/raw/adapter/fastcgi.rb +48 -0
  8. data/lib/raw/adapter/mongrel.rb +146 -0
  9. data/lib/raw/adapter/script.rb +94 -0
  10. data/lib/raw/adapter/webrick.rb +144 -0
  11. data/lib/raw/adapter/webrick/vcr.rb +91 -0
  12. data/lib/raw/cgi.rb +323 -0
  13. data/lib/raw/cgi/cookie.rb +47 -0
  14. data/lib/raw/cgi/http.rb +62 -0
  15. data/lib/raw/compiler.rb +138 -0
  16. data/lib/raw/compiler/filter/cleanup.rb +21 -0
  17. data/lib/raw/compiler/filter/elements.rb +166 -0
  18. data/lib/raw/compiler/filter/elements/element.rb +210 -0
  19. data/lib/raw/compiler/filter/localization.rb +23 -0
  20. data/lib/raw/compiler/filter/markup.rb +32 -0
  21. data/lib/raw/compiler/filter/morph.rb +123 -0
  22. data/lib/raw/compiler/filter/morph/each.rb +34 -0
  23. data/lib/raw/compiler/filter/morph/for.rb +11 -0
  24. data/lib/raw/compiler/filter/morph/if.rb +26 -0
  25. data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
  26. data/lib/raw/compiler/filter/morph/standard.rb +55 -0
  27. data/lib/raw/compiler/filter/morph/times.rb +27 -0
  28. data/lib/raw/compiler/filter/script.rb +116 -0
  29. data/lib/raw/compiler/filter/squeeze.rb +16 -0
  30. data/lib/raw/compiler/filter/static_include.rb +74 -0
  31. data/lib/raw/compiler/filter/template.rb +121 -0
  32. data/lib/raw/compiler/reloader.rb +96 -0
  33. data/lib/raw/context.rb +154 -0
  34. data/lib/raw/context/flash.rb +157 -0
  35. data/lib/raw/context/global.rb +88 -0
  36. data/lib/raw/context/request.rb +338 -0
  37. data/lib/raw/context/response.rb +57 -0
  38. data/lib/raw/context/session.rb +198 -0
  39. data/lib/raw/context/session/drb.rb +11 -0
  40. data/lib/raw/context/session/file.rb +15 -0
  41. data/lib/raw/context/session/memcached.rb +13 -0
  42. data/lib/raw/context/session/memory.rb +12 -0
  43. data/lib/raw/context/session/og.rb +15 -0
  44. data/lib/raw/context/session/pstore.rb +13 -0
  45. data/lib/raw/control.rb +18 -0
  46. data/lib/raw/control/attribute.rb +91 -0
  47. data/lib/raw/control/attribute/checkbox.rb +25 -0
  48. data/lib/raw/control/attribute/datetime.rb +21 -0
  49. data/lib/raw/control/attribute/file.rb +20 -0
  50. data/lib/raw/control/attribute/fixnum.rb +26 -0
  51. data/lib/raw/control/attribute/float.rb +26 -0
  52. data/lib/raw/control/attribute/options.rb +38 -0
  53. data/lib/raw/control/attribute/password.rb +16 -0
  54. data/lib/raw/control/attribute/text.rb +16 -0
  55. data/lib/raw/control/attribute/textarea.rb +16 -0
  56. data/lib/raw/control/none.rb +16 -0
  57. data/lib/raw/control/relation.rb +59 -0
  58. data/lib/raw/control/relation/belongs_to.rb +0 -0
  59. data/lib/raw/control/relation/has_many.rb +97 -0
  60. data/lib/raw/control/relation/joins_many.rb +0 -0
  61. data/lib/raw/control/relation/many_to_many.rb +0 -0
  62. data/lib/raw/control/relation/refers_to.rb +29 -0
  63. data/lib/raw/controller.rb +37 -0
  64. data/lib/raw/controller/publishable.rb +160 -0
  65. data/lib/raw/dispatcher.rb +209 -0
  66. data/lib/raw/dispatcher/format.rb +108 -0
  67. data/lib/raw/dispatcher/format/atom.rb +31 -0
  68. data/lib/raw/dispatcher/format/css.rb +0 -0
  69. data/lib/raw/dispatcher/format/html.rb +42 -0
  70. data/lib/raw/dispatcher/format/json.rb +31 -0
  71. data/lib/raw/dispatcher/format/rss.rb +33 -0
  72. data/lib/raw/dispatcher/format/xoxo.rb +31 -0
  73. data/lib/raw/dispatcher/mounter.rb +60 -0
  74. data/lib/raw/dispatcher/router.rb +111 -0
  75. data/lib/raw/errors.rb +19 -0
  76. data/lib/raw/helper.rb +86 -0
  77. data/lib/raw/helper/benchmark.rb +23 -0
  78. data/lib/raw/helper/buffer.rb +60 -0
  79. data/lib/raw/helper/cookie.rb +32 -0
  80. data/lib/raw/helper/debug.rb +28 -0
  81. data/lib/raw/helper/default.rb +16 -0
  82. data/lib/raw/helper/feed.rb +451 -0
  83. data/lib/raw/helper/form.rb +284 -0
  84. data/lib/raw/helper/javascript.rb +59 -0
  85. data/lib/raw/helper/layout.rb +40 -0
  86. data/lib/raw/helper/navigation.rb +87 -0
  87. data/lib/raw/helper/pager.rb +305 -0
  88. data/lib/raw/helper/table.rb +247 -0
  89. data/lib/raw/helper/xhtml.rb +218 -0
  90. data/lib/raw/helper/xml.rb +125 -0
  91. data/lib/raw/mixin/magick.rb +35 -0
  92. data/lib/raw/mixin/sweeper.rb +71 -0
  93. data/lib/raw/mixin/thumbnails.rb +1 -0
  94. data/lib/raw/mixin/webfile.rb +165 -0
  95. data/lib/raw/render.rb +271 -0
  96. data/lib/raw/render/builder.rb +26 -0
  97. data/lib/raw/render/caching.rb +81 -0
  98. data/lib/raw/render/call.rb +43 -0
  99. data/lib/raw/render/send_file.rb +46 -0
  100. data/lib/raw/render/stream.rb +39 -0
  101. data/lib/raw/scaffold.rb +13 -0
  102. data/lib/raw/scaffold/controller.rb +25 -0
  103. data/lib/raw/scaffold/model.rb +157 -0
  104. data/lib/raw/test.rb +5 -0
  105. data/lib/raw/test/assertions.rb +169 -0
  106. data/lib/raw/test/context.rb +55 -0
  107. data/lib/raw/test/testcase.rb +79 -0
  108. data/lib/raw/util/attr.rb +128 -0
  109. data/lib/raw/util/encode_uri.rb +149 -0
  110. data/lib/raw/util/html_filter.rb +538 -0
  111. data/lib/raw/util/markup.rb +130 -0
  112. data/test/glue/tc_webfile.rb +1 -0
  113. data/test/nitro/CONFIG.rb +3 -0
  114. data/test/nitro/adapter/raw_post1.bin +9 -0
  115. data/test/nitro/adapter/tc_webrick.rb +16 -0
  116. data/test/nitro/cgi/tc_cookie.rb +14 -0
  117. data/test/nitro/cgi/tc_request.rb +61 -0
  118. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  119. data/test/nitro/compiler/tc_compiler.rb +25 -0
  120. data/test/nitro/dispatcher/tc_mounter.rb +47 -0
  121. data/test/nitro/helper/tc_feed.rb +135 -0
  122. data/test/nitro/helper/tc_navbar.rb +74 -0
  123. data/test/nitro/helper/tc_pager.rb +35 -0
  124. data/test/nitro/helper/tc_table.rb +68 -0
  125. data/test/nitro/helper/tc_xhtml.rb +19 -0
  126. data/test/nitro/tc_caching.rb +19 -0
  127. data/test/nitro/tc_cgi.rb +222 -0
  128. data/test/nitro/tc_context.rb +17 -0
  129. data/test/nitro/tc_controller.rb +103 -0
  130. data/test/nitro/tc_controller_aspect.rb +32 -0
  131. data/test/nitro/tc_controller_params.rb +885 -0
  132. data/test/nitro/tc_dispatcher.rb +109 -0
  133. data/test/nitro/tc_element.rb +85 -0
  134. data/test/nitro/tc_flash.rb +59 -0
  135. data/test/nitro/tc_helper.rb +47 -0
  136. data/test/nitro/tc_render.rb +119 -0
  137. data/test/nitro/tc_router.rb +61 -0
  138. data/test/nitro/tc_server.rb +35 -0
  139. data/test/nitro/tc_session.rb +66 -0
  140. data/test/nitro/tc_template.rb +71 -0
  141. data/test/nitro/util/tc_encode_url.rb +87 -0
  142. data/test/nitro/util/tc_markup.rb +31 -0
  143. data/test/public/blog/another/very_litle/index.xhtml +1 -0
  144. data/test/public/blog/inc1.xhtml +2 -0
  145. data/test/public/blog/inc2.xhtml +1 -0
  146. data/test/public/blog/list.xhtml +9 -0
  147. data/test/public/dummy_mailer/registration.xhtml +5 -0
  148. metadata +244 -0
@@ -0,0 +1,94 @@
1
+ require "raw/adapter"
2
+
3
+ module Raw
4
+
5
+ # The script adapter. Useful when running in console mode, or
6
+ # when creating scripts for cron jobs, testing and more. Allows
7
+ # you to programmatically 'drive' the web application.
8
+
9
+ class ScriptAdapter < Adapter
10
+ include AdapterHandlerMixin
11
+
12
+ # The last generated response.
13
+
14
+ attr_accessor :response
15
+
16
+ def start(application)
17
+ info "This console is attached to the application context."
18
+ info ""
19
+ info "* $app points to the application"
20
+ info "* $srv points to the adapter"
21
+ info "* use get(uri), post(uri), response() to programmatically call actions"
22
+ info ""
23
+
24
+ $app = $application = @application = application
25
+ $srv = $adapter = self
26
+ end
27
+
28
+ # Perform a programatic http request to the web app.
29
+ #
30
+ # === Examples
31
+ #
32
+ # $srv.get 'users/logout'
33
+ # $srv.post 'users/login', :params => { :name => 'gmosx', :password => 'pass' }
34
+ # $srv.post 'users/login?name=gmosx;password=pass
35
+ # $srv.post 'articles/view/1'
36
+
37
+ def handle(uri, options = {})
38
+ # Perform default rewriting rules.
39
+ # rewrite(req)
40
+
41
+ context = Context.new(@application)
42
+
43
+ context.get_params = options.fetch(:get_params, {})
44
+ context.post_params = options.fetch(:post_params, {})
45
+ context.headers = options.fetch(:headers, {})
46
+
47
+ context.headers["REQUEST_URI"] = uri
48
+ context.headers["REQUEST_METHOD"] = options.fetch(:method, :get)
49
+ context.headers["HTTP_COOKIE"] ||= options[:cookies]
50
+
51
+ handle_context(context)
52
+
53
+ @response = context
54
+
55
+ context.close
56
+
57
+ return context
58
+ end
59
+
60
+ # Perform a programmatic http get request to the web app.
61
+
62
+ def get(uri, options = {})
63
+ options[:method] = "get"
64
+ handle(uri, options)
65
+ end
66
+
67
+ # Perform a programmatic http post request to the web app.
68
+
69
+ def post(uri, options = {})
70
+ options[:method] = "post"
71
+ handle(uri, options)
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ # Add some convienience methods.
79
+
80
+ if ENV["NITRO_ADAPTER"] == "script"
81
+
82
+ def get(*args)
83
+ $srv.get(*args)
84
+ end
85
+
86
+ def post(*args)
87
+ $srv.post(*args)
88
+ end
89
+
90
+ def response
91
+ $srv.response
92
+ end
93
+
94
+ end
@@ -0,0 +1,144 @@
1
+ require "stringio"
2
+ require "webrick"
3
+
4
+ require "raw/adapter"
5
+
6
+ module Raw
7
+
8
+ # A Webrick Adapter for Nitro. Webrick is a pure Ruby web server
9
+ # included in the default Ruby distribution. The Webrick Adapter
10
+ # is the prefered adapter in development/debug environments. It
11
+ # is also extremely easy to setup.
12
+ #
13
+ # However, for live/production environments, you should prefer
14
+ # a more performant adapter like Mongrel or FCGI. Mongrel is the
15
+ # suggested adapter for production applications.
16
+
17
+ class WebrickAdapter < Adapter
18
+
19
+ class Swallow # :nodoc: all
20
+ def self.method_missing(*args)
21
+ # drink it!
22
+ end
23
+ end
24
+
25
+ # Start the adapter.
26
+
27
+ def start(app)
28
+ super
29
+
30
+ if RUBY_PLATFORM !~ /mswin32/
31
+ wblog = WEBrick::BasicLog::new("/dev/null")
32
+ elsif File.exist? "log"
33
+ wblog = WEBrick::BasicLog::new("log/access.log")
34
+ else
35
+ wblog = STDERR
36
+ end
37
+
38
+ webrick_options = app.options.dup
39
+
40
+ require "webrick/https" if webrick_options[:SSLEnable]
41
+
42
+ webrick_options.update(
43
+ :BindAddress => app.address,
44
+ :Port => app.port,
45
+ :DocumentRoot => app.public_dir,
46
+ :Logger => Swallow,
47
+ :AccessLog => [
48
+ [wblog, WEBrick::AccessLog::COMMON_LOG_FORMAT],
49
+ [wblog, WEBrick::AccessLog::REFERER_LOG_FORMAT]
50
+ ]
51
+ )
52
+
53
+ trap("INT") { stop }
54
+
55
+ @webrick = WEBrick::HTTPServer.new(webrick_options)
56
+ @webrick.mount("/", WebrickHandler, app)
57
+ @webrick.start
58
+ end
59
+
60
+ # Stop the adapter.
61
+
62
+ def stop
63
+ super
64
+ @webrick.shutdown
65
+ end
66
+
67
+ end
68
+
69
+ # The Webrick Handler, handles an HTTP request.
70
+ #--
71
+ # TODO: add some way to prevent the display of template files
72
+ # if the public dir is used as the template dir.
73
+ #++
74
+
75
+ class WebrickHandler < WEBrick::HTTPServlet::AbstractServlet
76
+ include WEBrick
77
+ include AdapterHandlerMixin
78
+
79
+ def initialize(webrick, app)
80
+ @application = app
81
+
82
+ # Handles static resources. Useful when running
83
+ # a stand-alone webrick server.
84
+
85
+ @file_handler = WEBrick::HTTPServlet::FileHandler.new(
86
+ webrick, app.public_dir, app.options
87
+ )
88
+ end
89
+
90
+ # Handle a static file. Also handles cached pages. Typically
91
+ # *not* used in production applications.
92
+
93
+ def handle_file(req, res)
94
+ return false unless @application.handle_static_files
95
+ @file_handler.do_GET(req, res)
96
+ return true
97
+ rescue WEBrick::HTTPStatus::PartialContent, WEBrick::HTTPStatus::NotModified => err
98
+ res.set_error(err)
99
+ return true
100
+ rescue WEBrick::HTTPStatus::NotFound => ex
101
+ return false
102
+ end
103
+
104
+ # Handle the request.
105
+
106
+ def handle(req, res)
107
+ # Perform default rewriting rules.
108
+
109
+ rewrite(req)
110
+
111
+ # First, try to serve a static file from disk.
112
+
113
+ return if handle_file(req, res)
114
+
115
+ # No static file found, attempt to dynamically generate
116
+ # a response.
117
+
118
+ context = Context.new(@application)
119
+
120
+ context.in = StringIO.new(req.body || "")
121
+
122
+ context.headers = {}
123
+ req.header.each { |h, v| context.headers[h.upcase] = v.first }
124
+ context.headers.update(req.meta_vars)
125
+
126
+ # gmosx: make compatible with fastcgi.
127
+
128
+ context.headers["REQUEST_URI"].slice!(/http:\/\/(.*?)\//)
129
+ context.headers["REQUEST_URI"] = '/' + context.headers['REQUEST_URI']
130
+
131
+ res.body = handle_context(context)
132
+
133
+ res.status = context.status
134
+ res.instance_variable_set(:@header, context.response_headers || {})
135
+ res.instance_variable_set(:@cookies, context.response_cookies || {})
136
+
137
+ context.close
138
+ end
139
+
140
+ alias_method :do_GET, :handle
141
+ alias_method :do_POST, :handle
142
+ end
143
+
144
+ end
@@ -0,0 +1,91 @@
1
+ require "raw/adapter/webrick"
2
+
3
+ module Raw
4
+
5
+ class WebrickAdapter < Adapter
6
+
7
+ def setup(server)
8
+ if $record_session_filename
9
+ require "raw/adapter/webrick/vcr"
10
+ vcr_record($record_session_filename)
11
+ end
12
+
13
+ if $playback_session_filename
14
+ require "raw/adapter/webrick/vcr"
15
+ vcr_playback($playback_session_filename)
16
+ end
17
+ end
18
+
19
+ # Enables session recording. The recorded data can be used
20
+ # for automatic app testing by means of the playback mode.
21
+
22
+ def vcr_record(filename = "session.yaml")
23
+ info "Recording application server session to '#{filename}'."
24
+
25
+ require "facets/core/file/self/create"
26
+
27
+ $record_session = []
28
+ $last_record_time = Time.now
29
+
30
+ Nitro::WebrickHandler.class_eval do
31
+ def do_GET(req, res)
32
+ record_context(req, res)
33
+ handle(req, res)
34
+ end
35
+ alias_method :do_POST, :do_GET
36
+
37
+ def record_context(req, res)
38
+ delta = Time.now - $last_record_time
39
+ $last_record_time = Time.now
40
+ $record_session << [delta, req, res]
41
+ end
42
+ end
43
+
44
+ at_exit do
45
+ File.create(filename, YAML.dump($record_session))
46
+ end
47
+ end
48
+
49
+ # Playback a recorded session. Typically used for testing.
50
+
51
+ def vcr_playback(filename = "session.yaml")
52
+ info "Playing back application server session from '#{filename}'."
53
+
54
+ $playback_session = YAML.load_file(filename)
55
+ $playback_exception_count = 0
56
+
57
+ WEBrick::HTTPServer.class_eval do
58
+ def start(&block)
59
+ run(nil)
60
+ end
61
+
62
+ def run(sock)
63
+ while true
64
+ delta, req, res = $playback_session.shift
65
+
66
+ if delta
67
+ sleep(delta)
68
+ begin
69
+ service(req, res)
70
+ rescue Object => ex
71
+ $playback_exception_count += 1
72
+ p "---", ex
73
+ end
74
+ else
75
+ return
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ at_exit do
82
+ puts "\n\n"
83
+ puts "Playback raised #$playback_exception_count exceptions.\n"
84
+ puts "\n"
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
@@ -0,0 +1,323 @@
1
+ require "cgi"
2
+ require "stringio"
3
+
4
+ require "facets/more/settings"
5
+
6
+ require "raw/cgi/http"
7
+
8
+ module Raw
9
+
10
+ # Nitro CGI (Common Gateway Interface) methods. Typically
11
+ # handles HTTP Request parsing and HTTP Response generation.
12
+
13
+ class Cgi
14
+ include Http
15
+
16
+ # Maximum content length allowed in requests.
17
+
18
+ setting :max_content_length, :default => (2 * 1024 * 1024), :doc => 'Maximum content length allowed in requests'
19
+
20
+ # Multipart parsing buffer size.
21
+
22
+ setting :buffer_size, :default => (10 * 1024), :doc => 'Multipart parsing buffer size'
23
+
24
+ # Process a CGI request. This is a general method reused by
25
+ # many adapters.
26
+ #--
27
+ # A CGI request is process-based so there is no need for Og
28
+ # connection cleanup.
29
+ #++
30
+
31
+ def self.process(server, cgi, inp, out)
32
+ context = Context.new(server)
33
+
34
+ unless inp.respond_to?(:rewind)
35
+ # The module Request#raw_body requires a rewind method,
36
+ # so if the input stream doesn't have one, *cough* FCgi,
37
+ # we convert it to a StringIO.
38
+
39
+ inp = StringIO.new(inp.read.to_s) # if read returns nil, to_s makes it ""
40
+ end
41
+
42
+ context.in = inp
43
+ context.headers = cgi.env
44
+
45
+ #--
46
+ # gmosx: only handle nitro requests.
47
+ #++
48
+ # gmosx: QUERY_STRING is sometimes not populated.
49
+
50
+ if context.query_string.empty? and context.uri =~ /\?/
51
+ context.headers['QUERY_STRING'] = context.uri.split('?').last
52
+ end
53
+
54
+ Cgi.parse_params(context)
55
+ Cgi.parse_cookies(context)
56
+ context.render(context.path)
57
+
58
+ out.print(Cgi.response_headers(context))
59
+
60
+ if context.out.is_a?(IO)
61
+ begin
62
+ while buf = context.out.read(4096)
63
+ out.write(buf)
64
+ end
65
+ ensure
66
+ context.out.close
67
+ end
68
+ else
69
+ out.print(context.out)
70
+ end
71
+
72
+ $autoreload_dirty = false
73
+
74
+ context.close
75
+ end
76
+
77
+ # push a parameter into the params hash
78
+
79
+ def self.structure_param(params, key, val)
80
+ if key =~ /(.+)\[(.+)\]$/ or key =~ /([^\.]+)\.(.+)$/
81
+ params[$1] ||= Dictionary.new
82
+ params[$1] = structure_param(params[$1], $2, val)
83
+ elsif key =~ /(.+)\[\]$/
84
+ params[$1] ||= Array.new
85
+ params[$1] << val.to_s
86
+ else
87
+ params[key] = val.nil? ? nil : val
88
+ end
89
+ return params
90
+ end
91
+
92
+ # Returns a hash with the pairs from the query
93
+ # string. The implicit hash construction that is done
94
+ # in parse_request_params is not done here.
95
+
96
+ def self.parse_query_string(query_string)
97
+ params = Dictionary.new
98
+
99
+ # gmosx, THINK: better return nil here?
100
+ return params if (query_string.nil? or query_string.empty?)
101
+
102
+ query_string.split(/[&;]/).each do |p|
103
+ key, val = p.split('=')
104
+
105
+ key = CGI.unescape(key) unless key.nil?
106
+ val = CGI.unescape(val) unless val.nil?
107
+
108
+ params = self.structure_param(params, key, val)
109
+ end
110
+
111
+ return params
112
+ end
113
+
114
+ # Parse the HTTP_COOKIE header and returns the
115
+ # cookies as a key->value hash. For efficiency no
116
+ # cookie objects are created.
117
+ #
118
+ # [+context+]
119
+ # The context
120
+
121
+ def self.parse_cookies(context)
122
+ env = context.env
123
+
124
+ # FIXME: dont precreate?
125
+ context.cookies = {}
126
+
127
+ #if env.has_key?('HTTP_COOKIE') or env.has_key?('COOKIE')
128
+ if env['HTTP_COOKIE'] or env['COOKIE']
129
+ (env['HTTP_COOKIE'] or env['COOKIE']).split(/; /).each do |c|
130
+ key, val = c.split(/=/, 2)
131
+ val ||= ""
132
+ key = CGI.unescape(key)
133
+ val = val.split(/&/).collect{|v| CGI::unescape(v)}.join("\0")
134
+
135
+ if context.cookies.include?(key)
136
+ context.cookies[key] += "\0" + val
137
+ else
138
+ context.cookies[key] = val
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # Build the response headers for the context.
145
+ #
146
+ # [+context+]
147
+ # The context of the response.
148
+ #
149
+ # [+proto+]
150
+ # If true emmit the protocol line. Useful for MOD_RUBY.
151
+ #--
152
+ # FIXME: return the correct protocol from env.
153
+ # TODO: Perhaps I can optimize status calc.
154
+ #++
155
+
156
+ def self.response_headers(context, proto = false)
157
+ reason = STATUS_STRINGS[context.status]
158
+
159
+ if proto
160
+ buf = "HTTP/1.1 #{context.status} #{reason}#{EOL}Date: #{CGI::rfc1123_date(Time.now)}#{EOL}"
161
+ else
162
+ buf = "Status: #{context.status} #{reason}#{EOL}"
163
+ end
164
+
165
+ context.response_headers.each do |key, value|
166
+ tmp = key.gsub(/\bwww|^te$|\b\w/) { |s| s.upcase }
167
+ buf << "#{tmp}: #{value}" << EOL
168
+ end
169
+
170
+ context.response_cookies.each do |cookie|
171
+ buf << "Set-Cookie: " << cookie.to_s << EOL
172
+ end if context.response_cookies
173
+
174
+ buf << EOL
175
+
176
+ return buf
177
+ end
178
+
179
+ # Initialize the request params.
180
+ # Handles multipart forms (in particular, forms that involve
181
+ # file uploads). Reads query parameters in the @params field,
182
+ # and cookies into @cookies.
183
+
184
+ def self.parse_params(context)
185
+ method = context.method
186
+ if (:post == method) and
187
+ %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(context.headers['CONTENT_TYPE'])
188
+ boundary = $1.dup
189
+ context.post_params = parse_multipart(context, boundary)
190
+
191
+ # Also include the URI parameters.
192
+ context.get_params.update(Cgi.parse_query_string(context.query_string))
193
+ else
194
+ case method
195
+ when :get, :head
196
+ context.get_params = Cgi.parse_query_string(context.query_string)
197
+ when :post
198
+ context.in.binmode if defined?(context.in.binmode)
199
+ context.post_params = Cgi.parse_query_string(context.in.read(context.content_length) || '')
200
+ end
201
+ end
202
+ end
203
+
204
+ # Parse a multipart request.
205
+ # Adapted from Ruby's cgi.rb
206
+ #--
207
+ # TODO: optimize and rationalize this.
208
+ #++
209
+
210
+ def self.parse_multipart(context, boundary)
211
+ input = context.in
212
+ content_length = context.content_length
213
+ env_table = context.env
214
+
215
+ params = Hash.new()
216
+ boundary = "--" + boundary
217
+ quoted_boundary = Regexp.quote(boundary, "n")
218
+ buf = ""
219
+ boundary_end=""
220
+
221
+ # start multipart/form-data
222
+ input.binmode if defined? input.binmode
223
+ boundary_size = boundary.size + EOL.size
224
+ content_length -= boundary_size
225
+ status = input.read(boundary_size)
226
+
227
+ if nil == status
228
+ raise EOFError, "no content body"
229
+ elsif boundary + EOL != status
230
+ raise EOFError, "bad content body"
231
+ end
232
+
233
+ loop do
234
+ head = nil
235
+
236
+ if 10240 < content_length
237
+ body = Tempfile.new("CGI")
238
+ else
239
+ begin
240
+ require "stringio"
241
+ body = StringIO.new
242
+ rescue LoadError
243
+ body = Tempfile.new("CGI")
244
+ end
245
+ end
246
+ body.binmode if defined? body.binmode
247
+
248
+ until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
249
+
250
+ if (not head) and /#{EOL}#{EOL}/n.match(buf)
251
+ buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
252
+ head = $1.dup
253
+ ""
254
+ end
255
+ next
256
+ end
257
+
258
+ if head and ( (EOL + boundary + EOL).size < buf.size )
259
+ body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
260
+ buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
261
+ end
262
+
263
+ c = if Cgi.buffer_size < content_length
264
+ input.read(Cgi.buffer_size)
265
+ else
266
+ input.read(content_length)
267
+ end
268
+ if c.nil? || c.empty?
269
+ raise EOFError, "bad content body"
270
+ end
271
+ buf.concat(c)
272
+ content_length -= c.size
273
+ end
274
+
275
+ buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
276
+ body.print $1
277
+ if "--" == $2
278
+ content_length = -1
279
+ end
280
+ boundary_end = $2.dup
281
+ ""
282
+ end
283
+
284
+ body.rewind
285
+
286
+ /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
287
+
288
+ filename = ($1 or "")
289
+
290
+ if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
291
+ /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
292
+ (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
293
+ filename = CGI::unescape(filename)
294
+ end
295
+
296
+ /Content-Type: (.*)/ni.match(head)
297
+ content_type = ($1 or "")
298
+
299
+ (class << body; self; end).class_eval do
300
+ alias_method :local_path, :path
301
+ define_method(:original_filename) { filename.dup.taint }
302
+ define_method(:content_type) { content_type.dup.taint }
303
+
304
+ # gmosx: this hides the performance hit!!
305
+ define_method(:to_s) { str = read; rewind; return str}
306
+ end
307
+
308
+ /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
309
+ name = $1.dup
310
+
311
+ params = self.structure_param(params, name, body)
312
+
313
+ break if buf.size == 0
314
+ break if content_length === -1
315
+ end
316
+ raise EOFError, "bad boundary end of body" unless boundary_end =~ /--/
317
+
318
+ return params
319
+ end
320
+
321
+ end
322
+
323
+ end