raw 0.49.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/CONTRIBUTORS +106 -0
- data/doc/LICENSE +32 -0
- data/doc/coding_conventions.txt +11 -0
- data/lib/raw.rb +42 -0
- data/lib/raw/adapter.rb +113 -0
- data/lib/raw/adapter/cgi.rb +41 -0
- data/lib/raw/adapter/fastcgi.rb +48 -0
- data/lib/raw/adapter/mongrel.rb +146 -0
- data/lib/raw/adapter/script.rb +94 -0
- data/lib/raw/adapter/webrick.rb +144 -0
- data/lib/raw/adapter/webrick/vcr.rb +91 -0
- data/lib/raw/cgi.rb +323 -0
- data/lib/raw/cgi/cookie.rb +47 -0
- data/lib/raw/cgi/http.rb +62 -0
- data/lib/raw/compiler.rb +138 -0
- data/lib/raw/compiler/filter/cleanup.rb +21 -0
- data/lib/raw/compiler/filter/elements.rb +166 -0
- data/lib/raw/compiler/filter/elements/element.rb +210 -0
- data/lib/raw/compiler/filter/localization.rb +23 -0
- data/lib/raw/compiler/filter/markup.rb +32 -0
- data/lib/raw/compiler/filter/morph.rb +123 -0
- data/lib/raw/compiler/filter/morph/each.rb +34 -0
- data/lib/raw/compiler/filter/morph/for.rb +11 -0
- data/lib/raw/compiler/filter/morph/if.rb +26 -0
- data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
- data/lib/raw/compiler/filter/morph/standard.rb +55 -0
- data/lib/raw/compiler/filter/morph/times.rb +27 -0
- data/lib/raw/compiler/filter/script.rb +116 -0
- data/lib/raw/compiler/filter/squeeze.rb +16 -0
- data/lib/raw/compiler/filter/static_include.rb +74 -0
- data/lib/raw/compiler/filter/template.rb +121 -0
- data/lib/raw/compiler/reloader.rb +96 -0
- data/lib/raw/context.rb +154 -0
- data/lib/raw/context/flash.rb +157 -0
- data/lib/raw/context/global.rb +88 -0
- data/lib/raw/context/request.rb +338 -0
- data/lib/raw/context/response.rb +57 -0
- data/lib/raw/context/session.rb +198 -0
- data/lib/raw/context/session/drb.rb +11 -0
- data/lib/raw/context/session/file.rb +15 -0
- data/lib/raw/context/session/memcached.rb +13 -0
- data/lib/raw/context/session/memory.rb +12 -0
- data/lib/raw/context/session/og.rb +15 -0
- data/lib/raw/context/session/pstore.rb +13 -0
- data/lib/raw/control.rb +18 -0
- data/lib/raw/control/attribute.rb +91 -0
- data/lib/raw/control/attribute/checkbox.rb +25 -0
- data/lib/raw/control/attribute/datetime.rb +21 -0
- data/lib/raw/control/attribute/file.rb +20 -0
- data/lib/raw/control/attribute/fixnum.rb +26 -0
- data/lib/raw/control/attribute/float.rb +26 -0
- data/lib/raw/control/attribute/options.rb +38 -0
- data/lib/raw/control/attribute/password.rb +16 -0
- data/lib/raw/control/attribute/text.rb +16 -0
- data/lib/raw/control/attribute/textarea.rb +16 -0
- data/lib/raw/control/none.rb +16 -0
- data/lib/raw/control/relation.rb +59 -0
- data/lib/raw/control/relation/belongs_to.rb +0 -0
- data/lib/raw/control/relation/has_many.rb +97 -0
- data/lib/raw/control/relation/joins_many.rb +0 -0
- data/lib/raw/control/relation/many_to_many.rb +0 -0
- data/lib/raw/control/relation/refers_to.rb +29 -0
- data/lib/raw/controller.rb +37 -0
- data/lib/raw/controller/publishable.rb +160 -0
- data/lib/raw/dispatcher.rb +209 -0
- data/lib/raw/dispatcher/format.rb +108 -0
- data/lib/raw/dispatcher/format/atom.rb +31 -0
- data/lib/raw/dispatcher/format/css.rb +0 -0
- data/lib/raw/dispatcher/format/html.rb +42 -0
- data/lib/raw/dispatcher/format/json.rb +31 -0
- data/lib/raw/dispatcher/format/rss.rb +33 -0
- data/lib/raw/dispatcher/format/xoxo.rb +31 -0
- data/lib/raw/dispatcher/mounter.rb +60 -0
- data/lib/raw/dispatcher/router.rb +111 -0
- data/lib/raw/errors.rb +19 -0
- data/lib/raw/helper.rb +86 -0
- data/lib/raw/helper/benchmark.rb +23 -0
- data/lib/raw/helper/buffer.rb +60 -0
- data/lib/raw/helper/cookie.rb +32 -0
- data/lib/raw/helper/debug.rb +28 -0
- data/lib/raw/helper/default.rb +16 -0
- data/lib/raw/helper/feed.rb +451 -0
- data/lib/raw/helper/form.rb +284 -0
- data/lib/raw/helper/javascript.rb +59 -0
- data/lib/raw/helper/layout.rb +40 -0
- data/lib/raw/helper/navigation.rb +87 -0
- data/lib/raw/helper/pager.rb +305 -0
- data/lib/raw/helper/table.rb +247 -0
- data/lib/raw/helper/xhtml.rb +218 -0
- data/lib/raw/helper/xml.rb +125 -0
- data/lib/raw/mixin/magick.rb +35 -0
- data/lib/raw/mixin/sweeper.rb +71 -0
- data/lib/raw/mixin/thumbnails.rb +1 -0
- data/lib/raw/mixin/webfile.rb +165 -0
- data/lib/raw/render.rb +271 -0
- data/lib/raw/render/builder.rb +26 -0
- data/lib/raw/render/caching.rb +81 -0
- data/lib/raw/render/call.rb +43 -0
- data/lib/raw/render/send_file.rb +46 -0
- data/lib/raw/render/stream.rb +39 -0
- data/lib/raw/scaffold.rb +13 -0
- data/lib/raw/scaffold/controller.rb +25 -0
- data/lib/raw/scaffold/model.rb +157 -0
- data/lib/raw/test.rb +5 -0
- data/lib/raw/test/assertions.rb +169 -0
- data/lib/raw/test/context.rb +55 -0
- data/lib/raw/test/testcase.rb +79 -0
- data/lib/raw/util/attr.rb +128 -0
- data/lib/raw/util/encode_uri.rb +149 -0
- data/lib/raw/util/html_filter.rb +538 -0
- data/lib/raw/util/markup.rb +130 -0
- data/test/glue/tc_webfile.rb +1 -0
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/raw_post1.bin +9 -0
- data/test/nitro/adapter/tc_webrick.rb +16 -0
- data/test/nitro/cgi/tc_cookie.rb +14 -0
- data/test/nitro/cgi/tc_request.rb +61 -0
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +25 -0
- data/test/nitro/dispatcher/tc_mounter.rb +47 -0
- data/test/nitro/helper/tc_feed.rb +135 -0
- data/test/nitro/helper/tc_navbar.rb +74 -0
- data/test/nitro/helper/tc_pager.rb +35 -0
- data/test/nitro/helper/tc_table.rb +68 -0
- data/test/nitro/helper/tc_xhtml.rb +19 -0
- data/test/nitro/tc_caching.rb +19 -0
- data/test/nitro/tc_cgi.rb +222 -0
- data/test/nitro/tc_context.rb +17 -0
- data/test/nitro/tc_controller.rb +103 -0
- data/test/nitro/tc_controller_aspect.rb +32 -0
- data/test/nitro/tc_controller_params.rb +885 -0
- data/test/nitro/tc_dispatcher.rb +109 -0
- data/test/nitro/tc_element.rb +85 -0
- data/test/nitro/tc_flash.rb +59 -0
- data/test/nitro/tc_helper.rb +47 -0
- data/test/nitro/tc_render.rb +119 -0
- data/test/nitro/tc_router.rb +61 -0
- data/test/nitro/tc_server.rb +35 -0
- data/test/nitro/tc_session.rb +66 -0
- data/test/nitro/tc_template.rb +71 -0
- data/test/nitro/util/tc_encode_url.rb +87 -0
- data/test/nitro/util/tc_markup.rb +31 -0
- data/test/public/blog/another/very_litle/index.xhtml +1 -0
- data/test/public/blog/inc1.xhtml +2 -0
- data/test/public/blog/inc2.xhtml +1 -0
- data/test/public/blog/list.xhtml +9 -0
- data/test/public/dummy_mailer/registration.xhtml +5 -0
- 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
|
+
|
data/lib/raw/cgi.rb
ADDED
@@ -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
|