eac-rack 1.1.1

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.
Files changed (111) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +399 -0
  4. data/bin/rackup +4 -0
  5. data/contrib/rack_logo.svg +111 -0
  6. data/example/lobster.ru +4 -0
  7. data/example/protectedlobster.rb +14 -0
  8. data/example/protectedlobster.ru +8 -0
  9. data/lib/rack.rb +92 -0
  10. data/lib/rack/adapter/camping.rb +22 -0
  11. data/lib/rack/auth/abstract/handler.rb +37 -0
  12. data/lib/rack/auth/abstract/request.rb +37 -0
  13. data/lib/rack/auth/basic.rb +58 -0
  14. data/lib/rack/auth/digest/md5.rb +124 -0
  15. data/lib/rack/auth/digest/nonce.rb +51 -0
  16. data/lib/rack/auth/digest/params.rb +55 -0
  17. data/lib/rack/auth/digest/request.rb +40 -0
  18. data/lib/rack/builder.rb +80 -0
  19. data/lib/rack/cascade.rb +41 -0
  20. data/lib/rack/chunked.rb +49 -0
  21. data/lib/rack/commonlogger.rb +49 -0
  22. data/lib/rack/conditionalget.rb +47 -0
  23. data/lib/rack/config.rb +15 -0
  24. data/lib/rack/content_length.rb +29 -0
  25. data/lib/rack/content_type.rb +23 -0
  26. data/lib/rack/deflater.rb +96 -0
  27. data/lib/rack/directory.rb +157 -0
  28. data/lib/rack/etag.rb +23 -0
  29. data/lib/rack/file.rb +90 -0
  30. data/lib/rack/handler.rb +88 -0
  31. data/lib/rack/handler/cgi.rb +61 -0
  32. data/lib/rack/handler/evented_mongrel.rb +8 -0
  33. data/lib/rack/handler/fastcgi.rb +89 -0
  34. data/lib/rack/handler/lsws.rb +63 -0
  35. data/lib/rack/handler/mongrel.rb +90 -0
  36. data/lib/rack/handler/scgi.rb +62 -0
  37. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  38. data/lib/rack/handler/thin.rb +18 -0
  39. data/lib/rack/handler/webrick.rb +69 -0
  40. data/lib/rack/head.rb +19 -0
  41. data/lib/rack/lint.rb +575 -0
  42. data/lib/rack/lobster.rb +65 -0
  43. data/lib/rack/lock.rb +16 -0
  44. data/lib/rack/logger.rb +20 -0
  45. data/lib/rack/methodoverride.rb +27 -0
  46. data/lib/rack/mime.rb +206 -0
  47. data/lib/rack/mock.rb +189 -0
  48. data/lib/rack/nulllogger.rb +18 -0
  49. data/lib/rack/recursive.rb +57 -0
  50. data/lib/rack/reloader.rb +109 -0
  51. data/lib/rack/request.rb +271 -0
  52. data/lib/rack/response.rb +149 -0
  53. data/lib/rack/rewindable_input.rb +100 -0
  54. data/lib/rack/runtime.rb +27 -0
  55. data/lib/rack/sendfile.rb +142 -0
  56. data/lib/rack/server.rb +212 -0
  57. data/lib/rack/session/abstract/id.rb +140 -0
  58. data/lib/rack/session/cookie.rb +90 -0
  59. data/lib/rack/session/memcache.rb +119 -0
  60. data/lib/rack/session/pool.rb +100 -0
  61. data/lib/rack/showexceptions.rb +349 -0
  62. data/lib/rack/showstatus.rb +106 -0
  63. data/lib/rack/static.rb +38 -0
  64. data/lib/rack/urlmap.rb +56 -0
  65. data/lib/rack/utils.rb +614 -0
  66. data/rack.gemspec +38 -0
  67. data/test/spec_rack_auth_basic.rb +73 -0
  68. data/test/spec_rack_auth_digest.rb +226 -0
  69. data/test/spec_rack_builder.rb +84 -0
  70. data/test/spec_rack_camping.rb +51 -0
  71. data/test/spec_rack_cascade.rb +48 -0
  72. data/test/spec_rack_cgi.rb +89 -0
  73. data/test/spec_rack_chunked.rb +62 -0
  74. data/test/spec_rack_commonlogger.rb +61 -0
  75. data/test/spec_rack_conditionalget.rb +41 -0
  76. data/test/spec_rack_config.rb +24 -0
  77. data/test/spec_rack_content_length.rb +43 -0
  78. data/test/spec_rack_content_type.rb +30 -0
  79. data/test/spec_rack_deflater.rb +127 -0
  80. data/test/spec_rack_directory.rb +61 -0
  81. data/test/spec_rack_etag.rb +17 -0
  82. data/test/spec_rack_fastcgi.rb +89 -0
  83. data/test/spec_rack_file.rb +75 -0
  84. data/test/spec_rack_handler.rb +43 -0
  85. data/test/spec_rack_head.rb +30 -0
  86. data/test/spec_rack_lint.rb +528 -0
  87. data/test/spec_rack_lobster.rb +45 -0
  88. data/test/spec_rack_lock.rb +38 -0
  89. data/test/spec_rack_logger.rb +21 -0
  90. data/test/spec_rack_methodoverride.rb +60 -0
  91. data/test/spec_rack_mock.rb +243 -0
  92. data/test/spec_rack_mongrel.rb +189 -0
  93. data/test/spec_rack_nulllogger.rb +13 -0
  94. data/test/spec_rack_recursive.rb +77 -0
  95. data/test/spec_rack_request.rb +545 -0
  96. data/test/spec_rack_response.rb +221 -0
  97. data/test/spec_rack_rewindable_input.rb +118 -0
  98. data/test/spec_rack_runtime.rb +35 -0
  99. data/test/spec_rack_sendfile.rb +86 -0
  100. data/test/spec_rack_session_cookie.rb +73 -0
  101. data/test/spec_rack_session_memcache.rb +273 -0
  102. data/test/spec_rack_session_pool.rb +172 -0
  103. data/test/spec_rack_showexceptions.rb +21 -0
  104. data/test/spec_rack_showstatus.rb +72 -0
  105. data/test/spec_rack_static.rb +37 -0
  106. data/test/spec_rack_thin.rb +91 -0
  107. data/test/spec_rack_urlmap.rb +215 -0
  108. data/test/spec_rack_utils.rb +554 -0
  109. data/test/spec_rack_webrick.rb +130 -0
  110. data/test/spec_rackup.rb +154 -0
  111. metadata +311 -0
