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.

Files changed (48) hide show
  1. data/AUTHORS +2 -0
  2. data/RDOX +46 -1
  3. data/README +19 -4
  4. data/Rakefile +9 -8
  5. data/bin/rackup +2 -0
  6. data/example/protectedlobster.rb +14 -0
  7. data/lib/rack.rb +19 -0
  8. data/lib/rack/adapter/camping.rb +6 -0
  9. data/lib/rack/auth/abstract/handler.rb +28 -0
  10. data/lib/rack/auth/abstract/request.rb +39 -0
  11. data/lib/rack/auth/basic.rb +58 -0
  12. data/lib/rack/auth/digest/md5.rb +124 -0
  13. data/lib/rack/auth/digest/nonce.rb +52 -0
  14. data/lib/rack/auth/digest/params.rb +55 -0
  15. data/lib/rack/auth/digest/request.rb +40 -0
  16. data/lib/rack/commonlogger.rb +1 -1
  17. data/lib/rack/file.rb +1 -1
  18. data/lib/rack/handler/cgi.rb +7 -7
  19. data/lib/rack/handler/fastcgi.rb +1 -1
  20. data/lib/rack/handler/mongrel.rb +8 -7
  21. data/lib/rack/handler/webrick.rb +7 -6
  22. data/lib/rack/lint.rb +3 -3
  23. data/lib/rack/lobster.rb +4 -4
  24. data/lib/rack/mock.rb +9 -33
  25. data/lib/rack/recursive.rb +1 -1
  26. data/lib/rack/reloader.rb +1 -1
  27. data/lib/rack/request.rb +32 -9
  28. data/lib/rack/response.rb +54 -8
  29. data/lib/rack/session/cookie.rb +73 -0
  30. data/lib/rack/showexceptions.rb +2 -2
  31. data/lib/rack/showstatus.rb +103 -0
  32. data/lib/rack/static.rb +38 -0
  33. data/lib/rack/urlmap.rb +1 -1
  34. data/lib/rack/utils.rb +45 -3
  35. data/test/spec_rack_auth_basic.rb +68 -0
  36. data/test/spec_rack_auth_digest.rb +167 -0
  37. data/test/spec_rack_camping.rb +3 -0
  38. data/test/spec_rack_mock.rb +2 -0
  39. data/test/spec_rack_mongrel.rb +12 -0
  40. data/test/spec_rack_request.rb +60 -0
  41. data/test/spec_rack_response.rb +50 -1
  42. data/test/spec_rack_session_cookie.rb +49 -0
  43. data/test/spec_rack_showstatus.rb +71 -0
  44. data/test/spec_rack_static.rb +37 -0
  45. data/test/spec_rack_urlmap.rb +1 -7
  46. data/test/spec_rack_webrick.rb +17 -0
  47. metadata +23 -3
  48. data/lib/rack/adapter/rails.rb +0 -65
data/AUTHORS CHANGED
@@ -1,3 +1,5 @@
1
1
  * Christian Neukirchen <chneukirchen@gmail.com>
2
2
  * Rails adapter: Christoffer Sawicki <christoffer.sawicki@gmail.com>
3
+ * HTTP authentication: Tim Fletcher <twoggle@gmail.com>
4
+ * Cookie sessions, Static handler: Luc Heinrich <luc@honk-honk.com>
3
5
  * Official Logo: Armin Ronacher
data/RDOX CHANGED
@@ -1,4 +1,21 @@
1
1
 
2
+ == Rack::Auth::Basic
3
+ * should challenge correctly when no credentials are specified
4
+ * should rechallenge if incorrect credentials are specified
5
+ * should return application output if correct credentials are specified
6
+ * should return 400 Bad Request if different auth scheme used
7
+
8
+ == Rack::Auth::Digest::MD5
9
+ * should challenge when no credentials are specified
10
+ * should return application output if correct credentials given
11
+ * should return application output if correct credentials given (hashed passwords)
12
+ * should rechallenge if incorrect username given
13
+ * should rechallenge if incorrect password given
14
+ * should rechallenge with stale parameter if nonce is stale
15
+ * should return 400 Bad Request if incorrect qop given
16
+ * should return 400 Bad Request if incorrect uri given
17
+ * should return 400 Bad Request if different auth scheme used
18
+
2
19
  == Rack::Adapter::Camping
3
20
  * works with GET
4
21
  * works with POST
