rack 1.0.1 → 1.1.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 (82) hide show
  1. data/COPYING +1 -1
  2. data/KNOWN-ISSUES +3 -0
  3. data/RDOX +0 -428
  4. data/README +61 -26
  5. data/SPEC +8 -1
  6. data/bin/rackup +2 -174
  7. data/lib/rack.rb +10 -8
  8. data/lib/rack/builder.rb +17 -0
  9. data/lib/rack/cascade.rb +17 -12
  10. data/lib/rack/chunked.rb +2 -2
  11. data/lib/rack/commonlogger.rb +31 -43
  12. data/lib/rack/config.rb +15 -0
  13. data/lib/rack/content_type.rb +1 -1
  14. data/lib/rack/directory.rb +6 -2
  15. data/lib/rack/etag.rb +23 -0
  16. data/lib/rack/file.rb +4 -2
  17. data/lib/rack/handler.rb +19 -0
  18. data/lib/rack/handler/cgi.rb +1 -1
  19. data/lib/rack/handler/fastcgi.rb +2 -3
  20. data/lib/rack/handler/lsws.rb +4 -1
  21. data/lib/rack/handler/mongrel.rb +8 -5
  22. data/lib/rack/handler/scgi.rb +4 -4
  23. data/lib/rack/handler/webrick.rb +2 -4
  24. data/lib/rack/lint.rb +44 -15
  25. data/lib/rack/logger.rb +20 -0
  26. data/lib/rack/mime.rb +3 -1
  27. data/lib/rack/mock.rb +30 -4
  28. data/lib/rack/nulllogger.rb +18 -0
  29. data/lib/rack/reloader.rb +4 -1
  30. data/lib/rack/request.rb +40 -15
  31. data/lib/rack/response.rb +5 -39
  32. data/lib/rack/runtime.rb +27 -0
  33. data/lib/rack/sendfile.rb +142 -0
  34. data/lib/rack/server.rb +212 -0
  35. data/lib/rack/session/abstract/id.rb +3 -5
  36. data/lib/rack/session/cookie.rb +3 -4
  37. data/lib/rack/session/memcache.rb +53 -43
  38. data/lib/rack/session/pool.rb +1 -1
  39. data/lib/rack/urlmap.rb +9 -8
  40. data/lib/rack/utils.rb +230 -11
  41. data/rack.gemspec +33 -49
  42. data/test/spec_rack_cascade.rb +3 -5
  43. data/test/spec_rack_cgi.rb +3 -3
  44. data/test/spec_rack_commonlogger.rb +39 -10
  45. data/test/spec_rack_config.rb +24 -0
  46. data/test/spec_rack_directory.rb +1 -1
  47. data/test/spec_rack_etag.rb +17 -0
  48. data/test/spec_rack_fastcgi.rb +2 -2
  49. data/test/spec_rack_file.rb +1 -1
  50. data/test/spec_rack_lint.rb +26 -19
  51. data/test/spec_rack_logger.rb +21 -0
  52. data/test/spec_rack_mock.rb +87 -1
  53. data/test/spec_rack_mongrel.rb +4 -4
  54. data/test/spec_rack_nulllogger.rb +13 -0
  55. data/test/spec_rack_request.rb +47 -6
  56. data/test/spec_rack_response.rb +3 -0
  57. data/test/spec_rack_runtime.rb +35 -0
  58. data/test/spec_rack_sendfile.rb +86 -0
  59. data/test/spec_rack_session_cookie.rb +1 -10
  60. data/test/spec_rack_session_memcache.rb +53 -20
  61. data/test/spec_rack_urlmap.rb +30 -0
  62. data/test/spec_rack_utils.rb +171 -6
  63. data/test/spec_rack_webrick.rb +4 -4
  64. data/test/spec_rackup.rb +154 -0
  65. metadata +37 -79
  66. data/Rakefile +0 -164
  67. data/lib/rack/auth/openid.rb +0 -480
  68. data/test/cgi/lighttpd.conf +0 -20
  69. data/test/cgi/test +0 -9
  70. data/test/cgi/test.fcgi +0 -8
  71. data/test/cgi/test.ru +0 -7
  72. data/test/multipart/binary +0 -0
  73. data/test/multipart/empty +0 -10
  74. data/test/multipart/ie +0 -6
  75. data/test/multipart/nested +0 -10
  76. data/test/multipart/none +0 -9
  77. data/test/multipart/semicolon +0 -6
  78. data/test/multipart/text +0 -10
  79. data/test/spec_rack_auth_openid.rb +0 -84
  80. data/test/testrequest.rb +0 -57
  81. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  82. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/SPEC CHANGED
