Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:rack/rack

  • Loading branch information...
commit d084a14fc5be6587fb1720a4f126bae2c7e74d10 2 parents 1038c7e + b83d7be
Konstantin Haase rkh authored
2  COPYING
View
@@ -1,4 +1,4 @@
-Copyright (c) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
+Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012 Christian Neukirchen <purl.org/net/chneukirchen>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
2  lib/rack/auth/digest/nonce.rb
View
@@ -38,7 +38,7 @@ def valid?
end
def stale?
- !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit
+ !self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit
end
def fresh?
41 lib/rack/backports/uri/common_192.rb
View
@@ -17,21 +17,18 @@
require 'uri/common'
module URI
- 256.times do |i|
- TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
- end
- TBLENCWWWCOMP_[' '] = '+'
- TBLENCWWWCOMP_.freeze
-
- 256.times do |i|
- h, l = i>>4, i&15
- TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
- TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
+ TBLDECWWWCOMP_ = {} unless const_defined?(:TBLDECWWWCOMP_) #:nodoc:
+ if TBLDECWWWCOMP_.empty?
+ 256.times do |i|
+ h, l = i>>4, i&15
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
+ end
+ TBLDECWWWCOMP_['+'] = ' '
+ TBLDECWWWCOMP_.freeze
end
- TBLDECWWWCOMP_['+'] = ' '
- TBLDECWWWCOMP_.freeze
def self.decode_www_form(str, enc=Encoding::UTF_8)
return [] if str.empty?
@@ -46,22 +43,6 @@ def self.decode_www_form(str, enc=Encoding::UTF_8)
end
def self.decode_www_form_component(str, enc=Encoding::UTF_8)
- if TBLDECWWWCOMP_.empty?
- tbl = {}
- 256.times do |i|
- h, l = i>>4, i&15
- tbl['%%%X%X' % [h, l]] = i.chr
- tbl['%%%x%X' % [h, l]] = i.chr
- tbl['%%%X%x' % [h, l]] = i.chr
- tbl['%%%x%x' % [h, l]] = i.chr
- end
- tbl['+'] = ' '
- begin
- TBLDECWWWCOMP_.replace(tbl)
- TBLDECWWWCOMP_.freeze
- rescue
- end
- end
raise ArgumentError, "invalid %-encoding (#{str})" unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
str.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
end
2  lib/rack/builder.rb
View
@@ -38,7 +38,7 @@ def self.parse_file(config, opts = Server::Options.new)
end
cfgfile.sub!(/^__END__\n.*\Z/m, '')
app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",
- TOPLEVEL_BINDING, config
+ TOPLEVEL_BINDING, config, 0
else
require config
app = Object.const_get(::File.basename(config, '.rb').capitalize)
2  lib/rack/directory.rb
View
@@ -80,7 +80,7 @@ def list_directory
@files = [['../','Parent Directory','','','']]
glob = F.join(@path, '*')
- url_head = ([@script_name] + @path_info.split('/')).map do |part|
+ url_head = (@script_name.split('/') + @path_info.split('/')).map do |part|
Rack::Utils.escape part
end
7 lib/rack/etag.rb
View
@@ -28,8 +28,11 @@ def call(env)
end
unless headers['Cache-Control']
- headers['Cache-Control'] =
- (digest ? @cache_control : @no_cache_control) || ""
+ if digest
+ headers['Cache-Control'] = @cache_control if @cache_control
+ else
+ headers['Cache-Control'] = @no_cache_control if @no_cache_control
+ end
end
[status, headers, body]
15 lib/rack/file.rb
View
@@ -21,9 +21,16 @@ class File
alias :to_path :path
- def initialize(root, cache_control = nil)
+ def initialize(root, headers={})
@root = root
- @cache_control = cache_control
+ # Allow a cache_control string for backwards compatibility
+ if headers.instance_of? String
+ warn \
+ "Rack::File headers parameter replaces cache_control after Rack 1.5."
+ @headers = { 'Cache-Control' => headers }
+ else
+ @headers = headers
+ end
end
def call(env)
@@ -78,7 +85,9 @@ def serving(env)
},
env["REQUEST_METHOD"] == "HEAD" ? [] : self
]
- response[1]['Cache-Control'] = @cache_control if @cache_control
+
+ # Set custom headers
+ @headers.each { |field, content| response[1][field] = content } if @headers
# NOTE:
# We check via File::size? whether this file provides size info
28 lib/rack/server.rb
View
@@ -247,11 +247,14 @@ def start &blk
pp app
end
+ check_pid! if options[:pid]
+
# Touch the wrapped app, so that the config.ru is loaded before
# daemonization (i.e. before chdir, etc).
wrapped_app
daemonize_app if options[:daemonize]
+
write_pid if options[:pid]
trap(:INT) do
@@ -274,7 +277,7 @@ def parse_options(args)
options = default_options
# Don't evaluate CGI ISINDEX parameters.
- # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
+ # http://www.meb.uni-bonn.de/docs/cgi/cl.html
args.clear if ENV.include?("REQUEST_METHOD")
options.merge! opt_parser.parse!(args)
@@ -319,5 +322,28 @@ 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
+
+ def check_pid!
+ case pidfile_process_status
+ when :running, :not_owned
+ $stderr.puts "A server is already running. Check #{options[:pid]}."
+ exit(1)
+ when :dead
+ ::File.delete(options[:pid])
+ end
+ end
+
+ def pidfile_process_status
+ return :exited unless ::File.exist?(options[:pid])
+
+ pid = ::File.read(options[:pid]).to_i
+ Process.kill(0, pid)
+ :running
+ rescue Errno::ESRCH
+ :dead
+ rescue Errno::EPERM
+ :not_owned
+ end
+
end
end
5 lib/rack/session/abstract/id.rb
View
@@ -120,6 +120,11 @@ def empty?
super
end
+ def merge!(hash)
+ load_for_write!
+ super
+ end
+
private
def load_for_read!
17 lib/rack/static.rb
View
@@ -32,6 +32,16 @@ module Rack
#
# use Rack::Static, :root => 'public', :cache_control => 'public'
#
+ # Set custom HTTP Headers for all served files:
+ #
+ # use Rack::Static, :root => 'public', :headers =>
+ # {'Cache-Control' => 'public, max-age=31536000',
+ # 'Access-Control-Allow-Origin' => '*'}
+ #
+ # Note: If both :headers => {'Cache-Control' => 'public, max-age=42'}
+ # and :cache_control => 'public, max-age=38' are being provided
+ # the :headers setting takes precedence
+ #
class Static
@@ -40,8 +50,11 @@ def initialize(app, options={})
@urls = options[:urls] || ["/favicon.ico"]
@index = options[:index]
root = options[:root] || Dir.pwd
- cache_control = options[:cache_control]
- @file_server = Rack::File.new(root, cache_control)
+ headers = options[:headers] || {}
+ # Allow for legacy :cache_control option
+ # while prioritizing :headers => {'Cache-Control' => ''} settings
+ headers['Cache-Control'] ||= options[:cache_control] if options[:cache_control]
+ @file_server = Rack::File.new(root, headers)
end
def overwrite_file_path(path)
2  lib/rack/utils.rb
View
@@ -9,7 +9,7 @@
if major == 1 && minor < 9
require 'rack/backports/uri/common_18'
-elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL < 318 && RUBY_ENGINE != 'jruby'
+elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL <= 320 && RUBY_ENGINE != 'jruby'
require 'rack/backports/uri/common_192'
elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
require 'rack/backports/uri/common_193'
1  test/builder/line.ru
View
@@ -0,0 +1 @@
+run lambda{ |env| [200, {'Content-Type' => 'text/plain'}, [__LINE__.to_s]] }
14 test/spec_auth_digest.rb
View
@@ -153,6 +153,20 @@ def assert_bad_request(response)
end
end
+ should 'not rechallenge if nonce is not stale' do
+ begin
+ Rack::Auth::Digest::Nonce.time_limit = 10
+
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 1 do |response|
+ response.status.should.equal 200
+ response.body.to_s.should.equal 'Hi Alice'
+ response.headers['WWW-Authenticate'].should.not =~ /\bstale=true\b/
+ end
+ ensure
+ Rack::Auth::Digest::Nonce.time_limit = nil
+ end
+ end
+
should 'rechallenge with stale parameter if nonce is stale' do
begin
Rack::Auth::Digest::Nonce.time_limit = 1
6 test/spec_builder.rb
View
@@ -197,5 +197,11 @@ def config_file(name)
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
$:.pop
end
+
+ it "sets __LINE__ correctly" do
+ app, options = Rack::Builder.parse_file config_file('line.ru')
+ options = nil # ignored, prevents warning
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
+ end
end
end
18 test/spec_directory.rb
View
@@ -67,4 +67,22 @@
res = mr.get("/cgi/test%2bdirectory/test%2bfile")
res.should.be.ok
end
+
+ should "correctly escape script name" do
+ app2 = Rack::Builder.new do
+ map '/script-path' do
+ run app
+ end
+ end
+
+ mr = Rack::MockRequest.new(Rack::Lint.new(app2))
+
+ res = mr.get("/script-path/cgi/test%2bdirectory")
+
+ res.should.be.ok
+ res.body.should =~ %r[/script-path/cgi/test%2Bdirectory/test%2Bfile]
+
+ res = mr.get("/script-path/cgi/test%2bdirectory/test%2bfile")
+ res.should.be.ok
+ end
end
6 test/spec_etag.rb
View
@@ -54,6 +54,12 @@ def res.to_path ; "/tmp/hello.txt" ; end
response[1]['Cache-Control'].should.equal 'public'
end
+ should "not set Cache-Control if directive isn't present" do
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
+ response = etag(app, nil, nil).call(request)
+ response[1]['Cache-Control'].should.equal nil
+ end
+
should "not change ETag if it is already set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] }
response = etag(app).call(request)
27 test/spec_file.rb
View
@@ -65,13 +65,13 @@ def file(*args)
should "not allow unsafe directory traversal" do
req = Rack::MockRequest.new(file(DOCROOT))
- res = req.get("/../README")
+ res = req.get("/../README.rdoc")
res.should.be.client_error
- res = req.get("../test")
+ res = req.get("../test/spec_file.rb")
res.should.be.client_error
- res = req.get("..")
+ res = req.get("../README.rdoc")
res.should.be.client_error
res.should.be.not_found
@@ -145,7 +145,7 @@ def file(*args)
res["Content-Range"].should.equal "bytes */193"
end
- should "support cache control options" do
+ should "support legacy cache control options provided as string" do
env = Rack::MockRequest.env_for("/cgi/test")
status, heads, _ = file(DOCROOT, 'public, max-age=38').call(env)
@@ -153,6 +153,25 @@ def file(*args)
heads['Cache-Control'].should.equal 'public, max-age=38'
end
+ should "support custom http headers" do
+ env = Rack::MockRequest.env_for("/cgi/test")
+ status, heads, _ = file(DOCROOT, 'Cache-Control' => 'public, max-age=38',
+ 'Access-Control-Allow-Origin' => '*').call(env)
+
+ status.should.equal 200
+ heads['Cache-Control'].should.equal 'public, max-age=38'
+ heads['Access-Control-Allow-Origin'].should.equal '*'
+ end
+
+ should "support not add custom http headers if none are supplied" do
+ env = Rack::MockRequest.env_for("/cgi/test")
+ status, heads, _ = file(DOCROOT).call(env)
+
+ status.should.equal 200
+ heads['Cache-Control'].should.equal nil
+ heads['Access-Control-Allow-Origin'].should.equal nil
+ end
+
should "only support GET and HEAD requests" do
req = Rack::MockRequest.new(file(DOCROOT))
47 test/spec_server.rb
View
@@ -10,6 +10,13 @@ def app
lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] }
end
+ def with_stderr
+ old, $stderr = $stderr, StringIO.new
+ yield $stderr
+ ensure
+ $stderr = old
+ end
+
it "overrides :config if :app is passed in" do
server = Rack::Server.new(:app => "FOO")
server.app.should == "FOO"
@@ -71,4 +78,44 @@ def app
open(pidfile) { |f| f.read.should.eql $$.to_s }
end
+ should "check pid file presence and running process" do
+ pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path
+ server = Rack::Server.new(:pid => pidfile)
+ server.send(:pidfile_process_status).should.eql :running
+ end
+
+ should "check pid file presence and dead process" do
+ dead_pid = `echo $$`.to_i
+ pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path
+ server = Rack::Server.new(:pid => pidfile)
+ server.send(:pidfile_process_status).should.eql :dead
+ end
+
+ should "check pid file presence and exited process" do
+ pidfile = Tempfile.open('pidfile') { |f| break f }.path
+ ::File.delete(pidfile)
+ server = Rack::Server.new(:pid => pidfile)
+ server.send(:pidfile_process_status).should.eql :exited
+ end
+
+ should "check pid file presence and not owned process" do
+ pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
+ server = Rack::Server.new(:pid => pidfile)
+ server.send(:pidfile_process_status).should.eql :not_owned
+ end
+
+ should "inform the user about existing pidfiles with running processes" do
+ pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
+ server = Rack::Server.new(:pid => pidfile)
+ with_stderr do |err|
+ should.raise(SystemExit) do
+ server.start
+ end
+ err.rewind
+ output = err.read
+ output.should.match(/already running/)
+ output.should.include? pidfile
+ end
+ end
+
end
7 test/spec_session_cookie.rb
View
@@ -338,4 +338,11 @@ def call(env)
response = response_for(:app => session_id, :request => request)
response.body.should.match(/foo/)
end
+
+ it "allows modifying session data with session data from middleware in front" do
+ request = { 'rack.session' => { :foo => 'bar' }}
+ response = response_for(:app => incrementor, :request => request)
+ response.body.should.match(/counter/)
+ response.body.should.match(/foo/)
+ end
end
21 test/spec_static.rb
View
@@ -12,7 +12,7 @@ def call(env)
def static(app, *args)
Rack::Lint.new Rack::Static.new(app, *args)
end
-
+
root = File.expand_path(File.dirname(__FILE__))
OPTIONS = {:urls => ["/cgi"], :root => root}
@@ -78,4 +78,23 @@ def static(app, *args)
res.headers['Cache-Control'].should == 'public'
end
+ it "supports serving custom http headers" do
+ opts = OPTIONS.merge(:headers => {'Cache-Control' => 'public, max-age=42',
+ 'Access-Control-Allow-Origin' => '*'})
+ request = Rack::MockRequest.new(static(DummyApp.new, opts))
+ res = request.get("/cgi/test")
+ res.should.be.ok
+ res.headers['Cache-Control'].should == 'public, max-age=42'
+ res.headers['Access-Control-Allow-Origin'].should == '*'
+ end
+
+ it "allows headers hash to take priority over fixed cache-control" do
+ opts = OPTIONS.merge(:cache_control => 'public, max-age=38',
+ :headers => {'Cache-Control' => 'public, max-age=42'})
+ request = Rack::MockRequest.new(static(DummyApp.new, opts))
+ res = request.get("/cgi/test")
+ res.should.be.ok
+ res.headers['Cache-Control'].should == 'public, max-age=42'
+ end
+
end
2  test/spec_utils.rb
View
@@ -197,7 +197,7 @@ def kcodeu
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
should.raise(TypeError).
- message.should.match /expected Array \(got [^)]*\) for param `x'/
+ message.should.match(/expected Array \(got [^)]*\) for param `x'/)
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
should.raise(TypeError).
Please sign in to comment.
Something went wrong with that request. Please try again.