@@ -82,6 +99,7 @@
82
99
  * should have CGI headers on POST
83
100
  * should support HTTP auth
84
101
  * should set status
102
+ * should provide a .run
85
103
 
86
104
  == Rack::Recursive
87
105
  * should allow for subrequests
@@ -93,12 +111,17 @@
93
111
  * can figure out the correct host
94
112
  * can parse the query string
95
113
  * can parse POST data
114
+ * can get value by key from params with #[]
115
+ * can set value to key on params with #[]=
116
+ * values_at answers values by keys in order given
117
+ * referrer should be extracted correct
96
118
  * can cache, but invalidates the cache
97
119
  * can figure out if called via XHR
98
120
  * can parse cookies
99
121
  * provides setters
100
122
  * provides the original env
101
123
  * can restore the URL
124
+ * can restore the full path
102
125
  * can parse multipart form data
103
126
  * can parse big multipart form data
104
127
  * can detect invalid multipart form data
@@ -112,10 +135,31 @@
112
135
  * has a useful constructor
113
136
  * has a constructor that can take a block
114
137
  * doesn't return invalid responses
138
+ * knows if it's empty
139
+ * should provide access to the HTTP status
140
+ * should provide access to the HTTP headers
141
+
142
+ == Rack::Session::Cookie
143
+ * creates a new cookie
144
+ * loads from a cookie
145
+ * survives broken cookies
146
+ * barks on too big cookies
115
147
 
116
148
  == Rack::ShowExceptions
117
149
  * catches exceptions
118
150
 
151
+ == Rack::ShowStatus
152
+ * should provide a default status message
153
+ * should let the app provide additional information
154
+ * should not replace existing messages
155
+ * should pass on original headers
156
+ * should replace existing messages if there is detail
157
+
158
+ == Rack::Static
159
+ * serves files
160
+ * 404s if url root is known but it can't find the file
161
+ * calls down the chain if url root is not known
162
+
119
163
  == Rack::URLMap
120
164
  * dispatches paths correctly
121
165
  * dispatches hosts correctly
@@ -140,5 +184,6 @@
140
184
  * should have CGI headers on POST
141
185
  * should support HTTP auth
142
186
  * should set status
187
+ * should provide a .run
143
188
 
144
- 102 specifications (428 requirements), 0 failures
189
+ 137 specifications (546 requirements), 0 failures
data/README CHANGED
@@ -1,6 +1,6 @@
1
1
  = Rack, a modular Ruby webserver interface
2
2
 
3
- Rack provides minimal, modular and adaptable interface for developing
3
+ Rack provides a minimal, modular and adaptable interface for developing
4
4
  web applications in Ruby. By wrapping HTTP requests and responses in
5
5
  the simplest way possible, it unifies and distills the API for web
6
6
  servers, web frameworks, and software in between (the so-called
@@ -13,6 +13,7 @@ which all Rack applications should conform to.
13
13
 
14
14
  The included *handlers* connect all kinds of web servers to Rack:
15
15
  * Mongrel
16
+ * Mongrel/Swiftcore (require it before Rack.)
16
17
  * WEBrick
17
18
  * FCGI
18
19
  * CGI
@@ -76,11 +77,11 @@ Try the lobster!
76
77
 
77
78
  Either with the embedded WEBrick starter:
78
79
 
79
- ruby -Ilib lib/rack/lobster.rb
80
+ ruby -Ilib lib/rack/lobster.rb
80
81
 
81
82
  Or with rackup:
82
83
 
83
- bin/rackup -Ilib example/lobster.ru
84
+ bin/rackup -Ilib example/lobster.ru
84
85
 
85
86
  By default, the lobster is found at http://localhost:9292.
86
87
 
@@ -99,6 +100,16 @@ at my site:
99
100
 
100
101
  * March 3rd, 2007: First public release 0.1.
101
102
 
103
+ * May 16th, 2007: Second public release 0.2.
104
+ * HTTP Basic authentication.
105
+ * Cookie Sessions.
106
+ * Static file handler.
107
+ * Improved Rack::Request.
108
+ * Improved Rack::Response.
109
+ * Added Rack::ShowStatus, for better default error messages.
110
+ * Bug fixes in the Camping adapter.
111
+ * Removed Rails adapter, was too alpha.
112
+
102
113
  == Contact
103
114
 
104
115
  Please mail bugs, suggestions and patches to
@@ -111,9 +122,13 @@ You are also welcome to join the #rack channel on irc.freenode.net.
111
122
 
112
123
  == Thanks to
113
124
 
114
- * Michael Fellinger, for the helpful discussion.
125
+ * Michael Fellinger, for the helpful discussion, bugfixes and a better
126
+ Rack::Request interface.
115
127
  * Christoffer Sawicki, for the Rails adapter.
128
+ * Tim Fletcher, for the HTTP authentication code.
116
129
  * Armin Ronacher, for the logo and racktools.
130
+ * Aredridel, for bug fixing.
131
+ * Gary Wright, for proposing a better Rack::Response interface.
117
132
  * Alexander Kellett for testing the Gem and reviewing the announce.
118
133
  * Marcus Rückert, for help with configuring and debugging lighttpd.
119
134
  * The WSGI team for the well-done and documented work they've done and
data/Rakefile CHANGED
@@ -12,13 +12,14 @@ task :predist => [:chmod, :changelog, :rdoc]
12
12
 
13
13
  desc "Make an archive as .tar.gz"
14
14
  task :dist => :fulltest do
15
- system "export DARCS_REPO=#{File.expand_path "."}; " +
16
- "darcs dist -d rack-#{get_darcs_tree_version}"
15
+ sh "export DARCS_REPO=#{File.expand_path "."}; " +
16
+ "darcs dist -d rack-#{get_darcs_tree_version}"
17
17
  end
18
18
 
19
19
  # Helper to retrieve the "revision number" of the darcs tree.
20
20
  def get_darcs_tree_version
21
21
  unless File.directory? "_darcs"
22
+ $: << "lib"
22
23
  require 'rack'
23
24
  return Rack.version
24
25
  end
@@ -58,13 +59,13 @@ end
58
59
 
59
60
  desc "Generate a ChangeLog"
60
61
  task :changelog do
61
- system "darcs changes --repo=#{ENV["DARCS_REPO"] || "."} >ChangeLog"
62
+ sh "darcs changes --repo=#{ENV["DARCS_REPO"] || "."} >ChangeLog"
62
63
  end
63
64
 
64
65
 
65
66
  desc "Generate RDox"
66
67
  task "RDOX" do
67
- system "specrb -Ilib:test -a --rdox >RDOX"
68
+ sh "specrb -Ilib:test -a --rdox >RDOX"
68
69
  end
69
70
 
70
71
  desc "Generate Rack Specification"
@@ -80,12 +81,12 @@ end
80
81
 
81
82
  desc "Run all the fast tests"
82
83
  task :test do
83
- system "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS'] || '-t "^(?!Rack::Handler|Rack::Adapter)"'}"
84
+ sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS'] || '-t "^(?!Rack::Handler|Rack::Adapter)"'}"
84
85
  end
85
86
 
86
87
  desc "Run all the tests"
87
88
  task :fulltest do
88
- system "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
89
+ sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
89
90
  end
90
91
 
91
92
  begin
@@ -155,8 +156,8 @@ end
155
156
  task :rdoc => ["SPEC", "RDOX"]
156
157
 
157
158
  task :pushsite => [:rdoc] do
158
- system "rsync -avz doc/ chneukirchen@rack.rubyforge.org:/var/www/gforge-projects/rack/doc/"
159
- system "rsync -avz site/ chneukirchen@rack.rubyforge.org:/var/www/gforge-projects/rack/"
159
+ sh "rsync -avz doc/ chneukirchen@rack.rubyforge.org:/var/www/gforge-projects/rack/doc/"
160
+ sh "rsync -avz site/ chneukirchen@rack.rubyforge.org:/var/www/gforge-projects/rack/"
160
161
  end
161
162
 
162
163
  begin
data/bin/rackup CHANGED
@@ -72,6 +72,8 @@ opts = OptionParser.new("", 24, ' ') { |opts|
72
72
  opts.parse! ARGV
73
73
  }
74
74
 
75
+ require 'pp' if $DEBUG
76
+
75
77
  config = ARGV[0] || "config.ru"
76
78
  if !File.exist? config
77
79
  abort "configuration #{config} not found"
@@ -0,0 +1,14 @@
1
+ require 'rack'
2
+ require 'rack/lobster'
3
+
4
+ lobster = Rack::Lobster.new
5
+
6
+ protected_lobster = Rack::Auth::Basic.new(lobster) do |username, password|
7
+ 'secret' == password
8
+ end
9
+
10
+ protected_lobster.realm = 'Lobster 2.0'
11
+
12
+ pretty_protected_lobster = Rack::ShowStatus.new(Rack::ShowExceptions.new(protected_lobster))
13
+
14
+ Rack::Handler::WEBrick.run pretty_protected_lobster, :Port => 9292
@@ -22,6 +22,7 @@ module Rack
22
22
  end
23
23
 
24
24
  autoload :Builder, "rack/builder"
25
+ autoload :Cascade, "rack/cascade"
25
26
  autoload :CommonLogger, "rack/commonlogger"
26
27
  autoload :File, "rack/file"
27
28
  autoload :ForwardRequest, "rack/recursive"
@@ -29,6 +30,8 @@ module Rack
29
30
  autoload :Recursive, "rack/recursive"
30
31
  autoload :Reloader, "rack/reloader"
31
32
  autoload :ShowExceptions, "rack/showexceptions"
33
+ autoload :ShowStatus, "rack/showstatus"
34
+ autoload :Static, "rack/static"
32
35
  autoload :URLMap, "rack/urlmap"
33
36
  autoload :Utils, "rack/utils"
34
37
 
@@ -38,6 +41,22 @@ module Rack
38
41
  autoload :Request, "rack/request"
39
42
  autoload :Response, "rack/response"
40
43
 
44
+ module Auth
45
+ autoload :Basic, "rack/auth/basic"
46
+ autoload :AbstractRequest, "rack/auth/abstract/request"
47
+ autoload :AbstractHandler, "rack/auth/abstract/handler"
48
+ module Digest
49
+ autoload :MD5, "rack/auth/digest/md5"
50
+ autoload :Nonce, "rack/auth/digest/nonce"
51
+ autoload :Params, "rack/auth/digest/params"
52
+ autoload :Request, "rack/auth/digest/request"
53
+ end
54
+ end
55
+
56
+ module Session
57
+ autoload :Cookie, "rack/session/cookie"
58
+ end
59
+
41
60
  # *Adapters* connect Rack with third party web frameworks.
42
61
  #
43
62
  # Rack includes adapters for Camping and Rails.
@@ -9,6 +9,12 @@ module Rack
9
9
  env["PATH_INFO"] ||= ""
10
10
  env["SCRIPT_NAME"] ||= ""
11
11
  controller = @app.run(env['rack.input'], env)
12
+ h = controller.headers
13
+ h.each_pair do |k,v|
14
+ if v.kind_of? URI
15
+ h[k] = v.to_s
16
+ end
17
+ end
12
18
  [controller.status, controller.headers, controller.body]
13
19
  end
14
20
  end
@@ -0,0 +1,28 @@
1
+ module Rack
2
+ module Auth
3
+ # Rack::Auth::AbstractHandler implements common authentication functionality.
4
+ #
5
+ # +realm+ should be set for all handlers.
6
+
7
+ class AbstractHandler
8
+
9
+ attr_accessor :realm
10
+
11
+ def initialize(app, &authenticator)
12
+ @app, @authenticator = app, authenticator
13
+ end
14
+
15
+
16
+ private
17
+
18
+ def unauthorized(www_authenticate = challenge)
19
+ return [ 401, { 'WWW-Authenticate' => www_authenticate.to_s }, [] ]
20
+ end
21
+
22
+ def bad_request
23
+ [ 400, {}, [] ]
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ require 'base64'
2
+
3
+ module Rack
4
+ module Auth
5
+ class AbstractRequest
6
+
7
+ def initialize(env)
8
+ @env = env
9
+ end
10
+
11
+ def provided?
12
+ !authorization_key.nil?
13
+ end
14
+
15
+ def parts
16
+ @parts ||= @env[authorization_key].split(' ', 2)
17
+ end
18
+
19
+ def scheme
20
+ @scheme ||= parts.first.downcase.to_sym
21
+ end
22
+
23
+ def params
24
+ @params ||= parts.last
25
+ end
26
+
27
+
28
+ private
29
+
30
+ AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
31
+
32
+ def authorization_key
33
+ @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) }
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,58 @@
1
+ require 'rack/auth/abstract/handler'
2
+ require 'rack/auth/abstract/request'
3
+
4
+ module Rack
5
+ module Auth
6
+ # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617.
7
+ #
8
+ # Initialize with the Rack application that you want protecting,
9
+ # and a block that checks if a username and password pair are valid.
10
+ #
11
+ # See also: <tt>example/protectedlobster.rb</tt>
12
+
13
+ class Basic < AbstractHandler
14
+
15
+ def call(env)
16
+ auth = Basic::Request.new(env)
17
+
18
+ return unauthorized unless auth.provided?
19
+
20
+ return bad_request unless auth.basic?
21
+
22
+ if valid?(auth)
23
+ env['REMOTE_USER'] = auth.username
24
+
25
+ return @app.call(env)
26
+ end
27
+
28
+ unauthorized
29
+ end
30
+
31
+
32
+ private
33
+
34
+ def challenge
35
+ 'Basic realm="%s"' % realm
36
+ end
37
+
38
+ def valid?(auth)
39
+ @authenticator.call(*auth.credentials)
40
+ end
41
+
42
+ class Request < Auth::AbstractRequest
43
+ def basic?
44
+ :basic == scheme
45
+ end
46
+
47
+ def credentials
48
+ @credentials ||= Base64.decode64(params).split(/:/, 2)
49
+ end
50
+
51
+ def username
52
+ credentials.first
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,124 @@
1
+ require 'rack/auth/abstract/handler'
2
+ require 'rack/auth/digest/request'
3
+ require 'rack/auth/digest/params'
4
+ require 'rack/auth/digest/nonce'
5
+ require 'digest/md5'
6
+
7
+ module Rack
8
+ module Auth
9
+ module Digest
10
+ # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
11
+ # HTTP Digest Authentication, as per RFC 2617.
12
+ #
13
+ # Initialize with the [Rack] application that you want protecting,
14
+ # and a block that looks up a plaintext password for a given username.
15
+ #
16
+ # +opaque+ needs to be set to a constant base64/hexadecimal string.
17
+ #
18
+ class MD5 < AbstractHandler
19
+
20
+ attr_accessor :opaque
21
+
22
+ attr_writer :passwords_hashed
23
+
24
+ def initialize(app)
25
+ super
26
+ @passwords_hashed = nil
27
+ end
28
+
29
+ def passwords_hashed?
30
+ !!@passwords_hashed
31
+ end
32
+
33
+ def call(env)
34
+ auth = Request.new(env)
35
+
36
+ unless auth.provided?
37
+ return unauthorized
38
+ end
39
+
40
+ if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
41
+ return bad_request
42
+ end
43
+
44
+ if valid?(auth)
45
+ if auth.nonce.stale?
46
+ return unauthorized(challenge(:stale => true))
47
+ else
48
+ env['REMOTE_USER'] = auth.username
49
+
50
+ return @app.call(env)
51
+ end
52
+ end
53
+
54
+ unauthorized
55
+ end
56
+
57
+
58
+ private
59
+
60
+ QOP = 'auth'.freeze
61
+
62
+ def params(hash = {})
63
+ Params.new do |params|
64
+ params['realm'] = realm
65
+ params['nonce'] = Nonce.new.to_s
66
+ params['opaque'] = H(opaque)
67
+ params['qop'] = QOP
68
+
69
+ hash.each { |k, v| params[k] = v }
70
+ end
71
+ end
72
+
73
+ def challenge(hash = {})
74
+ "Digest #{params(hash)}"
75
+ end
76
+
77
+ def valid?(auth)
78
+ valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
79
+ end
80
+
81
+ def valid_qop?(auth)
82
+ QOP == auth.qop
83
+ end
84
+
85
+ def valid_opaque?(auth)
86
+ H(opaque) == auth.opaque
87
+ end
88
+
89
+ def valid_nonce?(auth)
90
+ auth.nonce.valid?
91
+ end
92
+
93
+ def valid_digest?(auth)
94
+ digest(auth, @authenticator.call(auth.username)) == auth.response
95
+ end
96
+
97
+ def md5(data)
98
+ ::Digest::MD5.hexdigest(data)
99
+ end
100
+
101
+ alias :H :md5
102
+
103
+ def KD(secret, data)
104
+ H([secret, data] * ':')
105
+ end
106
+
107
+ def A1(auth, password)
108
+ [ auth.username, auth.realm, password ] * ':'
109
+ end
110
+
111
+ def A2(auth)
112
+ [ auth.method, auth.uri ] * ':'
113
+ end
114
+
115
+ def digest(auth, password)
116
+ password_hash = passwords_hashed? ? password : H(A1(auth, password))
117
+
118
+ KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':')
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+ end