@@ -0,0 +1,140 @@
1
+ # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
+ # bugrep: Andreas Zehnder
3
+
4
+ require 'time'
5
+ require 'rack/request'
6
+ require 'rack/response'
7
+
8
+ module Rack
9
+
10
+ module Session
11
+
12
+ module Abstract
13
+
14
+ # ID sets up a basic framework for implementing an id based sessioning
15
+ # service. Cookies sent to the client for maintaining sessions will only
16
+ # contain an id reference. Only #get_session and #set_session are
17
+ # required to be overwritten.
18
+ #
19
+ # All parameters are optional.
20
+ # * :key determines the name of the cookie, by default it is
21
+ # 'rack.session'
22
+ # * :path, :domain, :expire_after, :secure, and :httponly set the related
23
+ # cookie options as by Rack::Response#add_cookie
24
+ # * :defer will not set a cookie in the response.
25
+ # * :renew (implementation dependent) will prompt the generation of a new
26
+ # session id, and migration of data to be referenced at the new id. If
27
+ # :defer is set, it will be overridden and the cookie will be set.
28
+ # * :sidbits sets the number of bits in length that a generated session
29
+ # id will be.
30
+ #
31
+ # These options can be set on a per request basis, at the location of
32
+ # env['rack.session.options']. Additionally the id of the session can be
33
+ # found within the options hash at the key :id. It is highly not
34
+ # recommended to change its value.
35
+ #
36
+ # Is Rack::Utils::Context compatible.
37
+
38
+ class ID
39
+ DEFAULT_OPTIONS = {
40
+ :path => '/',
41
+ :domain => nil,
42
+ :expire_after => nil,
43
+ :secure => false,
44
+ :httponly => true,
45
+ :defer => false,
46
+ :renew => false,
47
+ :sidbits => 128
48
+ }
49
+
50
+ attr_reader :key, :default_options
51
+ def initialize(app, options={})
52
+ @app = app
53
+ @key = options[:key] || "rack.session"
54
+ @default_options = self.class::DEFAULT_OPTIONS.merge(options)
55
+ end
56
+
57
+ def call(env)
58
+ context(env)
59
+ end
60
+
61
+ def context(env, app=@app)
62
+ load_session(env)
63
+ status, headers, body = app.call(env)
64
+ commit_session(env, status, headers, body)
65
+ end
66
+
67
+ private
68
+
69
+ # Generate a new session id using Ruby #rand. The size of the
70
+ # session id is controlled by the :sidbits option.
71
+ # Monkey patch this to use custom methods for session id generation.
72
+
73
+ def generate_sid
74
+ "%0#{@default_options[:sidbits] / 4}x" %
75
+ rand(2**@default_options[:sidbits] - 1)
76
+ end
77
+
78
+ # Extracts the session id from provided cookies and passes it and the
79
+ # environment to #get_session. It then sets the resulting session into
80
+ # 'rack.session', and places options and session metadata into
81
+ # 'rack.session.options'.
82
+
83
+ def load_session(env)
84
+ request = Rack::Request.new(env)
85
+ session_id = request.cookies[@key]
86
+
87
+ begin
88
+ session_id, session = get_session(env, session_id)
89
+ env['rack.session'] = session
90
+ rescue
91
+ env['rack.session'] = Hash.new
92
+ end
93
+
94
+ env['rack.session.options'] = @default_options.
95
+ merge(:id => session_id)
96
+ end
97
+
98
+ # Acquires the session from the environment and the session id from
99
+ # the session options and passes them to #set_session. If successful
100
+ # and the :defer option is not true, a cookie will be added to the
101
+ # response with the session's id.
102
+
103
+ def commit_session(env, status, headers, body)
104
+ session = env['rack.session']
105
+ options = env['rack.session.options']
106
+ session_id = options[:id]
107
+
108
+ if not session_id = set_session(env, session_id, session, options)
109
+ env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
110
+ elsif options[:defer] and not options[:renew]
111
+ env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
112
+ else
113
+ cookie = Hash.new
114
+ cookie[:value] = session_id
115
+ cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
116
+ Utils.set_cookie_header!(headers, @key, cookie.merge(options))
117
+ end
118
+
119
+ [status, headers, body]
120
+ end
121
+
122
+ # All thread safety and session retrival proceedures should occur here.
123
+ # Should return [session_id, session].
124
+ # If nil is provided as the session id, generation of a new valid id
125
+ # should occur within.
126
+
127
+ def get_session(env, sid)
128
+ raise '#get_session not implemented.'
129
+ end
130
+
131
+ # All thread safety and session storage proceedures should occur here.
132
+ # Should return true or false dependant on whether or not the session
133
+ # was saved or not.
134
+ def set_session(env, sid, session, options)
135
+ raise '#set_session not implemented.'
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,90 @@
1
+ require 'openssl'
2
+ require 'rack/request'
3
+ require 'rack/response'
4
+
5
+ module Rack
6
+
7
+ module Session
8
+
9
+ # Rack::Session::Cookie provides simple cookie based session management.
10
+ # The session is a Ruby Hash stored as base64 encoded marshalled data
11
+ # set to :key (default: rack.session).
12
+ # When the secret key is set, cookie data is checked for data integrity.
13
+ #
14
+ # Example:
15
+ #
16
+ # use Rack::Session::Cookie, :key => 'rack.session',
17
+ # :domain => 'foo.com',
18
+ # :path => '/',
19
+ # :expire_after => 2592000,
20
+ # :secret => 'change_me'
21
+ #
22
+ # All parameters are optional.
23
+
24
+ class Cookie
25
+
26
+ def initialize(app, options={})
27
+ @app = app
28
+ @key = options[:key] || "rack.session"
29
+ @secret = options[:secret]
30
+ @default_options = {:domain => nil,
31
+ :path => "/",
32
+ :expire_after => nil}.merge(options)
33
+ end
34
+
35
+ def call(env)
36
+ load_session(env)
37
+ status, headers, body = @app.call(env)
38
+ commit_session(env, status, headers, body)
39
+ end
40
+
41
+ private
42
+
43
+ def load_session(env)
44
+ request = Rack::Request.new(env)
45
+ session_data = request.cookies[@key]
46
+
47
+ if @secret && session_data
48
+ session_data, digest = session_data.split("--")
49
+ session_data = nil unless digest == generate_hmac(session_data)
50
+ end
51
+
52
+ begin
53
+ session_data = session_data.unpack("m*").first
54
+ session_data = Marshal.load(session_data)
55
+ env["rack.session"] = session_data
56
+ rescue
57
+ env["rack.session"] = Hash.new
58
+ end
59
+
60
+ env["rack.session.options"] = @default_options.dup
61
+ end
62
+
63
+ def commit_session(env, status, headers, body)
64
+ session_data = Marshal.dump(env["rack.session"])
65
+ session_data = [session_data].pack("m*")
66
+
67
+ if @secret
68
+ session_data = "#{session_data}--#{generate_hmac(session_data)}"
69
+ end
70
+
71
+ if session_data.size > (4096 - @key.size)
72
+ env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
73
+ else
74
+ options = env["rack.session.options"]
75
+ cookie = Hash.new
76
+ cookie[:value] = session_data
77
+ cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
78
+ Utils.set_cookie_header!(headers, @key, cookie.merge(options))
79
+ end
80
+
81
+ [status, headers, body]
82
+ end
83
+
84
+ def generate_hmac(data)
85
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,119 @@
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 caveats.
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. However, the cookie sent to the client would expire
17
+ # immediately.
18
+ #
19
+ # Note that memcache does drop data before it may be listed to expire. For
20
+ # a full description of behaviour, please see memcache's documentation.
21
+
22
+ class Memcache < Abstract::ID
23
+ attr_reader :mutex, :pool
24
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
25
+ :namespace => 'rack:session',
26
+ :memcache_server => 'localhost:11211'
27
+
28
+ def initialize(app, options={})
29
+ super
30
+
31
+ @mutex = Mutex.new
32
+ mserv = @default_options[:memcache_server]
33
+ mopts = @default_options.
34
+ reject{|k,v| MemCache::DEFAULT_OPTIONS.include? k }
35
+ @pool = MemCache.new mserv, mopts
36
+ unless @pool.active? and @pool.servers.any?{|c| c.alive? }
37
+ raise 'No memcache servers'
38
+ end
39
+ end
40
+
41
+ def generate_sid
42
+ loop do
43
+ sid = super
44
+ break sid unless @pool.get(sid, true)
45
+ end
46
+ end
47
+
48
+ def get_session(env, session_id)
49
+ @mutex.lock if env['rack.multithread']
50
+ unless session_id and session = @pool.get(session_id)
51
+ session_id, session = generate_sid, {}
52
+ unless /^STORED/ =~ @pool.add(session_id, session)
53
+ raise "Session collision on '#{session_id.inspect}'"
54
+ end
55
+ end
56
+ session.instance_variable_set '@old', @pool.get(session_id, true)
57
+ return [session_id, session]
58
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
59
+ # MemCache server cannot be contacted
60
+ warn "#{self} is unable to find memcached server."
61
+ warn $!.inspect
62
+ return [ nil, {} ]
63
+ ensure
64
+ @mutex.unlock if @mutex.locked?
65
+ end
66
+
67
+ def set_session(env, session_id, new_session, options)
68
+ expiry = options[:expire_after]
69
+ expiry = expiry.nil? ? 0 : expiry + 1
70
+
71
+ @mutex.lock if env['rack.multithread']
72
+ if options[:renew] or options[:drop]
73
+ @pool.delete session_id
74
+ return false if options[:drop]
75
+ session_id = generate_sid
76
+ @pool.add session_id, {} # so we don't worry about cache miss on #set
77
+ end
78
+
79
+ session = @pool.get(session_id) || {}
80
+ old_session = new_session.instance_variable_get '@old'
81
+ old_session = old_session ? Marshal.load(old_session) : {}
82
+
83
+ unless Hash === old_session and Hash === new_session
84
+ env['rack.errors'].
85
+ puts 'Bad old_session or new_session sessions provided.'
86
+ else # merge sessions
87
+ # alterations are either update or delete, making as few changes as
88
+ # possible to prevent possible issues.
89
+
90
+ # removed keys
91
+ delete = old_session.keys - new_session.keys
92
+ if $VERBOSE and not delete.empty?
93
+ env['rack.errors'].
94
+ puts "//@#{session_id}: delete #{delete*','}"
95
+ end
96
+ delete.each{|k| session.delete k }
97
+
98
+ # added or altered keys
99
+ update = new_session.keys.
100
+ select{|k| new_session[k] != old_session[k] }
101
+ if $VERBOSE and not update.empty?
102
+ env['rack.errors'].puts "//@#{session_id}: update #{update*','}"
103
+ end
104
+ update.each{|k| session[k] = new_session[k] }
105
+ end
106
+
107
+ @pool.set session_id, session, expiry
108
+ return session_id
109
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
110
+ # MemCache server cannot be contacted
111
+ warn "#{self} is unable to find memcached server."
112
+ warn $!.inspect
113
+ return false
114
+ ensure
115
+ @mutex.unlock if @mutex.locked?
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,100 @@
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
+ # The :drop option is available in rack.session.options if you wish to
17
+ # explicitly remove the session from the session cache.
18
+ #
19
+ # Example:
20
+ # myapp = MyRackApp.new
21
+ # sessioned = Rack::Session::Pool.new(myapp,
22
+ # :domain => 'foo.com',
23
+ # :expire_after => 2592000
24
+ # )
25
+ # Rack::Handler::WEBrick.run sessioned
26
+
27
+ class Pool < Abstract::ID
28
+ attr_reader :mutex, :pool
29
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
30
+
31
+ def initialize(app, options={})
32
+ super
33
+ @pool = Hash.new
34
+ @mutex = Mutex.new
35
+ end
36
+
37
+ def generate_sid
38
+ loop do
39
+ sid = super
40
+ break sid unless @pool.key? sid
41
+ end
42
+ end
43
+
44
+ def get_session(env, sid)
45
+ session = @pool[sid] if sid
46
+ @mutex.lock if env['rack.multithread']
47
+ unless sid and session
48
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
49
+ session = {}
50
+ sid = generate_sid
51
+ @pool.store sid, session
52
+ end
53
+ session.instance_variable_set('@old', {}.merge(session))
54
+ return [sid, session]
55
+ ensure
56
+ @mutex.unlock if env['rack.multithread']
57
+ end
58
+
59
+ def set_session(env, session_id, new_session, options)
60
+ @mutex.lock if env['rack.multithread']
61
+ session = @pool[session_id]
62
+ if options[:renew] or options[:drop]
63
+ @pool.delete session_id
64
+ return false if options[:drop]
65
+ session_id = generate_sid
66
+ @pool.store session_id, 0
67
+ end
68
+ old_session = new_session.instance_variable_get('@old') || {}
69
+ session = merge_sessions session_id, old_session, new_session, session
70
+ @pool.store session_id, session
71
+ return session_id
72
+ rescue
73
+ warn "#{new_session.inspect} has been lost."
74
+ warn $!.inspect
75
+ ensure
76
+ @mutex.unlock if env['rack.multithread']
77
+ end
78
+
79
+ private
80
+
81
+ def merge_sessions sid, old, new, cur=nil
82
+ cur ||= {}
83
+ unless Hash === old and Hash === new
84
+ warn 'Bad old or new sessions provided.'
85
+ return cur
86
+ end
87
+
88
+ delete = old.keys - new.keys
89
+ warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
90
+ delete.each{|k| cur.delete k }
91
+
92
+ update = new.keys.select{|k| new[k] != old[k] }
93
+ warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
94
+ update.each{|k| cur[k] = new[k] }
95
+
96
+ cur
97
+ end
98
+ end
99
+ end
100
+ end