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,18 @@
1
+ module Rack
2
+ class NullLogger
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ env['rack.logger'] = self
9
+ @app.call(env)
10
+ end
11
+
12
+ def info(progname = nil, &block); end
13
+ def debug(progname = nil, &block); end
14
+ def warn(progname = nil, &block); end
15
+ def error(progname = nil, &block); end
16
+ def fatal(progname = nil, &block); end
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ require 'uri'
2
+
3
+ module Rack
4
+ # Rack::ForwardRequest gets caught by Rack::Recursive and redirects
5
+ # the current request to the app at +url+.
6
+ #
7
+ # raise ForwardRequest.new("/not-found")
8
+ #
9
+
10
+ class ForwardRequest < Exception
11
+ attr_reader :url, :env
12
+
13
+ def initialize(url, env={})
14
+ @url = URI(url)
15
+ @env = env
16
+
17
+ @env["PATH_INFO"] = @url.path
18
+ @env["QUERY_STRING"] = @url.query if @url.query
19
+ @env["HTTP_HOST"] = @url.host if @url.host
20
+ @env["HTTP_PORT"] = @url.port if @url.port
21
+ @env["rack.url_scheme"] = @url.scheme if @url.scheme
22
+
23
+ super "forwarding to #{url}"
24
+ end
25
+ end
26
+
27
+ # Rack::Recursive allows applications called down the chain to
28
+ # include data from other applications (by using
29
+ # <tt>rack['rack.recursive.include'][...]</tt> or raise a
30
+ # ForwardRequest to redirect internally.
31
+
32
+ class Recursive
33
+ def initialize(app)
34
+ @app = app
35
+ end
36
+
37
+ def call(env)
38
+ @script_name = env["SCRIPT_NAME"]
39
+ @app.call(env.merge('rack.recursive.include' => method(:include)))
40
+ rescue ForwardRequest => req
41
+ call(env.merge(req.env))
42
+ end
43
+
44
+ def include(env, path)
45
+ unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ ||
46
+ path[@script_name.size].nil?)
47
+ raise ArgumentError, "can only include below #{@script_name}, not #{path}"
48
+ end
49
+
50
+ env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name,
51
+ "REQUEST_METHOD" => "GET",
52
+ "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
53
+ "rack.input" => StringIO.new(""))
54
+ @app.call(env)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,109 @@
1
+ # Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
2
+ # Rack::Reloader is subject to the terms of an MIT-style license.
3
+ # See COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ require 'pathname'
6
+
7
+ module Rack
8
+
9
+ # High performant source reloader
10
+ #
11
+ # This class acts as Rack middleware.
12
+ #
13
+ # What makes it especially suited for use in a production environment is that
14
+ # any file will only be checked once and there will only be made one system
15
+ # call stat(2).
16
+ #
17
+ # Please note that this will not reload files in the background, it does so
18
+ # only when actively called.
19
+ #
20
+ # It is performing a check/reload cycle at the start of every request, but
21
+ # also respects a cool down time, during which nothing will be done.
22
+ class Reloader
23
+ def initialize(app, cooldown = 10, backend = Stat)
24
+ @app = app
25
+ @cooldown = cooldown
26
+ @last = (Time.now - cooldown)
27
+ @cache = {}
28
+ @mtimes = {}
29
+
30
+ extend backend
31
+ end
32
+
33
+ def call(env)
34
+ if @cooldown and Time.now > @last + @cooldown
35
+ if Thread.list.size > 1
36
+ Thread.exclusive{ reload! }
37
+ else
38
+ reload!
39
+ end
40
+
41
+ @last = Time.now
42
+ end
43
+
44
+ @app.call(env)
45
+ end
46
+
47
+ def reload!(stderr = $stderr)
48
+ rotation do |file, mtime|
49
+ previous_mtime = @mtimes[file] ||= mtime
50
+ safe_load(file, mtime, stderr) if mtime > previous_mtime
51
+ end
52
+ end
53
+
54
+ # A safe Kernel::load, issuing the hooks depending on the results
55
+ def safe_load(file, mtime, stderr = $stderr)
56
+ load(file)
57
+ stderr.puts "#{self.class}: reloaded `#{file}'"
58
+ file
59
+ rescue LoadError, SyntaxError => ex
60
+ stderr.puts ex
61
+ ensure
62
+ @mtimes[file] = mtime
63
+ end
64
+
65
+ module Stat
66
+ def rotation
67
+ files = [$0, *$LOADED_FEATURES].uniq
68
+ paths = ['./', *$LOAD_PATH].uniq
69
+
70
+ files.map{|file|
71
+ next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
72
+
73
+ found, stat = figure_path(file, paths)
74
+ next unless found && stat && mtime = stat.mtime
75
+
76
+ @cache[file] = found
77
+
78
+ yield(found, mtime)
79
+ }.compact
80
+ end
81
+
82
+ # Takes a relative or absolute +file+ name, a couple possible +paths+ that
83
+ # the +file+ might reside in. Returns the full path and File::Stat for the
84
+ # path.
85
+ def figure_path(file, paths)
86
+ found = @cache[file]
87
+ found = file if !found and Pathname.new(file).absolute?
88
+ found, stat = safe_stat(found)
89
+ return found, stat if found
90
+
91
+ paths.find do |possible_path|
92
+ path = ::File.join(possible_path, file)
93
+ found, stat = safe_stat(path)
94
+ return ::File.expand_path(found), stat if found
95
+ end
96
+
97
+ return false, false
98
+ end
99
+
100
+ def safe_stat(file)
101
+ return unless file
102
+ stat = ::File.stat(file)
103
+ return file, stat if stat.file?
104
+ rescue Errno::ENOENT, Errno::ENOTDIR
105
+ @cache.delete(file) and false
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,271 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+ # Rack::Request provides a convenient interface to a Rack
5
+ # environment. It is stateless, the environment +env+ passed to the
6
+ # constructor will be directly modified.
7
+ #
8
+ # req = Rack::Request.new(env)
9
+ # req.post?
10
+ # req.params["data"]
11
+ #
12
+ # The environment hash passed will store a reference to the Request object
13
+ # instantiated so that it will only instantiate if an instance of the Request
14
+ # object doesn't already exist.
15
+
16
+ class Request
17
+ # The environment of the request.
18
+ attr_reader :env
19
+
20
+ def initialize(env)
21
+ @env = env
22
+ end
23
+
24
+ def body; @env["rack.input"] end
25
+ def scheme; @env["rack.url_scheme"] end
26
+ def script_name; @env["SCRIPT_NAME"].to_s end
27
+ def path_info; @env["PATH_INFO"].to_s end
28
+ def port; @env["SERVER_PORT"].to_i end
29
+ def request_method; @env["REQUEST_METHOD"] end
30
+ def query_string; @env["QUERY_STRING"].to_s end
31
+ def content_length; @env['CONTENT_LENGTH'] end
32
+ def content_type; @env['CONTENT_TYPE'] end
33
+ def session; @env['rack.session'] ||= {} end
34
+ def session_options; @env['rack.session.options'] ||= {} end
35
+ def logger; @env['rack.logger'] end
36
+
37
+ # The media type (type/subtype) portion of the CONTENT_TYPE header
38
+ # without any media type parameters. e.g., when CONTENT_TYPE is
39
+ # "text/plain;charset=utf-8", the media-type is "text/plain".
40
+ #
41
+ # For more information on the use of media types in HTTP, see:
42
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
43
+ def media_type
44
+ content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
45
+ end
46
+
47
+ # The media type parameters provided in CONTENT_TYPE as a Hash, or
48
+ # an empty Hash if no CONTENT_TYPE or media-type parameters were
49
+ # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
50
+ # this method responds with the following Hash:
51
+ # { 'charset' => 'utf-8' }
52
+ def media_type_params
53
+ return {} if content_type.nil?
54
+ content_type.split(/\s*[;,]\s*/)[1..-1].
55
+ collect { |s| s.split('=', 2) }.
56
+ inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash }
57
+ end
58
+
59
+ # The character set of the request body if a "charset" media type
60
+ # parameter was given, or nil if no "charset" was specified. Note
61
+ # that, per RFC2616, text/* media types that specify no explicit
62
+ # charset are to be considered ISO-8859-1.
63
+ def content_charset
64
+ media_type_params['charset']
65
+ end
66
+
67
+ def host_with_port
68
+ if forwarded = @env["HTTP_X_FORWARDED_HOST"]
69
+ forwarded.split(/,\s?/).last
70
+ else
71
+ @env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}"
72
+ end
73
+ end
74
+
75
+ def host
76
+ # Remove port number.
77
+ host_with_port.to_s.gsub(/:\d+\z/, '')
78
+ end
79
+
80
+ def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
81
+ def path_info=(s); @env["PATH_INFO"] = s.to_s end
82
+
83
+ def get?; request_method == "GET" end
84
+ def post?; request_method == "POST" end
85
+ def put?; request_method == "PUT" end
86
+ def delete?; request_method == "DELETE" end
87
+ def head?; request_method == "HEAD" end
88
+
89
+ # The set of form-data media-types. Requests that do not indicate
90
+ # one of the media types presents in this list will not be eligible
91
+ # for form-data / param parsing.
92
+ FORM_DATA_MEDIA_TYPES = [
93
+ 'application/x-www-form-urlencoded',
94
+ 'multipart/form-data'
95
+ ]
96
+
97
+ # The set of media-types. Requests that do not indicate
98
+ # one of the media types presents in this list will not be eligible
99
+ # for param parsing like soap attachments or generic multiparts
100
+ PARSEABLE_DATA_MEDIA_TYPES = [
101
+ 'multipart/related',
102
+ 'multipart/mixed'
103
+ ]
104
+
105
+ # Determine whether the request body contains form-data by checking
106
+ # the request Content-Type for one of the media-types:
107
+ # "application/x-www-form-urlencoded" or "multipart/form-data". The
108
+ # list of form-data media types can be modified through the
109
+ # +FORM_DATA_MEDIA_TYPES+ array.
110
+ #
111
+ # A request body is also assumed to contain form-data when no
112
+ # Content-Type header is provided and the request_method is POST.
113
+ def form_data?
114
+ type = media_type
115
+ meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
116
+ (meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
117
+ end
118
+
119
+ # Determine whether the request body contains data by checking
120
+ # the request media_type against registered parse-data media-types
121
+ def parseable_data?
122
+ PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
123
+ end
124
+
125
+ # Returns the data recieved in the query string.
126
+ def GET
127
+ if @env["rack.request.query_string"] == query_string
128
+ @env["rack.request.query_hash"]
129
+ else
130
+ @env["rack.request.query_string"] = query_string
131
+ @env["rack.request.query_hash"] = parse_query(query_string)
132
+ end
133
+ end
134
+
135
+ # Returns the data recieved in the request body.
136
+ #
137
+ # This method support both application/x-www-form-urlencoded and
138
+ # multipart/form-data.
139
+ def POST
140
+ if @env["rack.input"].nil?
141
+ raise "Missing rack.input"
142
+ elsif @env["rack.request.form_input"].eql? @env["rack.input"]
143
+ @env["rack.request.form_hash"]
144
+ elsif form_data? || parseable_data?
145
+ @env["rack.request.form_input"] = @env["rack.input"]
146
+ unless @env["rack.request.form_hash"] = parse_multipart(env)
147
+ form_vars = @env["rack.input"].read
148
+
149
+ # Fix for Safari Ajax postings that always append \0
150
+ form_vars.sub!(/\0\z/, '')
151
+
152
+ @env["rack.request.form_vars"] = form_vars
153
+ @env["rack.request.form_hash"] = parse_query(form_vars)
154
+
155
+ @env["rack.input"].rewind
156
+ end
157
+ @env["rack.request.form_hash"]
158
+ else
159
+ {}
160
+ end
161
+ end
162
+
163
+ # The union of GET and POST data.
164
+ def params
165
+ self.GET.update(self.POST)
166
+ rescue EOFError => e
167
+ self.GET
168
+ end
169
+
170
+ # shortcut for request.params[key]
171
+ def [](key)
172
+ params[key.to_s]
173
+ end
174
+
175
+ # shortcut for request.params[key] = value
176
+ def []=(key, value)
177
+ params[key.to_s] = value
178
+ end
179
+
180
+ # like Hash#values_at
181
+ def values_at(*keys)
182
+ keys.map{|key| params[key] }
183
+ end
184
+
185
+ # the referer of the client or '/'
186
+ def referer
187
+ @env['HTTP_REFERER'] || '/'
188
+ end
189
+ alias referrer referer
190
+
191
+ def user_agent
192
+ @env['HTTP_USER_AGENT']
193
+ end
194
+
195
+ def cookies
196
+ return {} unless @env["HTTP_COOKIE"]
197
+
198
+ if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
199
+ @env["rack.request.cookie_hash"]
200
+ else
201
+ @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
202
+ # According to RFC 2109:
203
+ # If multiple cookies satisfy the criteria above, they are ordered in
204
+ # the Cookie header such that those with more specific Path attributes
205
+ # precede those with less specific. Ordering with respect to other
206
+ # attributes (e.g., Domain) is unspecified.
207
+ @env["rack.request.cookie_hash"] =
208
+ Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)|
209
+ h[k] = Array === v ? v.first : v
210
+ h
211
+ }
212
+ end
213
+ end
214
+
215
+ def xhr?
216
+ @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
217
+ end
218
+
219
+ # Tries to return a remake of the original request URL as a string.
220
+ def url
221
+ url = scheme + "://"
222
+ url << host
223
+
224
+ if scheme == "https" && port != 443 ||
225
+ scheme == "http" && port != 80
226
+ url << ":#{port}"
227
+ end
228
+
229
+ url << fullpath
230
+
231
+ url
232
+ end
233
+
234
+ def path
235
+ script_name + path_info
236
+ end
237
+
238
+ def fullpath
239
+ query_string.empty? ? path : "#{path}?#{query_string}"
240
+ end
241
+
242
+ def accept_encoding
243
+ @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part|
244
+ m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
245
+
246
+ if m
247
+ [m[1], (m[2] || 1.0).to_f]
248
+ else
249
+ raise "Invalid value for Accept-Encoding: #{part.inspect}"
250
+ end
251
+ end
252
+ end
253
+
254
+ def ip
255
+ if addr = @env['HTTP_X_FORWARDED_FOR']
256
+ addr.split(',').last.strip
257
+ else
258
+ @env['REMOTE_ADDR']
259
+ end
260
+ end
261
+
262
+ protected
263
+ def parse_query(qs)
264
+ Utils.parse_nested_query(qs)
265
+ end
266
+
267
+ def parse_multipart(env)
268
+ Utils::Multipart.parse_multipart(env)
269
+ end
270
+ end
271
+ end