Vendor Rack 1.1
Also clean up some View stuff.
This commit is contained in:
parent
77014652a3
commit
a705709f9a
74 changed files with 3080 additions and 608 deletions
16
vendor/plugins/rack/lib/rack.rb
vendored
16
vendor/plugins/rack/lib/rack.rb
vendored
|
@ -3,10 +3,6 @@
|
|||
# Rack is freely distributable under the terms of an MIT-style license.
|
||||
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
path = File.expand_path(File.dirname(__FILE__))
|
||||
$:.unshift(path) unless $:.include?(path)
|
||||
|
||||
|
||||
# The Rack main module, serving as a namespace for all core Rack
|
||||
# modules and classes.
|
||||
#
|
||||
|
@ -15,7 +11,7 @@ $:.unshift(path) unless $:.include?(path)
|
|||
|
||||
module Rack
|
||||
# The Rack protocol version number implemented.
|
||||
VERSION = [1,0]
|
||||
VERSION = [1,1]
|
||||
|
||||
# Return the Rack protocol version as a dotted string.
|
||||
def self.version
|
||||
|
@ -24,7 +20,7 @@ module Rack
|
|||
|
||||
# Return the Rack release as a dotted string.
|
||||
def self.release
|
||||
"1.0"
|
||||
"1.1"
|
||||
end
|
||||
|
||||
autoload :Builder, "rack/builder"
|
||||
|
@ -32,8 +28,10 @@ module Rack
|
|||
autoload :Chunked, "rack/chunked"
|
||||
autoload :CommonLogger, "rack/commonlogger"
|
||||
autoload :ConditionalGet, "rack/conditionalget"
|
||||
autoload :Config, "rack/config"
|
||||
autoload :ContentLength, "rack/content_length"
|
||||
autoload :ContentType, "rack/content_type"
|
||||
autoload :ETag, "rack/etag"
|
||||
autoload :File, "rack/file"
|
||||
autoload :Deflater, "rack/deflater"
|
||||
autoload :Directory, "rack/directory"
|
||||
|
@ -42,10 +40,15 @@ module Rack
|
|||
autoload :Head, "rack/head"
|
||||
autoload :Lint, "rack/lint"
|
||||
autoload :Lock, "rack/lock"
|
||||
autoload :Logger, "rack/logger"
|
||||
autoload :MethodOverride, "rack/methodoverride"
|
||||
autoload :Mime, "rack/mime"
|
||||
autoload :NullLogger, "rack/nulllogger"
|
||||
autoload :Recursive, "rack/recursive"
|
||||
autoload :Reloader, "rack/reloader"
|
||||
autoload :Runtime, "rack/runtime"
|
||||
autoload :Sendfile, "rack/sendfile"
|
||||
autoload :Server, "rack/server"
|
||||
autoload :ShowExceptions, "rack/showexceptions"
|
||||
autoload :ShowStatus, "rack/showstatus"
|
||||
autoload :Static, "rack/static"
|
||||
|
@ -62,7 +65,6 @@ module Rack
|
|||
autoload :Basic, "rack/auth/basic"
|
||||
autoload :AbstractRequest, "rack/auth/abstract/request"
|
||||
autoload :AbstractHandler, "rack/auth/abstract/handler"
|
||||
autoload :OpenID, "rack/auth/openid"
|
||||
module Digest
|
||||
autoload :MD5, "rack/auth/digest/md5"
|
||||
autoload :Nonce, "rack/auth/digest/nonce"
|
||||
|
|
17
vendor/plugins/rack/lib/rack/builder.rb
vendored
17
vendor/plugins/rack/lib/rack/builder.rb
vendored
|
@ -24,6 +24,23 @@ module Rack
|
|||
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
||||
|
||||
class Builder
|
||||
def self.parse_file(config, opts = Server::Options.new)
|
||||
options = {}
|
||||
if config =~ /\.ru$/
|
||||
cfgfile = ::File.read(config)
|
||||
if cfgfile[/^#\\(.*)/] && opts
|
||||
options = opts.parse! $1.split(/\s+/)
|
||||
end
|
||||
cfgfile.sub!(/^__END__\n.*/, '')
|
||||
app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
|
||||
TOPLEVEL_BINDING, config
|
||||
else
|
||||
require config
|
||||
app = Object.const_get(::File.basename(config, '.rb').capitalize)
|
||||
end
|
||||
return app, options
|
||||
end
|
||||
|
||||
def initialize(&block)
|
||||
@ins = []
|
||||
instance_eval(&block) if block_given?
|
||||
|
|
29
vendor/plugins/rack/lib/rack/cascade.rb
vendored
29
vendor/plugins/rack/lib/rack/cascade.rb
vendored
|
@ -4,31 +4,36 @@ module Rack
|
|||
# status codes).
|
||||
|
||||
class Cascade
|
||||
NotFound = [404, {}, []]
|
||||
|
||||
attr_reader :apps
|
||||
|
||||
def initialize(apps, catch=404)
|
||||
@apps = apps
|
||||
@catch = [*catch]
|
||||
@apps = []; @has_app = {}
|
||||
apps.each { |app| add app }
|
||||
|
||||
@catch = {}
|
||||
[*catch].each { |status| @catch[status] = true }
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status = headers = body = nil
|
||||
raise ArgumentError, "empty cascade" if @apps.empty?
|
||||
@apps.each { |app|
|
||||
begin
|
||||
status, headers, body = app.call(env)
|
||||
break unless @catch.include?(status.to_i)
|
||||
end
|
||||
}
|
||||
[status, headers, body]
|
||||
result = NotFound
|
||||
|
||||
@apps.each do |app|
|
||||
result = app.call(env)
|
||||
break unless @catch.include?(result[0].to_i)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def add app
|
||||
@has_app[app] = true
|
||||
@apps << app
|
||||
end
|
||||
|
||||
def include? app
|
||||
@apps.include? app
|
||||
@has_app.include? app
|
||||
end
|
||||
|
||||
alias_method :<<, :add
|
||||
|
|
4
vendor/plugins/rack/lib/rack/chunked.rb
vendored
4
vendor/plugins/rack/lib/rack/chunked.rb
vendored
|
@ -19,7 +19,7 @@ module Rack
|
|||
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
|
||||
headers['Content-Length'] ||
|
||||
headers['Transfer-Encoding']
|
||||
[status, headers.to_hash, body]
|
||||
[status, headers, body]
|
||||
else
|
||||
dup.chunk(status, headers, body)
|
||||
end
|
||||
|
@ -29,7 +29,7 @@ module Rack
|
|||
@body = body
|
||||
headers.delete('Content-Length')
|
||||
headers['Transfer-Encoding'] = 'chunked'
|
||||
[status, headers.to_hash, self]
|
||||
[status, headers, self]
|
||||
end
|
||||
|
||||
def each
|
||||
|
|
74
vendor/plugins/rack/lib/rack/commonlogger.rb
vendored
74
vendor/plugins/rack/lib/rack/commonlogger.rb
vendored
|
@ -2,60 +2,48 @@ module Rack
|
|||
# Rack::CommonLogger forwards every request to an +app+ given, and
|
||||
# logs a line in the Apache common log format to the +logger+, or
|
||||
# rack.errors by default.
|
||||
|
||||
class CommonLogger
|
||||
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
|
||||
# lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
|
||||
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
|
||||
FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
|
||||
|
||||
def initialize(app, logger=nil)
|
||||
@app = app
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def call(env)
|
||||
dup._call(env)
|
||||
began_at = Time.now
|
||||
status, header, body = @app.call(env)
|
||||
header = Utils::HeaderHash.new(header)
|
||||
log(env, status, header, began_at)
|
||||
[status, header, body]
|
||||
end
|
||||
|
||||
def _call(env)
|
||||
@env = env
|
||||
@logger ||= self
|
||||
@time = Time.now
|
||||
@status, @header, @body = @app.call(env)
|
||||
[@status, @header, self]
|
||||
private
|
||||
|
||||
def log(env, status, header, began_at)
|
||||
now = Time.now
|
||||
length = extract_content_length(header)
|
||||
|
||||
logger = @logger || env['rack.errors']
|
||||
logger.write FORMAT % [
|
||||
env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
|
||||
env["REMOTE_USER"] || "-",
|
||||
now.strftime("%d/%b/%Y %H:%M:%S"),
|
||||
env["REQUEST_METHOD"],
|
||||
env["PATH_INFO"],
|
||||
env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
|
||||
env["HTTP_VERSION"],
|
||||
status.to_s[0..3],
|
||||
length,
|
||||
now - began_at ]
|
||||
end
|
||||
|
||||
def close
|
||||
@body.close if @body.respond_to? :close
|
||||
end
|
||||
|
||||
# By default, log to rack.errors.
|
||||
def <<(str)
|
||||
@env["rack.errors"].write(str)
|
||||
@env["rack.errors"].flush
|
||||
end
|
||||
|
||||
def each
|
||||
length = 0
|
||||
@body.each { |part|
|
||||
length += part.size
|
||||
yield part
|
||||
}
|
||||
|
||||
@now = Time.now
|
||||
|
||||
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
|
||||
# lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
|
||||
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
|
||||
@logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} %
|
||||
[
|
||||
@env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-",
|
||||
@env["REMOTE_USER"] || "-",
|
||||
@now.strftime("%d/%b/%Y %H:%M:%S"),
|
||||
@env["REQUEST_METHOD"],
|
||||
@env["PATH_INFO"],
|
||||
@env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"],
|
||||
@env["HTTP_VERSION"],
|
||||
@status.to_s[0..3],
|
||||
(length.zero? ? "-" : length.to_s),
|
||||
@now - @time
|
||||
]
|
||||
def extract_content_length(headers)
|
||||
value = headers['Content-Length'] or return '-'
|
||||
value.to_s == '0' ? '-' : value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
15
vendor/plugins/rack/lib/rack/config.rb
vendored
Normal file
15
vendor/plugins/rack/lib/rack/config.rb
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
module Rack
|
||||
# Rack::Config modifies the environment using the block given during
|
||||
# initialization.
|
||||
class Config
|
||||
def initialize(app, &block)
|
||||
@app = app
|
||||
@block = block
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@block.call(env)
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
2
vendor/plugins/rack/lib/rack/content_type.rb
vendored
2
vendor/plugins/rack/lib/rack/content_type.rb
vendored
|
@ -17,7 +17,7 @@ module Rack
|
|||
status, headers, body = @app.call(env)
|
||||
headers = Utils::HeaderHash.new(headers)
|
||||
headers['Content-Type'] ||= @content_type
|
||||
[status, headers.to_hash, body]
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
8
vendor/plugins/rack/lib/rack/directory.rb
vendored
8
vendor/plugins/rack/lib/rack/directory.rb
vendored
|
@ -71,7 +71,9 @@ table { width:100%%; }
|
|||
|
||||
body = "Forbidden\n"
|
||||
size = Rack::Utils.bytesize(body)
|
||||
return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
|
||||
return [403, {"Content-Type" => "text/plain",
|
||||
"Content-Length" => size.to_s,
|
||||
"X-Cascade" => "pass"}, [body]]
|
||||
end
|
||||
|
||||
def list_directory
|
||||
|
@ -123,7 +125,9 @@ table { width:100%%; }
|
|||
def entity_not_found
|
||||
body = "Entity not found: #{@path_info}\n"
|
||||
size = Rack::Utils.bytesize(body)
|
||||
return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
|
||||
return [404, {"Content-Type" => "text/plain",
|
||||
"Content-Length" => size.to_s,
|
||||
"X-Cascade" => "pass"}, [body]]
|
||||
end
|
||||
|
||||
def each
|
||||
|
|
23
vendor/plugins/rack/lib/rack/etag.rb
vendored
Normal file
23
vendor/plugins/rack/lib/rack/etag.rb
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
require 'digest/md5'
|
||||
|
||||
module Rack
|
||||
# Automatically sets the ETag header on all String bodies
|
||||
class ETag
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
|
||||
if !headers.has_key?('ETag')
|
||||
parts = []
|
||||
body.each { |part| parts << part.to_s }
|
||||
headers['ETag'] = %("#{Digest::MD5.hexdigest(parts.join(""))}")
|
||||
[status, headers, parts]
|
||||
else
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
6
vendor/plugins/rack/lib/rack/file.rb
vendored
6
vendor/plugins/rack/lib/rack/file.rb
vendored
|
@ -45,7 +45,8 @@ module Rack
|
|||
def forbidden
|
||||
body = "Forbidden\n"
|
||||
[403, {"Content-Type" => "text/plain",
|
||||
"Content-Length" => body.size.to_s},
|
||||
"Content-Length" => body.size.to_s,
|
||||
"X-Cascade" => "pass"},
|
||||
[body]]
|
||||
end
|
||||
|
||||
|
@ -73,7 +74,8 @@ module Rack
|
|||
def not_found
|
||||
body = "File not found: #{@path_info}\n"
|
||||
[404, {"Content-Type" => "text/plain",
|
||||
"Content-Length" => body.size.to_s},
|
||||
"Content-Length" => body.size.to_s,
|
||||
"X-Cascade" => "pass"},
|
||||
[body]]
|
||||
end
|
||||
|
||||
|
|
19
vendor/plugins/rack/lib/rack/handler.rb
vendored
19
vendor/plugins/rack/lib/rack/handler.rb
vendored
|
@ -22,6 +22,25 @@ module Rack
|
|||
end
|
||||
end
|
||||
|
||||
def self.default(options = {})
|
||||
# Guess.
|
||||
if ENV.include?("PHP_FCGI_CHILDREN")
|
||||
# We already speak FastCGI
|
||||
options.delete :File
|
||||
options.delete :Port
|
||||
|
||||
Rack::Handler::FastCGI
|
||||
elsif ENV.include?("REQUEST_METHOD")
|
||||
Rack::Handler::CGI
|
||||
else
|
||||
begin
|
||||
Rack::Handler::Mongrel
|
||||
rescue LoadError => e
|
||||
Rack::Handler::WEBrick
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Transforms server-name constants to their canonical form as filenames,
|
||||
# then tries to require them but silences the LoadError if not found
|
||||
#
|
||||
|
|
2
vendor/plugins/rack/lib/rack/handler/cgi.rb
vendored
2
vendor/plugins/rack/lib/rack/handler/cgi.rb
vendored
|
@ -15,7 +15,7 @@ module Rack
|
|||
|
||||
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
||||
|
||||
env.update({"rack.version" => [1,0],
|
||||
env.update({"rack.version" => [1,1],
|
||||
"rack.input" => $stdin,
|
||||
"rack.errors" => $stderr,
|
||||
|
||||
|
|
|
@ -33,10 +33,10 @@ module Rack
|
|||
env.delete "HTTP_CONTENT_LENGTH"
|
||||
|
||||
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
||||
|
||||
|
||||
rack_input = RewindableInput.new(request.in)
|
||||
|
||||
env.update({"rack.version" => [1,0],
|
||||
env.update({"rack.version" => [1,1],
|
||||
"rack.input" => rack_input,
|
||||
"rack.errors" => request.err,
|
||||
|
||||
|
@ -50,7 +50,6 @@ module Rack
|
|||
env["QUERY_STRING"] ||= ""
|
||||
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
||||
env["REQUEST_PATH"] ||= "/"
|
||||
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
|
||||
env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
|
||||
env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
|
||||
|
||||
|
|
5
vendor/plugins/rack/lib/rack/handler/lsws.rb
vendored
5
vendor/plugins/rack/lib/rack/handler/lsws.rb
vendored
|
@ -1,5 +1,6 @@
|
|||
require 'lsapi'
|
||||
require 'rack/content_length'
|
||||
require 'rack/rewindable_input'
|
||||
|
||||
module Rack
|
||||
module Handler
|
||||
|
@ -19,7 +20,7 @@ module Rack
|
|||
rack_input = RewindableInput.new($stdin.read.to_s)
|
||||
|
||||
env.update(
|
||||
"rack.version" => [1,0],
|
||||
"rack.version" => [1,1],
|
||||
"rack.input" => rack_input,
|
||||
"rack.errors" => $stderr,
|
||||
"rack.multithread" => false,
|
||||
|
@ -38,6 +39,8 @@ module Rack
|
|||
ensure
|
||||
body.close if body.respond_to? :close
|
||||
end
|
||||
ensure
|
||||
rack_input.close
|
||||
end
|
||||
def self.send_headers(status, headers)
|
||||
print "Status: #{status}\r\n"
|
||||
|
|
13
vendor/plugins/rack/lib/rack/handler/mongrel.rb
vendored
13
vendor/plugins/rack/lib/rack/handler/mongrel.rb
vendored
|
@ -7,10 +7,14 @@ module Rack
|
|||
module Handler
|
||||
class Mongrel < ::Mongrel::HttpHandler
|
||||
def self.run(app, options={})
|
||||
server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
|
||||
options[:Port] || 8080)
|
||||
server = ::Mongrel::HttpServer.new(
|
||||
options[:Host] || '0.0.0.0',
|
||||
options[:Port] || 8080,
|
||||
options[:num_processors] || 950,
|
||||
options[:throttle] || 0,
|
||||
options[:timeout] || 60)
|
||||
# Acts like Rack::URLMap, utilizing Mongrel's own path finding methods.
|
||||
# Use is similar to #run, replacing the app argument with a hash of
|
||||
# Use is similar to #run, replacing the app argument with a hash of
|
||||
# { path=>app, ... } or an instance of Rack::URLMap.
|
||||
if options[:map]
|
||||
if app.is_a? Hash
|
||||
|
@ -48,7 +52,7 @@ module Rack
|
|||
rack_input = request.body || StringIO.new('')
|
||||
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
|
||||
|
||||
env.update({"rack.version" => [1,0],
|
||||
env.update({"rack.version" => [1,1],
|
||||
"rack.input" => rack_input,
|
||||
"rack.errors" => $stderr,
|
||||
|
||||
|
@ -59,7 +63,6 @@ module Rack
|
|||
"rack.url_scheme" => "http",
|
||||
})
|
||||
env["QUERY_STRING"] ||= ""
|
||||
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
|
||||
|
||||
status, headers, body = @app.call(env)
|
||||
|
||||
|
|
8
vendor/plugins/rack/lib/rack/handler/scgi.rb
vendored
8
vendor/plugins/rack/lib/rack/handler/scgi.rb
vendored
|
@ -7,14 +7,14 @@ module Rack
|
|||
module Handler
|
||||
class SCGI < ::SCGI::Processor
|
||||
attr_accessor :app
|
||||
|
||||
|
||||
def self.run(app, options=nil)
|
||||
new(options.merge(:app=>app,
|
||||
:host=>options[:Host],
|
||||
:port=>options[:Port],
|
||||
:socket=>options[:Socket])).listen
|
||||
end
|
||||
|
||||
|
||||
def initialize(settings = {})
|
||||
@app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
|
||||
@log = Object.new
|
||||
|
@ -22,7 +22,7 @@ module Rack
|
|||
def @log.error(*args); end
|
||||
super(settings)
|
||||
end
|
||||
|
||||
|
||||
def process_request(request, input_body, socket)
|
||||
env = {}.replace(request)
|
||||
env.delete "HTTP_CONTENT_TYPE"
|
||||
|
@ -36,7 +36,7 @@ module Rack
|
|||
rack_input = StringIO.new(input_body)
|
||||
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
|
||||
|
||||
env.update({"rack.version" => [1,0],
|
||||
env.update({"rack.version" => [1,1],
|
||||
"rack.input" => rack_input,
|
||||
"rack.errors" => $stderr,
|
||||
"rack.multithread" => true,
|
||||
|
|
|
@ -27,7 +27,7 @@ module Rack
|
|||
rack_input = StringIO.new(req.body.to_s)
|
||||
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
|
||||
|
||||
env.update({"rack.version" => [1,0],
|
||||
env.update({"rack.version" => [1,1],
|
||||
"rack.input" => rack_input,
|
||||
"rack.errors" => $stderr,
|
||||
|
||||
|
@ -41,9 +41,7 @@ module Rack
|
|||
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
||||
env["QUERY_STRING"] ||= ""
|
||||
env["REQUEST_PATH"] ||= "/"
|
||||
if env["PATH_INFO"] == ""
|
||||
env.delete "PATH_INFO"
|
||||
else
|
||||
unless env["PATH_INFO"] == ""
|
||||
path, n = req.request_uri.path, env["SCRIPT_NAME"].length
|
||||
env["PATH_INFO"] = path[n, path.length-n]
|
||||
end
|
||||
|
|
59
vendor/plugins/rack/lib/rack/lint.rb
vendored
59
vendor/plugins/rack/lib/rack/lint.rb
vendored
|
@ -61,7 +61,7 @@ module Rack
|
|||
## subclassing allowed) that includes CGI-like headers.
|
||||
## The application is free to modify the environment.
|
||||
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
|
||||
env.instance_of? Hash
|
||||
env.kind_of? Hash
|
||||
}
|
||||
|
||||
##
|
||||
|
@ -111,7 +111,7 @@ module Rack
|
|||
## In addition to this, the Rack environment must include these
|
||||
## Rack-specific variables:
|
||||
|
||||
## <tt>rack.version</tt>:: The Array [1,0], representing this version of Rack.
|
||||
## <tt>rack.version</tt>:: The Array [1,1], representing this version of Rack.
|
||||
## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
|
||||
## <tt>rack.input</tt>:: See below, the input stream.
|
||||
## <tt>rack.errors</tt>:: See below, the error stream.
|
||||
|
@ -148,6 +148,35 @@ module Rack
|
|||
}
|
||||
end
|
||||
|
||||
## <tt>rack.logger</tt>:: A common object interface for logging messages.
|
||||
## The object must implement:
|
||||
if logger = env['rack.logger']
|
||||
## info(message, &block)
|
||||
assert("logger #{logger.inspect} must respond to info") {
|
||||
logger.respond_to?(:info)
|
||||
}
|
||||
|
||||
## debug(message, &block)
|
||||
assert("logger #{logger.inspect} must respond to debug") {
|
||||
logger.respond_to?(:debug)
|
||||
}
|
||||
|
||||
## warn(message, &block)
|
||||
assert("logger #{logger.inspect} must respond to warn") {
|
||||
logger.respond_to?(:warn)
|
||||
}
|
||||
|
||||
## error(message, &block)
|
||||
assert("logger #{logger.inspect} must respond to error") {
|
||||
logger.respond_to?(:error)
|
||||
}
|
||||
|
||||
## fatal(message, &block)
|
||||
assert("logger #{logger.inspect} must respond to fatal") {
|
||||
logger.respond_to?(:fatal)
|
||||
}
|
||||
end
|
||||
|
||||
## The server or the application can store their own data in the
|
||||
## environment, too. The keys must contain at least one dot,
|
||||
## and should be prefixed uniquely. The prefix <tt>rack.</tt>
|
||||
|
@ -175,7 +204,7 @@ module Rack
|
|||
env.each { |key, value|
|
||||
next if key.include? "." # Skip extensions
|
||||
assert("env variable #{key} has non-string value #{value.inspect}") {
|
||||
value.instance_of? String
|
||||
value.kind_of? String
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +213,7 @@ module Rack
|
|||
|
||||
## * <tt>rack.version</tt> must be an array of Integers.
|
||||
assert("rack.version must be an Array, was #{env["rack.version"].class}") {
|
||||
env["rack.version"].instance_of? Array
|
||||
env["rack.version"].kind_of? Array
|
||||
}
|
||||
## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
|
||||
assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") {
|
||||
|
@ -243,7 +272,7 @@ module Rack
|
|||
assert("rack.input #{input} is not opened in binary mode") {
|
||||
input.binmode?
|
||||
} if input.respond_to?(:binmode?)
|
||||
|
||||
|
||||
## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
|
||||
[:gets, :each, :read, :rewind].each { |method|
|
||||
assert("rack.input #{input} does not respond to ##{method}") {
|
||||
|
@ -269,7 +298,7 @@ module Rack
|
|||
assert("rack.input#gets called with arguments") { args.size == 0 }
|
||||
v = @input.gets
|
||||
assert("rack.input#gets didn't return a String") {
|
||||
v.nil? or v.instance_of? String
|
||||
v.nil? or v.kind_of? String
|
||||
}
|
||||
v
|
||||
end
|
||||
|
@ -300,18 +329,18 @@ module Rack
|
|||
args[1].kind_of?(String)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
v = @input.read(*args)
|
||||
|
||||
|
||||
assert("rack.input#read didn't return nil or a String") {
|
||||
v.nil? or v.instance_of? String
|
||||
v.nil? or v.kind_of? String
|
||||
}
|
||||
if args[0].nil?
|
||||
assert("rack.input#read(nil) returned nil on EOF") {
|
||||
!v.nil?
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
v
|
||||
end
|
||||
|
||||
|
@ -320,12 +349,12 @@ module Rack
|
|||
assert("rack.input#each called with arguments") { args.size == 0 }
|
||||
@input.each { |line|
|
||||
assert("rack.input#each didn't yield a String") {
|
||||
line.instance_of? String
|
||||
line.kind_of? String
|
||||
}
|
||||
yield line
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
## * +rewind+ must be called without arguments. It rewinds the input
|
||||
## stream back to the beginning. It must not raise Errno::ESPIPE:
|
||||
## that is, it may not be a pipe or a socket. Therefore, handler
|
||||
|
@ -373,7 +402,7 @@ module Rack
|
|||
|
||||
## * +write+ must be called with a single argument that is a String.
|
||||
def write(str)
|
||||
assert("rack.errors#write not called with a String") { str.instance_of? String }
|
||||
assert("rack.errors#write not called with a String") { str.kind_of? String }
|
||||
@error.write str
|
||||
end
|
||||
|
||||
|
@ -407,7 +436,7 @@ module Rack
|
|||
header.each { |key, value|
|
||||
## The header keys must be Strings.
|
||||
assert("header key must be a string, was #{key.class}") {
|
||||
key.instance_of? String
|
||||
key.kind_of? String
|
||||
}
|
||||
## The header must not contain a +Status+ key,
|
||||
assert("header must not contain Status") { key.downcase != "status" }
|
||||
|
@ -499,7 +528,7 @@ module Rack
|
|||
@body.each { |part|
|
||||
## and must only yield String values.
|
||||
assert("Body yielded non-string value #{part.inspect}") {
|
||||
part.instance_of? String
|
||||
part.kind_of? String
|
||||
}
|
||||
yield part
|
||||
}
|
||||
|
|
20
vendor/plugins/rack/lib/rack/logger.rb
vendored
Normal file
20
vendor/plugins/rack/lib/rack/logger.rb
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'logger'
|
||||
|
||||
module Rack
|
||||
# Sets up rack.logger to write to rack.errors stream
|
||||
class Logger
|
||||
def initialize(app, level = ::Logger::INFO)
|
||||
@app, @level = app, level
|
||||
end
|
||||
|
||||
def call(env)
|
||||
logger = ::Logger.new(env['rack.errors'])
|
||||
logger.level = @level
|
||||
|
||||
env['rack.logger'] = logger
|
||||
@app.call(env)
|
||||
ensure
|
||||
logger.close
|
||||
end
|
||||
end
|
||||
end
|
4
vendor/plugins/rack/lib/rack/mime.rb
vendored
4
vendor/plugins/rack/lib/rack/mime.rb
vendored
|
@ -14,7 +14,7 @@ module Rack
|
|||
# Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
|
||||
|
||||
def mime_type(ext, fallback='application/octet-stream')
|
||||
MIME_TYPES.fetch(ext, fallback)
|
||||
MIME_TYPES.fetch(ext.to_s.downcase, fallback)
|
||||
end
|
||||
module_function :mime_type
|
||||
|
||||
|
@ -105,6 +105,7 @@ module Rack
|
|||
".m3u" => "audio/x-mpegurl",
|
||||
".m4v" => "video/mp4",
|
||||
".man" => "text/troff",
|
||||
".manifest"=> "text/cache-manifest",
|
||||
".mathml" => "application/mathml+xml",
|
||||
".mbox" => "application/mbox",
|
||||
".mdoc" => "text/troff",
|
||||
|
@ -126,6 +127,7 @@ module Rack
|
|||
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
|
||||
".odt" => "application/vnd.oasis.opendocument.text",
|
||||
".ogg" => "application/ogg",
|
||||
".ogv" => "video/ogg",
|
||||
".p" => "text/x-pascal",
|
||||
".pas" => "text/x-pascal",
|
||||
".pbm" => "image/x-portable-bitmap",
|
||||
|
|
34
vendor/plugins/rack/lib/rack/mock.rb
vendored
34
vendor/plugins/rack/lib/rack/mock.rb
vendored
|
@ -40,7 +40,7 @@ module Rack
|
|||
end
|
||||
|
||||
DEFAULT_ENV = {
|
||||
"rack.version" => [1,0],
|
||||
"rack.version" => [1,1],
|
||||
"rack.input" => StringIO.new,
|
||||
"rack.errors" => StringIO.new,
|
||||
"rack.multithread" => true,
|
||||
|
@ -73,14 +73,17 @@ module Rack
|
|||
# Return the Rack environment used for a request to +uri+.
|
||||
def self.env_for(uri="", opts={})
|
||||
uri = URI(uri)
|
||||
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
||||
|
||||
env = DEFAULT_ENV.dup
|
||||
|
||||
env["REQUEST_METHOD"] = opts[:method] || "GET"
|
||||
env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
|
||||
env["SERVER_NAME"] = uri.host || "example.org"
|
||||
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
|
||||
env["QUERY_STRING"] = uri.query.to_s
|
||||
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
|
||||
env["rack.url_scheme"] = uri.scheme || "http"
|
||||
env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
|
||||
|
||||
env["SCRIPT_NAME"] = opts[:script_name] || ""
|
||||
|
||||
|
@ -90,7 +93,30 @@ module Rack
|
|||
env["rack.errors"] = StringIO.new
|
||||
end
|
||||
|
||||
opts[:input] ||= ""
|
||||
if params = opts[:params]
|
||||
if env["REQUEST_METHOD"] == "GET"
|
||||
params = Utils.parse_nested_query(params) if params.is_a?(String)
|
||||
params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
|
||||
env["QUERY_STRING"] = Utils.build_nested_query(params)
|
||||
elsif !opts.has_key?(:input)
|
||||
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||
if params.is_a?(Hash)
|
||||
if data = Utils::Multipart.build_multipart(params)
|
||||
opts[:input] = data
|
||||
opts["CONTENT_LENGTH"] ||= data.length.to_s
|
||||
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
|
||||
else
|
||||
opts[:input] = Utils.build_nested_query(params)
|
||||
end
|
||||
else
|
||||
opts[:input] = params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
empty_str = ""
|
||||
empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
|
||||
opts[:input] ||= empty_str
|
||||
if String === opts[:input]
|
||||
rack_input = StringIO.new(opts[:input])
|
||||
else
|
||||
|
@ -128,7 +154,7 @@ module Rack
|
|||
@body = ""
|
||||
body.each { |part| @body << part }
|
||||
|
||||
@errors = errors.string
|
||||
@errors = errors.string if errors.respond_to?(:string)
|
||||
end
|
||||
|
||||
# Status
|
||||
|
|
18
vendor/plugins/rack/lib/rack/nulllogger.rb
vendored
Normal file
18
vendor/plugins/rack/lib/rack/nulllogger.rb
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Rack
|
||||
class NullLogger
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env['rack.logger'] = self
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def info(progname = nil, &block); end
|
||||
def debug(progname = nil, &block); end
|
||||
def warn(progname = nil, &block); end
|
||||
def error(progname = nil, &block); end
|
||||
def fatal(progname = nil, &block); end
|
||||
end
|
||||
end
|
5
vendor/plugins/rack/lib/rack/reloader.rb
vendored
5
vendor/plugins/rack/lib/rack/reloader.rb
vendored
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
|
||||
# All files in this distribution are subject to the terms of the Ruby license.
|
||||
# Rack::Reloader is subject to the terms of an MIT-style license.
|
||||
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
require 'pathname'
|
||||
|
||||
|
@ -92,6 +93,8 @@ module Rack
|
|||
found, stat = safe_stat(path)
|
||||
return ::File.expand_path(found), stat if found
|
||||
end
|
||||
|
||||
return false, false
|
||||
end
|
||||
|
||||
def safe_stat(file)
|
||||
|
|
55
vendor/plugins/rack/lib/rack/request.rb
vendored
55
vendor/plugins/rack/lib/rack/request.rb
vendored
|
@ -32,6 +32,7 @@ module Rack
|
|||
def content_type; @env['CONTENT_TYPE'] end
|
||||
def session; @env['rack.session'] ||= {} end
|
||||
def session_options; @env['rack.session.options'] ||= {} end
|
||||
def logger; @env['rack.logger'] end
|
||||
|
||||
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
||||
# without any media type parameters. e.g., when CONTENT_TYPE is
|
||||
|
@ -63,9 +64,17 @@ module Rack
|
|||
media_type_params['charset']
|
||||
end
|
||||
|
||||
def host_with_port
|
||||
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
|
||||
forwarded.split(/,\s?/).last
|
||||
else
|
||||
@env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}"
|
||||
end
|
||||
end
|
||||
|
||||
def host
|
||||
# Remove port number.
|
||||
(@env["HTTP_HOST"] || @env["SERVER_NAME"]).to_s.gsub(/:\d+\z/, '')
|
||||
host_with_port.to_s.gsub(/:\d+\z/, '')
|
||||
end
|
||||
|
||||
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
|
||||
|
@ -81,7 +90,6 @@ module Rack
|
|||
# one of the media types presents in this list will not be eligible
|
||||
# for form-data / param parsing.
|
||||
FORM_DATA_MEDIA_TYPES = [
|
||||
nil,
|
||||
'application/x-www-form-urlencoded',
|
||||
'multipart/form-data'
|
||||
]
|
||||
|
@ -92,15 +100,20 @@ module Rack
|
|||
PARSEABLE_DATA_MEDIA_TYPES = [
|
||||
'multipart/related',
|
||||
'multipart/mixed'
|
||||
]
|
||||
]
|
||||
|
||||
# Determine whether the request body contains form-data by checking
|
||||
# the request media_type against registered form-data media-types:
|
||||
# "application/x-www-form-urlencoded" and "multipart/form-data". The
|
||||
# the request Content-Type for one of the media-types:
|
||||
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
||||
# list of form-data media types can be modified through the
|
||||
# +FORM_DATA_MEDIA_TYPES+ array.
|
||||
#
|
||||
# A request body is also assumed to contain form-data when no
|
||||
# Content-Type header is provided and the request_method is POST.
|
||||
def form_data?
|
||||
FORM_DATA_MEDIA_TYPES.include?(media_type)
|
||||
type = media_type
|
||||
meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
|
||||
(meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
|
||||
end
|
||||
|
||||
# Determine whether the request body contains data by checking
|
||||
|
@ -115,8 +128,7 @@ module Rack
|
|||
@env["rack.request.query_hash"]
|
||||
else
|
||||
@env["rack.request.query_string"] = query_string
|
||||
@env["rack.request.query_hash"] =
|
||||
Utils.parse_nested_query(query_string)
|
||||
@env["rack.request.query_hash"] = parse_query(query_string)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -125,19 +137,20 @@ module Rack
|
|||
# This method support both application/x-www-form-urlencoded and
|
||||
# multipart/form-data.
|
||||
def POST
|
||||
if @env["rack.request.form_input"].eql? @env["rack.input"]
|
||||
if @env["rack.input"].nil?
|
||||
raise "Missing rack.input"
|
||||
elsif @env["rack.request.form_input"].eql? @env["rack.input"]
|
||||
@env["rack.request.form_hash"]
|
||||
elsif form_data? || parseable_data?
|
||||
@env["rack.request.form_input"] = @env["rack.input"]
|
||||
unless @env["rack.request.form_hash"] =
|
||||
Utils::Multipart.parse_multipart(env)
|
||||
unless @env["rack.request.form_hash"] = parse_multipart(env)
|
||||
form_vars = @env["rack.input"].read
|
||||
|
||||
# Fix for Safari Ajax postings that always append \0
|
||||
form_vars.sub!(/\0\z/, '')
|
||||
|
||||
@env["rack.request.form_vars"] = form_vars
|
||||
@env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars)
|
||||
@env["rack.request.form_hash"] = parse_query(form_vars)
|
||||
|
||||
@env["rack.input"].rewind
|
||||
end
|
||||
|
@ -149,7 +162,7 @@ module Rack
|
|||
|
||||
# The union of GET and POST data.
|
||||
def params
|
||||
self.put? ? self.GET : self.GET.update(self.POST)
|
||||
self.GET.update(self.POST)
|
||||
rescue EOFError => e
|
||||
self.GET
|
||||
end
|
||||
|
@ -175,6 +188,9 @@ module Rack
|
|||
end
|
||||
alias referrer referer
|
||||
|
||||
def user_agent
|
||||
@env['HTTP_USER_AGENT']
|
||||
end
|
||||
|
||||
def cookies
|
||||
return {} unless @env["HTTP_COOKIE"]
|
||||
|
@ -214,11 +230,11 @@ module Rack
|
|||
|
||||
url
|
||||
end
|
||||
|
||||
|
||||
def path
|
||||
script_name + path_info
|
||||
end
|
||||
|
||||
|
||||
def fullpath
|
||||
query_string.empty? ? path : "#{path}?#{query_string}"
|
||||
end
|
||||
|
@ -242,5 +258,14 @@ module Rack
|
|||
@env['REMOTE_ADDR']
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def parse_query(qs)
|
||||
Utils.parse_nested_query(qs)
|
||||
end
|
||||
|
||||
def parse_multipart(env)
|
||||
Utils::Multipart.parse_multipart(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
44
vendor/plugins/rack/lib/rack/response.rb
vendored
44
vendor/plugins/rack/lib/rack/response.rb
vendored
|
@ -19,7 +19,7 @@ module Rack
|
|||
attr_accessor :length
|
||||
|
||||
def initialize(body=[], status=200, header={}, &block)
|
||||
@status = status
|
||||
@status = status.to_i
|
||||
@header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.
|
||||
merge(header))
|
||||
|
||||
|
@ -54,45 +54,11 @@ module Rack
|
|||
end
|
||||
|
||||
def set_cookie(key, value)
|
||||
case value
|
||||
when Hash
|
||||
domain = "; domain=" + value[:domain] if value[:domain]
|
||||
path = "; path=" + value[:path] if value[:path]
|
||||
# According to RFC 2109, we need dashes here.
|
||||
# N.B.: cgi.rb uses spaces...
|
||||
expires = "; expires=" + value[:expires].clone.gmtime.
|
||||
strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
|
||||
secure = "; secure" if value[:secure]
|
||||
httponly = "; HttpOnly" if value[:httponly]
|
||||
value = value[:value]
|
||||
end
|
||||
value = [value] unless Array === value
|
||||
cookie = Utils.escape(key) + "=" +
|
||||
value.map { |v| Utils.escape v }.join("&") +
|
||||
"#{domain}#{path}#{expires}#{secure}#{httponly}"
|
||||
|
||||
case self["Set-Cookie"]
|
||||
when Array
|
||||
self["Set-Cookie"] << cookie
|
||||
when String
|
||||
self["Set-Cookie"] = [self["Set-Cookie"], cookie]
|
||||
when nil
|
||||
self["Set-Cookie"] = cookie
|
||||
end
|
||||
Utils.set_cookie_header!(header, key, value)
|
||||
end
|
||||
|
||||
def delete_cookie(key, value={})
|
||||
unless Array === self["Set-Cookie"]
|
||||
self["Set-Cookie"] = [self["Set-Cookie"]].compact
|
||||
end
|
||||
|
||||
self["Set-Cookie"].reject! { |cookie|
|
||||
cookie =~ /\A#{Utils.escape(key)}=/
|
||||
}
|
||||
|
||||
set_cookie(key,
|
||||
{:value => '', :path => nil, :domain => nil,
|
||||
:expires => Time.at(0) }.merge(value))
|
||||
Utils.delete_cookie_header!(header, key, value)
|
||||
end
|
||||
|
||||
def redirect(target, status=302)
|
||||
|
@ -105,9 +71,9 @@ module Rack
|
|||
|
||||
if [204, 304].include?(status.to_i)
|
||||
header.delete "Content-Type"
|
||||
[status.to_i, header.to_hash, []]
|
||||
[status.to_i, header, []]
|
||||
else
|
||||
[status.to_i, header.to_hash, self]
|
||||
[status.to_i, header, self]
|
||||
end
|
||||
end
|
||||
alias to_a finish # For *response
|
||||
|
|
20
vendor/plugins/rack/lib/rack/rewindable_input.rb
vendored
20
vendor/plugins/rack/lib/rack/rewindable_input.rb
vendored
|
@ -16,27 +16,27 @@ module Rack
|
|||
@rewindable_io = nil
|
||||
@unlinked = false
|
||||
end
|
||||
|
||||
|
||||
def gets
|
||||
make_rewindable unless @rewindable_io
|
||||
@rewindable_io.gets
|
||||
end
|
||||
|
||||
|
||||
def read(*args)
|
||||
make_rewindable unless @rewindable_io
|
||||
@rewindable_io.read(*args)
|
||||
end
|
||||
|
||||
|
||||
def each(&block)
|
||||
make_rewindable unless @rewindable_io
|
||||
@rewindable_io.each(&block)
|
||||
end
|
||||
|
||||
|
||||
def rewind
|
||||
make_rewindable unless @rewindable_io
|
||||
@rewindable_io.rewind
|
||||
end
|
||||
|
||||
|
||||
# Closes this RewindableInput object without closing the originally
|
||||
# wrapped IO oject. Cleans up any temporary resources that this RewindableInput
|
||||
# has created.
|
||||
|
@ -52,9 +52,9 @@ module Rack
|
|||
@rewindable_io = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
# Ruby's Tempfile class has a bug. Subclass it and fix it.
|
||||
class Tempfile < ::Tempfile
|
||||
def _close
|
||||
|
@ -72,11 +72,13 @@ module Rack
|
|||
# access it because we have the file handle open.
|
||||
@rewindable_io = Tempfile.new('RackRewindableInput')
|
||||
@rewindable_io.chmod(0000)
|
||||
@rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
|
||||
@rewindable_io.binmode
|
||||
if filesystem_has_posix_semantics? && !tempfile_unlink_contains_bug?
|
||||
@rewindable_io.unlink
|
||||
@unlinked = true
|
||||
end
|
||||
|
||||
|
||||
buffer = ""
|
||||
while @io.read(1024 * 4, buffer)
|
||||
entire_buffer_written_out = false
|
||||
|
@ -90,7 +92,7 @@ module Rack
|
|||
end
|
||||
@rewindable_io.rewind
|
||||
end
|
||||
|
||||
|
||||
def filesystem_has_posix_semantics?
|
||||
RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
|
||||
end
|
||||
|
|
27
vendor/plugins/rack/lib/rack/runtime.rb
vendored
Normal file
27
vendor/plugins/rack/lib/rack/runtime.rb
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
module Rack
|
||||
# Sets an "X-Runtime" response header, indicating the response
|
||||
# time of the request, in seconds
|
||||
#
|
||||
# You can put it right before the application to see the processing
|
||||
# time, or before all the other middlewares to include time for them,
|
||||
# too.
|
||||
class Runtime
|
||||
def initialize(app, name = nil)
|
||||
@app = app
|
||||
@header_name = "X-Runtime"
|
||||
@header_name << "-#{name}" if name
|
||||
end
|
||||
|
||||
def call(env)
|
||||
start_time = Time.now
|
||||
status, headers, body = @app.call(env)
|
||||
request_time = Time.now - start_time
|
||||
|
||||
if !headers.has_key?(@header_name)
|
||||
headers[@header_name] = "%0.6f" % request_time
|
||||
end
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
142
vendor/plugins/rack/lib/rack/sendfile.rb
vendored
Normal file
142
vendor/plugins/rack/lib/rack/sendfile.rb
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
require 'rack/file'
|
||||
|
||||
module Rack
|
||||
class File #:nodoc:
|
||||
alias :to_path :path
|
||||
end
|
||||
|
||||
# = Sendfile
|
||||
#
|
||||
# The Sendfile middleware intercepts responses whose body is being
|
||||
# served from a file and replaces it with a server specific X-Sendfile
|
||||
# header. The web server is then responsible for writing the file contents
|
||||
# to the client. This can dramatically reduce the amount of work required
|
||||
# by the Ruby backend and takes advantage of the web servers optimized file
|
||||
# delivery code.
|
||||
#
|
||||
# In order to take advantage of this middleware, the response body must
|
||||
# respond to +to_path+ and the request must include an X-Sendfile-Type
|
||||
# header. Rack::File and other components implement +to_path+ so there's
|
||||
# rarely anything you need to do in your application. The X-Sendfile-Type
|
||||
# header is typically set in your web servers configuration. The following
|
||||
# sections attempt to document
|
||||
#
|
||||
# === Nginx
|
||||
#
|
||||
# Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
|
||||
# but requires parts of the filesystem to be mapped into a private URL
|
||||
# hierarachy.
|
||||
#
|
||||
# The following example shows the Nginx configuration required to create
|
||||
# a private "/files/" area, enable X-Accel-Redirect, and pass the special
|
||||
# X-Sendfile-Type and X-Accel-Mapping headers to the backend:
|
||||
#
|
||||
# location /files/ {
|
||||
# internal;
|
||||
# alias /var/www/;
|
||||
# }
|
||||
#
|
||||
# location / {
|
||||
# proxy_redirect false;
|
||||
#
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
#
|
||||
# proxy_set_header X-Sendfile-Type X-Accel-Redirect
|
||||
# proxy_set_header X-Accel-Mapping /files/=/var/www/;
|
||||
#
|
||||
# proxy_pass http://127.0.0.1:8080/;
|
||||
# }
|
||||
#
|
||||
# Note that the X-Sendfile-Type header must be set exactly as shown above. The
|
||||
# X-Accel-Mapping header should specify the name of the private URL pattern,
|
||||
# followed by an equals sign (=), followed by the location on the file system
|
||||
# that it maps to. The middleware performs a simple substitution on the
|
||||
# resulting path.
|
||||
#
|
||||
# See Also: http://wiki.codemongers.com/NginxXSendfile
|
||||
#
|
||||
# === lighttpd
|
||||
#
|
||||
# Lighttpd has supported some variation of the X-Sendfile header for some
|
||||
# time, although only recent version support X-Sendfile in a reverse proxy
|
||||
# configuration.
|
||||
#
|
||||
# $HTTP["host"] == "example.com" {
|
||||
# proxy-core.protocol = "http"
|
||||
# proxy-core.balancer = "round-robin"
|
||||
# proxy-core.backends = (
|
||||
# "127.0.0.1:8000",
|
||||
# "127.0.0.1:8001",
|
||||
# ...
|
||||
# )
|
||||
#
|
||||
# proxy-core.allow-x-sendfile = "enable"
|
||||
# proxy-core.rewrite-request = (
|
||||
# "X-Sendfile-Type" => (".*" => "X-Sendfile")
|
||||
# )
|
||||
# }
|
||||
#
|
||||
# See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore
|
||||
#
|
||||
# === Apache
|
||||
#
|
||||
# X-Sendfile is supported under Apache 2.x using a separate module:
|
||||
#
|
||||
# http://tn123.ath.cx/mod_xsendfile/
|
||||
#
|
||||
# Once the module is compiled and installed, you can enable it using
|
||||
# XSendFile config directive:
|
||||
#
|
||||
# RequestHeader Set X-Sendfile-Type X-Sendfile
|
||||
# ProxyPassReverse / http://localhost:8001/
|
||||
# XSendFile on
|
||||
|
||||
class Sendfile
|
||||
F = ::File
|
||||
|
||||
def initialize(app, variation=nil)
|
||||
@app = app
|
||||
@variation = variation
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
if body.respond_to?(:to_path)
|
||||
case type = variation(env)
|
||||
when 'X-Accel-Redirect'
|
||||
path = F.expand_path(body.to_path)
|
||||
if url = map_accel_path(env, path)
|
||||
headers[type] = url
|
||||
body = []
|
||||
else
|
||||
env['rack.errors'] << "X-Accel-Mapping header missing"
|
||||
end
|
||||
when 'X-Sendfile', 'X-Lighttpd-Send-File'
|
||||
path = F.expand_path(body.to_path)
|
||||
headers[type] = path
|
||||
body = []
|
||||
when '', nil
|
||||
else
|
||||
env['rack.errors'] << "Unknown x-sendfile variation: '#{variation}'.\n"
|
||||
end
|
||||
end
|
||||
[status, headers, body]
|
||||
end
|
||||
|
||||
private
|
||||
def variation(env)
|
||||
@variation ||
|
||||
env['sendfile.type'] ||
|
||||
env['HTTP_X_SENDFILE_TYPE']
|
||||
end
|
||||
|
||||
def map_accel_path(env, file)
|
||||
if mapping = env['HTTP_X_ACCEL_MAPPING']
|
||||
internal, external = mapping.split('=', 2).map{ |p| p.strip }
|
||||
file.sub(/^#{internal}/i, external)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
212
vendor/plugins/rack/lib/rack/server.rb
vendored
Normal file
212
vendor/plugins/rack/lib/rack/server.rb
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
require 'optparse'
|
||||
|
||||
module Rack
|
||||
class Server
|
||||
class Options
|
||||
def parse!(args)
|
||||
options = {}
|
||||
opt_parser = OptionParser.new("", 24, ' ') do |opts|
|
||||
opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]"
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Ruby options:"
|
||||
|
||||
lineno = 1
|
||||
opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
|
||||
eval line, TOPLEVEL_BINDING, "-e", lineno
|
||||
lineno += 1
|
||||
}
|
||||
|
||||
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
|
||||
options[:debug] = true
|
||||
}
|
||||
opts.on("-w", "--warn", "turn warnings on for your script") {
|
||||
options[:warn] = true
|
||||
}
|
||||
|
||||
opts.on("-I", "--include PATH",
|
||||
"specify $LOAD_PATH (may be used more than once)") { |path|
|
||||
options[:include] = path.split(":")
|
||||
}
|
||||
|
||||
opts.on("-r", "--require LIBRARY",
|
||||
"require the library, before executing your script") { |library|
|
||||
options[:require] = library
|
||||
}
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Rack options:"
|
||||
opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s|
|
||||
options[:server] = s
|
||||
}
|
||||
|
||||
opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host|
|
||||
options[:Host] = host
|
||||
}
|
||||
|
||||
opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port|
|
||||
options[:Port] = port
|
||||
}
|
||||
|
||||
opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
|
||||
options[:environment] = e
|
||||
}
|
||||
|
||||
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
|
||||
options[:daemonize] = d ? true : false
|
||||
}
|
||||
|
||||
opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
|
||||
options[:pid] = ::File.expand_path(f)
|
||||
}
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Common options:"
|
||||
|
||||
opts.on_tail("-h", "--help", "Show this message") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
|
||||
opts.on_tail("--version", "Show version") do
|
||||
puts "Rack #{Rack.version}"
|
||||
exit
|
||||
end
|
||||
end
|
||||
opt_parser.parse! args
|
||||
options[:rack_file] = args.last if args.last
|
||||
options
|
||||
end
|
||||
end
|
||||
|
||||
def self.start
|
||||
new.start
|
||||
end
|
||||
|
||||
attr_accessor :options
|
||||
|
||||
def initialize(options = nil)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= parse_options(ARGV)
|
||||
end
|
||||
|
||||
def default_options
|
||||
{
|
||||
:environment => "development",
|
||||
:pid => nil,
|
||||
:Port => 9292,
|
||||
:Host => "0.0.0.0",
|
||||
:AccessLog => [],
|
||||
:rack_file => ::File.expand_path("config.ru")
|
||||
}
|
||||
end
|
||||
|
||||
def app
|
||||
@app ||= begin
|
||||
if !::File.exist? options[:rack_file]
|
||||
abort "configuration #{options[:rack_file]} not found"
|
||||
end
|
||||
|
||||
app, options = Rack::Builder.parse_file(self.options[:rack_file], opt_parser)
|
||||
self.options.merge! options
|
||||
app
|
||||
end
|
||||
end
|
||||
|
||||
def self.middleware
|
||||
@middleware ||= begin
|
||||
m = Hash.new {|h,k| h[k] = []}
|
||||
m["deployment"].concat [lambda {|server| server.server =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr] }]
|
||||
m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]]
|
||||
m
|
||||
end
|
||||
end
|
||||
|
||||
def middleware
|
||||
self.class.middleware
|
||||
end
|
||||
|
||||
def start
|
||||
if options[:debug]
|
||||
$DEBUG = true
|
||||
require 'pp'
|
||||
p options[:server]
|
||||
pp wrapped_app
|
||||
pp app
|
||||
end
|
||||
|
||||
if options[:warn]
|
||||
$-w = true
|
||||
end
|
||||
|
||||
if includes = options[:include]
|
||||
$LOAD_PATH.unshift *includes
|
||||
end
|
||||
|
||||
if library = options[:require]
|
||||
require library
|
||||
end
|
||||
|
||||
daemonize_app if options[:daemonize]
|
||||
write_pid if options[:pid]
|
||||
server.run wrapped_app, options
|
||||
end
|
||||
|
||||
def server
|
||||
@_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default
|
||||
end
|
||||
|
||||
private
|
||||
def parse_options(args)
|
||||
options = default_options
|
||||
|
||||
# Don't evaluate CGI ISINDEX parameters.
|
||||
# http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
|
||||
args.clear if ENV.include?("REQUEST_METHOD")
|
||||
|
||||
options.merge! opt_parser.parse! args
|
||||
options
|
||||
end
|
||||
|
||||
def opt_parser
|
||||
Options.new
|
||||
end
|
||||
|
||||
def build_app(app)
|
||||
middleware[options[:environment]].reverse_each do |middleware|
|
||||
middleware = middleware.call(self) if middleware.respond_to?(:call)
|
||||
next unless middleware
|
||||
klass = middleware.shift
|
||||
app = klass.new(app, *middleware)
|
||||
end
|
||||
app
|
||||
end
|
||||
|
||||
def wrapped_app
|
||||
@wrapped_app ||= build_app app
|
||||
end
|
||||
|
||||
def daemonize_app
|
||||
if RUBY_VERSION < "1.9"
|
||||
exit if fork
|
||||
Process.setsid
|
||||
exit if fork
|
||||
Dir.chdir "/"
|
||||
::File.umask 0000
|
||||
STDIN.reopen "/dev/null"
|
||||
STDOUT.reopen "/dev/null", "a"
|
||||
STDERR.reopen "/dev/null", "a"
|
||||
else
|
||||
Process.daemon
|
||||
end
|
||||
end
|
||||
|
||||
def write_pid
|
||||
::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
|
||||
at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -107,18 +107,16 @@ module Rack
|
|||
|
||||
if not session_id = set_session(env, session_id, session, options)
|
||||
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
||||
[status, headers, body]
|
||||
elsif options[:defer] and not options[:renew]
|
||||
env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
|
||||
[status, headers, body]
|
||||
else
|
||||
cookie = Hash.new
|
||||
cookie[:value] = session_id
|
||||
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
|
||||
response = Rack::Response.new(body, status, headers)
|
||||
response.set_cookie(@key, cookie.merge(options))
|
||||
response.to_a
|
||||
Utils.set_cookie_header!(headers, @key, cookie.merge(options))
|
||||
end
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
|
||||
# All thread safety and session retrival proceedures should occur here.
|
||||
|
|
|
@ -70,16 +70,15 @@ module Rack
|
|||
|
||||
if session_data.size > (4096 - @key.size)
|
||||
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
|
||||
[status, headers, body]
|
||||
else
|
||||
options = env["rack.session.options"]
|
||||
cookie = Hash.new
|
||||
cookie[:value] = session_data
|
||||
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
|
||||
response = Rack::Response.new(body, status, headers)
|
||||
response.set_cookie(@key, cookie.merge(options))
|
||||
response.to_a
|
||||
Utils.set_cookie_header!(headers, @key, cookie.merge(options))
|
||||
end
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
|
||||
def generate_hmac(data)
|
||||
|
|
96
vendor/plugins/rack/lib/rack/session/memcache.rb
vendored
96
vendor/plugins/rack/lib/rack/session/memcache.rb
vendored
|
@ -29,9 +29,13 @@ module Rack
|
|||
super
|
||||
|
||||
@mutex = Mutex.new
|
||||
@pool = MemCache.
|
||||
new @default_options[:memcache_server], @default_options
|
||||
raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?}
|
||||
mserv = @default_options[:memcache_server]
|
||||
mopts = @default_options.
|
||||
reject{|k,v| MemCache::DEFAULT_OPTIONS.include? k }
|
||||
@pool = MemCache.new mserv, mopts
|
||||
unless @pool.active? and @pool.servers.any?{|c| c.alive? }
|
||||
raise 'No memcache servers'
|
||||
end
|
||||
end
|
||||
|
||||
def generate_sid
|
||||
|
@ -41,24 +45,23 @@ module Rack
|
|||
end
|
||||
end
|
||||
|
||||
def get_session(env, sid)
|
||||
session = @pool.get(sid) if sid
|
||||
def get_session(env, session_id)
|
||||
@mutex.lock if env['rack.multithread']
|
||||
unless sid and session
|
||||
env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
|
||||
session = {}
|
||||
sid = generate_sid
|
||||
ret = @pool.add sid, session
|
||||
raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret
|
||||
unless session_id and session = @pool.get(session_id)
|
||||
session_id, session = generate_sid, {}
|
||||
unless /^STORED/ =~ @pool.add(session_id, session)
|
||||
raise "Session collision on '#{session_id.inspect}'"
|
||||
end
|
||||
end
|
||||
session.instance_variable_set('@old', {}.merge(session))
|
||||
return [sid, session]
|
||||
rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted
|
||||
warn "#{self} is unable to find server."
|
||||
session.instance_variable_set '@old', @pool.get(session_id, true)
|
||||
return [session_id, session]
|
||||
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
||||
# MemCache server cannot be contacted
|
||||
warn "#{self} is unable to find memcached server."
|
||||
warn $!.inspect
|
||||
return [ nil, {} ]
|
||||
ensure
|
||||
@mutex.unlock if env['rack.multithread']
|
||||
@mutex.unlock if @mutex.locked?
|
||||
end
|
||||
|
||||
def set_session(env, session_id, new_session, options)
|
||||
|
@ -66,43 +69,50 @@ module Rack
|
|||
expiry = expiry.nil? ? 0 : expiry + 1
|
||||
|
||||
@mutex.lock if env['rack.multithread']
|
||||
session = @pool.get(session_id) || {}
|
||||
if options[:renew] or options[:drop]
|
||||
@pool.delete session_id
|
||||
return false if options[:drop]
|
||||
session_id = generate_sid
|
||||
@pool.add session_id, 0 # so we don't worry about cache miss on #set
|
||||
@pool.add session_id, {} # so we don't worry about cache miss on #set
|
||||
end
|
||||
old_session = new_session.instance_variable_get('@old') || {}
|
||||
session = merge_sessions session_id, old_session, new_session, session
|
||||
|
||||
session = @pool.get(session_id) || {}
|
||||
old_session = new_session.instance_variable_get '@old'
|
||||
old_session = old_session ? Marshal.load(old_session) : {}
|
||||
|
||||
unless Hash === old_session and Hash === new_session
|
||||
env['rack.errors'].
|
||||
puts 'Bad old_session or new_session sessions provided.'
|
||||
else # merge sessions
|
||||
# alterations are either update or delete, making as few changes as
|
||||
# possible to prevent possible issues.
|
||||
|
||||
# removed keys
|
||||
delete = old_session.keys - new_session.keys
|
||||
if $VERBOSE and not delete.empty?
|
||||
env['rack.errors'].
|
||||
puts "//@#{session_id}: delete #{delete*','}"
|
||||
end
|
||||
delete.each{|k| session.delete k }
|
||||
|
||||
# added or altered keys
|
||||
update = new_session.keys.
|
||||
select{|k| new_session[k] != old_session[k] }
|
||||
if $VERBOSE and not update.empty?
|
||||
env['rack.errors'].puts "//@#{session_id}: update #{update*','}"
|
||||
end
|
||||
update.each{|k| session[k] = new_session[k] }
|
||||
end
|
||||
|
||||
@pool.set session_id, session, expiry
|
||||
return session_id
|
||||
rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted
|
||||
warn "#{self} is unable to find server."
|
||||
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
||||
# MemCache server cannot be contacted
|
||||
warn "#{self} is unable to find memcached server."
|
||||
warn $!.inspect
|
||||
return false
|
||||
ensure
|
||||
@mutex.unlock if env['rack.multithread']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_sessions sid, old, new, cur=nil
|
||||
cur ||= {}
|
||||
unless Hash === old and Hash === new
|
||||
warn 'Bad old or new sessions provided.'
|
||||
return cur
|
||||
end
|
||||
|
||||
delete = old.keys - new.keys
|
||||
warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty?
|
||||
delete.each{|k| cur.delete k }
|
||||
|
||||
update = new.keys.select{|k| new[k] != old[k] }
|
||||
warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty?
|
||||
update.each{|k| cur[k] = new[k] }
|
||||
|
||||
cur
|
||||
@mutex.unlock if @mutex.locked?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
2
vendor/plugins/rack/lib/rack/session/pool.rb
vendored
2
vendor/plugins/rack/lib/rack/session/pool.rb
vendored
|
@ -13,7 +13,7 @@ module Rack
|
|||
# In the context of a multithreaded environment, sessions being
|
||||
# committed to the pool is done in a merging manner.
|
||||
#
|
||||
# The :drop option is available in rack.session.options if you with to
|
||||
# The :drop option is available in rack.session.options if you wish to
|
||||
# explicitly remove the session from the session cache.
|
||||
#
|
||||
# Example:
|
||||
|
|
17
vendor/plugins/rack/lib/rack/urlmap.rb
vendored
17
vendor/plugins/rack/lib/rack/urlmap.rb
vendored
|
@ -28,27 +28,28 @@ module Rack
|
|||
raise ArgumentError, "paths need to start with /"
|
||||
end
|
||||
location = location.chomp('/')
|
||||
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
|
||||
|
||||
[host, location, app]
|
||||
}.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first
|
||||
[host, location, match, app]
|
||||
}.sort_by { |(h, l, m, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first
|
||||
end
|
||||
|
||||
def call(env)
|
||||
path = env["PATH_INFO"].to_s.squeeze("/")
|
||||
path = env["PATH_INFO"].to_s
|
||||
script_name = env['SCRIPT_NAME']
|
||||
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
|
||||
@mapping.each { |host, location, app|
|
||||
@mapping.each { |host, location, match, app|
|
||||
next unless (hHost == host || sName == host \
|
||||
|| (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
|
||||
next unless location == path[0, location.size]
|
||||
next unless path[location.size] == nil || path[location.size] == ?/
|
||||
next unless path =~ match && rest = $1
|
||||
next unless rest.empty? || rest[0] == ?/
|
||||
|
||||
return app.call(
|
||||
env.merge(
|
||||
'SCRIPT_NAME' => (script_name + location),
|
||||
'PATH_INFO' => path[location.size..-1]))
|
||||
'PATH_INFO' => rest))
|
||||
}
|
||||
[404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]]
|
||||
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
241
vendor/plugins/rack/lib/rack/utils.rb
vendored
241
vendor/plugins/rack/lib/rack/utils.rb
vendored
|
@ -27,7 +27,7 @@ module Rack
|
|||
module_function :unescape
|
||||
|
||||
DEFAULT_SEP = /[&;] */n
|
||||
|
||||
|
||||
# Stolen from Mongrel, with some small modifications:
|
||||
# Parses a query string by breaking it up at the '&'
|
||||
# and ';' characters. You can also use this to parse
|
||||
|
@ -38,7 +38,9 @@ module Rack
|
|||
|
||||
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
|
||||
k, v = p.split('=', 2).map { |x| unescape(x) }
|
||||
|
||||
if v =~ /^("|')(.*)\1$/
|
||||
v = $2.gsub('\\'+$1, $1)
|
||||
end
|
||||
if cur = params[k]
|
||||
if cur.class == Array
|
||||
params[k] << v
|
||||
|
@ -67,6 +69,9 @@ module Rack
|
|||
module_function :parse_nested_query
|
||||
|
||||
def normalize_params(params, name, v = nil)
|
||||
if v and v =~ /^("|')(.*)\1$/
|
||||
v = $2.gsub('\\'+$1, $1)
|
||||
end
|
||||
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
||||
k = $1 || ''
|
||||
after = $' || ''
|
||||
|
@ -109,6 +114,25 @@ module Rack
|
|||
end
|
||||
module_function :build_query
|
||||
|
||||
def build_nested_query(value, prefix = nil)
|
||||
case value
|
||||
when Array
|
||||
value.map { |v|
|
||||
build_nested_query(v, "#{prefix}[]")
|
||||
}.join("&")
|
||||
when Hash
|
||||
value.map { |k, v|
|
||||
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
||||
}.join("&")
|
||||
when String
|
||||
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
||||
"#{prefix}=#{escape(value)}"
|
||||
else
|
||||
prefix
|
||||
end
|
||||
end
|
||||
module_function :build_nested_query
|
||||
|
||||
# Escape ampersands, brackets and quotes to their HTML/XML entities.
|
||||
def escape_html(string)
|
||||
string.to_s.gsub("&", "&").
|
||||
|
@ -149,6 +173,54 @@ module Rack
|
|||
end
|
||||
module_function :select_best_encoding
|
||||
|
||||
def set_cookie_header!(header, key, value)
|
||||
case value
|
||||
when Hash
|
||||
domain = "; domain=" + value[:domain] if value[:domain]
|
||||
path = "; path=" + value[:path] if value[:path]
|
||||
# According to RFC 2109, we need dashes here.
|
||||
# N.B.: cgi.rb uses spaces...
|
||||
expires = "; expires=" + value[:expires].clone.gmtime.
|
||||
strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
|
||||
secure = "; secure" if value[:secure]
|
||||
httponly = "; HttpOnly" if value[:httponly]
|
||||
value = value[:value]
|
||||
end
|
||||
value = [value] unless Array === value
|
||||
cookie = escape(key) + "=" +
|
||||
value.map { |v| escape v }.join("&") +
|
||||
"#{domain}#{path}#{expires}#{secure}#{httponly}"
|
||||
|
||||
case header["Set-Cookie"]
|
||||
when Array
|
||||
header["Set-Cookie"] << cookie
|
||||
when String
|
||||
header["Set-Cookie"] = [header["Set-Cookie"], cookie]
|
||||
when nil
|
||||
header["Set-Cookie"] = cookie
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
module_function :set_cookie_header!
|
||||
|
||||
def delete_cookie_header!(header, key, value = {})
|
||||
unless Array === header["Set-Cookie"]
|
||||
header["Set-Cookie"] = [header["Set-Cookie"]].compact
|
||||
end
|
||||
|
||||
header["Set-Cookie"].reject! { |cookie|
|
||||
cookie =~ /\A#{escape(key)}=/
|
||||
}
|
||||
|
||||
set_cookie_header!(header, key,
|
||||
{:value => '', :path => nil, :domain => nil,
|
||||
:expires => Time.at(0) }.merge(value))
|
||||
|
||||
nil
|
||||
end
|
||||
module_function :delete_cookie_header!
|
||||
|
||||
# Return the bytesize of String; uses String#length under Ruby 1.8 and
|
||||
# String#bytesize under 1.9.
|
||||
if ''.respond_to?(:bytesize)
|
||||
|
@ -191,11 +263,22 @@ module Rack
|
|||
# A case-insensitive Hash that preserves the original case of a
|
||||
# header when set.
|
||||
class HeaderHash < Hash
|
||||
def self.new(hash={})
|
||||
HeaderHash === hash ? hash : super(hash)
|
||||
end
|
||||
|
||||
def initialize(hash={})
|
||||
super()
|
||||
@names = {}
|
||||
hash.each { |k, v| self[k] = v }
|
||||
end
|
||||
|
||||
def each
|
||||
super do |k, v|
|
||||
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
||||
end
|
||||
end
|
||||
|
||||
def to_hash
|
||||
inject({}) do |hash, (k,v)|
|
||||
if v.respond_to? :to_ary
|
||||
|
@ -208,21 +291,24 @@ module Rack
|
|||
end
|
||||
|
||||
def [](k)
|
||||
super @names[k.downcase]
|
||||
super(@names[k] ||= @names[k.downcase])
|
||||
end
|
||||
|
||||
def []=(k, v)
|
||||
delete k
|
||||
@names[k.downcase] = k
|
||||
@names[k] = @names[k.downcase] = k
|
||||
super k, v
|
||||
end
|
||||
|
||||
def delete(k)
|
||||
super @names.delete(k.downcase)
|
||||
canonical = k.downcase
|
||||
result = super @names.delete(canonical)
|
||||
@names.delete_if { |name,| name.downcase == canonical }
|
||||
result
|
||||
end
|
||||
|
||||
def include?(k)
|
||||
@names.has_key? k.downcase
|
||||
@names.include?(k) || @names.include?(k.downcase)
|
||||
end
|
||||
|
||||
alias_method :has_key?, :include?
|
||||
|
@ -238,13 +324,23 @@ module Rack
|
|||
hash = dup
|
||||
hash.merge! other
|
||||
end
|
||||
|
||||
def replace(other)
|
||||
clear
|
||||
other.each { |k, v| self[k] = v }
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
# Every standard HTTP code mapped to the appropriate message.
|
||||
# Stolen from Mongrel.
|
||||
# Generated with:
|
||||
# curl -s http://www.iana.org/assignments/http-status-codes | \
|
||||
# ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and
|
||||
# puts " #{m[1]} => \x27#{m[2].strip}x27,"'
|
||||
HTTP_STATUS_CODES = {
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing',
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
|
@ -252,12 +348,15 @@ module Rack
|
|||
204 => 'No Content',
|
||||
205 => 'Reset Content',
|
||||
206 => 'Partial Content',
|
||||
207 => 'Multi-Status',
|
||||
226 => 'IM Used',
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
306 => 'Reserved',
|
||||
307 => 'Temporary Redirect',
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
|
@ -273,27 +372,76 @@ module Rack
|
|||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Request Entity Too Large',
|
||||
414 => 'Request-URI Too Large',
|
||||
414 => 'Request-URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Requested Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
422 => 'Unprocessable Entity',
|
||||
423 => 'Locked',
|
||||
424 => 'Failed Dependency',
|
||||
426 => 'Upgrade Required',
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Timeout',
|
||||
505 => 'HTTP Version Not Supported'
|
||||
505 => 'HTTP Version Not Supported',
|
||||
506 => 'Variant Also Negotiates',
|
||||
507 => 'Insufficient Storage',
|
||||
510 => 'Not Extended',
|
||||
}
|
||||
|
||||
# Responses with HTTP status codes that should not have an entity body
|
||||
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
|
||||
|
||||
SYMBOL_TO_STATUS_CODE = HTTP_STATUS_CODES.inject({}) { |hash, (code, message)|
|
||||
hash[message.downcase.gsub(/\s|-/, '_').to_sym] = code
|
||||
hash
|
||||
}
|
||||
|
||||
def status_code(status)
|
||||
if status.is_a?(Symbol)
|
||||
SYMBOL_TO_STATUS_CODE[status] || 500
|
||||
else
|
||||
status.to_i
|
||||
end
|
||||
end
|
||||
module_function :status_code
|
||||
|
||||
# A multipart form data parser, adapted from IOWA.
|
||||
#
|
||||
# Usually, Rack::Request#POST takes care of calling this.
|
||||
|
||||
module Multipart
|
||||
class UploadedFile
|
||||
# The filename, *not* including the path, of the "uploaded" file
|
||||
attr_reader :original_filename
|
||||
|
||||
# The content type of the "uploaded" file
|
||||
attr_accessor :content_type
|
||||
|
||||
def initialize(path, content_type = "text/plain", binary = false)
|
||||
raise "#{path} file does not exist" unless ::File.exist?(path)
|
||||
@content_type = content_type
|
||||
@original_filename = ::File.basename(path)
|
||||
@tempfile = Tempfile.new(@original_filename)
|
||||
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
||||
@tempfile.binmode if binary
|
||||
FileUtils.copy_file(path, @tempfile.path)
|
||||
end
|
||||
|
||||
def path
|
||||
@tempfile.path
|
||||
end
|
||||
alias_method :local_path, :path
|
||||
|
||||
def method_missing(method_name, *args, &block) #:nodoc:
|
||||
@tempfile.__send__(method_name, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
EOL = "\r\n"
|
||||
MULTIPART_BOUNDARY = "AaB03x"
|
||||
|
||||
def self.parse_multipart(env)
|
||||
unless env['CONTENT_TYPE'] =~
|
||||
|
@ -378,7 +526,7 @@ module Rack
|
|||
:name => name, :tempfile => body, :head => head}
|
||||
elsif !filename && content_type
|
||||
body.rewind
|
||||
|
||||
|
||||
# Generic multipart cases, not coming from a form
|
||||
data = {:type => content_type,
|
||||
:name => name, :tempfile => body, :head => head}
|
||||
|
@ -388,7 +536,8 @@ module Rack
|
|||
|
||||
Utils.normalize_params(params, name, data) unless data.nil?
|
||||
|
||||
break if buf.empty? || content_length == -1
|
||||
# break if we're at the end of a buffer, but not if it is the end of a field
|
||||
break if (buf.empty? && $1 != EOL) || content_length == -1
|
||||
}
|
||||
|
||||
input.rewind
|
||||
|
@ -396,6 +545,76 @@ module Rack
|
|||
params
|
||||
end
|
||||
end
|
||||
|
||||
def self.build_multipart(params, first = true)
|
||||
if first
|
||||
unless params.is_a?(Hash)
|
||||
raise ArgumentError, "value must be a Hash"
|
||||
end
|
||||
|
||||
multipart = false
|
||||
query = lambda { |value|
|
||||
case value
|
||||
when Array
|
||||
value.each(&query)
|
||||
when Hash
|
||||
value.values.each(&query)
|
||||
when UploadedFile
|
||||
multipart = true
|
||||
end
|
||||
}
|
||||
params.values.each(&query)
|
||||
return nil unless multipart
|
||||
end
|
||||
|
||||
flattened_params = Hash.new
|
||||
|
||||
params.each do |key, value|
|
||||
k = first ? key.to_s : "[#{key}]"
|
||||
|
||||
case value
|
||||
when Array
|
||||
value.map { |v|
|
||||
build_multipart(v, false).each { |subkey, subvalue|
|
||||
flattened_params["#{k}[]#{subkey}"] = subvalue
|
||||
}
|
||||
}
|
||||
when Hash
|
||||
build_multipart(value, false).each { |subkey, subvalue|
|
||||
flattened_params[k + subkey] = subvalue
|
||||
}
|
||||
else
|
||||
flattened_params[k] = value
|
||||
end
|
||||
end
|
||||
|
||||
if first
|
||||
flattened_params.map { |name, file|
|
||||
if file.respond_to?(:original_filename)
|
||||
::File.open(file.path, "rb") do |f|
|
||||
f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
|
||||
<<-EOF
|
||||
--#{MULTIPART_BOUNDARY}\r
|
||||
Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r
|
||||
Content-Type: #{file.content_type}\r
|
||||
Content-Length: #{::File.stat(file.path).size}\r
|
||||
\r
|
||||
#{f.read}\r
|
||||
EOF
|
||||
end
|
||||
else
|
||||
<<-EOF
|
||||
--#{MULTIPART_BOUNDARY}\r
|
||||
Content-Disposition: form-data; name="#{name}"\r
|
||||
\r
|
||||
#{file}\r
|
||||
EOF
|
||||
end
|
||||
}.join + "--#{MULTIPART_BOUNDARY}--\r"
|
||||
else
|
||||
flattened_params
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue