rack 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- data/AUTHORS +2 -0
- data/RDOX +46 -1
- data/README +19 -4
- data/Rakefile +9 -8
- data/bin/rackup +2 -0
- data/example/protectedlobster.rb +14 -0
- data/lib/rack.rb +19 -0
- data/lib/rack/adapter/camping.rb +6 -0
- data/lib/rack/auth/abstract/handler.rb +28 -0
- data/lib/rack/auth/abstract/request.rb +39 -0
- data/lib/rack/auth/basic.rb +58 -0
- data/lib/rack/auth/digest/md5.rb +124 -0
- data/lib/rack/auth/digest/nonce.rb +52 -0
- data/lib/rack/auth/digest/params.rb +55 -0
- data/lib/rack/auth/digest/request.rb +40 -0
- data/lib/rack/commonlogger.rb +1 -1
- data/lib/rack/file.rb +1 -1
- data/lib/rack/handler/cgi.rb +7 -7
- data/lib/rack/handler/fastcgi.rb +1 -1
- data/lib/rack/handler/mongrel.rb +8 -7
- data/lib/rack/handler/webrick.rb +7 -6
- data/lib/rack/lint.rb +3 -3
- data/lib/rack/lobster.rb +4 -4
- data/lib/rack/mock.rb +9 -33
- data/lib/rack/recursive.rb +1 -1
- data/lib/rack/reloader.rb +1 -1
- data/lib/rack/request.rb +32 -9
- data/lib/rack/response.rb +54 -8
- data/lib/rack/session/cookie.rb +73 -0
- data/lib/rack/showexceptions.rb +2 -2
- data/lib/rack/showstatus.rb +103 -0
- data/lib/rack/static.rb +38 -0
- data/lib/rack/urlmap.rb +1 -1
- data/lib/rack/utils.rb +45 -3
- data/test/spec_rack_auth_basic.rb +68 -0
- data/test/spec_rack_auth_digest.rb +167 -0
- data/test/spec_rack_camping.rb +3 -0
- data/test/spec_rack_mock.rb +2 -0
- data/test/spec_rack_mongrel.rb +12 -0
- data/test/spec_rack_request.rb +60 -0
- data/test/spec_rack_response.rb +50 -1
- data/test/spec_rack_session_cookie.rb +49 -0
- data/test/spec_rack_showstatus.rb +71 -0
- data/test/spec_rack_static.rb +37 -0
- data/test/spec_rack_urlmap.rb +1 -7
- data/test/spec_rack_webrick.rb +17 -0
- metadata +23 -3
- data/lib/rack/adapter/rails.rb +0 -65
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
module Auth
|
6
|
+
module Digest
|
7
|
+
# Rack::Auth::Digest::Nonce is the default nonce generator for the
|
8
|
+
# Rack::Auth::Digest::MD5 authentication handler.
|
9
|
+
#
|
10
|
+
# +private_key+ needs to set to a constant string.
|
11
|
+
#
|
12
|
+
# +time_limit+ can be optionally set to an integer (number of seconds),
|
13
|
+
# to limit the validity of the generated nonces.
|
14
|
+
|
15
|
+
class Nonce
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :private_key, :time_limit
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse(string)
|
22
|
+
new(*Base64.decode64(string).split(' ', 2))
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(timestamp = Time.now, given_digest = nil)
|
26
|
+
@timestamp, @given_digest = timestamp.to_i, given_digest
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
Base64.encode64([ @timestamp, digest ] * ' ').strip
|
31
|
+
end
|
32
|
+
|
33
|
+
def digest
|
34
|
+
::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':')
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid?
|
38
|
+
digest == @given_digest
|
39
|
+
end
|
40
|
+
|
41
|
+
def stale?
|
42
|
+
!self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit
|
43
|
+
end
|
44
|
+
|
45
|
+
def fresh?
|
46
|
+
!stale?
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Rack
|
2
|
+
module Auth
|
3
|
+
module Digest
|
4
|
+
class Params < Hash
|
5
|
+
|
6
|
+
def self.parse(str)
|
7
|
+
split_header_value(str).inject(new) do |header, param|
|
8
|
+
k, v = param.split('=', 2)
|
9
|
+
header[k] = dequote(v)
|
10
|
+
header
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.dequote(str) # From WEBrick::HTTPUtils
|
15
|
+
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
|
16
|
+
ret.gsub!(/\\(.)/, "\\1")
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.split_header_value(str) # From WEBrick::HTTPUtils
|
21
|
+
str.scan(/((?:"(?:\\.|[^"])+?"|[^",]+)+)(?:,\s*|\Z)/n).collect{ |v| v[0] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
def [](k)
|
31
|
+
super k.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def []=(k, v)
|
35
|
+
super k.to_s, v.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
UNQUOTED = ['qop', 'nc', 'stale']
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
inject([]) do |parts, (k, v)|
|
42
|
+
parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
|
43
|
+
parts
|
44
|
+
end.join(', ')
|
45
|
+
end
|
46
|
+
|
47
|
+
def quote(str) # From WEBrick::HTTPUtils
|
48
|
+
'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rack/auth/abstract/request'
|
2
|
+
require 'rack/auth/digest/params'
|
3
|
+
require 'rack/auth/digest/nonce'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
module Auth
|
7
|
+
module Digest
|
8
|
+
class Request < Auth::AbstractRequest
|
9
|
+
|
10
|
+
def method
|
11
|
+
@env['REQUEST_METHOD']
|
12
|
+
end
|
13
|
+
|
14
|
+
def digest?
|
15
|
+
:digest == scheme
|
16
|
+
end
|
17
|
+
|
18
|
+
def correct_uri?
|
19
|
+
@env['PATH_INFO'] == uri
|
20
|
+
end
|
21
|
+
|
22
|
+
def nonce
|
23
|
+
@nonce ||= Nonce.parse(params['nonce'])
|
24
|
+
end
|
25
|
+
|
26
|
+
def params
|
27
|
+
@params ||= Params.parse(parts.last)
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(sym)
|
31
|
+
if params.has_key? key = sym.to_s
|
32
|
+
return params[key]
|
33
|
+
end
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/rack/commonlogger.rb
CHANGED
data/lib/rack/file.rb
CHANGED
data/lib/rack/handler/cgi.rb
CHANGED
@@ -4,7 +4,7 @@ module Rack
|
|
4
4
|
def self.run(app, options=nil)
|
5
5
|
serve app
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def self.serve(app)
|
9
9
|
env = ENV.to_hash
|
10
10
|
env.delete "HTTP_CONTENT_LENGTH"
|
@@ -14,18 +14,18 @@ module Rack
|
|
14
14
|
env.update({"rack.version" => [0,1],
|
15
15
|
"rack.input" => STDIN,
|
16
16
|
"rack.errors" => STDERR,
|
17
|
-
|
17
|
+
|
18
18
|
"rack.multithread" => false,
|
19
19
|
"rack.multiprocess" => true,
|
20
20
|
"rack.run_once" => true,
|
21
|
-
|
21
|
+
|
22
22
|
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
|
23
23
|
})
|
24
|
-
|
24
|
+
|
25
25
|
env["QUERY_STRING"] ||= ""
|
26
26
|
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
27
27
|
env["REQUEST_PATH"] ||= "/"
|
28
|
-
|
28
|
+
|
29
29
|
status, headers, body = app.call(env)
|
30
30
|
begin
|
31
31
|
send_headers status, headers
|
@@ -34,7 +34,7 @@ module Rack
|
|
34
34
|
body.close if body.respond_to? :close
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def self.send_headers(status, headers)
|
39
39
|
STDOUT.print "Status: #{status}\r\n"
|
40
40
|
headers.each { |k, vs|
|
@@ -45,7 +45,7 @@ module Rack
|
|
45
45
|
STDOUT.print "\r\n"
|
46
46
|
STDOUT.flush
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def self.send_body(body)
|
50
50
|
body.each { |part|
|
51
51
|
STDOUT.print part
|
data/lib/rack/handler/fastcgi.rb
CHANGED
data/lib/rack/handler/mongrel.rb
CHANGED
@@ -4,37 +4,38 @@ require 'stringio'
|
|
4
4
|
module Rack
|
5
5
|
module Handler
|
6
6
|
class Mongrel < ::Mongrel::HttpHandler
|
7
|
-
def self.run(app, options)
|
7
|
+
def self.run(app, options={})
|
8
8
|
server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
|
9
9
|
options[:Port] || 8080)
|
10
10
|
server.register('/', Rack::Handler::Mongrel.new(app))
|
11
|
+
yield server if block_given?
|
11
12
|
server.run.join
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
def initialize(app)
|
15
16
|
@app = app
|
16
17
|
end
|
17
|
-
|
18
|
+
|
18
19
|
def process(request, response)
|
19
20
|
env = {}.replace(request.params)
|
20
21
|
env.delete "HTTP_CONTENT_TYPE"
|
21
22
|
env.delete "HTTP_CONTENT_LENGTH"
|
22
23
|
|
23
24
|
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
24
|
-
|
25
|
+
|
25
26
|
env.update({"rack.version" => [0,1],
|
26
27
|
"rack.input" => request.body || StringIO.new(""),
|
27
28
|
"rack.errors" => STDERR,
|
28
|
-
|
29
|
+
|
29
30
|
"rack.multithread" => true,
|
30
31
|
"rack.multiprocess" => false, # ???
|
31
32
|
"rack.run_once" => false,
|
32
|
-
|
33
|
+
|
33
34
|
"rack.url_scheme" => "http",
|
34
35
|
})
|
35
36
|
env["QUERY_STRING"] ||= ""
|
36
37
|
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
|
37
|
-
|
38
|
+
|
38
39
|
status, headers, body = @app.call(env)
|
39
40
|
|
40
41
|
begin
|
data/lib/rack/handler/webrick.rb
CHANGED
@@ -8,29 +8,30 @@ module Rack
|
|
8
8
|
server = ::WEBrick::HTTPServer.new(options)
|
9
9
|
server.mount "/", Rack::Handler::WEBrick, app
|
10
10
|
trap(:INT) { server.shutdown }
|
11
|
+
yield server if block_given?
|
11
12
|
server.start
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
def initialize(server, app)
|
15
16
|
super server
|
16
17
|
@app = app
|
17
18
|
end
|
18
|
-
|
19
|
+
|
19
20
|
def service(req, res)
|
20
21
|
env = req.meta_vars
|
21
22
|
env.delete_if { |k, v| v.nil? }
|
22
|
-
|
23
|
+
|
23
24
|
env.update({"rack.version" => [0,1],
|
24
25
|
"rack.input" => StringIO.new(req.body.to_s),
|
25
26
|
"rack.errors" => STDERR,
|
26
|
-
|
27
|
+
|
27
28
|
"rack.multithread" => true,
|
28
29
|
"rack.multiprocess" => false,
|
29
30
|
"rack.run_once" => false,
|
30
|
-
|
31
|
+
|
31
32
|
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
|
32
33
|
})
|
33
|
-
|
34
|
+
|
34
35
|
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
35
36
|
env["QUERY_STRING"] ||= ""
|
36
37
|
env["REQUEST_PATH"] ||= "/"
|
data/lib/rack/lint.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Rack
|
2
2
|
# Rack::Lint validates your application and the requests and
|
3
3
|
# responses according to the Rack spec.
|
4
|
-
|
4
|
+
|
5
5
|
class Lint
|
6
6
|
def initialize(app)
|
7
7
|
@app = app
|
@@ -47,7 +47,7 @@ module Rack
|
|
47
47
|
check_content_type status, headers
|
48
48
|
[status, headers, self]
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
## == The Environment
|
52
52
|
def check_env(env)
|
53
53
|
## The environment must be an true instance of Hash (no
|
@@ -210,7 +210,7 @@ module Rack
|
|
210
210
|
def initialize(input)
|
211
211
|
@input = input
|
212
212
|
end
|
213
|
-
|
213
|
+
|
214
214
|
## * +gets+ must be called without arguments and return a string,
|
215
215
|
## or +nil+ on EOF.
|
216
216
|
def gets(*args)
|
data/lib/rack/lobster.rb
CHANGED
@@ -21,14 +21,14 @@ module Rack
|
|
21
21
|
lobster = LobsterString
|
22
22
|
href = "?flip"
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
[200, {"Content-Type" => "text/html"},
|
26
26
|
["<title>Lobstericious!</title>",
|
27
27
|
"<pre>", lobster, "</pre>",
|
28
28
|
"<a href='#{href}'>flip!</a>"]
|
29
29
|
]
|
30
30
|
}
|
31
|
-
|
31
|
+
|
32
32
|
def call(env)
|
33
33
|
req = Request.new(env)
|
34
34
|
if req.GET["flip"] == "left"
|
@@ -42,7 +42,7 @@ module Rack
|
|
42
42
|
lobster = LobsterString
|
43
43
|
href = "?flip=left"
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
Response.new.finish do |res|
|
47
47
|
res.write "<title>Lobstericious!</title>"
|
48
48
|
res.write "<pre>"
|
@@ -61,5 +61,5 @@ if $0 == __FILE__
|
|
61
61
|
require 'rack/showexceptions'
|
62
62
|
Rack::Handler::WEBrick.run \
|
63
63
|
Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)),
|
64
|
-
:Port =>
|
64
|
+
:Port => 9292
|
65
65
|
end
|
data/lib/rack/mock.rb
CHANGED
@@ -2,6 +2,7 @@ require 'uri'
|
|
2
2
|
require 'stringio'
|
3
3
|
require 'rack/lint'
|
4
4
|
require 'rack/utils'
|
5
|
+
require 'rack/response'
|
5
6
|
|
6
7
|
module Rack
|
7
8
|
# Rack::MockRequest helps testing your Rack application without
|
@@ -16,7 +17,7 @@ module Rack
|
|
16
17
|
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
|
17
18
|
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
|
18
19
|
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
|
19
|
-
|
20
|
+
|
20
21
|
class MockRequest
|
21
22
|
class FatalWarning < RuntimeError
|
22
23
|
end
|
@@ -32,6 +33,10 @@ module Rack
|
|
32
33
|
|
33
34
|
def flush
|
34
35
|
end
|
36
|
+
|
37
|
+
def string
|
38
|
+
""
|
39
|
+
end
|
35
40
|
end
|
36
41
|
|
37
42
|
DEFAULT_ENV = {
|
@@ -125,45 +130,13 @@ module Rack
|
|
125
130
|
# Status
|
126
131
|
attr_reader :status
|
127
132
|
|
128
|
-
def invalid?; @status < 100 || @status >= 600; end
|
129
|
-
|
130
|
-
def informational?; @status >= 100 && @status < 200; end
|
131
|
-
def successful?; @status >= 200 && @status < 300; end
|
132
|
-
def redirection?; @status >= 300 && @status < 400; end
|
133
|
-
def client_error?; @status >= 400 && @status < 500; end
|
134
|
-
def server_error?; @status >= 500 && @status < 600; end
|
135
|
-
|
136
|
-
def ok?; @status == 200; end
|
137
|
-
def forbidden?; @status == 403; end
|
138
|
-
def not_found?; @status == 404; end
|
139
|
-
|
140
|
-
def redirect?; [301, 302, 303, 307].include? @status; end
|
141
|
-
def empty?; [201, 204, 304].include? @status; end
|
142
|
-
|
143
133
|
# Headers
|
144
134
|
attr_reader :headers, :original_headers
|
145
135
|
|
146
|
-
def include?(header)
|
147
|
-
!!headers[header]
|
148
|
-
end
|
149
|
-
|
150
136
|
def [](field)
|
151
137
|
headers[field]
|
152
138
|
end
|
153
139
|
|
154
|
-
def content_type
|
155
|
-
headers["Content-Type"]
|
156
|
-
end
|
157
|
-
|
158
|
-
def content_length
|
159
|
-
cl = headers["Content-Length"]
|
160
|
-
cl ? cl.to_i : cl
|
161
|
-
end
|
162
|
-
|
163
|
-
def location
|
164
|
-
headers["Location"]
|
165
|
-
end
|
166
|
-
|
167
140
|
|
168
141
|
# Body
|
169
142
|
attr_reader :body
|
@@ -179,5 +152,8 @@ module Rack
|
|
179
152
|
|
180
153
|
# Errors
|
181
154
|
attr_accessor :errors
|
155
|
+
|
156
|
+
|
157
|
+
include Response::Helpers
|
182
158
|
end
|
183
159
|
end
|