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,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)
21
+ str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/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['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
12
+ end
13
+
14
+ def digest?
15
+ :digest == scheme
16
+ end
17
+
18
+ def correct_uri?
19
+ (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == 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
@@ -0,0 +1,80 @@
1
+ module Rack
2
+ # Rack::Builder implements a small DSL to iteratively construct Rack
3
+ # applications.
4
+ #
5
+ # Example:
6
+ #
7
+ # app = Rack::Builder.new {
8
+ # use Rack::CommonLogger
9
+ # use Rack::ShowExceptions
10
+ # map "/lobster" do
11
+ # use Rack::Lint
12
+ # run Rack::Lobster.new
13
+ # end
14
+ # }
15
+ #
16
+ # Or
17
+ #
18
+ # app = Rack::Builder.app do
19
+ # use Rack::CommonLogger
20
+ # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] }
21
+ # end
22
+ #
23
+ # +use+ adds a middleware to the stack, +run+ dispatches to an application.
24
+ # You can use +map+ to construct a Rack::URLMap in a convenient way.
25
+
26
+ class Builder
27
+ def self.parse_file(config, opts = Server::Options.new)
28
+ options = {}
29
+ if config =~ /\.ru$/
30
+ cfgfile = ::File.read(config)
31
+ if cfgfile[/^#\\(.*)/] && opts
32
+ options = opts.parse! $1.split(/\s+/)
33
+ end
34
+ cfgfile.sub!(/^__END__\n.*/, '')
35
+ app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
36
+ TOPLEVEL_BINDING, config
37
+ else
38
+ require config
39
+ app = Object.const_get(::File.basename(config, '.rb').capitalize)
40
+ end
41
+ return app, options
42
+ end
43
+
44
+ def initialize(&block)
45
+ @ins = []
46
+ instance_eval(&block) if block_given?
47
+ end
48
+
49
+ def self.app(&block)
50
+ self.new(&block).to_app
51
+ end
52
+
53
+ def use(middleware, *args, &block)
54
+ @ins << lambda { |app| middleware.new(app, *args, &block) }
55
+ end
56
+
57
+ def run(app)
58
+ @ins << app #lambda { |nothing| app }
59
+ end
60
+
61
+ def map(path, &block)
62
+ if @ins.last.kind_of? Hash
63
+ @ins.last[path] = self.class.new(&block).to_app
64
+ else
65
+ @ins << {}
66
+ map(path, &block)
67
+ end
68
+ end
69
+
70
+ def to_app
71
+ @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last
72
+ inner_app = @ins.last
73
+ @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) }
74
+ end
75
+
76
+ def call(env)
77
+ to_app.call(env)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,41 @@
1
+ module Rack
2
+ # Rack::Cascade tries an request on several apps, and returns the
3
+ # first response that is not 404 (or in a list of configurable
4
+ # status codes).
5
+
6
+ class Cascade
7
+ NotFound = [404, {}, []]
8
+
9
+ attr_reader :apps
10
+
11
+ def initialize(apps, catch=404)
12
+ @apps = []; @has_app = {}
13
+ apps.each { |app| add app }
14
+
15
+ @catch = {}
16
+ [*catch].each { |status| @catch[status] = true }
17
+ end
18
+
19
+ def call(env)
20
+ result = NotFound
21
+
22
+ @apps.each do |app|
23
+ result = app.call(env)
24
+ break unless @catch.include?(result[0].to_i)
25
+ end
26
+
27
+ result
28
+ end
29
+
30
+ def add app
31
+ @has_app[app] = true
32
+ @apps << app
33
+ end
34
+
35
+ def include? app
36
+ @has_app.include? app
37
+ end
38
+
39
+ alias_method :<<, :add
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+
5
+ # Middleware that applies chunked transfer encoding to response bodies
6
+ # when the response does not include a Content-Length header.
7
+ class Chunked
8
+ include Rack::Utils
9
+
10
+ def initialize(app)
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ status, headers, body = @app.call(env)
16
+ headers = HeaderHash.new(headers)
17
+
18
+ if env['HTTP_VERSION'] == 'HTTP/1.0' ||
19
+ STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
20
+ headers['Content-Length'] ||
21
+ headers['Transfer-Encoding']
22
+ [status, headers, body]
23
+ else
24
+ dup.chunk(status, headers, body)
25
+ end
26
+ end
27
+
28
+ def chunk(status, headers, body)
29
+ @body = body
30
+ headers.delete('Content-Length')
31
+ headers['Transfer-Encoding'] = 'chunked'
32
+ [status, headers, self]
33
+ end
34
+
35
+ def each
36
+ term = "\r\n"
37
+ @body.each do |chunk|
38
+ size = bytesize(chunk)
39
+ next if size == 0
40
+ yield [size.to_s(16), term, chunk, term].join
41
+ end
42
+ yield ["0", term, "", term].join
43
+ end
44
+
45
+ def close
46
+ @body.close if @body.respond_to?(:close)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ module Rack
2
+ # Rack::CommonLogger forwards every request to an +app+ given, and
3
+ # logs a line in the Apache common log format to the +logger+, or
4
+ # rack.errors by default.
5
+ class CommonLogger
6
+ # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
7
+ # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
8
+ # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
9
+ FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
10
+
11
+ def initialize(app, logger=nil)
12
+ @app = app
13
+ @logger = logger
14
+ end
15
+
16
+ def call(env)
17
+ began_at = Time.now
18
+ status, header, body = @app.call(env)
19
+ header = Utils::HeaderHash.new(header)
20
+ log(env, status, header, began_at)
21
+ [status, header, body]
22
+ end
23
+
24
+ private
25
+
26
+ def log(env, status, header, began_at)
27
+ now = Time.now
28
+ length = extract_content_length(header)
29
+
30
+ logger = @logger || env['rack.errors']
31
+ logger.write FORMAT % [
32
+ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
33
+ env["REMOTE_USER"] || "-",
34
+ now.strftime("%d/%b/%Y %H:%M:%S"),
35
+ env["REQUEST_METHOD"],
36
+ env["PATH_INFO"],
37
+ env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
38
+ env["HTTP_VERSION"],
39
+ status.to_s[0..3],
40
+ length,
41
+ now - began_at ]
42
+ end
43
+
44
+ def extract_content_length(headers)
45
+ value = headers['Content-Length'] or return '-'
46
+ value.to_s == '0' ? '-' : value
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+
5
+ # Middleware that enables conditional GET using If-None-Match and
6
+ # If-Modified-Since. The application should set either or both of the
7
+ # Last-Modified or Etag response headers according to RFC 2616. When
8
+ # either of the conditions is met, the response body is set to be zero
9
+ # length and the response status is set to 304 Not Modified.
10
+ #
11
+ # Applications that defer response body generation until the body's each
12
+ # message is received will avoid response body generation completely when
13
+ # a conditional GET matches.
14
+ #
15
+ # Adapted from Michael Klishin's Merb implementation:
16
+ # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb
17
+ class ConditionalGet
18
+ def initialize(app)
19
+ @app = app
20
+ end
21
+
22
+ def call(env)
23
+ return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD'])
24
+
25
+ status, headers, body = @app.call(env)
26
+ headers = Utils::HeaderHash.new(headers)
27
+ if etag_matches?(env, headers) || modified_since?(env, headers)
28
+ status = 304
29
+ headers.delete('Content-Type')
30
+ headers.delete('Content-Length')
31
+ body = []
32
+ end
33
+ [status, headers, body]
34
+ end
35
+
36
+ private
37
+ def etag_matches?(env, headers)
38
+ etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH']
39
+ end
40
+
41
+ def modified_since?(env, headers)
42
+ last_modified = headers['Last-Modified'] and
43
+ last_modified == env['HTTP_IF_MODIFIED_SINCE']
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,15 @@
1
+ module Rack
2
+ # Rack::Config modifies the environment using the block given during
3
+ # initialization.
4
+ class Config
5
+ def initialize(app, &block)
6
+ @app = app
7
+ @block = block
8
+ end
9
+
10
+ def call(env)
11
+ @block.call(env)
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+ # Sets the Content-Length header on responses with fixed-length bodies.
5
+ class ContentLength
6
+ include Rack::Utils
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ status, headers, body = @app.call(env)
14
+ headers = HeaderHash.new(headers)
15
+
16
+ if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
17
+ !headers['Content-Length'] &&
18
+ !headers['Transfer-Encoding'] &&
19
+ (body.respond_to?(:to_ary) || body.respond_to?(:to_str))
20
+
21
+ body = [body] if body.respond_to?(:to_str) # rack 0.4 compat
22
+ length = body.to_ary.inject(0) { |len, part| len + bytesize(part) }
23
+ headers['Content-Length'] = length.to_s
24
+ end
25
+
26
+ [status, headers, body]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'rack/utils'
2
+
3
+ module Rack
4
+
5
+ # Sets the Content-Type header on responses which don't have one.
6
+ #
7
+ # Builder Usage:
8
+ # use Rack::ContentType, "text/plain"
9
+ #
10
+ # When no content type argument is provided, "text/html" is assumed.
11
+ class ContentType
12
+ def initialize(app, content_type = "text/html")
13
+ @app, @content_type = app, content_type
14
+ end
15
+
16
+ def call(env)
17
+ status, headers, body = @app.call(env)
18
+ headers = Utils::HeaderHash.new(headers)
19
+ headers['Content-Type'] ||= @content_type
20
+ [status, headers, body]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,96 @@
1
+ require "zlib"
2
+ require "stringio"
3
+ require "time" # for Time.httpdate
4
+ require 'rack/utils'
5
+
6
+ module Rack
7
+ class Deflater
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ status, headers, body = @app.call(env)
14
+ headers = Utils::HeaderHash.new(headers)
15
+
16
+ # Skip compressing empty entity body responses and responses with
17
+ # no-transform set.
18
+ if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
19
+ headers['Cache-Control'].to_s =~ /\bno-transform\b/
20
+ return [status, headers, body]
21
+ end
22
+
23
+ request = Request.new(env)
24
+
25
+ encoding = Utils.select_best_encoding(%w(gzip deflate identity),
26
+ request.accept_encoding)
27
+
28
+ # Set the Vary HTTP header.
29
+ vary = headers["Vary"].to_s.split(",").map { |v| v.strip }
30
+ unless vary.include?("*") || vary.include?("Accept-Encoding")
31
+ headers["Vary"] = vary.push("Accept-Encoding").join(",")
32
+ end
33
+
34
+ case encoding
35
+ when "gzip"
36
+ headers['Content-Encoding'] = "gzip"
37
+ headers.delete('Content-Length')
38
+ mtime = headers.key?("Last-Modified") ?
39
+ Time.httpdate(headers["Last-Modified"]) : Time.now
40
+ [status, headers, GzipStream.new(body, mtime)]
41
+ when "deflate"
42
+ headers['Content-Encoding'] = "deflate"
43
+ headers.delete('Content-Length')
44
+ [status, headers, DeflateStream.new(body)]
45
+ when "identity"
46
+ [status, headers, body]
47
+ when nil
48
+ message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
49
+ [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
50
+ end
51
+ end
52
+
53
+ class GzipStream
54
+ def initialize(body, mtime)
55
+ @body = body
56
+ @mtime = mtime
57
+ end
58
+
59
+ def each(&block)
60
+ @writer = block
61
+ gzip =::Zlib::GzipWriter.new(self)
62
+ gzip.mtime = @mtime
63
+ @body.each { |part| gzip.write(part) }
64
+ @body.close if @body.respond_to?(:close)
65
+ gzip.close
66
+ @writer = nil
67
+ end
68
+
69
+ def write(data)
70
+ @writer.call(data)
71
+ end
72
+ end
73
+
74
+ class DeflateStream
75
+ DEFLATE_ARGS = [
76
+ Zlib::DEFAULT_COMPRESSION,
77
+ # drop the zlib header which causes both Safari and IE to choke
78
+ -Zlib::MAX_WBITS,
79
+ Zlib::DEF_MEM_LEVEL,
80
+ Zlib::DEFAULT_STRATEGY
81
+ ]
82
+
83
+ def initialize(body)
84
+ @body = body
85
+ end
86
+
87
+ def each
88
+ deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
89
+ @body.each { |part| yield deflater.deflate(part) }
90
+ @body.close if @body.respond_to?(:close)
91
+ yield deflater.finish
92
+ nil
93
+ end
94
+ end
95
+ end
96
+ end