raw 0.49.0

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