nitro 0.23.0 → 0.24.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.
- data/CHANGELOG +350 -0
- data/INSTALL +2 -2
- data/ProjectInfo +61 -0
- data/README +5 -4
- data/Rakefile +5 -4
- data/bin/nitrogen +3 -1
- data/doc/AUTHORS +27 -3
- data/doc/RELEASES +193 -0
- data/doc/lhttpd.txt +4 -0
- data/lib/nitro.rb +1 -1
- data/lib/nitro/adapter/cgi.rb +6 -321
- data/lib/nitro/adapter/fastcgi.rb +2 -14
- data/lib/nitro/adapter/scgi.rb +237 -71
- data/lib/nitro/adapter/webrick.rb +25 -7
- data/lib/nitro/caching.rb +1 -0
- data/lib/nitro/cgi.rb +296 -0
- data/lib/nitro/{cookie.rb → cgi/cookie.rb} +0 -0
- data/lib/nitro/cgi/http.rb +62 -0
- data/lib/nitro/{request.rb → cgi/request.rb} +4 -1
- data/lib/nitro/{response.rb → cgi/response.rb} +0 -0
- data/lib/nitro/cgi/stream.rb +43 -0
- data/lib/nitro/cgi/utils.rb +38 -0
- data/lib/nitro/compiler.rb +23 -11
- data/lib/nitro/compiler/css.rb +8 -0
- data/lib/nitro/compiler/morphing.rb +66 -0
- data/lib/nitro/context.rb +21 -30
- data/lib/nitro/controller.rb +23 -100
- data/lib/nitro/dispatcher.rb +18 -8
- data/lib/nitro/element.rb +6 -2
- data/lib/nitro/flash.rb +2 -2
- data/lib/nitro/mixin/buffer.rb +2 -2
- data/lib/nitro/mixin/form.rb +204 -93
- data/lib/nitro/mixin/javascript.rb +170 -11
- data/lib/nitro/mixin/markup.rb +1 -0
- data/lib/nitro/mixin/pager.rb +7 -4
- data/lib/nitro/mixin/rss.rb +2 -0
- data/lib/nitro/mixin/table.rb +23 -6
- data/lib/nitro/mixin/xhtml.rb +2 -2
- data/lib/nitro/render.rb +19 -5
- data/lib/nitro/scaffold.rb +12 -6
- data/lib/nitro/server.rb +4 -6
- data/lib/nitro/server/runner.rb +2 -2
- data/lib/nitro/session.rb +8 -1
- data/lib/nitro/session/file.rb +40 -0
- data/lib/part/admin.rb +2 -0
- data/lib/part/admin/controller.rb +7 -3
- data/lib/part/admin/skin.rb +8 -1
- data/lib/part/admin/template/index.xhtml +39 -1
- data/proto/public/error.xhtml +5 -3
- data/proto/public/js/behaviour.js +254 -254
- data/proto/public/js/controls.js +427 -165
- data/proto/public/js/dragdrop.js +255 -276
- data/proto/public/js/effects.js +476 -277
- data/proto/public/js/prototype.js +561 -127
- data/proto/public/js/scaffold.js +74 -0
- data/proto/public/js/scriptaculous.js +44 -0
- data/proto/public/js/util.js +548 -0
- data/proto/public/scaffold/list.xhtml +4 -1
- data/proto/scgi.rb +333 -0
- data/script/scgi_ctl +221 -0
- data/script/scgi_service +120 -0
- data/test/nitro/adapter/raw_post1.bin +0 -0
- data/test/nitro/{tc_cookie.rb → cgi/tc_cookie.rb} +1 -1
- data/test/nitro/{tc_request.rb → cgi/tc_request.rb} +1 -1
- data/test/nitro/mixin/tc_xhtml.rb +1 -1
- data/test/nitro/{adapter/tc_cgi.rb → tc_cgi.rb} +12 -12
- data/test/nitro/tc_controller.rb +9 -5
- metadata +159 -169
- data/benchmark/bench.rb +0 -5
- data/benchmark/simple-webrick-n-200.txt +0 -44
- data/benchmark/static-webrick-n-200.txt +0 -43
- data/benchmark/tiny-lhttpd-n-200-c-5.txt +0 -43
- data/benchmark/tiny-webrick-n-200-c-5.txt +0 -44
- data/benchmark/tiny-webrick-n-200.txt +0 -44
- data/benchmark/tiny2-webrick-n-200.txt +0 -44
- data/examples/README +0 -7
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ Name = Release[/\D+/].sub(/\-+$/, '') || 'unknown'
|
|
10
10
|
Version = Release[/[\d.]+/] || 'unknown'
|
11
11
|
|
12
12
|
AuthorName, AuthorMail = 'George Moschovitis', 'gm@navel.gr'
|
13
|
-
RubyVersion = '1.8.
|
13
|
+
RubyVersion = '1.8.3'
|
14
14
|
|
15
15
|
# Description = (readme[/README\s+(.+?)\n\n/m, 1] || "unknown").gsub(/\s+/, " ")
|
16
16
|
# Summary = Description[/^.+?>/] || "unknown"
|
@@ -62,10 +62,11 @@ spec = Gem::Specification.new do |s|
|
|
62
62
|
|
63
63
|
s.add_dependency 'glue', "= #{s.version}"
|
64
64
|
s.add_dependency 'og', "= #{s.version}"
|
65
|
-
s.add_dependency '
|
66
|
-
s.add_dependency '
|
65
|
+
s.add_dependency 'gen', "= #{s.version}"
|
66
|
+
s.add_dependency 'ruby-breakpoint', '= 0.5'
|
67
|
+
s.add_dependency 'RedCloth', '= 3.0.3'
|
67
68
|
|
68
|
-
s.required_ruby_version = '
|
69
|
+
s.required_ruby_version = '= 1.8.3'
|
69
70
|
|
70
71
|
s.files = FileList[
|
71
72
|
'[A-Z]*', 'install.rb', '{bin,benchmark,examples,doc,proto,lib,test,vendor}/**/*'
|
data/bin/nitrogen
CHANGED
data/doc/AUTHORS
CHANGED
@@ -3,7 +3,8 @@ MAIN DEVELOPER:
|
|
3
3
|
* George Moschovitis <gm@navel.gr>
|
4
4
|
Project leader, architecture and design, main coder,
|
5
5
|
documentation, maintainer.
|
6
|
-
|
6
|
+
|
7
|
+
|
7
8
|
IDEAS, ADDITIONAL CODING, SUPPORT:
|
8
9
|
|
9
10
|
* Guillaume Pierronnet <guillaume.pierronnet@laposte.net>
|
@@ -15,22 +16,45 @@ IDEAS, ADDITIONAL CODING, SUPPORT:
|
|
15
16
|
* Dan Yoder <dan@zeraweb.com>
|
16
17
|
Original 'Elements' implementation, bug reports.
|
17
18
|
|
18
|
-
*
|
19
|
-
|
19
|
+
* Aleksi Niemela <Aleksi.Niemela@cs.helsinki.fi>
|
20
|
+
Bug fixes, patches, documentation.
|
20
21
|
|
22
|
+
* Chris Farmiloe <chris.farmiloe@farmiloe.com>
|
23
|
+
Bug fixes, Scaffolding improvements, patches.
|
24
|
+
|
25
|
+
* Rob Pitt <rob@motionpath.co.uk>
|
26
|
+
Bug fixes, Scaffolding improvements, patches.
|
27
|
+
|
28
|
+
* Zed A. Shaw <zedshaw@zedshaw.com>
|
29
|
+
SCGI adapter.
|
30
|
+
|
31
|
+
* Anastasios Koutoumanos <ak@navel.gr>
|
32
|
+
Design, bug reports.
|
33
|
+
|
34
|
+
* TRANS <transfire@gmail.com>
|
35
|
+
Small bug fixes.
|
36
|
+
|
21
37
|
* Kostas Nasis <kostas@nasis.com>
|
22
38
|
Ideas and bug reports.
|
23
39
|
|
40
|
+
|
24
41
|
INTEGRATED LIBRARIES:
|
25
42
|
|
26
43
|
Nitro integrates several open source libraries:
|
27
44
|
|
45
|
+
* Facets (Nano/Mega): The best collection of
|
46
|
+
Ruby extensions/utilities.
|
47
|
+
Tomas Sawyer, http://nano.rubyforge.org, http://mega.rubyforge.org
|
48
|
+
|
28
49
|
* Prototype: Object Oriented Javascript library
|
29
50
|
Sam Stepherson, http://www.conio.net
|
30
51
|
|
31
52
|
* Script.aculo.us: Special FX Javascript library
|
32
53
|
Thomas Fuchs, http://script.aculo.us
|
33
54
|
|
55
|
+
* Breakpointer
|
56
|
+
|
57
|
+
|
34
58
|
CODE FROM OTHER PROJECTS:
|
35
59
|
|
36
60
|
Occasionaly, Nitro may include code from other open source
|
data/doc/RELEASES
CHANGED
@@ -1,3 +1,196 @@
|
|
1
|
+
== Version 0.24.0
|
2
|
+
|
3
|
+
A snapshot of the latest developments. This version features
|
4
|
+
tons of big and small features and bug fixes that were
|
5
|
+
implemented during the last month. Special thanks fly to
|
6
|
+
Tom, Chris, Guill, Rob, Aleksi, and all others that sent
|
7
|
+
patches and code.
|
8
|
+
|
9
|
+
Most notable additions:
|
10
|
+
|
11
|
+
|
12
|
+
* Totaly recoded annotation / property system. The property
|
13
|
+
system is now based on Facet annotations and inheritors.
|
14
|
+
You can now annotate every object, attribute or method
|
15
|
+
in Nitro. For example you can annotate your actions with
|
16
|
+
routing rules or sitemap strings etc, etc. One unified
|
17
|
+
system for annotations and metadata is used throughout
|
18
|
+
the whole Framework.
|
19
|
+
|
20
|
+
Here is an example:
|
21
|
+
|
22
|
+
class A
|
23
|
+
attr_accessor :val
|
24
|
+
ann :val, :klass => String
|
25
|
+
ann :self, :doc => 'Self annotation'
|
26
|
+
end
|
27
|
+
|
28
|
+
A.ann.val.class => String
|
29
|
+
A.ann.self.doc =>
|
30
|
+
|
31
|
+
if A.ann.val[:class]
|
32
|
+
|
33
|
+
etc, etc...
|
34
|
+
|
35
|
+
* Implemented one of the most requested features. An Og
|
36
|
+
frontend for KirbyBase. The KirbyBase store does not yet
|
37
|
+
support all features, but is really useful. For example
|
38
|
+
it can power the Spark wiki example. Switching between
|
39
|
+
KirbyBase, Mysql, Postgres, Sqlite, etc by changing
|
40
|
+
one line of code is really cool ;-)
|
41
|
+
|
42
|
+
* Better Seperation of Concerns for Og managed classes. Og
|
43
|
+
can now annotate and handle classes from other libraries.
|
44
|
+
Lets say you have the following class:
|
45
|
+
|
46
|
+
class User
|
47
|
+
attr_accessor :name
|
48
|
+
attr_accessor :body
|
49
|
+
end
|
50
|
+
|
51
|
+
Using Ruby's open classes and Nitro's advanced annotation
|
52
|
+
system you can easily prepare this class for Og management
|
53
|
+
|
54
|
+
class User
|
55
|
+
ann :user, :klass => String
|
56
|
+
ann :body, :klass => String
|
57
|
+
end
|
58
|
+
|
59
|
+
or even better:
|
60
|
+
|
61
|
+
class User
|
62
|
+
property :user, String
|
63
|
+
property :body, String
|
64
|
+
end
|
65
|
+
|
66
|
+
This style promotes SOC: You define your classes in one
|
67
|
+
place and annotate them for Og in another place.
|
68
|
+
|
69
|
+
* Introduced a simple code generation framework. Two example
|
70
|
+
generators are included:
|
71
|
+
|
72
|
+
* app: generates the directory structure for a new
|
73
|
+
nitro application.
|
74
|
+
|
75
|
+
* form: generates an xhtml form for a Ruby class.
|
76
|
+
This generator leverages the annotations of the
|
77
|
+
object class.
|
78
|
+
|
79
|
+
* Improved scaffolding code and django style auto
|
80
|
+
administration system. The new scaffolder supports all
|
81
|
+
Og relation types and provides an intuitive DHTML
|
82
|
+
interface for editing.
|
83
|
+
|
84
|
+
* SCGI (http://python.ca/nas/scgi/protocol.txt) support.
|
85
|
+
Thanks to a refactoring of Nitro's cgi code this
|
86
|
+
version integrates with Zed Shaw's SCGI adapter. This
|
87
|
+
provides FastCGI's performance with an easy
|
88
|
+
installation.
|
89
|
+
|
90
|
+
* Experimental HTTP streaming support. Here is a
|
91
|
+
simple example:
|
92
|
+
|
93
|
+
def index
|
94
|
+
response.content_type = "text/plain"
|
95
|
+
stream do
|
96
|
+
5.times do |i|
|
97
|
+
print "#{i}"*10000 + "\n"
|
98
|
+
sleep 1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
* Simple Og automatic evolution system. Lets say you have a class Article
|
104
|
+
|
105
|
+
class Article
|
106
|
+
property :title, String
|
107
|
+
property :nbody, String
|
108
|
+
property :dumy, Fixnum
|
109
|
+
end
|
110
|
+
|
111
|
+
lets you want to change your
|
112
|
+
class to this one:
|
113
|
+
|
114
|
+
class NewArticle
|
115
|
+
property :ntitle, String
|
116
|
+
property :nbody, String
|
117
|
+
property :new, Float
|
118
|
+
end
|
119
|
+
|
120
|
+
First you export the database:
|
121
|
+
|
122
|
+
og.export
|
123
|
+
|
124
|
+
Then you import the database. Some rules
|
125
|
+
are needed when renaming classes or properties.
|
126
|
+
New properties or deleted properties are handled
|
127
|
+
automatically.
|
128
|
+
|
129
|
+
rules = {
|
130
|
+
:Article => {
|
131
|
+
:self => :NewArticle, # rename the class
|
132
|
+
:title => :ntitle,
|
133
|
+
:body => :nbody
|
134
|
+
}
|
135
|
+
}
|
136
|
+
og.import :evolution => rules
|
137
|
+
|
138
|
+
Thats all. In a future version this will be integrated into
|
139
|
+
the default runner scripts.
|
140
|
+
|
141
|
+
* Og helpers to create simple rdbms management scripts. Here
|
142
|
+
is an example:
|
143
|
+
|
144
|
+
mysql "-u root -p", <<-END
|
145
|
+
drop database if exists weblog_development;
|
146
|
+
create database weblog_development;
|
147
|
+
grant all on weblog_development.* to #{`id -un`.strip}@localhost;
|
148
|
+
END
|
149
|
+
|
150
|
+
At the moment this is only available for Mysql.
|
151
|
+
|
152
|
+
* Added support for dynamic CSS using Nitro's advanced templating
|
153
|
+
system. Create the dynamic CSS file with an .csst extension in
|
154
|
+
your template root.
|
155
|
+
|
156
|
+
<?r
|
157
|
+
green = '#79b500'
|
158
|
+
background = '#fff'
|
159
|
+
?>
|
160
|
+
|
161
|
+
.page {
|
162
|
+
background: #{green}
|
163
|
+
|
164
|
+
<?r 10.times do ?>
|
165
|
+
padding: 5px;
|
166
|
+
<?r end ?>
|
167
|
+
|
168
|
+
// or even
|
169
|
+
|
170
|
+
<% 3.times do %>
|
171
|
+
margin: 5px;
|
172
|
+
<% end %>
|
173
|
+
}
|
174
|
+
..
|
175
|
+
|
176
|
+
then, add this line in run.rb:
|
177
|
+
|
178
|
+
Compiler.precompile 'style.css'
|
179
|
+
|
180
|
+
The CSS file will be regenerated automatically whenever the
|
181
|
+
template changes...
|
182
|
+
|
183
|
+
* Updated to support latest Prototype, Scriptaculous etc.
|
184
|
+
|
185
|
+
* Cleaned up Og implementation.
|
186
|
+
|
187
|
+
* Fixed minor Ruby 1.8.3 compatibility issues.
|
188
|
+
|
189
|
+
* Even better integration with Ruby Facets.
|
190
|
+
|
191
|
+
* Tons of bug fixes and small but useful features.
|
192
|
+
|
193
|
+
|
1
194
|
== Version 0.23.0
|
2
195
|
|
3
196
|
The summer vacations are over and there is a brand new Nitro
|
data/doc/lhttpd.txt
ADDED
data/lib/nitro.rb
CHANGED
data/lib/nitro/adapter/cgi.rb
CHANGED
@@ -2,30 +2,21 @@ require 'cgi'
|
|
2
2
|
require 'stringio'
|
3
3
|
require 'tempfile'
|
4
4
|
|
5
|
-
require 'glue/attribute'
|
6
|
-
|
7
5
|
# Speeds things up, more comaptible with OSX.
|
8
6
|
|
9
7
|
Socket.do_not_reverse_lookup = true
|
10
8
|
|
11
9
|
module Nitro
|
12
10
|
|
13
|
-
class
|
14
|
-
# Maximum content length allowed in requests.
|
15
|
-
|
16
|
-
cattr_accessor :max_content_length, (2 * 1024 * 1024)
|
17
|
-
|
18
|
-
# Multipart parsing buffer size.
|
19
|
-
|
20
|
-
cattr_accessor :buffer_size, (10 * 1024)
|
21
|
-
|
11
|
+
class CgiAdapter
|
22
12
|
#--
|
23
13
|
# No need for connection pooling, CGI uses process.
|
24
14
|
#++
|
25
15
|
|
26
16
|
def self.start(server)
|
27
17
|
cgi = CGI.new
|
28
|
-
|
18
|
+
CgiUtils.process(server, cgi, cgi, cgi)
|
19
|
+
=begin
|
29
20
|
context = Context.new(server)
|
30
21
|
context.headers = ENV
|
31
22
|
|
@@ -36,319 +27,13 @@ class Cgi
|
|
36
27
|
|
37
28
|
cgi.print(CgiUtils.response_headers(context))
|
38
29
|
cgi.print(context.out)
|
30
|
+
=end
|
39
31
|
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# CGI utility methods.
|
43
|
-
|
44
|
-
class CgiUtils
|
45
|
-
|
46
|
-
# HTTP protocol EOL constants
|
47
|
-
|
48
|
-
CR = "\x0d"
|
49
|
-
LF = "\x0a"
|
50
|
-
CRLF = "\x0d\x0a"
|
51
|
-
EOL = CRLF
|
52
|
-
|
53
|
-
# Constants for readable code
|
54
|
-
|
55
|
-
STATUS_OK = 200
|
56
|
-
STATUS_PARTIAL_CONTENT = 206
|
57
|
-
STATUS_MOVED = 301
|
58
|
-
STATUS_REDIRECT = 302
|
59
|
-
STATUS_SEE_OTHER = 303 # gmosx: VERIFY THIS
|
60
|
-
STATUS_SEE_OTHER_307 = 307 # gmosx: VERIFY THIS
|
61
|
-
STATUS_NOT_MODIFIED = 304
|
62
|
-
STATUS_BAD_REQUEST = 400
|
63
|
-
STATUS_AUTH_REQUIRED = 401
|
64
|
-
STATUS_FORBIDDEN = 403
|
65
|
-
STATUS_NOT_FOUND = 404
|
66
|
-
STATUS_METHOD_NOT_ALLOWED = 405
|
67
|
-
STATUS_NOT_ACCEPTABLE = 406
|
68
|
-
STATUS_LENGTH_REQUIRED = 411
|
69
|
-
STATUS_PRECONDITION_FAILED = 412
|
70
|
-
STATUS_SERVER_ERROR = 500
|
71
|
-
STATUS_NOT_IMPLEMENTED = 501
|
72
|
-
STATUS_BAD_GATEWAY = 502
|
73
|
-
STATUS_VARIANT_ALSO_VARIES = 506
|
74
|
-
|
75
|
-
# Hash to allow id to description maping.
|
76
|
-
|
77
|
-
STATUS_STRINGS = {
|
78
|
-
200 => "OK",
|
79
|
-
206 => "Partial Content",
|
80
|
-
300 => "Multiple Choices",
|
81
|
-
301 => "Moved Permanently",
|
82
|
-
302 => "Found",
|
83
|
-
303 => "See other", # gmosx: VERIFY THIS
|
84
|
-
304 => "Not Modified",
|
85
|
-
307 => "See other 07", # gmosx: VERIFY THIS
|
86
|
-
400 => "Bad Request",
|
87
|
-
401 => "Authorization Required",
|
88
|
-
403 => "Forbidden",
|
89
|
-
404 => "Not Found",
|
90
|
-
405 => "Method Not Allowed",
|
91
|
-
406 => "Not Acceptable",
|
92
|
-
411 => "Length Required",
|
93
|
-
412 => "Precondition Failed",
|
94
|
-
500 => "Internal Server Error",
|
95
|
-
501 => "Method Not Implemented",
|
96
|
-
502 => "Bad Gateway",
|
97
|
-
506 => "Variant Also Negotiates"
|
98
|
-
}
|
99
|
-
|
100
|
-
# Returns a hash with the pairs from the query
|
101
|
-
# string. The implicit hash construction that is done
|
102
|
-
# in parse_request_params is not done here.
|
103
|
-
#
|
104
|
-
# Parameters in the form xxx[] are converted
|
105
|
-
# to arrays.
|
106
|
-
#
|
107
|
-
# Use the field.attr or field[attr] notation to pass
|
108
|
-
# compound objects.
|
109
|
-
|
110
|
-
def self.parse_query_string(query_string)
|
111
|
-
params = {}
|
112
|
-
|
113
|
-
# gmosx, THINK: better return nil here?
|
114
|
-
return params if (query_string.nil? or query_string.empty?)
|
115
|
-
|
116
|
-
query_string.split(/[&;]/).each do |p|
|
117
|
-
key, val = p.split('=')
|
118
|
-
|
119
|
-
key = CGI.unescape(key) unless key.nil?
|
120
|
-
val = CGI.unescape(val) unless val.nil?
|
121
|
-
|
122
|
-
if key =~ /(.*)\[\]$/
|
123
|
-
# Multiple values, for example a checkbox collection.
|
124
|
-
# Stored as an array.
|
125
|
-
if params.has_key?($1)
|
126
|
-
params[$1] << val
|
127
|
-
else
|
128
|
-
params[$1] = [val]
|
129
|
-
end
|
130
|
-
elsif key =~ /(.*)\[(.*)\]$/ or key =~ /(.*)\.(.*)$/
|
131
|
-
# A compound object with attributes.
|
132
|
-
# Stored as a Hash.
|
133
|
-
params[$1] ||= {}
|
134
|
-
params[$1][$2] = val
|
135
|
-
else
|
136
|
-
# Standard single valued parameter.
|
137
|
-
params[key] = val.nil? ? nil : val
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
return params
|
142
|
-
end
|
143
|
-
|
144
|
-
# Parse the HTTP_COOKIE header and returns the
|
145
|
-
# cookies as a key->value hash. For efficiency no
|
146
|
-
# cookie objects are created.
|
147
|
-
#
|
148
|
-
# [+context+]
|
149
|
-
# The context
|
150
|
-
|
151
|
-
def self.parse_cookies(context)
|
152
|
-
env = context.env
|
153
|
-
|
154
|
-
# FIXME: dont precreate?
|
155
|
-
context.cookies = {}
|
156
|
-
|
157
|
-
if env.has_key?('HTTP_COOKIE') or env.has_key?('COOKIE')
|
158
|
-
(env['HTTP_COOKIE'] or env['COOKIE']).split(/; /).each do |c|
|
159
|
-
key, val = c.split(/=/, 2)
|
160
|
-
|
161
|
-
key = CGI.unescape(key)
|
162
|
-
val = val.split(/&/).collect{|v| CGI::unescape(v)}.join("\0")
|
163
|
-
|
164
|
-
if context.cookies.include?(key)
|
165
|
-
context.cookies[key] += "\0" + val
|
166
|
-
else
|
167
|
-
context.cookies[key] = val
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Build the response headers for the context.
|
174
|
-
#
|
175
|
-
# [+context+]
|
176
|
-
# The context of the response.
|
177
|
-
#
|
178
|
-
# [+proto+]
|
179
|
-
# If true emmit the protocol line. Useful for MOD_RUBY.
|
180
|
-
#--
|
181
|
-
# FIXME: return the correct protocol from env.
|
182
|
-
# TODO: Perhaps I can optimize status calc.
|
183
|
-
#++
|
184
|
-
|
185
|
-
def self.response_headers(context, proto = false)
|
186
|
-
reason = STATUS_STRINGS[context.status]
|
187
|
-
|
188
|
-
if proto
|
189
|
-
buf = "HTTP/1.1 #{context.status} #{reason}#{EOL}Date: #{CGI::rfc1123_date(Time.now)}#{EOL}"
|
190
|
-
else
|
191
|
-
buf = "Status: #{context.status} #{reason}#{EOL}"
|
192
|
-
end
|
193
|
-
|
194
|
-
context.response_headers.each do |key, value|
|
195
|
-
tmp = key.gsub(/\bwww|^te$|\b\w/) { |s| s.upcase }
|
196
|
-
buf << "#{tmp}: #{value}" << EOL
|
197
|
-
end
|
198
32
|
|
199
|
-
context.response_cookies.each do |cookie|
|
200
|
-
buf << "Set-Cookie: " << cookie.to_s << EOL
|
201
|
-
end if context.response_cookies
|
202
|
-
|
203
|
-
buf << EOL
|
204
|
-
|
205
|
-
return buf
|
206
|
-
end
|
207
|
-
|
208
|
-
# Initialize the request params.
|
209
|
-
# Handles multipart forms (in particular, forms that involve
|
210
|
-
# file uploads). Reads query parameters in the @params field,
|
211
|
-
# and cookies into @cookies.
|
212
|
-
|
213
|
-
def self.parse_params(context)
|
214
|
-
method = context.method
|
215
|
-
if (:post == method) and
|
216
|
-
%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(context.headers['CONTENT_TYPE'])
|
217
|
-
boundary = $1.dup
|
218
|
-
context.params = parse_multipart(context, boundary)
|
219
|
-
|
220
|
-
# Also include the URL parameters.
|
221
|
-
context.params.update(CgiUtils.parse_query_string(context.query_string))
|
222
|
-
else
|
223
|
-
case method
|
224
|
-
when :get, :head
|
225
|
-
context.params = CgiUtils.parse_query_string(context.query_string)
|
226
|
-
when :post
|
227
|
-
context.in.binmode # if defined?(context.in.binmode)
|
228
|
-
context.params = CgiUtils.parse_query_string(context.in.read(context.content_length) || '')
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
# Parse a multipart request.
|
234
|
-
# Adapted from Ruby's cgi.rb
|
235
|
-
#--
|
236
|
-
# TODO: optimize and rationalize this.
|
237
|
-
#++
|
238
|
-
|
239
|
-
def self.parse_multipart(context, boundary)
|
240
|
-
input = context.in
|
241
|
-
content_length = context.content_length
|
242
|
-
env_table = context.env
|
243
|
-
|
244
|
-
params = Hash.new([])
|
245
|
-
boundary = "--" + boundary
|
246
|
-
buf = ""
|
247
|
-
|
248
|
-
input.binmode if defined? input.binmode
|
249
|
-
boundary_size = boundary.size + EOL.size
|
250
|
-
content_length -= boundary_size
|
251
|
-
status = input.read(boundary_size)
|
252
|
-
|
253
|
-
if nil == status
|
254
|
-
raise EOFError, "no content body"
|
255
|
-
elsif boundary + EOL != status
|
256
|
-
raise EOFError, "bad content body"
|
257
|
-
end
|
258
|
-
|
259
|
-
loop do
|
260
|
-
head = nil
|
261
|
-
|
262
|
-
if 10240 < content_length
|
263
|
-
body = Tempfile.new("CGI")
|
264
|
-
else
|
265
|
-
begin
|
266
|
-
body = StringIO.new
|
267
|
-
rescue LoadError
|
268
|
-
body = Tempfile.new("CGI")
|
269
|
-
end
|
270
|
-
end
|
271
|
-
body.binmode if defined? body.binmode
|
272
|
-
|
273
|
-
until head and /#{boundary}(?:#{EOL}|--)/n.match(buf)
|
274
|
-
|
275
|
-
if (not head) and /#{EOL}#{EOL}/n.match(buf)
|
276
|
-
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
|
277
|
-
head = $1.dup
|
278
|
-
""
|
279
|
-
end
|
280
|
-
next
|
281
|
-
end
|
282
|
-
|
283
|
-
if head and ( (EOL + boundary + EOL).size < buf.size )
|
284
|
-
body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
|
285
|
-
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
|
286
|
-
end
|
287
|
-
|
288
|
-
c = if Cgi.buffer_size < content_length
|
289
|
-
input.read(Cgi.buffer_size)
|
290
|
-
else
|
291
|
-
input.read(content_length)
|
292
|
-
end
|
293
|
-
if c.nil?
|
294
|
-
raise EOFError, "bad content body"
|
295
|
-
end
|
296
|
-
buf.concat(c)
|
297
|
-
content_length -= c.size
|
298
|
-
end
|
299
|
-
|
300
|
-
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{boundary}([\r\n]{1,2}|--)/n) do
|
301
|
-
body.print $1
|
302
|
-
if "--" == $2
|
303
|
-
content_length = -1
|
304
|
-
end
|
305
|
-
""
|
306
|
-
end
|
307
|
-
|
308
|
-
body.rewind
|
309
|
-
|
310
|
-
/Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
|
311
|
-
|
312
|
-
filename = ($1 or "")
|
313
|
-
|
314
|
-
if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
|
315
|
-
/Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
|
316
|
-
(not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
|
317
|
-
filename = CGI::unescape(filename)
|
318
|
-
end
|
319
|
-
|
320
|
-
/Content-Type: (.*)/ni.match(head)
|
321
|
-
content_type = ($1 or "")
|
322
|
-
|
323
|
-
(class << body; self; end).class_eval do
|
324
|
-
alias local_path path
|
325
|
-
define_method(:original_filename) { filename.dup.taint }
|
326
|
-
define_method(:content_type) { content_type.dup.taint }
|
327
|
-
|
328
|
-
# gmosx: this hides the performance hit!!
|
329
|
-
define_method(:to_s) { read }
|
330
|
-
end
|
331
|
-
|
332
|
-
/Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
|
333
|
-
name = $1.dup
|
334
|
-
|
335
|
-
if params.has_key?(name)
|
336
|
-
params[name] = [params[name]] << body
|
337
|
-
else
|
338
|
-
params[name] = body
|
339
|
-
end
|
340
|
-
|
341
|
-
break if buf.size == 0
|
342
|
-
break if content_length === -1
|
343
|
-
end
|
344
|
-
|
345
|
-
return params
|
346
|
-
end
|
347
|
-
|
348
33
|
end
|
349
34
|
|
350
35
|
end
|
351
36
|
|
352
|
-
# * George Moschovitis <gm@navel.gr>
|
353
37
|
# * James Britt <james_b@neurogami.com>
|
354
|
-
# *
|
38
|
+
# * George Moschovitis <gm@navel.gr>
|
39
|
+
|