@@ -52,7 +52,7 @@ below.
52
52
  request.
53
53
  In addition to this, the Rack environment must include these
54
54
  Rack-specific variables:
55
- <tt>rack.version</tt>:: The Array [1,0], representing this version of Rack.
55
+ <tt>rack.version</tt>:: The Array [1,1], representing this version of Rack.
56
56
  <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
57
57
  <tt>rack.input</tt>:: See below, the input stream.
58
58
  <tt>rack.errors</tt>:: See below, the error stream.
@@ -68,6 +68,13 @@ be implemented by the server.
68
68
  fetch(key, default = nil) (aliased as []);
69
69
  delete(key);
70
70
  clear;
71
+ <tt>rack.logger</tt>:: A common object interface for logging messages.
72
+ The object must implement:
73
+ info(message, &block)
74
+ debug(message, &block)
75
+ warn(message, &block)
76
+ error(message, &block)
77
+ fatal(message, &block)
71
78
  The server or the application can store their own data in the
72
79
  environment, too. The keys must contain at least one dot,
73
80
  and should be prefixed uniquely. The prefix <tt>rack.</tt>
data/bin/rackup CHANGED
@@ -1,176 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # -*- ruby -*-
3
2
 
4
- $LOAD_PATH.unshift File.expand_path("#{__FILE__}/../../lib")
5
- autoload :Rack, 'rack'
6
-
7
- require 'optparse'
8
-
9
- automatic = false
10
- server = nil
11
- env = "development"
12
- daemonize = false
13
- pid = nil
14
- options = {:Port => 9292, :Host => "0.0.0.0", :AccessLog => []}
15
-
16
- # Don't evaluate CGI ISINDEX parameters.
17
- # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
18
- ARGV.clear if ENV.include?("REQUEST_METHOD")
19
-
20
- opts = OptionParser.new("", 24, ' ') { |opts|
21
- opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]"
22
-
23
- opts.separator ""
24
- opts.separator "Ruby options:"
25
-
26
- lineno = 1
27
- opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
28
- eval line, TOPLEVEL_BINDING, "-e", lineno
29
- lineno += 1
30
- }
31
-
32
- opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
33
- $DEBUG = true
34
- }
35
- opts.on("-w", "--warn", "turn warnings on for your script") {
36
- $-w = true
37
- }
38
-
39
- opts.on("-I", "--include PATH",
40
- "specify $LOAD_PATH (may be used more than once)") { |path|
41
- $LOAD_PATH.unshift(*path.split(":"))
42
- }
43
-
44
- opts.on("-r", "--require LIBRARY",
45
- "require the library, before executing your script") { |library|
46
- require library
47
- }
48
-
49
- opts.separator ""
50
- opts.separator "Rack options:"
51
- opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s|
52
- server = s
53
- }
54
-
55
- opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host|
56
- options[:Host] = host
57
- }
58
-
59
- opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port|
60
- options[:Port] = port
61
- }
62
-
63
- opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
64
- env = e
65
- }
66
-
67
- opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
68
- daemonize = d ? true : false
69
- }
70
-
71
- opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
72
- pid = File.expand_path(f)
73
- }
74
-
75
- opts.separator ""
76
- opts.separator "Common options:"
77
-
78
- opts.on_tail("-h", "--help", "Show this message") do
79
- puts opts
80
- exit
81
- end
82
-
83
- opts.on_tail("--version", "Show version") do
84
- puts "Rack #{Rack.version}"
85
- exit
86
- end
87
-
88
- opts.parse! ARGV
89
- }
90
-
91
- require 'pp' if $DEBUG
92
-
93
- config = ARGV[0] || "config.ru"
94
- if !File.exist? config
95
- abort "configuration #{config} not found"
96
- end
97
-
98
- if config =~ /\.ru$/
99
- cfgfile = File.read(config)
100
- if cfgfile[/^#\\(.*)/]
101
- opts.parse! $1.split(/\s+/)
102
- end
103
- inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
104
- nil, config
105
- else
106
- require config
107
- inner_app = Object.const_get(File.basename(config, '.rb').capitalize)
108
- end
109
-
110
- unless server = Rack::Handler.get(server)
111
- # Guess.
112
- if ENV.include?("PHP_FCGI_CHILDREN")
113
- server = Rack::Handler::FastCGI
114
-
115
- # We already speak FastCGI
116
- options.delete :File
117
- options.delete :Port
118
- elsif ENV.include?("REQUEST_METHOD")
119
- server = Rack::Handler::CGI
120
- else
121
- begin
122
- server = Rack::Handler::Mongrel
123
- rescue LoadError => e
124
- server = Rack::Handler::WEBrick
125
- end
126
- end
127
- end
128
-
129
- p server if $DEBUG
130
-
131
- case env
132
- when "development"
133
- app = Rack::Builder.new {
134
- use Rack::CommonLogger, $stderr unless server.name =~ /CGI/
135
- use Rack::ShowExceptions
136
- use Rack::Lint
137
- run inner_app
138
- }.to_app
139
-
140
- when "deployment"
141
- app = Rack::Builder.new {
142
- use Rack::CommonLogger, $stderr unless server.name =~ /CGI/
143
- run inner_app
144
- }.to_app
145
-
146
- when "none"
147
- app = inner_app
148
-
149
- end
150
-
151
- if $DEBUG
152
- pp app
153
- pp inner_app
154
- end
155
-
156
- if daemonize
157
- if RUBY_VERSION < "1.9"
158
- exit if fork
159
- Process.setsid
160
- exit if fork
161
- Dir.chdir "/"
162
- File.umask 0000
163
- STDIN.reopen "/dev/null"
164
- STDOUT.reopen "/dev/null", "a"
165
- STDERR.reopen "/dev/null", "a"
166
- else
167
- Process.daemon
168
- end
169
-
170
- if pid
171
- File.open(pid, 'w'){ |f| f.write("#{Process.pid}") }
172
- at_exit { File.delete(pid) if File.exist?(pid) }
173
- end
174
- end
175
-
176
- server.run app, options
3
+ require "rack"
4
+ Rack::Server.start
@@ -1,12 +1,8 @@
1
- # Copyright (C) 2007, 2008, 2009 Christian Neukirchen <purl.org/net/chneukirchen>
1
+ # Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
2
2
  #
3
3
  # Rack is freely distributable under the terms of an MIT-style license.
4
4
  # See COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
 
6
- path = File.expand_path(File.dirname(__FILE__))
7
- $:.unshift(path) unless $:.include?(path)
8
-
9
-
10
6
  # The Rack main module, serving as a namespace for all core Rack
11
7
  # modules and classes.
12
8
  #
@@ -15,7 +11,7 @@ $:.unshift(path) unless $:.include?(path)
15
11
 
16
12
  module Rack
17
13
  # The Rack protocol version number implemented.
18
- VERSION = [1,0]
14
+ VERSION = [1,1]
19
15
 
20
16
  # Return the Rack protocol version as a dotted string.
21
17
  def self.version
@@ -24,7 +20,7 @@ module Rack
24
20
 
25
21
  # Return the Rack release as a dotted string.
26
22
  def self.release
27
- "1.0"
23
+ "1.1"
28
24
  end
29
25
 
30
26
  autoload :Builder, "rack/builder"
@@ -32,8 +28,10 @@ module Rack
32
28
  autoload :Chunked, "rack/chunked"
33
29
  autoload :CommonLogger, "rack/commonlogger"
34
30
  autoload :ConditionalGet, "rack/conditionalget"
31
+ autoload :Config, "rack/config"
35
32
  autoload :ContentLength, "rack/content_length"
36
33
  autoload :ContentType, "rack/content_type"
34
+ autoload :ETag, "rack/etag"
37
35
  autoload :File, "rack/file"
38
36
  autoload :Deflater, "rack/deflater"
39
37
  autoload :Directory, "rack/directory"
@@ -42,10 +40,15 @@ module Rack
42
40
  autoload :Head, "rack/head"
43
41
  autoload :Lint, "rack/lint"
44
42
  autoload :Lock, "rack/lock"
43
+ autoload :Logger, "rack/logger"
45
44
  autoload :MethodOverride, "rack/methodoverride"
46
45
  autoload :Mime, "rack/mime"
46
+ autoload :NullLogger, "rack/nulllogger"
47
47
  autoload :Recursive, "rack/recursive"
48
48
  autoload :Reloader, "rack/reloader"
49
+ autoload :Runtime, "rack/runtime"
50
+ autoload :Sendfile, "rack/sendfile"
51
+ autoload :Server, "rack/server"
49
52
  autoload :ShowExceptions, "rack/showexceptions"
50
53
  autoload :ShowStatus, "rack/showstatus"
51
54
  autoload :Static, "rack/static"
@@ -62,7 +65,6 @@ module Rack
62
65
  autoload :Basic, "rack/auth/basic"
63
66
  autoload :AbstractRequest, "rack/auth/abstract/request"
64
67
  autoload :AbstractHandler, "rack/auth/abstract/handler"
65
- autoload :OpenID, "rack/auth/openid"
66
68
  module Digest
67
69
  autoload :MD5, "rack/auth/digest/md5"
68
70
  autoload :Nonce, "rack/auth/digest/nonce"
@@ -24,6 +24,23 @@ module Rack
24
24
  # You can use +map+ to construct a Rack::URLMap in a convenient way.
25
25
 
26
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
+
27
44
  def initialize(&block)
28
45
  @ins = []
29
46
  instance_eval(&block) if block_given?
@@ -4,31 +4,36 @@ module Rack
4
4
  # status codes).
5
5
 
6
6
  class Cascade
7
+ NotFound = [404, {}, []]
8
+
7
9
  attr_reader :apps
8
10
 
9
11
  def initialize(apps, catch=404)
10
- @apps = apps
11
- @catch = [*catch]
12
+ @apps = []; @has_app = {}
13
+ apps.each { |app| add app }
14
+
15
+ @catch = {}
16
+ [*catch].each { |status| @catch[status] = true }
12
17
  end
13
18
 
14
19
  def call(env)
15
- status = headers = body = nil
16
- raise ArgumentError, "empty cascade" if @apps.empty?
17
- @apps.each { |app|
18
- begin
19
- status, headers, body = app.call(env)
20
- break unless @catch.include?(status.to_i)
21
- end
22
- }
23
- [status, headers, body]
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
24
28
  end
25
29
 
26
30
  def add app
31
+ @has_app[app] = true
27
32
  @apps << app
28
33
  end
29
34
 
30
35
  def include? app
31
- @apps.include? app
36
+ @has_app.include? app
32
37
  end
33
38
 
34
39
  alias_method :<<, :add
@@ -19,7 +19,7 @@ module Rack
19
19
  STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
20
20
  headers['Content-Length'] ||
21
21
  headers['Transfer-Encoding']
22
- [status, headers.to_hash, body]
22
+ [status, headers, body]
23
23
  else
24
24
  dup.chunk(status, headers, body)
25
25
  end
@@ -29,7 +29,7 @@ module Rack
29
29
  @body = body
30
30
  headers.delete('Content-Length')
31
31
  headers['Transfer-Encoding'] = 'chunked'
32
- [status, headers.to_hash, self]
32
+ [status, headers, self]
33
33
  end
34
34
 
35
35
  def each
@@ -2,60 +2,48 @@ module Rack
2
2
  # Rack::CommonLogger forwards every request to an +app+ given, and
3
3
  # logs a line in the Apache common log format to the +logger+, or
4
4
  # rack.errors by default.
5
-
6
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
+
7
11
  def initialize(app, logger=nil)
8
12
  @app = app
9
13
  @logger = logger
10
14
  end
11
15
 
12
16
  def call(env)
13
- dup._call(env)
14
- end
15
-
16
- def _call(env)
17
- @env = env
18
- @logger ||= self
19
- @time = Time.now
20
- @status, @header, @body = @app.call(env)
21
- [@status, @header, self]
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
22
  end
23
23
 
24
- def close
25
- @body.close if @body.respond_to? :close
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 ]
26
42
  end
27
43
 
28
- # By default, log to rack.errors.
29
- def <<(str)
30
- @env["rack.errors"].write(str)
31
- @env["rack.errors"].flush
32
- end
33
-
34
- def each
35
- length = 0
36
- @body.each { |part|
37
- length += part.size
38
- yield part
39
- }
40
-
41
- @now = Time.now
42
-
43
- # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
44
- # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
45
- # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
46
- @logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} %
47
- [
48
- @env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-",
49
- @env["REMOTE_USER"] || "-",
50
- @now.strftime("%d/%b/%Y %H:%M:%S"),
51
- @env["REQUEST_METHOD"],
52
- @env["PATH_INFO"],
53
- @env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"],
54
- @env["HTTP_VERSION"],
55
- @status.to_s[0..3],
56
- (length.zero? ? "-" : length.to_s),
57
- @now - @time
58
- ]
44
+ def extract_content_length(headers)
45
+ value = headers['Content-Length'] or return '-'
46
+ value.to_s == '0' ? '-' : value
59
47
  end
60
48
  end
61
49
  end