sinatra 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

Files changed (83) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +22 -0
  3. data/Manifest +78 -1
  4. data/lib/sinatra.rb +12 -1
  5. data/sinatra.gemspec +7 -14
  6. data/vendor/rack/AUTHORS +7 -0
  7. data/vendor/rack/COPYING +18 -0
  8. data/vendor/rack/KNOWN-ISSUES +18 -0
  9. data/vendor/rack/README +242 -0
  10. data/vendor/rack/Rakefile +174 -0
  11. data/vendor/rack/bin/rackup +153 -0
  12. data/vendor/rack/contrib/rack_logo.svg +111 -0
  13. data/vendor/rack/example/lobster.ru +4 -0
  14. data/vendor/rack/example/protectedlobster.rb +14 -0
  15. data/vendor/rack/example/protectedlobster.ru +8 -0
  16. data/vendor/rack/lib/rack.rb +92 -0
  17. data/vendor/rack/lib/rack/adapter/camping.rb +22 -0
  18. data/vendor/rack/lib/rack/auth/abstract/handler.rb +28 -0
  19. data/vendor/rack/lib/rack/auth/abstract/request.rb +37 -0
  20. data/vendor/rack/lib/rack/auth/basic.rb +58 -0
  21. data/vendor/rack/lib/rack/auth/digest/md5.rb +124 -0
  22. data/vendor/rack/lib/rack/auth/digest/nonce.rb +51 -0
  23. data/vendor/rack/lib/rack/auth/digest/params.rb +55 -0
  24. data/vendor/rack/lib/rack/auth/digest/request.rb +40 -0
  25. data/vendor/rack/lib/rack/auth/openid.rb +116 -0
  26. data/vendor/rack/lib/rack/builder.rb +56 -0
  27. data/vendor/rack/lib/rack/cascade.rb +36 -0
  28. data/vendor/rack/lib/rack/commonlogger.rb +56 -0
  29. data/vendor/rack/lib/rack/file.rb +112 -0
  30. data/vendor/rack/lib/rack/handler/cgi.rb +57 -0
  31. data/vendor/rack/lib/rack/handler/fastcgi.rb +83 -0
  32. data/vendor/rack/lib/rack/handler/lsws.rb +52 -0
  33. data/vendor/rack/lib/rack/handler/mongrel.rb +78 -0
  34. data/vendor/rack/lib/rack/handler/scgi.rb +57 -0
  35. data/vendor/rack/lib/rack/handler/webrick.rb +57 -0
  36. data/vendor/rack/lib/rack/lint.rb +394 -0
  37. data/vendor/rack/lib/rack/lobster.rb +65 -0
  38. data/vendor/rack/lib/rack/mock.rb +160 -0
  39. data/vendor/rack/lib/rack/recursive.rb +57 -0
  40. data/vendor/rack/lib/rack/reloader.rb +64 -0
  41. data/vendor/rack/lib/rack/request.rb +197 -0
  42. data/vendor/rack/lib/rack/response.rb +166 -0
  43. data/vendor/rack/lib/rack/session/abstract/id.rb +126 -0
  44. data/vendor/rack/lib/rack/session/cookie.rb +71 -0
  45. data/vendor/rack/lib/rack/session/memcache.rb +83 -0
  46. data/vendor/rack/lib/rack/session/pool.rb +67 -0
  47. data/vendor/rack/lib/rack/showexceptions.rb +344 -0
  48. data/vendor/rack/lib/rack/showstatus.rb +103 -0
  49. data/vendor/rack/lib/rack/static.rb +38 -0
  50. data/vendor/rack/lib/rack/urlmap.rb +48 -0
  51. data/vendor/rack/lib/rack/utils.rb +240 -0
  52. data/vendor/rack/test/cgi/lighttpd.conf +20 -0
  53. data/vendor/rack/test/cgi/test +9 -0
  54. data/vendor/rack/test/cgi/test.fcgi +7 -0
  55. data/vendor/rack/test/cgi/test.ru +7 -0
  56. data/vendor/rack/test/spec_rack_auth_basic.rb +69 -0
  57. data/vendor/rack/test/spec_rack_auth_digest.rb +169 -0
  58. data/vendor/rack/test/spec_rack_builder.rb +50 -0
  59. data/vendor/rack/test/spec_rack_camping.rb +47 -0
  60. data/vendor/rack/test/spec_rack_cascade.rb +50 -0
  61. data/vendor/rack/test/spec_rack_cgi.rb +91 -0
  62. data/vendor/rack/test/spec_rack_commonlogger.rb +32 -0
  63. data/vendor/rack/test/spec_rack_fastcgi.rb +91 -0
  64. data/vendor/rack/test/spec_rack_file.rb +40 -0
  65. data/vendor/rack/test/spec_rack_lint.rb +317 -0
  66. data/vendor/rack/test/spec_rack_lobster.rb +45 -0
  67. data/vendor/rack/test/spec_rack_mock.rb +152 -0
  68. data/vendor/rack/test/spec_rack_mongrel.rb +165 -0
  69. data/vendor/rack/test/spec_rack_recursive.rb +77 -0
  70. data/vendor/rack/test/spec_rack_request.rb +384 -0
  71. data/vendor/rack/test/spec_rack_response.rb +167 -0
  72. data/vendor/rack/test/spec_rack_session_cookie.rb +49 -0
  73. data/vendor/rack/test/spec_rack_session_memcache.rb +100 -0
  74. data/vendor/rack/test/spec_rack_session_pool.rb +84 -0
  75. data/vendor/rack/test/spec_rack_showexceptions.rb +21 -0
  76. data/vendor/rack/test/spec_rack_showstatus.rb +71 -0
  77. data/vendor/rack/test/spec_rack_static.rb +37 -0
  78. data/vendor/rack/test/spec_rack_urlmap.rb +175 -0
  79. data/vendor/rack/test/spec_rack_utils.rb +57 -0
  80. data/vendor/rack/test/spec_rack_webrick.rb +106 -0
  81. data/vendor/rack/test/testrequest.rb +43 -0
  82. metadata +81 -4
  83. data/Rakefile +0 -24
@@ -0,0 +1,166 @@
1
+ require 'rack/request'
2
+ require 'rack/utils'
3
+
4
+ module Rack
5
+ # Rack::Response provides a convenient interface to create a Rack
6
+ # response.
7
+ #
8
+ # It allows setting of headers and cookies, and provides useful
9
+ # defaults (a OK response containing HTML).
10
+ #
11
+ # You can use Response#write to iteratively generate your response,
12
+ # but note that this is buffered by Rack::Response until you call
13
+ # +finish+. +finish+ however can take a block inside which calls to
14
+ # +write+ are syncronous with the Rack response.
15
+ #
16
+ # Your application's +call+ should end returning Response#finish.
17
+
18
+ class Response
19
+ def initialize(body=[], status=200, header={}, &block)
20
+ @status = status
21
+ @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
22
+ merge(header))
23
+
24
+ @writer = lambda { |x| @body << x }
25
+ @block = nil
26
+
27
+ @body = []
28
+
29
+ if body.respond_to? :to_str
30
+ write body.to_str
31
+ elsif body.respond_to?(:each)
32
+ body.each { |part|
33
+ write part.to_s
34
+ }
35
+ else
36
+ raise TypeError, "stringable or iterable required"
37
+ end
38
+
39
+ yield self if block_given?
40
+ end
41
+
42
+ attr_reader :header
43
+ attr_accessor :status, :body
44
+
45
+ def [](key)
46
+ header[key]
47
+ end
48
+
49
+ def []=(key, value)
50
+ header[key] = value
51
+ end
52
+
53
+ def set_cookie(key, value)
54
+ case value
55
+ when Hash
56
+ domain = "; domain=" + value[:domain] if value[:domain]
57
+ path = "; path=" + value[:path] if value[:path]
58
+ # According to RFC 2109, we need dashes here.
59
+ # N.B.: cgi.rb uses spaces...
60
+ expires = "; expires=" + value[:expires].clone.gmtime.
61
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
62
+ value = value[:value]
63
+ end
64
+ value = [value] unless Array === value
65
+ cookie = Utils.escape(key) + "=" +
66
+ value.map { |v| Utils.escape v }.join("&") +
67
+ "#{domain}#{path}#{expires}"
68
+
69
+ case self["Set-Cookie"]
70
+ when Array
71
+ self["Set-Cookie"] << cookie
72
+ when String
73
+ self["Set-Cookie"] = [self["Set-Cookie"], cookie]
74
+ when nil
75
+ self["Set-Cookie"] = cookie
76
+ end
77
+ end
78
+
79
+ def delete_cookie(key, value={})
80
+ unless Array === self["Set-Cookie"]
81
+ self["Set-Cookie"] = [self["Set-Cookie"]].compact
82
+ end
83
+
84
+ self["Set-Cookie"].reject! { |cookie|
85
+ cookie =~ /\A#{Utils.escape(key)}=/
86
+ }
87
+
88
+ set_cookie(key,
89
+ {:value => '', :path => nil, :domain => nil,
90
+ :expires => Time.at(0) }.merge(value))
91
+ end
92
+
93
+
94
+ def finish(&block)
95
+ @block = block
96
+
97
+ if [204, 304].include?(status.to_i)
98
+ header.delete "Content-Type"
99
+ [status.to_i, header.to_hash, []]
100
+ else
101
+ [status.to_i, header.to_hash, self]
102
+ end
103
+ end
104
+ alias to_a finish # For *response
105
+
106
+ def each(&callback)
107
+ @body.each(&callback)
108
+ @writer = callback
109
+ @block.call(self) if @block
110
+ end
111
+
112
+ def write(str)
113
+ @writer.call str.to_s
114
+ str
115
+ end
116
+
117
+ def close
118
+ body.close if body.respond_to?(:close)
119
+ end
120
+
121
+ def empty?
122
+ @block == nil && @body.empty?
123
+ end
124
+
125
+ alias headers header
126
+
127
+ module Helpers
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
+ # Headers
144
+ attr_reader :headers, :original_headers
145
+
146
+ def include?(header)
147
+ !!headers[header]
148
+ end
149
+
150
+ def content_type
151
+ headers["Content-Type"]
152
+ end
153
+
154
+ def content_length
155
+ cl = headers["Content-Length"]
156
+ cl ? cl.to_i : cl
157
+ end
158
+
159
+ def location
160
+ headers["Location"]
161
+ end
162
+ end
163
+
164
+ include Helpers
165
+ end
166
+ end
@@ -0,0 +1,126 @@
1
+ # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
+
3
+ require 'rack/utils'
4
+
5
+ module Rack
6
+ module Session
7
+ module Abstract
8
+ # ID sets up a basic framework for implementing an id based sessioning
9
+ # service. Cookies sent to the client for maintaining sessions will only
10
+ # contain an id reference. Only #get_session and #set_session should
11
+ # need to be overwritten.
12
+ #
13
+ # All parameters are optional.
14
+ # * :key determines the name of the cookie, by default it is
15
+ # 'rack.session'
16
+ # * :domain and :path set the related cookie values, by default
17
+ # domain is nil, and the path is '/'.
18
+ # * :expire_after is the number of seconds in which the session
19
+ # cookie will expire. By default it is set not to provide any
20
+ # expiry time.
21
+ class ID
22
+ attr_reader :key
23
+ DEFAULT_OPTIONS = {
24
+ :key => 'rack.session',
25
+ :path => '/',
26
+ :domain => nil,
27
+ :expire_after => nil
28
+ }
29
+
30
+ def initialize(app, options={})
31
+ @default_options = self.class::DEFAULT_OPTIONS.merge(options)
32
+ @key = @default_options[:key]
33
+ @default_context = context app
34
+ end
35
+
36
+ def call(env)
37
+ @default_context.call(env)
38
+ end
39
+
40
+ def context(app)
41
+ Rack::Utils::Context.new self, app do |env|
42
+ load_session env
43
+ response = app.call(env)
44
+ commit_session env, response
45
+ response
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # Extracts the session id from provided cookies and passes it and the
52
+ # environment to #get_session. It then sets the resulting session into
53
+ # 'rack.session', and places options and session metadata into
54
+ # 'rack.session.options'.
55
+ def load_session(env)
56
+ sid = env.fetch('HTTP_COOKIE','')[/#{@key}=([^,;]+)/,1]
57
+ sid, session = get_session(env, sid)
58
+ unless session.is_a?(Hash)
59
+ puts 'Session: '+sid.inspect+"\n"+session.inspect if $DEBUG
60
+ raise TypeError, 'Session not a Hash'
61
+ end
62
+ options = @default_options.
63
+ merge({ :id => sid, :by => self, :at => Time.now })
64
+
65
+ env['rack.session'] = session
66
+ env['rack.session.options'] = options
67
+ end
68
+
69
+ # Acquires the session from the environment and the session id from
70
+ # the session options and passes them to #set_session. It then
71
+ # proceeds to set a cookie up in the response with the session's id.
72
+ def commit_session(env, response)
73
+ unless response.is_a?(Array)
74
+ puts 'Response: '+response.inspect if $DEBUG
75
+ raise ArgumentError, 'Response is not an array.'
76
+ end
77
+
78
+ options = env['rack.session.options']
79
+ unless options.is_a?(Hash)
80
+ puts 'Options: '+options.inspect if $DEBUG
81
+ raise TypeError, 'Options not a Hash'
82
+ end
83
+
84
+ sid, time, z = options.values_at(:id, :at, :by)
85
+ unless self == z
86
+ warn "#{self} not managing this session."
87
+ return
88
+ end
89
+
90
+ unless env['rack.session'].is_a?(Hash)
91
+ puts 'Session: '+sid.inspect+"\n"+session.inspect if $DEBUG
92
+ raise TypeError, 'Session not a Hash'
93
+ end
94
+ set_session(env, sid)
95
+
96
+ expiry = options[:expire_after] && time+options[:expire_after]
97
+ cookie = Utils.escape(@key)+'='+Utils.escape(sid)
98
+ cookie<< "; domain=#{options[:domain]}" if options[:domain]
99
+ cookie<< "; path=#{options[:path]}" if options[:path]
100
+ cookie<< "; expires=#{expiry}" if expiry
101
+
102
+ puts "Cookie: "+cookie.inspect if $DEBUG
103
+
104
+ case a = (h = response[1])['Set-Cookie']
105
+ when Array then a << cookie
106
+ when String then h['Set-Cookie'] = [a, cookie]
107
+ when nil then h['Set-Cookie'] = cookie
108
+ end
109
+ end
110
+
111
+ # Should return [session_id, session]. All thread safety and session
112
+ # retrival proceedures should occur here.
113
+ # If nil is provided as the session id, generation of a new valid id
114
+ # should occur within.
115
+ def get_session(env, sid)
116
+ raise '#get_session needs to be implemented.'
117
+ end
118
+
119
+ # All thread safety and session storage proceedures should occur here.
120
+ def set_session(env, sid)
121
+ raise '#set_session needs to be implemented.'
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,71 @@
1
+ module Rack
2
+
3
+ module Session
4
+
5
+ # Rack::Session::Cookie provides simple cookie based session management.
6
+ # The session is a Ruby Hash stored as base64 encoded marshalled data
7
+ # set to :key (default: rack.session).
8
+ #
9
+ # Example:
10
+ #
11
+ # use Rack::Session::Cookie, :key => 'rack.session',
12
+ # :domain => 'foo.com',
13
+ # :path => '/',
14
+ # :expire_after => 2592000
15
+ #
16
+ # All parameters are optional.
17
+
18
+ class Cookie
19
+
20
+ def initialize(app, options={})
21
+ @app = app
22
+ @key = options[:key] || "rack.session"
23
+ @default_options = {:domain => nil,
24
+ :path => "/",
25
+ :expire_after => nil}.merge(options)
26
+ end
27
+
28
+ def call(env)
29
+ load_session(env)
30
+ status, headers, body = @app.call(env)
31
+ commit_session(env, status, headers, body)
32
+ end
33
+
34
+ private
35
+
36
+ def load_session(env)
37
+ request = Rack::Request.new(env)
38
+ session_data = request.cookies[@key]
39
+
40
+ begin
41
+ session_data = session_data.unpack("m*").first
42
+ session_data = Marshal.load(session_data)
43
+ env["rack.session"] = session_data
44
+ rescue
45
+ env["rack.session"] = Hash.new
46
+ end
47
+
48
+ env["rack.session.options"] = @default_options.dup
49
+ end
50
+
51
+ def commit_session(env, status, headers, body)
52
+ session_data = Marshal.dump(env["rack.session"])
53
+ session_data = [session_data].pack("m*")
54
+
55
+ if session_data.size > (4096 - @key.size)
56
+ env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
57
+ [status, headers, body]
58
+ else
59
+ options = env["rack.session.options"]
60
+ cookie = Hash.new
61
+ cookie[:value] = session_data
62
+ cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
63
+ response = Rack::Response.new(body, status, headers)
64
+ response.set_cookie(@key, cookie.merge(options))
65
+ response.to_a
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,83 @@
1
+ # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
+
3
+ require 'rack/session/abstract/id'
4
+ require 'memcache'
5
+
6
+ module Rack
7
+ module Session
8
+ # Rack::Session::Memcache provides simple cookie based session management.
9
+ # Session data is stored in memcached. The corresponding session key is
10
+ # maintained in the cookie.
11
+ # You may treat Session::Memcache as you would Session::Pool with the
12
+ # following differences.
13
+ #
14
+ # * Setting :expire_after to 0 would note to the Memcache server to hang
15
+ # onto the session data until it would drop it according to it's own
16
+ # specifications.
17
+ #
18
+ # Note that memcache does drop data before it may be listed to expire. For
19
+ # a full description of behaviour, please see memcache's documentation.
20
+
21
+ class Memcache < Abstract::ID
22
+ attr_reader :mutex, :pool
23
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge({
24
+ :namespace => 'rack:session',
25
+ :memcache_server => 'localhost:11211'
26
+ })
27
+
28
+ def initialize(app, options={})
29
+ super
30
+ @pool = MemCache.new @default_options[:memcache_server], @default_options
31
+ @mutex = Mutex.new
32
+ end
33
+
34
+ private
35
+
36
+ def get_session(env, sid)
37
+ session = sid && @pool.get(sid)
38
+ unless session and session.is_a?(Hash)
39
+ session = {}
40
+ lc = 0
41
+ @mutex.synchronize do
42
+ begin
43
+ raise RuntimeError, 'Unique id finding looping excessively' if (lc+=1) > 1000
44
+ sid = "%08x" % rand(0xffffffff)
45
+ ret = @pool.add(sid, session)
46
+ end until /^STORED/ =~ ret
47
+ end
48
+ end
49
+ class << session
50
+ @deleted = []
51
+ def delete key
52
+ (@deleted||=[]) << key
53
+ super
54
+ end
55
+ end
56
+ [sid, session]
57
+ end
58
+
59
+ def set_session(env, sid)
60
+ session = env['rack.session']
61
+ options = env['rack.session.options']
62
+ expiry = options[:expire_after] || 0
63
+ o, s = @mutex.synchronize do
64
+ old_session = @pool.get(sid)
65
+ unless old_session.is_a?(Hash)
66
+ warn 'Session not properly initialized.' if $DEBUG
67
+ old_session = {}
68
+ @pool.add sid, old_session, expiry
69
+ end
70
+ session.instance_eval do
71
+ @deleted.each{|k| old_session.delete(k) } if defined? @deleted
72
+ end
73
+ @pool.set sid, old_session.merge(session), expiry
74
+ [old_session, session]
75
+ end
76
+ s.each do |k,v|
77
+ next unless o.has_key?(k) and v != o[k]
78
+ warn "session value assignment collision at #{k}: #{o[k]} <- #{v}"
79
+ end if $DEBUG and env['rack.multithread']
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,67 @@
1
+ # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
+ # THANKS:
3
+ # apeiros, for session id generation, expiry setup, and threadiness
4
+ # sergio, threadiness and bugreps
5
+
6
+ require 'rack/session/abstract/id'
7
+ require 'thread'
8
+
9
+ module Rack
10
+ module Session
11
+ # Rack::Session::Pool provides simple cookie based session management.
12
+ # Session data is stored in a hash held by @pool.
13
+ # In the context of a multithreaded environment, sessions being
14
+ # committed to the pool is done in a merging manner.
15
+ #
16
+ # Example:
17
+ # myapp = MyRackApp.new
18
+ # sessioned = Rack::Session::Pool.new(myapp,
19
+ # :key => 'rack.session',
20
+ # :domain => 'foo.com',
21
+ # :path => '/',
22
+ # :expire_after => 2592000
23
+ # )
24
+ # Rack::Handler::WEBrick.run sessioned
25
+
26
+ class Pool < Abstract::ID
27
+ attr_reader :mutex, :pool
28
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.dup
29
+
30
+ def initialize(app, options={})
31
+ super
32
+ @pool = Hash.new
33
+ @mutex = Mutex.new
34
+ end
35
+
36
+ private
37
+
38
+ def get_session(env, sid)
39
+ session = @mutex.synchronize do
40
+ unless sess = @pool[sid] and ((expires = sess[:expire_at]).nil? or expires > Time.now)
41
+ @pool.delete_if{|k,v| expiry = v[:expire_at] and expiry < Time.now }
42
+ begin
43
+ sid = "%08x" % rand(0xffffffff)
44
+ end while @pool.has_key?(sid)
45
+ end
46
+ @pool[sid] ||= {}
47
+ end
48
+ [sid, session]
49
+ end
50
+
51
+ def set_session(env, sid)
52
+ options = env['rack.session.options']
53
+ expiry = options[:expire_after] && options[:at]+options[:expire_after]
54
+ @mutex.synchronize do
55
+ old_session = @pool[sid]
56
+ old_session[:expire_at] = expiry if expiry
57
+ session = old_session.merge(env['rack.session'])
58
+ @pool[sid] = session
59
+ session.each do |k,v|
60
+ next unless old_session.has_key?(k) and v != old_session[k]
61
+ warn "session value assignment collision at #{k}: #{old_session[k]} <- #{v}"
62
+ end if $DEBUG and env['rack.multithread']
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end