Merge branch 'bzr/golem' of /Users/distler/Sites/code/instiki
This commit is contained in:
commit
f50d7189f7
113 changed files with 5759 additions and 199 deletions
18
vendor/plugins/rack/COPYING
vendored
Normal file
18
vendor/plugins/rack/COPYING
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Copyright (c) 2007, 2008, 2009 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
|
||||||
|
deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
18
vendor/plugins/rack/KNOWN-ISSUES
vendored
Normal file
18
vendor/plugins/rack/KNOWN-ISSUES
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
= Known issues with Rack and Web servers
|
||||||
|
|
||||||
|
* Lighttpd sets wrong SCRIPT_NAME and PATH_INFO if you mount your
|
||||||
|
FastCGI app at "/". This can be fixed by using this middleware:
|
||||||
|
|
||||||
|
class LighttpdScriptNameFix
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
env["PATH_INFO"] = env["SCRIPT_NAME"].to_s + env["PATH_INFO"].to_s
|
||||||
|
env["SCRIPT_NAME"] = ""
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Of course, use this only when your app runs at "/".
|
364
vendor/plugins/rack/README
vendored
Normal file
364
vendor/plugins/rack/README
vendored
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
= Rack, a modular Ruby webserver interface
|
||||||
|
|
||||||
|
Rack provides a minimal, modular and adaptable interface for developing
|
||||||
|
web applications in Ruby. By wrapping HTTP requests and responses in
|
||||||
|
the simplest way possible, it unifies and distills the API for web
|
||||||
|
servers, web frameworks, and software in between (the so-called
|
||||||
|
middleware) into a single method call.
|
||||||
|
|
||||||
|
The exact details of this are described in the Rack specification,
|
||||||
|
which all Rack applications should conform to.
|
||||||
|
|
||||||
|
== Specification changes in this release
|
||||||
|
|
||||||
|
With Rack 1.0, the Rack specification (found in SPEC) changed in the
|
||||||
|
following backward-incompatible ways. This was done to properly
|
||||||
|
support Ruby 1.9 and to deprecate some problematic techniques:
|
||||||
|
|
||||||
|
* Rack::VERSION has been pushed to [1,0].
|
||||||
|
* Header values must be Strings now, split on "\n".
|
||||||
|
* rack.input must be rewindable and support reading into a buffer,
|
||||||
|
wrap with Rack::RewindableInput if it isn't.
|
||||||
|
* Content-Length can be missing, in this case chunked transfer
|
||||||
|
encoding is used.
|
||||||
|
* Bodies can now additionally respond to #to_path with a filename to
|
||||||
|
be served.
|
||||||
|
* String bodies are deprecated and will not work with Ruby 1.9, use an
|
||||||
|
Array with a single String instead.
|
||||||
|
* rack.session is now specified.
|
||||||
|
|
||||||
|
== Supported web servers
|
||||||
|
|
||||||
|
The included *handlers* connect all kinds of web servers to Rack:
|
||||||
|
* Mongrel
|
||||||
|
* EventedMongrel
|
||||||
|
* SwiftipliedMongrel
|
||||||
|
* WEBrick
|
||||||
|
* FCGI
|
||||||
|
* CGI
|
||||||
|
* SCGI
|
||||||
|
* LiteSpeed
|
||||||
|
* Thin
|
||||||
|
|
||||||
|
These web servers include Rack handlers in their distributions:
|
||||||
|
* Ebb
|
||||||
|
* Fuzed
|
||||||
|
* Phusion Passenger (which is mod_rack for Apache and for nginx)
|
||||||
|
* Unicorn
|
||||||
|
|
||||||
|
Any valid Rack app will run the same on all these handlers, without
|
||||||
|
changing anything.
|
||||||
|
|
||||||
|
== Supported web frameworks
|
||||||
|
|
||||||
|
The included *adapters* connect Rack with existing Ruby web frameworks:
|
||||||
|
* Camping
|
||||||
|
|
||||||
|
These frameworks include Rack adapters in their distributions:
|
||||||
|
* Camping
|
||||||
|
* Coset
|
||||||
|
* Halcyon
|
||||||
|
* Mack
|
||||||
|
* Maveric
|
||||||
|
* Merb
|
||||||
|
* Racktools::SimpleApplication
|
||||||
|
* Ramaze
|
||||||
|
* Ruby on Rails
|
||||||
|
* Rum
|
||||||
|
* Sinatra
|
||||||
|
* Sin
|
||||||
|
* Vintage
|
||||||
|
* Waves
|
||||||
|
* Wee
|
||||||
|
|
||||||
|
Current links to these projects can be found at
|
||||||
|
http://wiki.ramaze.net/Home#other-frameworks
|
||||||
|
|
||||||
|
== Available middleware
|
||||||
|
|
||||||
|
Between the server and the framework, Rack can be customized to your
|
||||||
|
applications needs using middleware, for example:
|
||||||
|
* Rack::URLMap, to route to multiple applications inside the same process.
|
||||||
|
* Rack::CommonLogger, for creating Apache-style logfiles.
|
||||||
|
* Rack::ShowException, for catching unhandled exceptions and
|
||||||
|
presenting them in a nice and helpful way with clickable backtrace.
|
||||||
|
* Rack::File, for serving static files.
|
||||||
|
* ...many others!
|
||||||
|
|
||||||
|
All these components use the same interface, which is described in
|
||||||
|
detail in the Rack specification. These optional components can be
|
||||||
|
used in any way you wish.
|
||||||
|
|
||||||
|
== Convenience
|
||||||
|
|
||||||
|
If you want to develop outside of existing frameworks, implement your
|
||||||
|
own ones, or develop middleware, Rack provides many helpers to create
|
||||||
|
Rack applications quickly and without doing the same web stuff all
|
||||||
|
over:
|
||||||
|
* Rack::Request, which also provides query string parsing and
|
||||||
|
multipart handling.
|
||||||
|
* Rack::Response, for convenient generation of HTTP replies and
|
||||||
|
cookie handling.
|
||||||
|
* Rack::MockRequest and Rack::MockResponse for efficient and quick
|
||||||
|
testing of Rack application without real HTTP round-trips.
|
||||||
|
|
||||||
|
== rack-contrib
|
||||||
|
|
||||||
|
The plethora of useful middleware created the need for a project that
|
||||||
|
collects fresh Rack middleware. rack-contrib includes a variety of
|
||||||
|
add-on components for Rack and it is easy to contribute new modules.
|
||||||
|
|
||||||
|
* http://github.com/rack/rack-contrib
|
||||||
|
|
||||||
|
== rackup
|
||||||
|
|
||||||
|
rackup is a useful tool for running Rack applications, which uses the
|
||||||
|
Rack::Builder DSL to configure middleware and build up applications
|
||||||
|
easily.
|
||||||
|
|
||||||
|
rackup automatically figures out the environment it is run in, and
|
||||||
|
runs your application as FastCGI, CGI, or standalone with Mongrel or
|
||||||
|
WEBrick---all from the same configuration.
|
||||||
|
|
||||||
|
== Quick start
|
||||||
|
|
||||||
|
Try the lobster!
|
||||||
|
|
||||||
|
Either with the embedded WEBrick starter:
|
||||||
|
|
||||||
|
ruby -Ilib lib/rack/lobster.rb
|
||||||
|
|
||||||
|
Or with rackup:
|
||||||
|
|
||||||
|
bin/rackup -Ilib example/lobster.ru
|
||||||
|
|
||||||
|
By default, the lobster is found at http://localhost:9292.
|
||||||
|
|
||||||
|
== Installing with RubyGems
|
||||||
|
|
||||||
|
A Gem of Rack is available. You can install it with:
|
||||||
|
|
||||||
|
gem install rack
|
||||||
|
|
||||||
|
I also provide a local mirror of the gems (and development snapshots)
|
||||||
|
at my site:
|
||||||
|
|
||||||
|
gem install rack --source http://chneukirchen.org/releases/gems/
|
||||||
|
|
||||||
|
== Running the tests
|
||||||
|
|
||||||
|
Testing Rack requires the test/spec testing framework:
|
||||||
|
|
||||||
|
gem install test-spec
|
||||||
|
|
||||||
|
There are two rake-based test tasks:
|
||||||
|
|
||||||
|
rake test tests all the fast tests (no Handlers or Adapters)
|
||||||
|
rake fulltest runs all the tests
|
||||||
|
|
||||||
|
The fast testsuite has no dependencies outside of the core Ruby
|
||||||
|
installation and test-spec.
|
||||||
|
|
||||||
|
To run the test suite completely, you need:
|
||||||
|
|
||||||
|
* camping
|
||||||
|
* fcgi
|
||||||
|
* memcache-client
|
||||||
|
* mongrel
|
||||||
|
* ruby-openid
|
||||||
|
* thin
|
||||||
|
|
||||||
|
The full set of tests test FCGI access with lighttpd (on port
|
||||||
|
9203) so you will need lighttpd installed as well as the FCGI
|
||||||
|
libraries and the fcgi gem:
|
||||||
|
|
||||||
|
Download and install lighttpd:
|
||||||
|
|
||||||
|
http://www.lighttpd.net/download
|
||||||
|
|
||||||
|
Installing the FCGI libraries:
|
||||||
|
|
||||||
|
curl -O http://www.fastcgi.com/dist/fcgi-2.4.0.tar.gz
|
||||||
|
tar xzvf fcgi-2.4.0.tar.gz
|
||||||
|
cd fcgi-2.4.0
|
||||||
|
./configure --prefix=/usr/local
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
Installing the Ruby fcgi gem:
|
||||||
|
|
||||||
|
gem install fcgi
|
||||||
|
|
||||||
|
Furthermore, to test Memcache sessions, you need memcached (will be
|
||||||
|
run on port 11211) and memcache-client installed.
|
||||||
|
|
||||||
|
== History
|
||||||
|
|
||||||
|
* March 3rd, 2007: First public release 0.1.
|
||||||
|
|
||||||
|
* May 16th, 2007: Second public release 0.2.
|
||||||
|
* HTTP Basic authentication.
|
||||||
|
* Cookie Sessions.
|
||||||
|
* Static file handler.
|
||||||
|
* Improved Rack::Request.
|
||||||
|
* Improved Rack::Response.
|
||||||
|
* Added Rack::ShowStatus, for better default error messages.
|
||||||
|
* Bug fixes in the Camping adapter.
|
||||||
|
* Removed Rails adapter, was too alpha.
|
||||||
|
|
||||||
|
* February 26th, 2008: Third public release 0.3.
|
||||||
|
* LiteSpeed handler, by Adrian Madrid.
|
||||||
|
* SCGI handler, by Jeremy Evans.
|
||||||
|
* Pool sessions, by blink.
|
||||||
|
* OpenID authentication, by blink.
|
||||||
|
* :Port and :File options for opening FastCGI sockets, by blink.
|
||||||
|
* Last-Modified HTTP header for Rack::File, by blink.
|
||||||
|
* Rack::Builder#use now accepts blocks, by Corey Jewett.
|
||||||
|
(See example/protectedlobster.ru)
|
||||||
|
* HTTP status 201 can contain a Content-Type and a body now.
|
||||||
|
* Many bugfixes, especially related to Cookie handling.
|
||||||
|
|
||||||
|
* August 21st, 2008: Fourth public release 0.4.
|
||||||
|
* New middleware, Rack::Deflater, by Christoffer Sawicki.
|
||||||
|
* OpenID authentication now needs ruby-openid 2.
|
||||||
|
* New Memcache sessions, by blink.
|
||||||
|
* Explicit EventedMongrel handler, by Joshua Peek <josh@joshpeek.com>
|
||||||
|
* Rack::Reloader is not loaded in rackup development mode.
|
||||||
|
* rackup can daemonize with -D.
|
||||||
|
* Many bugfixes, especially for pool sessions, URLMap, thread safety
|
||||||
|
and tempfile handling.
|
||||||
|
* Improved tests.
|
||||||
|
* Rack moved to Git.
|
||||||
|
|
||||||
|
* January 6th, 2009: Fifth public release 0.9.
|
||||||
|
* Rack is now managed by the Rack Core Team.
|
||||||
|
* Rack::Lint is stricter and follows the HTTP RFCs more closely.
|
||||||
|
* Added ConditionalGet middleware.
|
||||||
|
* Added ContentLength middleware.
|
||||||
|
* Added Deflater middleware.
|
||||||
|
* Added Head middleware.
|
||||||
|
* Added MethodOverride middleware.
|
||||||
|
* Rack::Mime now provides popular MIME-types and their extension.
|
||||||
|
* Mongrel Header now streams.
|
||||||
|
* Added Thin handler.
|
||||||
|
* Official support for swiftiplied Mongrel.
|
||||||
|
* Secure cookies.
|
||||||
|
* Made HeaderHash case-preserving.
|
||||||
|
* Many bugfixes and small improvements.
|
||||||
|
|
||||||
|
* January 9th, 2009: Sixth public release 0.9.1.
|
||||||
|
* Fix directory traversal exploits in Rack::File and Rack::Directory.
|
||||||
|
|
||||||
|
* April 25th, 2009: Seventh public release 1.0.0.
|
||||||
|
* SPEC change: Rack::VERSION has been pushed to [1,0].
|
||||||
|
* SPEC change: header values must be Strings now, split on "\n".
|
||||||
|
* SPEC change: Content-Length can be missing, in this case chunked transfer
|
||||||
|
encoding is used.
|
||||||
|
* SPEC change: rack.input must be rewindable and support reading into
|
||||||
|
a buffer, wrap with Rack::RewindableInput if it isn't.
|
||||||
|
* SPEC change: rack.session is now specified.
|
||||||
|
* SPEC change: Bodies can now additionally respond to #to_path with
|
||||||
|
a filename to be served.
|
||||||
|
* NOTE: String bodies break in 1.9, use an Array consisting of a
|
||||||
|
single String instead.
|
||||||
|
* New middleware Rack::Lock.
|
||||||
|
* New middleware Rack::ContentType.
|
||||||
|
* Rack::Reloader has been rewritten.
|
||||||
|
* Major update to Rack::Auth::OpenID.
|
||||||
|
* Support for nested parameter parsing in Rack::Response.
|
||||||
|
* Support for redirects in Rack::Response.
|
||||||
|
* HttpOnly cookie support in Rack::Response.
|
||||||
|
* The Rakefile has been rewritten.
|
||||||
|
* Many bugfixes and small improvements.
|
||||||
|
|
||||||
|
* October 18th, 2009: Eighth public release 1.0.1.
|
||||||
|
* Bump remainder of rack.versions.
|
||||||
|
* Support the pure Ruby FCGI implementation.
|
||||||
|
* Fix for form names containing "=": split first then unescape components
|
||||||
|
* Fixes the handling of the filename parameter with semicolons in names.
|
||||||
|
* Add anchor to nested params parsing regexp to prevent stack overflows
|
||||||
|
* Use more compatible gzip write api instead of "<<".
|
||||||
|
* Make sure that Reloader doesn't break when executed via ruby -e
|
||||||
|
* Make sure WEBrick respects the :Host option
|
||||||
|
* Many Ruby 1.9 fixes.
|
||||||
|
|
||||||
|
== Contact
|
||||||
|
|
||||||
|
Please mail bugs, suggestions and patches to
|
||||||
|
<mailto:rack-devel@googlegroups.com>.
|
||||||
|
|
||||||
|
Mailing list archives are available at
|
||||||
|
<http://groups.google.com/group/rack-devel>.
|
||||||
|
|
||||||
|
There is a bug tracker at <http://rack.lighthouseapp.com/>.
|
||||||
|
|
||||||
|
Git repository (send Git patches to the mailing list):
|
||||||
|
* http://github.com/rack/rack
|
||||||
|
* http://git.vuxu.org/cgi-bin/gitweb.cgi?p=rack.git
|
||||||
|
|
||||||
|
You are also welcome to join the #rack channel on irc.freenode.net.
|
||||||
|
|
||||||
|
== Thanks
|
||||||
|
|
||||||
|
The Rack Core Team, consisting of
|
||||||
|
|
||||||
|
* Christian Neukirchen (chneukirchen)
|
||||||
|
* James Tucker (raggi)
|
||||||
|
* Josh Peek (josh)
|
||||||
|
* Michael Fellinger (manveru)
|
||||||
|
* Ryan Tomayko (rtomayko)
|
||||||
|
* Scytrin dai Kinthra (scytrin)
|
||||||
|
|
||||||
|
would like to thank:
|
||||||
|
|
||||||
|
* Adrian Madrid, for the LiteSpeed handler.
|
||||||
|
* Christoffer Sawicki, for the first Rails adapter and Rack::Deflater.
|
||||||
|
* Tim Fletcher, for the HTTP authentication code.
|
||||||
|
* Luc Heinrich for the Cookie sessions, the static file handler and bugfixes.
|
||||||
|
* Armin Ronacher, for the logo and racktools.
|
||||||
|
* Aredridel, Ben Alpert, Dan Kubb, Daniel Roethlisberger, Matt Todd,
|
||||||
|
Tom Robinson, Phil Hagelberg, and S. Brent Faulkner for bug fixing
|
||||||
|
and other improvements.
|
||||||
|
* Brian Candler, for Rack::ContentType.
|
||||||
|
* Graham Batty, for improved handler loading.
|
||||||
|
* Stephen Bannasch, for bug reports and documentation.
|
||||||
|
* Gary Wright, for proposing a better Rack::Response interface.
|
||||||
|
* Jonathan Buch, for improvements regarding Rack::Response.
|
||||||
|
* Armin Röhrl, for tracking down bugs in the Cookie generator.
|
||||||
|
* Alexander Kellett for testing the Gem and reviewing the announcement.
|
||||||
|
* Marcus Rückert, for help with configuring and debugging lighttpd.
|
||||||
|
* The WSGI team for the well-done and documented work they've done and
|
||||||
|
Rack builds up on.
|
||||||
|
* All bug reporters and patch contributers not mentioned above.
|
||||||
|
|
||||||
|
== Copyright
|
||||||
|
|
||||||
|
Copyright (C) 2007, 2008, 2009 Christian Neukirchen <http://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
|
||||||
|
deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
== Links
|
||||||
|
|
||||||
|
Rack:: <http://rack.rubyforge.org/>
|
||||||
|
Rack's Rubyforge project:: <http://rubyforge.org/projects/rack>
|
||||||
|
Official Rack repositories:: <http://github.com/rack>
|
||||||
|
rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
|
||||||
|
|
||||||
|
Christian Neukirchen:: <http://chneukirchen.org/>
|
||||||
|
|
164
vendor/plugins/rack/Rakefile
vendored
Normal file
164
vendor/plugins/rack/Rakefile
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
# Rakefile for Rack. -*-ruby-*-
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
require 'rake/testtask'
|
||||||
|
|
||||||
|
|
||||||
|
desc "Run all the tests"
|
||||||
|
task :default => [:test]
|
||||||
|
|
||||||
|
|
||||||
|
desc "Make an archive as .tar.gz"
|
||||||
|
task :dist => [:chmod, :changelog, :rdoc, "SPEC", "rack.gemspec"] do
|
||||||
|
FileUtils.touch("RDOX")
|
||||||
|
sh "git archive --format=tar --prefix=#{release}/ HEAD^{tree} >#{release}.tar"
|
||||||
|
sh "pax -waf #{release}.tar -s ':^:#{release}/:' RDOX SPEC ChangeLog doc rack.gemspec"
|
||||||
|
sh "gzip -f -9 #{release}.tar"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Make an official release"
|
||||||
|
task :officialrelease do
|
||||||
|
puts "Official build for #{release}..."
|
||||||
|
sh "rm -rf stage"
|
||||||
|
sh "git clone --shared . stage"
|
||||||
|
sh "cd stage && rake officialrelease_really"
|
||||||
|
sh "mv stage/#{release}.tar.gz stage/#{release}.gem ."
|
||||||
|
end
|
||||||
|
|
||||||
|
task :officialrelease_really => [:fulltest, "RDOX", "SPEC", :dist, :gem] do
|
||||||
|
sh "sha1sum #{release}.tar.gz #{release}.gem"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def version
|
||||||
|
abort "You need to pass VERSION=... to build packages." unless ENV["VERSION"]
|
||||||
|
ENV["VERSION"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def release
|
||||||
|
"rack-#{version}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def manifest
|
||||||
|
`git ls-files`.split("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
desc "Make binaries executable"
|
||||||
|
task :chmod do
|
||||||
|
Dir["bin/*"].each { |binary| File.chmod(0775, binary) }
|
||||||
|
Dir["test/cgi/test*"].each { |binary| File.chmod(0775, binary) }
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Generate a ChangeLog"
|
||||||
|
task :changelog do
|
||||||
|
File.open("ChangeLog", "w") { |out|
|
||||||
|
`git log -z`.split("\0").map { |chunk|
|
||||||
|
author = chunk[/Author: (.*)/, 1].strip
|
||||||
|
date = chunk[/Date: (.*)/, 1].strip
|
||||||
|
desc, detail = $'.strip.split("\n", 2)
|
||||||
|
detail ||= ""
|
||||||
|
detail = detail.gsub(/.*darcs-hash:.*/, '')
|
||||||
|
detail.rstrip!
|
||||||
|
out.puts "#{date} #{author}"
|
||||||
|
out.puts " * #{desc.strip}"
|
||||||
|
out.puts detail unless detail.empty?
|
||||||
|
out.puts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
desc "Generate RDox"
|
||||||
|
task "RDOX" do
|
||||||
|
sh "specrb -Ilib:test -a --rdox >RDOX"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Generate Rack Specification"
|
||||||
|
task "SPEC" do
|
||||||
|
File.open("SPEC", "wb") { |file|
|
||||||
|
IO.foreach("lib/rack/lint.rb") { |line|
|
||||||
|
if line =~ /## (.*)/
|
||||||
|
file.puts $1
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Run all the fast tests"
|
||||||
|
task :test do
|
||||||
|
sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS'] || '-t "^(?!Rack::Handler|Rack::Adapter|Rack::Session::Memcache|Rack::Auth::OpenID)"'}"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Run all the tests"
|
||||||
|
task :fulltest => [:chmod] do
|
||||||
|
sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'rubygems'
|
||||||
|
rescue LoadError
|
||||||
|
# Too bad.
|
||||||
|
else
|
||||||
|
task "rack.gemspec" do
|
||||||
|
spec = Gem::Specification.new do |s|
|
||||||
|
s.name = "rack"
|
||||||
|
s.version = version
|
||||||
|
s.platform = Gem::Platform::RUBY
|
||||||
|
s.summary = "a modular Ruby webserver interface"
|
||||||
|
|
||||||
|
s.description = <<-EOF
|
||||||
|
Rack provides minimal, modular and adaptable interface for developing
|
||||||
|
web applications in Ruby. By wrapping HTTP requests and responses in
|
||||||
|
the simplest way possible, it unifies and distills the API for web
|
||||||
|
servers, web frameworks, and software in between (the so-called
|
||||||
|
middleware) into a single method call.
|
||||||
|
|
||||||
|
Also see http://rack.rubyforge.org.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
s.files = manifest + %w(SPEC RDOX rack.gemspec)
|
||||||
|
s.bindir = 'bin'
|
||||||
|
s.executables << 'rackup'
|
||||||
|
s.require_path = 'lib'
|
||||||
|
s.has_rdoc = true
|
||||||
|
s.extra_rdoc_files = ['README', 'SPEC', 'RDOX', 'KNOWN-ISSUES']
|
||||||
|
s.test_files = Dir['test/{test,spec}_*.rb']
|
||||||
|
|
||||||
|
s.author = 'Christian Neukirchen'
|
||||||
|
s.email = 'chneukirchen@gmail.com'
|
||||||
|
s.homepage = 'http://rack.rubyforge.org'
|
||||||
|
s.rubyforge_project = 'rack'
|
||||||
|
|
||||||
|
s.add_development_dependency 'test-spec'
|
||||||
|
|
||||||
|
s.add_development_dependency 'camping'
|
||||||
|
s.add_development_dependency 'fcgi'
|
||||||
|
s.add_development_dependency 'memcache-client'
|
||||||
|
s.add_development_dependency 'mongrel'
|
||||||
|
s.add_development_dependency 'ruby-openid', '~> 2.0.0'
|
||||||
|
s.add_development_dependency 'thin'
|
||||||
|
end
|
||||||
|
|
||||||
|
File.open("rack.gemspec", "w") { |f| f << spec.to_ruby }
|
||||||
|
end
|
||||||
|
|
||||||
|
task :gem => ["rack.gemspec", "SPEC"] do
|
||||||
|
FileUtils.touch("RDOX")
|
||||||
|
sh "gem build rack.gemspec"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Generate RDoc documentation"
|
||||||
|
task :rdoc do
|
||||||
|
sh(*%w{rdoc --line-numbers --main README
|
||||||
|
--title 'Rack\ Documentation' --charset utf-8 -U -o doc} +
|
||||||
|
%w{README KNOWN-ISSUES SPEC RDOX} +
|
||||||
|
Dir["lib/**/*.rb"])
|
||||||
|
end
|
||||||
|
|
||||||
|
task :pushsite => [:rdoc] do
|
||||||
|
sh "cd site && git gc"
|
||||||
|
sh "rsync -avz doc/ chneukirchen@rack.rubyforge.org:/var/www/gforge-projects/rack/doc/"
|
||||||
|
sh "rsync -avz site/ chneukirchen@rack.rubyforge.org:/var/www/gforge-projects/rack/"
|
||||||
|
sh "cd site && git push"
|
||||||
|
end
|
176
vendor/plugins/rack/bin/rackup
vendored
Executable file
176
vendor/plugins/rack/bin/rackup
vendored
Executable file
|
@ -0,0 +1,176 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# -*- ruby -*-
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift File.expand_path("#{__FILE__}/../../lib")
|
||||||
|
autoload :Rack, 'rack'
|
||||||
|
|
||||||
|
require 'optparse'
|
||||||
|
|
||||||
|
automatic = false
|
||||||
|
server = nil
|
||||||
|
env = "development"
|
||||||
|
daemonize = false
|
||||||
|
pid = nil
|
||||||
|
options = {:Port => 9292, :Host => "0.0.0.0", :AccessLog => []}
|
||||||
|
|
||||||
|
# Don't evaluate CGI ISINDEX parameters.
|
||||||
|
# http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
|
||||||
|
ARGV.clear if ENV.include?("REQUEST_METHOD")
|
||||||
|
|
||||||
|
opts = OptionParser.new("", 24, ' ') { |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)") {
|
||||||
|
$DEBUG = true
|
||||||
|
}
|
||||||
|
opts.on("-w", "--warn", "turn warnings on for your script") {
|
||||||
|
$-w = true
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.on("-I", "--include PATH",
|
||||||
|
"specify $LOAD_PATH (may be used more than once)") { |path|
|
||||||
|
$LOAD_PATH.unshift(*path.split(":"))
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.on("-r", "--require LIBRARY",
|
||||||
|
"require the library, before executing your script") { |library|
|
||||||
|
require library
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.separator ""
|
||||||
|
opts.separator "Rack options:"
|
||||||
|
opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s|
|
||||||
|
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|
|
||||||
|
env = e
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
|
||||||
|
daemonize = d ? true : false
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
|
||||||
|
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
|
||||||
|
|
||||||
|
opts.parse! ARGV
|
||||||
|
}
|
||||||
|
|
||||||
|
require 'pp' if $DEBUG
|
||||||
|
|
||||||
|
config = ARGV[0] || "config.ru"
|
||||||
|
if !File.exist? config
|
||||||
|
abort "configuration #{config} not found"
|
||||||
|
end
|
||||||
|
|
||||||
|
if config =~ /\.ru$/
|
||||||
|
cfgfile = File.read(config)
|
||||||
|
if cfgfile[/^#\\(.*)/]
|
||||||
|
opts.parse! $1.split(/\s+/)
|
||||||
|
end
|
||||||
|
inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
|
||||||
|
nil, config
|
||||||
|
else
|
||||||
|
require config
|
||||||
|
inner_app = Object.const_get(File.basename(config, '.rb').capitalize)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless server = Rack::Handler.get(server)
|
||||||
|
# Guess.
|
||||||
|
if ENV.include?("PHP_FCGI_CHILDREN")
|
||||||
|
server = Rack::Handler::FastCGI
|
||||||
|
|
||||||
|
# We already speak FastCGI
|
||||||
|
options.delete :File
|
||||||
|
options.delete :Port
|
||||||
|
elsif ENV.include?("REQUEST_METHOD")
|
||||||
|
server = Rack::Handler::CGI
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
server = Rack::Handler::Mongrel
|
||||||
|
rescue LoadError => e
|
||||||
|
server = Rack::Handler::WEBrick
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
p server if $DEBUG
|
||||||
|
|
||||||
|
case env
|
||||||
|
when "development"
|
||||||
|
app = Rack::Builder.new {
|
||||||
|
use Rack::CommonLogger, $stderr unless server.name =~ /CGI/
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
use Rack::Lint
|
||||||
|
run inner_app
|
||||||
|
}.to_app
|
||||||
|
|
||||||
|
when "deployment"
|
||||||
|
app = Rack::Builder.new {
|
||||||
|
use Rack::CommonLogger, $stderr unless server.name =~ /CGI/
|
||||||
|
run inner_app
|
||||||
|
}.to_app
|
||||||
|
|
||||||
|
when "none"
|
||||||
|
app = inner_app
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if $DEBUG
|
||||||
|
pp app
|
||||||
|
pp inner_app
|
||||||
|
end
|
||||||
|
|
||||||
|
if daemonize
|
||||||
|
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
|
||||||
|
|
||||||
|
if pid
|
||||||
|
File.open(pid, 'w'){ |f| f.write("#{Process.pid}") }
|
||||||
|
at_exit { File.delete(pid) if File.exist?(pid) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
server.run app, options
|
111
vendor/plugins/rack/contrib/rack_logo.svg
vendored
Normal file
111
vendor/plugins/rack/contrib/rack_logo.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 35 KiB |
4
vendor/plugins/rack/example/lobster.ru
vendored
Normal file
4
vendor/plugins/rack/example/lobster.ru
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
require 'rack/lobster'
|
||||||
|
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
run Rack::Lobster.new
|
14
vendor/plugins/rack/example/protectedlobster.rb
vendored
Normal file
14
vendor/plugins/rack/example/protectedlobster.rb
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
require 'rack'
|
||||||
|
require 'rack/lobster'
|
||||||
|
|
||||||
|
lobster = Rack::Lobster.new
|
||||||
|
|
||||||
|
protected_lobster = Rack::Auth::Basic.new(lobster) do |username, password|
|
||||||
|
'secret' == password
|
||||||
|
end
|
||||||
|
|
||||||
|
protected_lobster.realm = 'Lobster 2.0'
|
||||||
|
|
||||||
|
pretty_protected_lobster = Rack::ShowStatus.new(Rack::ShowExceptions.new(protected_lobster))
|
||||||
|
|
||||||
|
Rack::Handler::WEBrick.run pretty_protected_lobster, :Port => 9292
|
8
vendor/plugins/rack/example/protectedlobster.ru
vendored
Normal file
8
vendor/plugins/rack/example/protectedlobster.ru
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
require 'rack/lobster'
|
||||||
|
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
use Rack::Auth::Basic, "Lobster 2.0" do |username, password|
|
||||||
|
'secret' == password
|
||||||
|
end
|
||||||
|
|
||||||
|
run Rack::Lobster.new
|
|
@ -60,7 +60,7 @@ module Rack
|
||||||
@writer = block
|
@writer = block
|
||||||
gzip =::Zlib::GzipWriter.new(self)
|
gzip =::Zlib::GzipWriter.new(self)
|
||||||
gzip.mtime = @mtime
|
gzip.mtime = @mtime
|
||||||
@body.each { |part| gzip << part }
|
@body.each { |part| gzip.write(part) }
|
||||||
@body.close if @body.respond_to?(:close)
|
@body.close if @body.respond_to?(:close)
|
||||||
gzip.close
|
gzip.close
|
||||||
@writer = nil
|
@writer = nil
|
|
@ -3,7 +3,8 @@ require 'socket'
|
||||||
require 'rack/content_length'
|
require 'rack/content_length'
|
||||||
require 'rack/rewindable_input'
|
require 'rack/rewindable_input'
|
||||||
|
|
||||||
class FCGI::Stream
|
if defined? FCGI::Stream
|
||||||
|
class FCGI::Stream
|
||||||
alias _rack_read_without_buffer read
|
alias _rack_read_without_buffer read
|
||||||
|
|
||||||
def read(n, buffer=nil)
|
def read(n, buffer=nil)
|
||||||
|
@ -11,6 +12,7 @@ class FCGI::Stream
|
||||||
buffer.replace(buf.to_s) if buffer
|
buffer.replace(buf.to_s) if buffer
|
||||||
buf
|
buf
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module Rack
|
module Rack
|
|
@ -15,14 +15,19 @@ module Rack
|
||||||
env = ENV.to_hash
|
env = ENV.to_hash
|
||||||
env.delete "HTTP_CONTENT_LENGTH"
|
env.delete "HTTP_CONTENT_LENGTH"
|
||||||
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
||||||
env.update({"rack.version" => [1,0],
|
|
||||||
"rack.input" => StringIO.new($stdin.read.to_s),
|
rack_input = RewindableInput.new($stdin.read.to_s)
|
||||||
|
|
||||||
|
env.update(
|
||||||
|
"rack.version" => [1,0],
|
||||||
|
"rack.input" => rack_input,
|
||||||
"rack.errors" => $stderr,
|
"rack.errors" => $stderr,
|
||||||
"rack.multithread" => false,
|
"rack.multithread" => false,
|
||||||
"rack.multiprocess" => true,
|
"rack.multiprocess" => true,
|
||||||
"rack.run_once" => false,
|
"rack.run_once" => false,
|
||||||
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
|
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
|
||||||
})
|
)
|
||||||
|
|
||||||
env["QUERY_STRING"] ||= ""
|
env["QUERY_STRING"] ||= ""
|
||||||
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
||||||
env["REQUEST_PATH"] ||= "/"
|
env["REQUEST_PATH"] ||= "/"
|
|
@ -45,8 +45,11 @@ module Rack
|
||||||
|
|
||||||
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
||||||
|
|
||||||
|
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,0],
|
||||||
"rack.input" => request.body || StringIO.new(""),
|
"rack.input" => rack_input,
|
||||||
"rack.errors" => $stderr,
|
"rack.errors" => $stderr,
|
||||||
|
|
||||||
"rack.multithread" => true,
|
"rack.multithread" => true,
|
|
@ -32,10 +32,13 @@ module Rack
|
||||||
env["PATH_INFO"] = env["REQUEST_PATH"]
|
env["PATH_INFO"] = env["REQUEST_PATH"]
|
||||||
env["QUERY_STRING"] ||= ""
|
env["QUERY_STRING"] ||= ""
|
||||||
env["SCRIPT_NAME"] = ""
|
env["SCRIPT_NAME"] = ""
|
||||||
env.update({"rack.version" => [1,0],
|
|
||||||
"rack.input" => StringIO.new(input_body),
|
|
||||||
"rack.errors" => $stderr,
|
|
||||||
|
|
||||||
|
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],
|
||||||
|
"rack.input" => rack_input,
|
||||||
|
"rack.errors" => $stderr,
|
||||||
"rack.multithread" => true,
|
"rack.multithread" => true,
|
||||||
"rack.multiprocess" => true,
|
"rack.multiprocess" => true,
|
||||||
"rack.run_once" => false,
|
"rack.run_once" => false,
|
|
@ -6,6 +6,7 @@ module Rack
|
||||||
module Handler
|
module Handler
|
||||||
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
|
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
|
||||||
def self.run(app, options={})
|
def self.run(app, options={})
|
||||||
|
options[:BindAddress] = options.delete(:Host) if options[:Host]
|
||||||
server = ::WEBrick::HTTPServer.new(options)
|
server = ::WEBrick::HTTPServer.new(options)
|
||||||
server.mount "/", Rack::Handler::WEBrick, app
|
server.mount "/", Rack::Handler::WEBrick, app
|
||||||
trap(:INT) { server.shutdown }
|
trap(:INT) { server.shutdown }
|
||||||
|
@ -23,8 +24,11 @@ module Rack
|
||||||
env = req.meta_vars
|
env = req.meta_vars
|
||||||
env.delete_if { |k, v| v.nil? }
|
env.delete_if { |k, v| v.nil? }
|
||||||
|
|
||||||
|
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,0],
|
||||||
"rack.input" => StringIO.new(req.body.to_s),
|
"rack.input" => rack_input,
|
||||||
"rack.errors" => $stderr,
|
"rack.errors" => $stderr,
|
||||||
|
|
||||||
"rack.multithread" => true,
|
"rack.multithread" => true,
|
|
@ -233,8 +233,17 @@ module Rack
|
||||||
## === The Input Stream
|
## === The Input Stream
|
||||||
##
|
##
|
||||||
## The input stream is an IO-like object which contains the raw HTTP
|
## The input stream is an IO-like object which contains the raw HTTP
|
||||||
## POST data. If it is a file then it must be opened in binary mode.
|
## POST data.
|
||||||
def check_input(input)
|
def check_input(input)
|
||||||
|
## When applicable, its external encoding must be "ASCII-8BIT" and it
|
||||||
|
## must be opened in binary mode, for Ruby 1.9 compatibility.
|
||||||
|
assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
|
||||||
|
input.external_encoding.name == "ASCII-8BIT"
|
||||||
|
} if input.respond_to?(:external_encoding)
|
||||||
|
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+.
|
## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
|
||||||
[:gets, :each, :read, :rewind].each { |method|
|
[:gets, :each, :read, :rewind].each { |method|
|
||||||
assert("rack.input #{input} does not respond to ##{method}") {
|
assert("rack.input #{input} does not respond to ##{method}") {
|
|
@ -73,17 +73,14 @@ module Rack
|
||||||
# Return the Rack environment used for a request to +uri+.
|
# Return the Rack environment used for a request to +uri+.
|
||||||
def self.env_for(uri="", opts={})
|
def self.env_for(uri="", opts={})
|
||||||
uri = URI(uri)
|
uri = URI(uri)
|
||||||
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
|
||||||
|
|
||||||
env = DEFAULT_ENV.dup
|
env = DEFAULT_ENV.dup
|
||||||
|
|
||||||
env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
|
env["REQUEST_METHOD"] = opts[:method] || "GET"
|
||||||
env["SERVER_NAME"] = uri.host || "example.org"
|
env["SERVER_NAME"] = uri.host || "example.org"
|
||||||
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
|
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
|
||||||
env["QUERY_STRING"] = uri.query.to_s
|
env["QUERY_STRING"] = uri.query.to_s
|
||||||
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
|
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
|
||||||
env["rack.url_scheme"] = uri.scheme || "http"
|
env["rack.url_scheme"] = uri.scheme || "http"
|
||||||
env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
|
|
||||||
|
|
||||||
env["SCRIPT_NAME"] = opts[:script_name] || ""
|
env["SCRIPT_NAME"] = opts[:script_name] || ""
|
||||||
|
|
||||||
|
@ -93,34 +90,16 @@ module Rack
|
||||||
env["rack.errors"] = StringIO.new
|
env["rack.errors"] = StringIO.new
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
opts[:input] ||= ""
|
opts[:input] ||= ""
|
||||||
if String === opts[:input]
|
if String === opts[:input]
|
||||||
env["rack.input"] = StringIO.new(opts[:input])
|
rack_input = StringIO.new(opts[:input])
|
||||||
else
|
else
|
||||||
env["rack.input"] = opts[:input]
|
rack_input = opts[:input]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
|
||||||
|
env['rack.input'] = rack_input
|
||||||
|
|
||||||
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
|
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
|
||||||
|
|
||||||
opts.each { |field, value|
|
opts.each { |field, value|
|
||||||
|
@ -149,7 +128,7 @@ module Rack
|
||||||
@body = ""
|
@body = ""
|
||||||
body.each { |part| @body << part }
|
body.each { |part| @body << part }
|
||||||
|
|
||||||
@errors = errors.string if errors.respond_to?(:string)
|
@errors = errors.string
|
||||||
end
|
end
|
||||||
|
|
||||||
# Status
|
# Status
|
|
@ -70,7 +70,7 @@ module Rack
|
||||||
next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
|
next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
|
||||||
|
|
||||||
found, stat = figure_path(file, paths)
|
found, stat = figure_path(file, paths)
|
||||||
next unless found and stat and mtime = stat.mtime
|
next unless found && stat && mtime = stat.mtime
|
||||||
|
|
||||||
@cache[file] = found
|
@cache[file] = found
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ module Rack
|
||||||
found, stat = safe_stat(found)
|
found, stat = safe_stat(found)
|
||||||
return found, stat if found
|
return found, stat if found
|
||||||
|
|
||||||
paths.each do |possible_path|
|
paths.find do |possible_path|
|
||||||
path = ::File.join(possible_path, file)
|
path = ::File.join(possible_path, file)
|
||||||
found, stat = safe_stat(path)
|
found, stat = safe_stat(path)
|
||||||
return ::File.expand_path(found), stat if found
|
return ::File.expand_path(found), stat if found
|
|
@ -17,14 +17,6 @@ module Rack
|
||||||
# The environment of the request.
|
# The environment of the request.
|
||||||
attr_reader :env
|
attr_reader :env
|
||||||
|
|
||||||
def self.new(env, *args)
|
|
||||||
if self == Rack::Request
|
|
||||||
env["rack.request"] ||= super
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(env)
|
def initialize(env)
|
||||||
@env = env
|
@env = env
|
||||||
end
|
end
|
||||||
|
@ -73,7 +65,7 @@ module Rack
|
||||||
|
|
||||||
def host
|
def host
|
||||||
# Remove port number.
|
# Remove port number.
|
||||||
(@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '')
|
(@env["HTTP_HOST"] || @env["SERVER_NAME"]).to_s.gsub(/:\d+\z/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
|
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
|
|
@ -13,7 +13,7 @@ module Rack
|
||||||
# version since it's faster. (Stolen from Camping).
|
# version since it's faster. (Stolen from Camping).
|
||||||
def escape(s)
|
def escape(s)
|
||||||
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
||||||
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
'%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
||||||
}.tr(' ', '+')
|
}.tr(' ', '+')
|
||||||
end
|
end
|
||||||
module_function :escape
|
module_function :escape
|
||||||
|
@ -26,16 +26,18 @@ module Rack
|
||||||
end
|
end
|
||||||
module_function :unescape
|
module_function :unescape
|
||||||
|
|
||||||
|
DEFAULT_SEP = /[&;] */n
|
||||||
|
|
||||||
# Stolen from Mongrel, with some small modifications:
|
# Stolen from Mongrel, with some small modifications:
|
||||||
# Parses a query string by breaking it up at the '&'
|
# Parses a query string by breaking it up at the '&'
|
||||||
# and ';' characters. You can also use this to parse
|
# and ';' characters. You can also use this to parse
|
||||||
# cookies by changing the characters used in the second
|
# cookies by changing the characters used in the second
|
||||||
# parameter (which defaults to '&;').
|
# parameter (which defaults to '&;').
|
||||||
def parse_query(qs, d = '&;')
|
def parse_query(qs, d = nil)
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
(qs || '').split(/[#{d}] */n).each do |p|
|
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
|
||||||
k, v = unescape(p).split('=', 2)
|
k, v = p.split('=', 2).map { |x| unescape(x) }
|
||||||
|
|
||||||
if cur = params[k]
|
if cur = params[k]
|
||||||
if cur.class == Array
|
if cur.class == Array
|
||||||
|
@ -52,10 +54,10 @@ module Rack
|
||||||
end
|
end
|
||||||
module_function :parse_query
|
module_function :parse_query
|
||||||
|
|
||||||
def parse_nested_query(qs, d = '&;')
|
def parse_nested_query(qs, d = nil)
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
(qs || '').split(/[#{d}] */n).each do |p|
|
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
|
||||||
k, v = unescape(p).split('=', 2)
|
k, v = unescape(p).split('=', 2)
|
||||||
normalize_params(params, k, v)
|
normalize_params(params, k, v)
|
||||||
end
|
end
|
||||||
|
@ -101,31 +103,12 @@ module Rack
|
||||||
if v.class == Array
|
if v.class == Array
|
||||||
build_query(v.map { |x| [k, x] })
|
build_query(v.map { |x| [k, x] })
|
||||||
else
|
else
|
||||||
escape(k) + "=" + escape(v)
|
"#{escape(k)}=#{escape(v)}"
|
||||||
end
|
end
|
||||||
}.join("&")
|
}.join("&")
|
||||||
end
|
end
|
||||||
module_function :build_query
|
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.
|
# Escape ampersands, brackets and quotes to their HTML/XML entities.
|
||||||
def escape_html(string)
|
def escape_html(string)
|
||||||
string.to_s.gsub("&", "&").
|
string.to_s.gsub("&", "&").
|
||||||
|
@ -310,35 +293,7 @@ module Rack
|
||||||
# Usually, Rack::Request#POST takes care of calling this.
|
# Usually, Rack::Request#POST takes care of calling this.
|
||||||
|
|
||||||
module Multipart
|
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"
|
EOL = "\r\n"
|
||||||
MULTIPART_BOUNDARY = "AaB03x"
|
|
||||||
|
|
||||||
def self.parse_multipart(env)
|
def self.parse_multipart(env)
|
||||||
unless env['CONTENT_TYPE'] =~
|
unless env['CONTENT_TYPE'] =~
|
||||||
|
@ -375,7 +330,7 @@ module Rack
|
||||||
head = buf.slice!(0, i+2) # First \r\n
|
head = buf.slice!(0, i+2) # First \r\n
|
||||||
buf.slice!(0, 2) # Second \r\n
|
buf.slice!(0, 2) # Second \r\n
|
||||||
|
|
||||||
filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
|
filename = head[/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni, 1]
|
||||||
content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
|
content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
|
||||||
name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
|
name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
|
||||||
|
|
||||||
|
@ -441,76 +396,6 @@ module Rack
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
20
vendor/plugins/rack/test/cgi/lighttpd.conf
vendored
Normal file
20
vendor/plugins/rack/test/cgi/lighttpd.conf
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
server.modules = ("mod_fastcgi", "mod_cgi")
|
||||||
|
server.document-root = "."
|
||||||
|
server.errorlog = "lighttpd.errors"
|
||||||
|
server.port = 9203
|
||||||
|
|
||||||
|
server.event-handler = "select"
|
||||||
|
|
||||||
|
cgi.assign = ("/test" => "",
|
||||||
|
# ".ru" => ""
|
||||||
|
)
|
||||||
|
|
||||||
|
fastcgi.server = ("test.fcgi" => ("localhost" =>
|
||||||
|
("min-procs" => 1,
|
||||||
|
"socket" => "/tmp/rack-test-fcgi",
|
||||||
|
"bin-path" => "test.fcgi")),
|
||||||
|
"test.ru" => ("localhost" =>
|
||||||
|
("min-procs" => 1,
|
||||||
|
"socket" => "/tmp/rack-test-ru-fcgi",
|
||||||
|
"bin-path" => "test.ru")),
|
||||||
|
)
|
9
vendor/plugins/rack/test/cgi/test
vendored
Executable file
9
vendor/plugins/rack/test/cgi/test
vendored
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# -*- ruby -*-
|
||||||
|
|
||||||
|
$: << File.join(File.dirname(__FILE__), "..", "..", "lib")
|
||||||
|
|
||||||
|
require 'rack'
|
||||||
|
require '../testrequest'
|
||||||
|
|
||||||
|
Rack::Handler::CGI.run(Rack::Lint.new(TestRequest.new))
|
8
vendor/plugins/rack/test/cgi/test.fcgi
vendored
Executable file
8
vendor/plugins/rack/test/cgi/test.fcgi
vendored
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# -*- ruby -*-
|
||||||
|
|
||||||
|
$:.unshift '../../lib'
|
||||||
|
require 'rack'
|
||||||
|
require '../testrequest'
|
||||||
|
|
||||||
|
Rack::Handler::FastCGI.run(Rack::Lint.new(TestRequest.new))
|
7
vendor/plugins/rack/test/cgi/test.ru
vendored
Executable file
7
vendor/plugins/rack/test/cgi/test.ru
vendored
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env ../../bin/rackup
|
||||||
|
#\ -E deployment -I ../../lib
|
||||||
|
# -*- ruby -*-
|
||||||
|
|
||||||
|
require '../testrequest'
|
||||||
|
|
||||||
|
run TestRequest.new
|
BIN
vendor/plugins/rack/test/multipart/binary
vendored
Normal file
BIN
vendor/plugins/rack/test/multipart/binary
vendored
Normal file
Binary file not shown.
10
vendor/plugins/rack/test/multipart/empty
vendored
Normal file
10
vendor/plugins/rack/test/multipart/empty
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="submit-name"
|
||||||
|
|
||||||
|
Larry
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="files"; filename="file1.txt"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
|
||||||
|
--AaB03x--
|
6
vendor/plugins/rack/test/multipart/ie
vendored
Normal file
6
vendor/plugins/rack/test/multipart/ie
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="files"; filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
contents
|
||||||
|
--AaB03x--
|
10
vendor/plugins/rack/test/multipart/nested
vendored
Normal file
10
vendor/plugins/rack/test/multipart/nested
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="foo[submit-name]"
|
||||||
|
|
||||||
|
Larry
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="foo[files]"; filename="file1.txt"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
contents
|
||||||
|
--AaB03x--
|
9
vendor/plugins/rack/test/multipart/none
vendored
Normal file
9
vendor/plugins/rack/test/multipart/none
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="submit-name"
|
||||||
|
|
||||||
|
Larry
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="files"; filename=""
|
||||||
|
|
||||||
|
|
||||||
|
--AaB03x--
|
6
vendor/plugins/rack/test/multipart/semicolon
vendored
Normal file
6
vendor/plugins/rack/test/multipart/semicolon
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="files"; filename="fi;le1.txt"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
contents
|
||||||
|
--AaB03x--
|
10
vendor/plugins/rack/test/multipart/text
vendored
Normal file
10
vendor/plugins/rack/test/multipart/text
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="submit-name"
|
||||||
|
|
||||||
|
Larry
|
||||||
|
--AaB03x
|
||||||
|
Content-Disposition: form-data; name="files"; filename="file1.txt"
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
contents
|
||||||
|
--AaB03x--
|
73
vendor/plugins/rack/test/spec_rack_auth_basic.rb
vendored
Normal file
73
vendor/plugins/rack/test/spec_rack_auth_basic.rb
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/auth/basic'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context 'Rack::Auth::Basic' do
|
||||||
|
|
||||||
|
def realm
|
||||||
|
'WallysWorld'
|
||||||
|
end
|
||||||
|
|
||||||
|
def unprotected_app
|
||||||
|
lambda { |env| [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_app
|
||||||
|
app = Rack::Auth::Basic.new(unprotected_app) { |username, password| 'Boss' == username }
|
||||||
|
app.realm = realm
|
||||||
|
app
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do
|
||||||
|
@request = Rack::MockRequest.new(protected_app)
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_with_basic_auth(username, password, &block)
|
||||||
|
request 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:#{password}"].pack("m*"), &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def request(headers = {})
|
||||||
|
yield @request.get('/', headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_basic_auth_challenge(response)
|
||||||
|
response.should.be.a.client_error
|
||||||
|
response.status.should.equal 401
|
||||||
|
response.should.include 'WWW-Authenticate'
|
||||||
|
response.headers['WWW-Authenticate'].should =~ /Basic realm="#{Regexp.escape(realm)}"/
|
||||||
|
response.body.should.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should challenge correctly when no credentials are specified' do
|
||||||
|
request do |response|
|
||||||
|
assert_basic_auth_challenge response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should rechallenge if incorrect credentials are specified' do
|
||||||
|
request_with_basic_auth 'joe', 'password' do |response|
|
||||||
|
assert_basic_auth_challenge response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return application output if correct credentials are specified' do
|
||||||
|
request_with_basic_auth 'Boss', 'password' do |response|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Boss'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return 400 Bad Request if different auth scheme used' do
|
||||||
|
request 'HTTP_AUTHORIZATION' => 'Digest params' do |response|
|
||||||
|
response.should.be.a.client_error
|
||||||
|
response.status.should.equal 400
|
||||||
|
response.should.not.include 'WWW-Authenticate'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'realm as optional constructor arg' do
|
||||||
|
app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
|
||||||
|
assert_equal realm, app.realm
|
||||||
|
end
|
||||||
|
end
|
226
vendor/plugins/rack/test/spec_rack_auth_digest.rb
vendored
Normal file
226
vendor/plugins/rack/test/spec_rack_auth_digest.rb
vendored
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/auth/digest/md5'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context 'Rack::Auth::Digest::MD5' do
|
||||||
|
|
||||||
|
def realm
|
||||||
|
'WallysWorld'
|
||||||
|
end
|
||||||
|
|
||||||
|
def unprotected_app
|
||||||
|
lambda do |env|
|
||||||
|
[ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_app
|
||||||
|
app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
|
||||||
|
{ 'Alice' => 'correct-password' }[username]
|
||||||
|
end
|
||||||
|
app.realm = realm
|
||||||
|
app.opaque = 'this-should-be-secret'
|
||||||
|
app
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_app_with_hashed_passwords
|
||||||
|
app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
|
||||||
|
username == 'Alice' ? Digest::MD5.hexdigest("Alice:#{realm}:correct-password") : nil
|
||||||
|
end
|
||||||
|
app.realm = realm
|
||||||
|
app.opaque = 'this-should-be-secret'
|
||||||
|
app.passwords_hashed = true
|
||||||
|
app
|
||||||
|
end
|
||||||
|
|
||||||
|
def partially_protected_app
|
||||||
|
Rack::URLMap.new({
|
||||||
|
'/' => unprotected_app,
|
||||||
|
'/protected' => protected_app
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_app_with_method_override
|
||||||
|
Rack::MethodOverride.new(protected_app)
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do
|
||||||
|
@request = Rack::MockRequest.new(protected_app)
|
||||||
|
end
|
||||||
|
|
||||||
|
def request(method, path, headers = {}, &block)
|
||||||
|
response = @request.request(method, path, headers)
|
||||||
|
block.call(response) if block
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
class MockDigestRequest
|
||||||
|
def initialize(params)
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
def method_missing(sym)
|
||||||
|
if @params.has_key? k = sym.to_s
|
||||||
|
return @params[k]
|
||||||
|
end
|
||||||
|
super
|
||||||
|
end
|
||||||
|
def method
|
||||||
|
@params['method']
|
||||||
|
end
|
||||||
|
def response(password)
|
||||||
|
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_with_digest_auth(method, path, username, password, options = {}, &block)
|
||||||
|
request_options = {}
|
||||||
|
request_options[:input] = options.delete(:input) if options.include? :input
|
||||||
|
|
||||||
|
response = request(method, path, request_options)
|
||||||
|
|
||||||
|
return response unless response.status == 401
|
||||||
|
|
||||||
|
if wait = options.delete(:wait)
|
||||||
|
sleep wait
|
||||||
|
end
|
||||||
|
|
||||||
|
challenge = response['WWW-Authenticate'].split(' ', 2).last
|
||||||
|
|
||||||
|
params = Rack::Auth::Digest::Params.parse(challenge)
|
||||||
|
|
||||||
|
params['username'] = username
|
||||||
|
params['nc'] = '00000001'
|
||||||
|
params['cnonce'] = 'nonsensenonce'
|
||||||
|
params['uri'] = path
|
||||||
|
|
||||||
|
params['method'] = method
|
||||||
|
|
||||||
|
params.update options
|
||||||
|
|
||||||
|
params['response'] = MockDigestRequest.new(params).response(password)
|
||||||
|
|
||||||
|
request(method, path, request_options.merge('HTTP_AUTHORIZATION' => "Digest #{params}"), &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_digest_auth_challenge(response)
|
||||||
|
response.should.be.a.client_error
|
||||||
|
response.status.should.equal 401
|
||||||
|
response.should.include 'WWW-Authenticate'
|
||||||
|
response.headers['WWW-Authenticate'].should =~ /^Digest /
|
||||||
|
response.body.should.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_bad_request(response)
|
||||||
|
response.should.be.a.client_error
|
||||||
|
response.status.should.equal 400
|
||||||
|
response.should.not.include 'WWW-Authenticate'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should challenge when no credentials are specified' do
|
||||||
|
request 'GET', '/' do |response|
|
||||||
|
assert_digest_auth_challenge response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return application output if correct credentials given' do
|
||||||
|
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return application output if correct credentials given (hashed passwords)' do
|
||||||
|
@request = Rack::MockRequest.new(protected_app_with_hashed_passwords)
|
||||||
|
|
||||||
|
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should rechallenge if incorrect username given' do
|
||||||
|
request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response|
|
||||||
|
assert_digest_auth_challenge response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should rechallenge if incorrect password given' do
|
||||||
|
request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response|
|
||||||
|
assert_digest_auth_challenge response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should rechallenge with stale parameter if nonce is stale' do
|
||||||
|
begin
|
||||||
|
Rack::Auth::Digest::Nonce.time_limit = 1
|
||||||
|
|
||||||
|
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 2 do |response|
|
||||||
|
assert_digest_auth_challenge response
|
||||||
|
response.headers['WWW-Authenticate'].should =~ /\bstale=true\b/
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
Rack::Auth::Digest::Nonce.time_limit = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return 400 Bad Request if incorrect qop given' do
|
||||||
|
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response|
|
||||||
|
assert_bad_request response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return 400 Bad Request if incorrect uri given' do
|
||||||
|
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response|
|
||||||
|
assert_bad_request response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return 400 Bad Request if different auth scheme used' do
|
||||||
|
request 'GET', '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response|
|
||||||
|
assert_bad_request response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should not require credentials for unprotected path' do
|
||||||
|
@request = Rack::MockRequest.new(partially_protected_app)
|
||||||
|
request 'GET', '/' do |response|
|
||||||
|
response.should.be.ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should challenge when no credentials are specified for protected path' do
|
||||||
|
@request = Rack::MockRequest.new(partially_protected_app)
|
||||||
|
request 'GET', '/protected' do |response|
|
||||||
|
assert_digest_auth_challenge response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return application output if correct credentials given for protected path' do
|
||||||
|
@request = Rack::MockRequest.new(partially_protected_app)
|
||||||
|
request_with_digest_auth 'GET', '/protected', 'Alice', 'correct-password' do |response|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return application output if correct credentials given for POST' do
|
||||||
|
request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should return application output if correct credentials given for PUT (using method override of POST)' do
|
||||||
|
@request = Rack::MockRequest.new(protected_app_with_method_override)
|
||||||
|
request_with_digest_auth 'POST', '/', 'Alice', 'correct-password', :input => "_method=put" do |response|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'realm as optional constructor arg' do
|
||||||
|
app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true }
|
||||||
|
assert_equal realm, app.realm
|
||||||
|
end
|
||||||
|
end
|
84
vendor/plugins/rack/test/spec_rack_auth_openid.rb
vendored
Normal file
84
vendor/plugins/rack/test/spec_rack_auth_openid.rb
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
begin
|
||||||
|
# requires the ruby-openid gem
|
||||||
|
require 'rack/auth/openid'
|
||||||
|
|
||||||
|
context "Rack::Auth::OpenID" do
|
||||||
|
OID = Rack::Auth::OpenID
|
||||||
|
host = 'host'
|
||||||
|
subd = 'sub.host'
|
||||||
|
wild = '*.host'
|
||||||
|
path = 'path'
|
||||||
|
long = 'path/long'
|
||||||
|
scheme = 'http://'
|
||||||
|
realm = scheme+host+'/'+path
|
||||||
|
|
||||||
|
specify 'realm uri should be valid' do
|
||||||
|
lambda{OID.new('/'+path)}.should.raise ArgumentError
|
||||||
|
lambda{OID.new('/'+long)}.should.raise ArgumentError
|
||||||
|
lambda{OID.new(scheme+host)}.should.not.raise
|
||||||
|
lambda{OID.new(scheme+host+'/')}.should.not.raise
|
||||||
|
lambda{OID.new(scheme+host+'/'+path)}.should.not.raise
|
||||||
|
lambda{OID.new(scheme+subd)}.should.not.raise
|
||||||
|
lambda{OID.new(scheme+subd+'/')}.should.not.raise
|
||||||
|
lambda{OID.new(scheme+subd+'/'+path)}.should.not.raise
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should be able to check if a uri is within the realm' do
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'return_to should be valid' do
|
||||||
|
uri = '/'+path
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError
|
||||||
|
uri = '/'+long
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError
|
||||||
|
uri = scheme+host
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError
|
||||||
|
uri = scheme+host+'/'+path
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.not.raise
|
||||||
|
uri = scheme+subd+'/'+path
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError
|
||||||
|
uri = scheme+host+'/'+long
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.not.raise
|
||||||
|
uri = scheme+subd+'/'+long
|
||||||
|
lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'extensions should have required constants defined' do
|
||||||
|
badext = Rack::Auth::OpenID::BadExtension
|
||||||
|
ext = Object.new
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext = Module.new
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::Request = nil
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::Response = nil
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::NS_URI = nil
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'extensions should have Request and Response defined and inherit from OpenID::Extension' do
|
||||||
|
$-w, w = nil, $-w # yuck
|
||||||
|
badext = Rack::Auth::OpenID::BadExtension
|
||||||
|
ext = Module.new
|
||||||
|
ext::Request = nil
|
||||||
|
ext::Response = nil
|
||||||
|
ext::NS_URI = nil
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::Request = Class.new()
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::Response = Class.new()
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::Request = Class.new(::OpenID::Extension)
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
ext::Response = Class.new(::OpenID::Extension)
|
||||||
|
lambda{OID.new(realm).add_extension(ext)}.should.raise(badext)
|
||||||
|
$-w = w
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue LoadError
|
||||||
|
$stderr.puts "Skipping Rack::Auth::OpenID tests (ruby-openid 2 is required). `gem install ruby-openid` and try again."
|
||||||
|
end
|
84
vendor/plugins/rack/test/spec_rack_builder.rb
vendored
Normal file
84
vendor/plugins/rack/test/spec_rack_builder.rb
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/builder'
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/showexceptions'
|
||||||
|
require 'rack/auth/basic'
|
||||||
|
|
||||||
|
context "Rack::Builder" do
|
||||||
|
specify "chains apps by default" do
|
||||||
|
app = Rack::Builder.new do
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
run lambda { |env| raise "bzzzt" }
|
||||||
|
end.to_app
|
||||||
|
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "has implicit #to_app" do
|
||||||
|
app = Rack::Builder.new do
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
run lambda { |env| raise "bzzzt" }
|
||||||
|
end
|
||||||
|
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "supports blocks on use" do
|
||||||
|
app = Rack::Builder.new do
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
use Rack::Auth::Basic do |username, password|
|
||||||
|
'secret' == password
|
||||||
|
end
|
||||||
|
|
||||||
|
run lambda { |env| [200, {}, ['Hi Boss']] }
|
||||||
|
end
|
||||||
|
|
||||||
|
response = Rack::MockRequest.new(app).get("/")
|
||||||
|
response.should.be.client_error
|
||||||
|
response.status.should.equal 401
|
||||||
|
|
||||||
|
# with auth...
|
||||||
|
response = Rack::MockRequest.new(app).get("/",
|
||||||
|
'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.to_s.should.equal 'Hi Boss'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "has explicit #to_app" do
|
||||||
|
app = Rack::Builder.app do
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
run lambda { |env| raise "bzzzt" }
|
||||||
|
end
|
||||||
|
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "apps are initialized once" do
|
||||||
|
app = Rack::Builder.new do
|
||||||
|
class AppClass
|
||||||
|
def initialize
|
||||||
|
@called = 0
|
||||||
|
end
|
||||||
|
def call(env)
|
||||||
|
raise "bzzzt" if @called > 0
|
||||||
|
@called += 1
|
||||||
|
[200, {'Content-Type' => 'text/plain'}, ['OK']]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
use Rack::ShowExceptions
|
||||||
|
run AppClass.new
|
||||||
|
end
|
||||||
|
|
||||||
|
Rack::MockRequest.new(app).get("/").status.should.equal 200
|
||||||
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
51
vendor/plugins/rack/test/spec_rack_camping.rb
vendored
Normal file
51
vendor/plugins/rack/test/spec_rack_camping.rb
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'stringio'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
$-w, w = nil, $-w # yuck
|
||||||
|
require 'camping'
|
||||||
|
require 'rack/adapter/camping'
|
||||||
|
|
||||||
|
Camping.goes :CampApp
|
||||||
|
module CampApp
|
||||||
|
module Controllers
|
||||||
|
class HW < R('/')
|
||||||
|
def get
|
||||||
|
@headers["X-Served-By"] = URI("http://rack.rubyforge.org")
|
||||||
|
"Camping works!"
|
||||||
|
end
|
||||||
|
|
||||||
|
def post
|
||||||
|
"Data: #{input.foo}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
$-w = w
|
||||||
|
|
||||||
|
context "Rack::Adapter::Camping" do
|
||||||
|
specify "works with GET" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Adapter::Camping.new(CampApp)).
|
||||||
|
get("/")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res["Content-Type"].should.equal "text/html"
|
||||||
|
res["X-Served-By"].should.equal "http://rack.rubyforge.org"
|
||||||
|
|
||||||
|
res.body.should.equal "Camping works!"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "works with POST" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Adapter::Camping.new(CampApp)).
|
||||||
|
post("/", :input => "foo=bar")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.equal "Data: bar"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue LoadError
|
||||||
|
$stderr.puts "Skipping Rack::Adapter::Camping tests (Camping is required). `gem install camping` and try again."
|
||||||
|
end
|
50
vendor/plugins/rack/test/spec_rack_cascade.rb
vendored
Normal file
50
vendor/plugins/rack/test/spec_rack_cascade.rb
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/cascade'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
require 'rack/urlmap'
|
||||||
|
require 'rack/file'
|
||||||
|
|
||||||
|
context "Rack::Cascade" do
|
||||||
|
docroot = File.expand_path(File.dirname(__FILE__))
|
||||||
|
app1 = Rack::File.new(docroot)
|
||||||
|
|
||||||
|
app2 = Rack::URLMap.new("/crash" => lambda { |env| raise "boom" })
|
||||||
|
|
||||||
|
app3 = Rack::URLMap.new("/foo" => lambda { |env|
|
||||||
|
[200, { "Content-Type" => "text/plain"}, [""]]})
|
||||||
|
|
||||||
|
specify "should dispatch onward on 404 by default" do
|
||||||
|
cascade = Rack::Cascade.new([app1, app2, app3])
|
||||||
|
Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
|
||||||
|
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
|
||||||
|
Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
|
||||||
|
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should dispatch onward on whatever is passed" do
|
||||||
|
cascade = Rack::Cascade.new([app1, app2, app3], [404, 403])
|
||||||
|
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should fail if empty" do
|
||||||
|
lambda { Rack::MockRequest.new(Rack::Cascade.new([])).get("/") }.
|
||||||
|
should.raise(ArgumentError)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should append new app" do
|
||||||
|
cascade = Rack::Cascade.new([], [404, 403])
|
||||||
|
lambda { Rack::MockRequest.new(cascade).get('/cgi/test') }.
|
||||||
|
should.raise(ArgumentError)
|
||||||
|
cascade << app2
|
||||||
|
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.not_found
|
||||||
|
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
|
||||||
|
cascade << app1
|
||||||
|
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
|
||||||
|
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.forbidden
|
||||||
|
Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
|
||||||
|
cascade << app3
|
||||||
|
Rack::MockRequest.new(cascade).get('/foo').should.be.ok
|
||||||
|
end
|
||||||
|
end
|
89
vendor/plugins/rack/test/spec_rack_cgi.rb
vendored
Normal file
89
vendor/plugins/rack/test/spec_rack_cgi.rb
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'testrequest'
|
||||||
|
|
||||||
|
context "Rack::Handler::CGI" do
|
||||||
|
include TestRequest::Helpers
|
||||||
|
|
||||||
|
setup do
|
||||||
|
@host = '0.0.0.0'
|
||||||
|
@port = 9203
|
||||||
|
end
|
||||||
|
|
||||||
|
# Keep this first.
|
||||||
|
specify "startup" do
|
||||||
|
$pid = fork {
|
||||||
|
Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
|
||||||
|
exec "lighttpd -D -f lighttpd.conf"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should respond" do
|
||||||
|
sleep 1
|
||||||
|
lambda {
|
||||||
|
GET("/test")
|
||||||
|
}.should.not.raise
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be a lighttpd" do
|
||||||
|
GET("/test")
|
||||||
|
status.should.be 200
|
||||||
|
response["SERVER_SOFTWARE"].should =~ /lighttpd/
|
||||||
|
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
||||||
|
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
||||||
|
response["SERVER_PORT"].should.equal @port.to_s
|
||||||
|
response["SERVER_NAME"].should =~ @host
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have rack headers" do
|
||||||
|
GET("/test")
|
||||||
|
response["rack.version"].should.equal [1,0]
|
||||||
|
response["rack.multithread"].should.be false
|
||||||
|
response["rack.multiprocess"].should.be true
|
||||||
|
response["rack.run_once"].should.be true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have CGI headers on GET" do
|
||||||
|
GET("/test")
|
||||||
|
response["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test"
|
||||||
|
response["REQUEST_PATH"].should.equal "/"
|
||||||
|
response["PATH_INFO"].should.be.nil
|
||||||
|
response["QUERY_STRING"].should.equal ""
|
||||||
|
response["test.postdata"].should.equal ""
|
||||||
|
|
||||||
|
GET("/test/foo?quux=1")
|
||||||
|
response["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test"
|
||||||
|
response["REQUEST_PATH"].should.equal "/"
|
||||||
|
response["PATH_INFO"].should.equal "/foo"
|
||||||
|
response["QUERY_STRING"].should.equal "quux=1"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have CGI headers on POST" do
|
||||||
|
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
||||||
|
status.should.equal 200
|
||||||
|
response["REQUEST_METHOD"].should.equal "POST"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test"
|
||||||
|
response["REQUEST_PATH"].should.equal "/"
|
||||||
|
response["QUERY_STRING"].should.equal ""
|
||||||
|
response["HTTP_X_TEST_HEADER"].should.equal "42"
|
||||||
|
response["test.postdata"].should.equal "rack-form-data=23"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should support HTTP auth" do
|
||||||
|
GET("/test", {:user => "ruth", :passwd => "secret"})
|
||||||
|
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should set status" do
|
||||||
|
GET("/test?secret")
|
||||||
|
status.should.equal 403
|
||||||
|
response["rack.url_scheme"].should.equal "http"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Keep this last.
|
||||||
|
specify "shutdown" do
|
||||||
|
Process.kill 15, $pid
|
||||||
|
Process.wait($pid).should.equal $pid
|
||||||
|
end
|
||||||
|
end
|
62
vendor/plugins/rack/test/spec_rack_chunked.rb
vendored
Normal file
62
vendor/plugins/rack/test/spec_rack_chunked.rb
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/chunked'
|
||||||
|
require 'rack/utils'
|
||||||
|
|
||||||
|
context "Rack::Chunked" do
|
||||||
|
|
||||||
|
before do
|
||||||
|
@env = Rack::MockRequest.
|
||||||
|
env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'chunks responses with no Content-Length' do
|
||||||
|
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
||||||
|
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
|
||||||
|
response.headers.should.not.include 'Content-Length'
|
||||||
|
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
||||||
|
response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'chunks empty bodies properly' do
|
||||||
|
app = lambda { |env| [200, {}, []] }
|
||||||
|
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
|
||||||
|
response.headers.should.not.include 'Content-Length'
|
||||||
|
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
||||||
|
response.body.should.equal "0\r\n\r\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'does not modify response when Content-Length header present' do
|
||||||
|
app = lambda { |env| [200, {'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] }
|
||||||
|
status, headers, body = Rack::Chunked.new(app).call(@env)
|
||||||
|
status.should.equal 200
|
||||||
|
headers.should.not.include 'Transfer-Encoding'
|
||||||
|
headers.should.include 'Content-Length'
|
||||||
|
body.join.should.equal 'Hello World!'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'does not modify response when client is HTTP/1.0' do
|
||||||
|
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
||||||
|
@env['HTTP_VERSION'] = 'HTTP/1.0'
|
||||||
|
status, headers, body = Rack::Chunked.new(app).call(@env)
|
||||||
|
status.should.equal 200
|
||||||
|
headers.should.not.include 'Transfer-Encoding'
|
||||||
|
body.join.should.equal 'Hello World!'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'does not modify response when Transfer-Encoding header already present' do
|
||||||
|
app = lambda { |env| [200, {'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] }
|
||||||
|
status, headers, body = Rack::Chunked.new(app).call(@env)
|
||||||
|
status.should.equal 200
|
||||||
|
headers['Transfer-Encoding'].should.equal 'identity'
|
||||||
|
body.join.should.equal 'Hello World!'
|
||||||
|
end
|
||||||
|
|
||||||
|
[100, 204, 304].each do |status_code|
|
||||||
|
specify "does not modify response when status code is #{status_code}" do
|
||||||
|
app = lambda { |env| [status_code, {}, []] }
|
||||||
|
status, headers, body = Rack::Chunked.new(app).call(@env)
|
||||||
|
status.should.equal status_code
|
||||||
|
headers.should.not.include 'Transfer-Encoding'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
32
vendor/plugins/rack/test/spec_rack_commonlogger.rb
vendored
Normal file
32
vendor/plugins/rack/test/spec_rack_commonlogger.rb
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
require 'rack/commonlogger'
|
||||||
|
require 'rack/lobster'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::CommonLogger" do
|
||||||
|
app = lambda { |env|
|
||||||
|
[200,
|
||||||
|
{"Content-Type" => "text/html"},
|
||||||
|
["foo"]]}
|
||||||
|
|
||||||
|
specify "should log to rack.errors by default" do
|
||||||
|
log = StringIO.new
|
||||||
|
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
|
||||||
|
|
||||||
|
res.errors.should.not.be.empty
|
||||||
|
res.errors.should =~ /GET /
|
||||||
|
res.errors.should =~ / 200 / # status
|
||||||
|
res.errors.should =~ / 3 / # length
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should log to anything with <<" do
|
||||||
|
log = ""
|
||||||
|
res = Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
||||||
|
|
||||||
|
log.should =~ /GET /
|
||||||
|
log.should =~ / 200 / # status
|
||||||
|
log.should =~ / 3 / # length
|
||||||
|
end
|
||||||
|
end
|
41
vendor/plugins/rack/test/spec_rack_conditionalget.rb
vendored
Normal file
41
vendor/plugins/rack/test/spec_rack_conditionalget.rb
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'time'
|
||||||
|
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/conditionalget'
|
||||||
|
|
||||||
|
context "Rack::ConditionalGet" do
|
||||||
|
specify "should set a 304 status and truncate body when If-Modified-Since hits" do
|
||||||
|
timestamp = Time.now.httpdate
|
||||||
|
app = Rack::ConditionalGet.new(lambda { |env|
|
||||||
|
[200, {'Last-Modified'=>timestamp}, ['TEST']] })
|
||||||
|
|
||||||
|
response = Rack::MockRequest.new(app).
|
||||||
|
get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp)
|
||||||
|
|
||||||
|
response.status.should.equal 304
|
||||||
|
response.body.should.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should set a 304 status and truncate body when If-None-Match hits" do
|
||||||
|
app = Rack::ConditionalGet.new(lambda { |env|
|
||||||
|
[200, {'Etag'=>'1234'}, ['TEST']] })
|
||||||
|
|
||||||
|
response = Rack::MockRequest.new(app).
|
||||||
|
get("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
||||||
|
|
||||||
|
response.status.should.equal 304
|
||||||
|
response.body.should.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should not affect non-GET/HEAD requests" do
|
||||||
|
app = Rack::ConditionalGet.new(lambda { |env|
|
||||||
|
[200, {'Etag'=>'1234'}, ['TEST']] })
|
||||||
|
|
||||||
|
response = Rack::MockRequest.new(app).
|
||||||
|
post("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
||||||
|
|
||||||
|
response.status.should.equal 200
|
||||||
|
response.body.should.equal 'TEST'
|
||||||
|
end
|
||||||
|
end
|
43
vendor/plugins/rack/test/spec_rack_content_length.rb
vendored
Normal file
43
vendor/plugins/rack/test/spec_rack_content_length.rb
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/content_length'
|
||||||
|
|
||||||
|
context "Rack::ContentLength" do
|
||||||
|
specify "sets Content-Length on String bodies if none is set" do
|
||||||
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
|
||||||
|
response = Rack::ContentLength.new(app).call({})
|
||||||
|
response[1]['Content-Length'].should.equal '13'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "sets Content-Length on Array bodies if none is set" do
|
||||||
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
||||||
|
response = Rack::ContentLength.new(app).call({})
|
||||||
|
response[1]['Content-Length'].should.equal '13'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not set Content-Length on variable length bodies" do
|
||||||
|
body = lambda { "Hello World!" }
|
||||||
|
def body.each ; yield call ; end
|
||||||
|
|
||||||
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
|
||||||
|
response = Rack::ContentLength.new(app).call({})
|
||||||
|
response[1]['Content-Length'].should.be.nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not change Content-Length if it is already set" do
|
||||||
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] }
|
||||||
|
response = Rack::ContentLength.new(app).call({})
|
||||||
|
response[1]['Content-Length'].should.equal '1'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not set Content-Length on 304 responses" do
|
||||||
|
app = lambda { |env| [304, {'Content-Type' => 'text/plain'}, []] }
|
||||||
|
response = Rack::ContentLength.new(app).call({})
|
||||||
|
response[1]['Content-Length'].should.equal nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not set Content-Length when Transfer-Encoding is chunked" do
|
||||||
|
app = lambda { |env| [200, {'Transfer-Encoding' => 'chunked'}, []] }
|
||||||
|
response = Rack::ContentLength.new(app).call({})
|
||||||
|
response[1]['Content-Length'].should.equal nil
|
||||||
|
end
|
||||||
|
end
|
30
vendor/plugins/rack/test/spec_rack_content_type.rb
vendored
Normal file
30
vendor/plugins/rack/test/spec_rack_content_type.rb
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/content_type'
|
||||||
|
|
||||||
|
context "Rack::ContentType" do
|
||||||
|
specify "sets Content-Type to default text/html if none is set" do
|
||||||
|
app = lambda { |env| [200, {}, "Hello, World!"] }
|
||||||
|
status, headers, body = Rack::ContentType.new(app).call({})
|
||||||
|
headers['Content-Type'].should.equal 'text/html'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "sets Content-Type to chosen default if none is set" do
|
||||||
|
app = lambda { |env| [200, {}, "Hello, World!"] }
|
||||||
|
status, headers, body =
|
||||||
|
Rack::ContentType.new(app, 'application/octet-stream').call({})
|
||||||
|
headers['Content-Type'].should.equal 'application/octet-stream'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not change Content-Type if it is already set" do
|
||||||
|
app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
|
||||||
|
status, headers, body = Rack::ContentType.new(app).call({})
|
||||||
|
headers['Content-Type'].should.equal 'foo/bar'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "case insensitive detection of Content-Type" do
|
||||||
|
app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
|
||||||
|
status, headers, body = Rack::ContentType.new(app).call({})
|
||||||
|
headers.to_a.select { |k,v| k.downcase == "content-type" }.
|
||||||
|
should.equal [["CONTENT-Type","foo/bar"]]
|
||||||
|
end
|
||||||
|
end
|
127
vendor/plugins/rack/test/spec_rack_deflater.rb
vendored
Normal file
127
vendor/plugins/rack/test/spec_rack_deflater.rb
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/deflater'
|
||||||
|
require 'stringio'
|
||||||
|
require 'time' # for Time#httpdate
|
||||||
|
|
||||||
|
context "Rack::Deflater" do
|
||||||
|
def build_response(status, body, accept_encoding, headers = {})
|
||||||
|
body = [body] if body.respond_to? :to_str
|
||||||
|
app = lambda { |env| [status, {}, body] }
|
||||||
|
request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding))
|
||||||
|
response = Rack::Deflater.new(app).call(request)
|
||||||
|
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to deflate bodies that respond to each" do
|
||||||
|
body = Object.new
|
||||||
|
class << body; def each; yield("foo"); yield("bar"); end; end
|
||||||
|
|
||||||
|
response = build_response(200, body, "deflate")
|
||||||
|
|
||||||
|
response[0].should.equal(200)
|
||||||
|
response[1].should.equal({
|
||||||
|
"Content-Encoding" => "deflate",
|
||||||
|
"Vary" => "Accept-Encoding"
|
||||||
|
})
|
||||||
|
buf = ''
|
||||||
|
response[2].each { |part| buf << part }
|
||||||
|
buf.should.equal("K\313\317OJ,\002\000")
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: This is really just a special case of the above...
|
||||||
|
specify "should be able to deflate String bodies" do
|
||||||
|
response = build_response(200, "Hello world!", "deflate")
|
||||||
|
|
||||||
|
response[0].should.equal(200)
|
||||||
|
response[1].should.equal({
|
||||||
|
"Content-Encoding" => "deflate",
|
||||||
|
"Vary" => "Accept-Encoding"
|
||||||
|
})
|
||||||
|
buf = ''
|
||||||
|
response[2].each { |part| buf << part }
|
||||||
|
buf.should.equal("\363H\315\311\311W(\317/\312IQ\004\000")
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to gzip bodies that respond to each" do
|
||||||
|
body = Object.new
|
||||||
|
class << body; def each; yield("foo"); yield("bar"); end; end
|
||||||
|
|
||||||
|
response = build_response(200, body, "gzip")
|
||||||
|
|
||||||
|
response[0].should.equal(200)
|
||||||
|
response[1].should.equal({
|
||||||
|
"Content-Encoding" => "gzip",
|
||||||
|
"Vary" => "Accept-Encoding",
|
||||||
|
})
|
||||||
|
|
||||||
|
buf = ''
|
||||||
|
response[2].each { |part| buf << part }
|
||||||
|
io = StringIO.new(buf)
|
||||||
|
gz = Zlib::GzipReader.new(io)
|
||||||
|
gz.read.should.equal("foobar")
|
||||||
|
gz.close
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to fallback to no deflation" do
|
||||||
|
response = build_response(200, "Hello world!", "superzip")
|
||||||
|
|
||||||
|
response[0].should.equal(200)
|
||||||
|
response[1].should.equal({ "Vary" => "Accept-Encoding" })
|
||||||
|
response[2].should.equal(["Hello world!"])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to skip when there is no response entity body" do
|
||||||
|
response = build_response(304, [], "gzip")
|
||||||
|
|
||||||
|
response[0].should.equal(304)
|
||||||
|
response[1].should.equal({})
|
||||||
|
response[2].should.equal([])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should handle the lack of an acceptable encoding" do
|
||||||
|
response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
|
||||||
|
response1[0].should.equal(406)
|
||||||
|
response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
|
||||||
|
response1[2].should.equal(["An acceptable encoding for the requested resource / could not be found."])
|
||||||
|
|
||||||
|
response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar")
|
||||||
|
response2[0].should.equal(406)
|
||||||
|
response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"})
|
||||||
|
response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should handle gzip response with Last-Modified header" do
|
||||||
|
last_modified = Time.now.httpdate
|
||||||
|
|
||||||
|
app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] }
|
||||||
|
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
|
||||||
|
response = Rack::Deflater.new(app).call(request)
|
||||||
|
|
||||||
|
response[0].should.equal(200)
|
||||||
|
response[1].should.equal({
|
||||||
|
"Content-Encoding" => "gzip",
|
||||||
|
"Vary" => "Accept-Encoding",
|
||||||
|
"Last-Modified" => last_modified
|
||||||
|
})
|
||||||
|
|
||||||
|
buf = ''
|
||||||
|
response[2].each { |part| buf << part }
|
||||||
|
io = StringIO.new(buf)
|
||||||
|
gz = Zlib::GzipReader.new(io)
|
||||||
|
gz.read.should.equal("Hello World!")
|
||||||
|
gz.close
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should do nothing when no-transform Cache-Control directive present" do
|
||||||
|
app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] }
|
||||||
|
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
|
||||||
|
response = Rack::Deflater.new(app).call(request)
|
||||||
|
|
||||||
|
response[0].should.equal(200)
|
||||||
|
response[1].should.not.include "Content-Encoding"
|
||||||
|
response[2].join.should.equal("Hello World!")
|
||||||
|
end
|
||||||
|
end
|
61
vendor/plugins/rack/test/spec_rack_directory.rb
vendored
Normal file
61
vendor/plugins/rack/test/spec_rack_directory.rb
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/directory'
|
||||||
|
require 'rack/lint'
|
||||||
|
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::Directory" do
|
||||||
|
DOCROOT = File.expand_path(File.dirname(__FILE__))
|
||||||
|
FILE_CATCH = proc{|env| [200, {'Content-Type'=>'text/plain', "Content-Length" => "7"}, ['passed!']] }
|
||||||
|
app = Rack::Directory.new DOCROOT, FILE_CATCH
|
||||||
|
|
||||||
|
specify "serves directory indices" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/cgi/")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.should =~ /<html><head>/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "passes to app if file found" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/cgi/test")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.should =~ /passed!/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "serves uri with URL encoded filenames" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/%63%67%69/") # "/cgi/test"
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.should =~ /<html><head>/
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/cgi/%74%65%73%74") # "/cgi/test"
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.should =~ /passed!/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not allow directory traversal" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/cgi/../test")
|
||||||
|
|
||||||
|
res.should.be.forbidden
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/cgi/%2E%2E/test")
|
||||||
|
|
||||||
|
res.should.be.forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "404s if it can't find the file" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).
|
||||||
|
get("/cgi/blubb")
|
||||||
|
|
||||||
|
res.should.be.not_found
|
||||||
|
end
|
||||||
|
end
|
89
vendor/plugins/rack/test/spec_rack_fastcgi.rb
vendored
Normal file
89
vendor/plugins/rack/test/spec_rack_fastcgi.rb
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'testrequest'
|
||||||
|
|
||||||
|
context "Rack::Handler::FastCGI" do
|
||||||
|
include TestRequest::Helpers
|
||||||
|
|
||||||
|
setup do
|
||||||
|
@host = '0.0.0.0'
|
||||||
|
@port = 9203
|
||||||
|
end
|
||||||
|
|
||||||
|
# Keep this first.
|
||||||
|
specify "startup" do
|
||||||
|
$pid = fork {
|
||||||
|
Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
|
||||||
|
exec "lighttpd -D -f lighttpd.conf"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should respond" do
|
||||||
|
sleep 1
|
||||||
|
lambda {
|
||||||
|
GET("/test.fcgi")
|
||||||
|
}.should.not.raise
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be a lighttpd" do
|
||||||
|
GET("/test.fcgi")
|
||||||
|
status.should.be 200
|
||||||
|
response["SERVER_SOFTWARE"].should =~ /lighttpd/
|
||||||
|
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
||||||
|
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
||||||
|
response["SERVER_PORT"].should.equal @port.to_s
|
||||||
|
response["SERVER_NAME"].should =~ @host
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have rack headers" do
|
||||||
|
GET("/test.fcgi")
|
||||||
|
response["rack.version"].should.equal [1,0]
|
||||||
|
response["rack.multithread"].should.be false
|
||||||
|
response["rack.multiprocess"].should.be true
|
||||||
|
response["rack.run_once"].should.be false
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have CGI headers on GET" do
|
||||||
|
GET("/test.fcgi")
|
||||||
|
response["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test.fcgi"
|
||||||
|
response["REQUEST_PATH"].should.equal "/"
|
||||||
|
response["PATH_INFO"].should.be.nil
|
||||||
|
response["QUERY_STRING"].should.equal ""
|
||||||
|
response["test.postdata"].should.equal ""
|
||||||
|
|
||||||
|
GET("/test.fcgi/foo?quux=1")
|
||||||
|
response["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test.fcgi"
|
||||||
|
response["REQUEST_PATH"].should.equal "/"
|
||||||
|
response["PATH_INFO"].should.equal "/foo"
|
||||||
|
response["QUERY_STRING"].should.equal "quux=1"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have CGI headers on POST" do
|
||||||
|
POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
||||||
|
status.should.equal 200
|
||||||
|
response["REQUEST_METHOD"].should.equal "POST"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test.fcgi"
|
||||||
|
response["REQUEST_PATH"].should.equal "/"
|
||||||
|
response["QUERY_STRING"].should.equal ""
|
||||||
|
response["HTTP_X_TEST_HEADER"].should.equal "42"
|
||||||
|
response["test.postdata"].should.equal "rack-form-data=23"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should support HTTP auth" do
|
||||||
|
GET("/test.fcgi", {:user => "ruth", :passwd => "secret"})
|
||||||
|
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should set status" do
|
||||||
|
GET("/test.fcgi?secret")
|
||||||
|
status.should.equal 403
|
||||||
|
response["rack.url_scheme"].should.equal "http"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Keep this last.
|
||||||
|
specify "shutdown" do
|
||||||
|
Process.kill 15, $pid
|
||||||
|
Process.wait($pid).should.equal $pid
|
||||||
|
end
|
||||||
|
end
|
75
vendor/plugins/rack/test/spec_rack_file.rb
vendored
Normal file
75
vendor/plugins/rack/test/spec_rack_file.rb
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/file'
|
||||||
|
require 'rack/lint'
|
||||||
|
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::File" do
|
||||||
|
DOCROOT = File.expand_path(File.dirname(__FILE__))
|
||||||
|
|
||||||
|
specify "serves files" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/cgi/test")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.should =~ /ruby/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "sets Last-Modified header" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/cgi/test")
|
||||||
|
|
||||||
|
path = File.join(DOCROOT, "/cgi/test")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res["Last-Modified"].should.equal File.mtime(path).httpdate
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "serves files with URL encoded filenames" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/cgi/%74%65%73%74") # "/cgi/test"
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.should =~ /ruby/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not allow directory traversal" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/cgi/../test")
|
||||||
|
|
||||||
|
res.should.be.forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not allow directory traversal with encoded periods" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/%2E%2E/README")
|
||||||
|
|
||||||
|
res.should.be.forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "404s if it can't find the file" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/cgi/blubb")
|
||||||
|
|
||||||
|
res.should.be.not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "detects SystemCallErrors" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
||||||
|
get("/cgi")
|
||||||
|
|
||||||
|
res.should.be.not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "returns bodies that respond to #to_path" do
|
||||||
|
env = Rack::MockRequest.env_for("/cgi/test")
|
||||||
|
status, headers, body = Rack::File.new(DOCROOT).call(env)
|
||||||
|
|
||||||
|
path = File.join(DOCROOT, "/cgi/test")
|
||||||
|
|
||||||
|
status.should.equal 200
|
||||||
|
body.should.respond_to :to_path
|
||||||
|
body.to_path.should.equal path
|
||||||
|
end
|
||||||
|
end
|
43
vendor/plugins/rack/test/spec_rack_handler.rb
vendored
Normal file
43
vendor/plugins/rack/test/spec_rack_handler.rb
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/handler'
|
||||||
|
|
||||||
|
class Rack::Handler::Lobster; end
|
||||||
|
class RockLobster; end
|
||||||
|
|
||||||
|
context "Rack::Handler" do
|
||||||
|
specify "has registered default handlers" do
|
||||||
|
Rack::Handler.get('cgi').should.equal Rack::Handler::CGI
|
||||||
|
Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI
|
||||||
|
Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
|
||||||
|
Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "handler that doesn't exist should raise a NameError" do
|
||||||
|
lambda {
|
||||||
|
Rack::Handler.get('boom')
|
||||||
|
}.should.raise(NameError)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should get unregistered, but already required, handler by name" do
|
||||||
|
Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should register custom handler" do
|
||||||
|
Rack::Handler.register('rock_lobster', 'RockLobster')
|
||||||
|
Rack::Handler.get('rock_lobster').should.equal RockLobster
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should not need registration for properly coded handlers even if not already required" do
|
||||||
|
begin
|
||||||
|
$:.push "test/unregistered_handler"
|
||||||
|
Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered
|
||||||
|
lambda {
|
||||||
|
Rack::Handler.get('UnRegistered')
|
||||||
|
}.should.raise(NameError)
|
||||||
|
Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
|
||||||
|
ensure
|
||||||
|
$:.delete "test/unregistered_handler"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
30
vendor/plugins/rack/test/spec_rack_head.rb
vendored
Normal file
30
vendor/plugins/rack/test/spec_rack_head.rb
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
require 'rack/head'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::Head" do
|
||||||
|
def test_response(headers = {})
|
||||||
|
app = lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] }
|
||||||
|
request = Rack::MockRequest.env_for("/", headers)
|
||||||
|
response = Rack::Head.new(app).call(request)
|
||||||
|
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "passes GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
|
||||||
|
%w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
|
||||||
|
resp = test_response("REQUEST_METHOD" => type)
|
||||||
|
|
||||||
|
resp[0].should.equal(200)
|
||||||
|
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
||||||
|
resp[2].should.equal(["foo"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "removes body from HEAD requests" do
|
||||||
|
resp = test_response("REQUEST_METHOD" => "HEAD")
|
||||||
|
|
||||||
|
resp[0].should.equal(200)
|
||||||
|
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
||||||
|
resp[2].should.equal([])
|
||||||
|
end
|
||||||
|
end
|
521
vendor/plugins/rack/test/spec_rack_lint.rb
vendored
Normal file
521
vendor/plugins/rack/test/spec_rack_lint.rb
vendored
Normal file
|
@ -0,0 +1,521 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
require 'rack/lint'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::Lint" do
|
||||||
|
def env(*args)
|
||||||
|
Rack::MockRequest.env_for("/", *args)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "passes valid request" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.not.raise
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices fatal errors" do
|
||||||
|
lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/No env given/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices environment errors" do
|
||||||
|
lambda { Rack::Lint.new(nil).call 5 }.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/not a Hash/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
e = env
|
||||||
|
e.delete("REQUEST_METHOD")
|
||||||
|
Rack::Lint.new(nil).call(e)
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/missing required key REQUEST_METHOD/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
e = env
|
||||||
|
e.delete("SERVER_NAME")
|
||||||
|
Rack::Lint.new(nil).call(e)
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/missing required key SERVER_NAME/)
|
||||||
|
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("HTTP_CONTENT_TYPE" => "text/plain"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/contains HTTP_CONTENT_TYPE/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("HTTP_CONTENT_LENGTH" => "42"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/contains HTTP_CONTENT_LENGTH/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("FOO" => Object.new))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/non-string value/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("rack.version" => "0.2"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must be an Array/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("rack.url_scheme" => "gopher"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/url_scheme unknown/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("rack.session" => []))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.equal("session [] must respond to store and []=")
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/REQUEST_METHOD/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must start with/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("PATH_INFO" => "../foo"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must start with/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("CONTENT_LENGTH" => "xcii"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/Invalid CONTENT_LENGTH/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
e = env
|
||||||
|
e.delete("PATH_INFO")
|
||||||
|
e.delete("SCRIPT_NAME")
|
||||||
|
Rack::Lint.new(nil).call(e)
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/One of .* must be set/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/"))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/cannot be .* make it ''/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices input errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("rack.input" => ""))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/does not respond to #gets/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
input = Object.new
|
||||||
|
def input.binmode?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
Rack::Lint.new(nil).call(env("rack.input" => input))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/is not opened in binary mode/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
input = Object.new
|
||||||
|
def input.external_encoding
|
||||||
|
result = Object.new
|
||||||
|
def result.name
|
||||||
|
"US-ASCII"
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
Rack::Lint.new(nil).call(env("rack.input" => input))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/does not have ASCII-8BIT as its external encoding/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices error errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(nil).call(env("rack.errors" => ""))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/does not respond to #puts/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices status errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
["cc", {}, ""]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must be >=100 seen as integer/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[42, {}, ""]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must be >=100 seen as integer/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices header errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, Object.new, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.equal("headers object should respond to #each, but doesn't (got Object as headers)")
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {true=>false}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.equal("header key must be a string, was TrueClass")
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Status" => "404"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must not contain Status/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-Type:" => "text/plain"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must not contain :/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-" => "text/plain"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/must not end/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"..%%quark%%.." => "text/plain"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.equal("invalid header name: ..%%quark%%..")
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Foo" => Object.new}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.equal("a header value must be a String, but the value of 'Foo' is a Object")
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Foo" => [1, 2, 3]}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.equal("a header value must be a String, but the value of 'Foo' is a Array")
|
||||||
|
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Foo-Bar" => "text\000plain"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/invalid header/)
|
||||||
|
|
||||||
|
# line ends (010) should be allowed in header values.
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices content-type errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/No Content-Type/)
|
||||||
|
|
||||||
|
[100, 101, 204, 304].each do |status|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[status, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/Content-Type header found/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices content-length errors" do
|
||||||
|
[100, 101, 204, 304].each do |status|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[status, {"Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/Content-Length header found/)
|
||||||
|
end
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/Content-Length header was 1, but should be 0/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices body errors" do
|
||||||
|
lambda {
|
||||||
|
status, header, body = Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
|
||||||
|
}).call(env({}))
|
||||||
|
body.each { |part| }
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/yielded non-string/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices input handling errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].gets("\r\n")
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/gets called with arguments/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(1, 2, 3)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read called with too many arguments/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read("foo")
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read called with non-integer and non-nil length/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(-1)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read called with a negative length/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(nil, nil)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read called with non-String buffer/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(nil, 1)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read called with non-String buffer/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].rewind(0)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/rewind called with arguments/)
|
||||||
|
|
||||||
|
weirdio = Object.new
|
||||||
|
class << weirdio
|
||||||
|
def gets
|
||||||
|
42
|
||||||
|
end
|
||||||
|
|
||||||
|
def read
|
||||||
|
23
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
yield 23
|
||||||
|
yield 42
|
||||||
|
end
|
||||||
|
|
||||||
|
def rewind
|
||||||
|
raise Errno::ESPIPE, "Errno::ESPIPE"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
eof_weirdio = Object.new
|
||||||
|
class << eof_weirdio
|
||||||
|
def gets
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
end
|
||||||
|
|
||||||
|
def rewind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].gets
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env("rack.input" => weirdio))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/gets didn't return a String/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].each { |x| }
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env("rack.input" => weirdio))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/each didn't yield a String/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env("rack.input" => weirdio))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read didn't return nil or a String/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env("rack.input" => eof_weirdio))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/read\(nil\) returned nil on EOF/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].rewind
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env("rack.input" => weirdio))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/rewind raised Errno::ESPIPE/)
|
||||||
|
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].close
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/close must not be called/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices error handling errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.errors"].write(42)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/write not called with a String/)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.errors"].close
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/close must not be called/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "notices HEAD errors" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
|
||||||
|
}).call(env({"REQUEST_METHOD" => "HEAD"}))
|
||||||
|
}.should.not.raise
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
|
||||||
|
}).call(env({"REQUEST_METHOD" => "HEAD"}))
|
||||||
|
}.should.raise(Rack::Lint::LintError).
|
||||||
|
message.should.match(/body was given for HEAD/)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "passes valid read calls" do
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({"rack.input" => StringIO.new("hello world")}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(0)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({"rack.input" => StringIO.new("hello world")}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(1)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({"rack.input" => StringIO.new("hello world")}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(nil)
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({"rack.input" => StringIO.new("hello world")}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(nil, '')
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({"rack.input" => StringIO.new("hello world")}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Lint.new(lambda { |env|
|
||||||
|
env["rack.input"].read(1, '')
|
||||||
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
||||||
|
}).call(env({"rack.input" => StringIO.new("hello world")}))
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Rack::Lint::InputWrapper" do
|
||||||
|
specify "delegates :size to underlying IO object" do
|
||||||
|
class IOMock
|
||||||
|
def size
|
||||||
|
101
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wrapper = Rack::Lint::InputWrapper.new(IOMock.new)
|
||||||
|
wrapper.size.should == 101
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "delegates :rewind to underlying IO object" do
|
||||||
|
io = StringIO.new("123")
|
||||||
|
wrapper = Rack::Lint::InputWrapper.new(io)
|
||||||
|
wrapper.read.should.equal "123"
|
||||||
|
wrapper.read.should.equal ""
|
||||||
|
wrapper.rewind
|
||||||
|
wrapper.read.should.equal "123"
|
||||||
|
end
|
||||||
|
end
|
45
vendor/plugins/rack/test/spec_rack_lobster.rb
vendored
Normal file
45
vendor/plugins/rack/test/spec_rack_lobster.rb
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/lobster'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::Lobster::LambdaLobster" do
|
||||||
|
specify "should be a single lambda" do
|
||||||
|
Rack::Lobster::LambdaLobster.should.be.kind_of Proc
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should look like a lobster" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/")
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.include "(,(,,(,,,("
|
||||||
|
res.body.should.include "?flip"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be flippable" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/?flip")
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.include "(,,,(,,(,("
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Rack::Lobster" do
|
||||||
|
specify "should look like a lobster" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lobster.new).get("/")
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.include "(,(,,(,,,("
|
||||||
|
res.body.should.include "?flip"
|
||||||
|
res.body.should.include "crash"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be flippable" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=left")
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.include "(,,,(,,(,("
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide crashing for testing purposes" do
|
||||||
|
lambda {
|
||||||
|
Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=crash")
|
||||||
|
}.should.raise
|
||||||
|
end
|
||||||
|
end
|
38
vendor/plugins/rack/test/spec_rack_lock.rb
vendored
Normal file
38
vendor/plugins/rack/test/spec_rack_lock.rb
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/lock'
|
||||||
|
|
||||||
|
context "Rack::Lock" do
|
||||||
|
class Lock
|
||||||
|
attr_reader :synchronized
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@synchronized = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def synchronize
|
||||||
|
@synchronized = true
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should call synchronize on lock" do
|
||||||
|
lock = Lock.new
|
||||||
|
env = Rack::MockRequest.env_for("/")
|
||||||
|
app = Rack::Lock.new(lambda { |env| }, lock)
|
||||||
|
lock.synchronized.should.equal false
|
||||||
|
app.call(env)
|
||||||
|
lock.synchronized.should.equal true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should set multithread flag to false" do
|
||||||
|
app = Rack::Lock.new(lambda { |env| env['rack.multithread'] })
|
||||||
|
app.call(Rack::MockRequest.env_for("/")).should.equal false
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should reset original multithread flag when exiting lock" do
|
||||||
|
app = Rack::Lock.new(lambda { |env| env })
|
||||||
|
app.call(Rack::MockRequest.env_for("/"))['rack.multithread'].should.equal true
|
||||||
|
end
|
||||||
|
end
|
60
vendor/plugins/rack/test/spec_rack_methodoverride.rb
vendored
Normal file
60
vendor/plugins/rack/test/spec_rack_methodoverride.rb
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/methodoverride'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
context "Rack::MethodOverride" do
|
||||||
|
specify "should not affect GET requests" do
|
||||||
|
env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET")
|
||||||
|
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
|
||||||
|
req = app.call(env)
|
||||||
|
|
||||||
|
req.env["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "_method parameter should modify REQUEST_METHOD for POST requests" do
|
||||||
|
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put")
|
||||||
|
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
|
||||||
|
req = app.call(env)
|
||||||
|
|
||||||
|
req.env["REQUEST_METHOD"].should.equal "PUT"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "X-HTTP-Method-Override header should modify REQUEST_METHOD for POST requests" do
|
||||||
|
env = Rack::MockRequest.env_for("/",
|
||||||
|
:method => "POST",
|
||||||
|
"HTTP_X_HTTP_METHOD_OVERRIDE" => "PUT"
|
||||||
|
)
|
||||||
|
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
|
||||||
|
req = app.call(env)
|
||||||
|
|
||||||
|
req.env["REQUEST_METHOD"].should.equal "PUT"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should not modify REQUEST_METHOD if the method is unknown" do
|
||||||
|
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo")
|
||||||
|
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
|
||||||
|
req = app.call(env)
|
||||||
|
|
||||||
|
req.env["REQUEST_METHOD"].should.equal "POST"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should not modify REQUEST_METHOD when _method is nil" do
|
||||||
|
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "foo=bar")
|
||||||
|
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
|
||||||
|
req = app.call(env)
|
||||||
|
|
||||||
|
req.env["REQUEST_METHOD"].should.equal "POST"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should store the original REQUEST_METHOD prior to overriding" do
|
||||||
|
env = Rack::MockRequest.env_for("/",
|
||||||
|
:method => "POST",
|
||||||
|
:input => "_method=options")
|
||||||
|
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
|
||||||
|
req = app.call(env)
|
||||||
|
|
||||||
|
req.env["rack.methodoverride.original_method"].should.equal "POST"
|
||||||
|
end
|
||||||
|
end
|
157
vendor/plugins/rack/test/spec_rack_mock.rb
vendored
Normal file
157
vendor/plugins/rack/test/spec_rack_mock.rb
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
require 'yaml'
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/request'
|
||||||
|
require 'rack/response'
|
||||||
|
|
||||||
|
app = lambda { |env|
|
||||||
|
req = Rack::Request.new(env)
|
||||||
|
|
||||||
|
env["mock.postdata"] = env["rack.input"].read
|
||||||
|
if req.GET["error"]
|
||||||
|
env["rack.errors"].puts req.GET["error"]
|
||||||
|
env["rack.errors"].flush
|
||||||
|
end
|
||||||
|
|
||||||
|
Rack::Response.new(env.to_yaml,
|
||||||
|
req.GET["status"] || 200,
|
||||||
|
"Content-Type" => "text/yaml").finish
|
||||||
|
}
|
||||||
|
|
||||||
|
context "Rack::MockRequest" do
|
||||||
|
specify "should return a MockResponse" do
|
||||||
|
res = Rack::MockRequest.new(app).get("")
|
||||||
|
res.should.be.kind_of Rack::MockResponse
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to only return the environment" do
|
||||||
|
env = Rack::MockRequest.env_for("")
|
||||||
|
env.should.be.kind_of Hash
|
||||||
|
env.should.include "rack.version"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide sensible defaults" do
|
||||||
|
res = Rack::MockRequest.new(app).request
|
||||||
|
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
env["SERVER_NAME"].should.equal "example.org"
|
||||||
|
env["SERVER_PORT"].should.equal "80"
|
||||||
|
env["QUERY_STRING"].should.equal ""
|
||||||
|
env["PATH_INFO"].should.equal "/"
|
||||||
|
env["SCRIPT_NAME"].should.equal ""
|
||||||
|
env["rack.url_scheme"].should.equal "http"
|
||||||
|
env["mock.postdata"].should.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should allow GET/POST/PUT/DELETE" do
|
||||||
|
res = Rack::MockRequest.new(app).get("", :input => "foo")
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).post("", :input => "foo")
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["REQUEST_METHOD"].should.equal "POST"
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).put("", :input => "foo")
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["REQUEST_METHOD"].should.equal "PUT"
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).delete("", :input => "foo")
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["REQUEST_METHOD"].should.equal "DELETE"
|
||||||
|
|
||||||
|
Rack::MockRequest.env_for("/", :method => "OPTIONS")["REQUEST_METHOD"].
|
||||||
|
should.equal "OPTIONS"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should set content length" do
|
||||||
|
env = Rack::MockRequest.env_for("/", :input => "foo")
|
||||||
|
env["CONTENT_LENGTH"].should.equal "3"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should allow posting" do
|
||||||
|
res = Rack::MockRequest.new(app).get("", :input => "foo")
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["mock.postdata"].should.equal "foo"
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).post("", :input => StringIO.new("foo"))
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["mock.postdata"].should.equal "foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should use all parts of an URL" do
|
||||||
|
res = Rack::MockRequest.new(app).
|
||||||
|
get("https://bla.example.org:9292/meh/foo?bar")
|
||||||
|
res.should.be.kind_of Rack::MockResponse
|
||||||
|
|
||||||
|
env = YAML.load(res.body)
|
||||||
|
env["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
env["SERVER_NAME"].should.equal "bla.example.org"
|
||||||
|
env["SERVER_PORT"].should.equal "9292"
|
||||||
|
env["QUERY_STRING"].should.equal "bar"
|
||||||
|
env["PATH_INFO"].should.equal "/meh/foo"
|
||||||
|
env["rack.url_scheme"].should.equal "https"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should behave valid according to the Rack spec" do
|
||||||
|
lambda {
|
||||||
|
res = Rack::MockRequest.new(app).
|
||||||
|
get("https://bla.example.org:9292/meh/foo?bar", :lint => true)
|
||||||
|
}.should.not.raise(Rack::Lint::LintError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Rack::MockResponse" do
|
||||||
|
specify "should provide access to the HTTP status" do
|
||||||
|
res = Rack::MockRequest.new(app).get("")
|
||||||
|
res.should.be.successful
|
||||||
|
res.should.be.ok
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).get("/?status=404")
|
||||||
|
res.should.not.be.successful
|
||||||
|
res.should.be.client_error
|
||||||
|
res.should.be.not_found
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).get("/?status=501")
|
||||||
|
res.should.not.be.successful
|
||||||
|
res.should.be.server_error
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).get("/?status=307")
|
||||||
|
res.should.be.redirect
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).get("/?status=201", :lint => true)
|
||||||
|
res.should.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide access to the HTTP headers" do
|
||||||
|
res = Rack::MockRequest.new(app).get("")
|
||||||
|
res.should.include "Content-Type"
|
||||||
|
res.headers["Content-Type"].should.equal "text/yaml"
|
||||||
|
res.original_headers["Content-Type"].should.equal "text/yaml"
|
||||||
|
res["Content-Type"].should.equal "text/yaml"
|
||||||
|
res.content_type.should.equal "text/yaml"
|
||||||
|
res.content_length.should.be 401 # needs change often.
|
||||||
|
res.location.should.be.nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide access to the HTTP body" do
|
||||||
|
res = Rack::MockRequest.new(app).get("")
|
||||||
|
res.body.should =~ /rack/
|
||||||
|
res.should =~ /rack/
|
||||||
|
res.should.match(/rack/)
|
||||||
|
res.should.satisfy { |r| r.match(/rack/) }
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide access to the Rack errors" do
|
||||||
|
res = Rack::MockRequest.new(app).get("/?error=foo", :lint => true)
|
||||||
|
res.should.be.ok
|
||||||
|
res.errors.should.not.be.empty
|
||||||
|
res.errors.should.include "foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should optionally make Rack errors fatal" do
|
||||||
|
lambda {
|
||||||
|
Rack::MockRequest.new(app).get("/?error=foo", :fatal => true)
|
||||||
|
}.should.raise(Rack::MockRequest::FatalWarning)
|
||||||
|
end
|
||||||
|
end
|
189
vendor/plugins/rack/test/spec_rack_mongrel.rb
vendored
Normal file
189
vendor/plugins/rack/test/spec_rack_mongrel.rb
vendored
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'rack/handler/mongrel'
|
||||||
|
require 'rack/urlmap'
|
||||||
|
require 'rack/lint'
|
||||||
|
require 'testrequest'
|
||||||
|
require 'timeout'
|
||||||
|
|
||||||
|
Thread.abort_on_exception = true
|
||||||
|
$tcp_defer_accept_opts = nil
|
||||||
|
$tcp_cork_opts = nil
|
||||||
|
|
||||||
|
context "Rack::Handler::Mongrel" do
|
||||||
|
include TestRequest::Helpers
|
||||||
|
|
||||||
|
setup do
|
||||||
|
server = Mongrel::HttpServer.new(@host='0.0.0.0', @port=9201)
|
||||||
|
server.register('/test',
|
||||||
|
Rack::Handler::Mongrel.new(Rack::Lint.new(TestRequest.new)))
|
||||||
|
server.register('/stream',
|
||||||
|
Rack::Handler::Mongrel.new(Rack::Lint.new(StreamingRequest)))
|
||||||
|
@acc = server.run
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should respond" do
|
||||||
|
lambda {
|
||||||
|
GET("/test")
|
||||||
|
}.should.not.raise
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be a Mongrel" do
|
||||||
|
GET("/test")
|
||||||
|
status.should.be 200
|
||||||
|
response["SERVER_SOFTWARE"].should =~ /Mongrel/
|
||||||
|
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
||||||
|
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
||||||
|
response["SERVER_PORT"].should.equal "9201"
|
||||||
|
response["SERVER_NAME"].should.equal "0.0.0.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have rack headers" do
|
||||||
|
GET("/test")
|
||||||
|
response["rack.version"].should.equal [1,0]
|
||||||
|
response["rack.multithread"].should.be true
|
||||||
|
response["rack.multiprocess"].should.be false
|
||||||
|
response["rack.run_once"].should.be false
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have CGI headers on GET" do
|
||||||
|
GET("/test")
|
||||||
|
response["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test"
|
||||||
|
response["REQUEST_PATH"].should.equal "/test"
|
||||||
|
response["PATH_INFO"].should.be.nil
|
||||||
|
response["QUERY_STRING"].should.equal ""
|
||||||
|
response["test.postdata"].should.equal ""
|
||||||
|
|
||||||
|
GET("/test/foo?quux=1")
|
||||||
|
response["REQUEST_METHOD"].should.equal "GET"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test"
|
||||||
|
response["REQUEST_PATH"].should.equal "/test/foo"
|
||||||
|
response["PATH_INFO"].should.equal "/foo"
|
||||||
|
response["QUERY_STRING"].should.equal "quux=1"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should have CGI headers on POST" do
|
||||||
|
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
||||||
|
status.should.equal 200
|
||||||
|
response["REQUEST_METHOD"].should.equal "POST"
|
||||||
|
response["SCRIPT_NAME"].should.equal "/test"
|
||||||
|
response["REQUEST_PATH"].should.equal "/test"
|
||||||
|
response["QUERY_STRING"].should.equal ""
|
||||||
|
response["HTTP_X_TEST_HEADER"].should.equal "42"
|
||||||
|
response["test.postdata"].should.equal "rack-form-data=23"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should support HTTP auth" do
|
||||||
|
GET("/test", {:user => "ruth", :passwd => "secret"})
|
||||||
|
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should set status" do
|
||||||
|
GET("/test?secret")
|
||||||
|
status.should.equal 403
|
||||||
|
response["rack.url_scheme"].should.equal "http"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide a .run" do
|
||||||
|
block_ran = false
|
||||||
|
Thread.new {
|
||||||
|
Rack::Handler::Mongrel.run(lambda {}, {:Port => 9211}) { |server|
|
||||||
|
server.should.be.kind_of Mongrel::HttpServer
|
||||||
|
block_ran = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep 1
|
||||||
|
block_ran.should.be true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide a .run that maps a hash" do
|
||||||
|
block_ran = false
|
||||||
|
Thread.new {
|
||||||
|
map = {'/'=>lambda{},'/foo'=>lambda{}}
|
||||||
|
Rack::Handler::Mongrel.run(map, :map => true, :Port => 9221) { |server|
|
||||||
|
server.should.be.kind_of Mongrel::HttpServer
|
||||||
|
server.classifier.uris.size.should.be 2
|
||||||
|
server.classifier.uris.should.not.include '/arf'
|
||||||
|
server.classifier.uris.should.include '/'
|
||||||
|
server.classifier.uris.should.include '/foo'
|
||||||
|
block_ran = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep 1
|
||||||
|
block_ran.should.be true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide a .run that maps a urlmap" do
|
||||||
|
block_ran = false
|
||||||
|
Thread.new {
|
||||||
|
map = Rack::URLMap.new({'/'=>lambda{},'/bar'=>lambda{}})
|
||||||
|
Rack::Handler::Mongrel.run(map, {:map => true, :Port => 9231}) { |server|
|
||||||
|
server.should.be.kind_of Mongrel::HttpServer
|
||||||
|
server.classifier.uris.size.should.be 2
|
||||||
|
server.classifier.uris.should.not.include '/arf'
|
||||||
|
server.classifier.uris.should.include '/'
|
||||||
|
server.classifier.uris.should.include '/bar'
|
||||||
|
block_ran = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep 1
|
||||||
|
block_ran.should.be true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide a .run that maps a urlmap restricting by host" do
|
||||||
|
block_ran = false
|
||||||
|
Thread.new {
|
||||||
|
map = Rack::URLMap.new({
|
||||||
|
'/' => lambda{},
|
||||||
|
'/foo' => lambda{},
|
||||||
|
'/bar' => lambda{},
|
||||||
|
'http://localhost/' => lambda{},
|
||||||
|
'http://localhost/bar' => lambda{},
|
||||||
|
'http://falsehost/arf' => lambda{},
|
||||||
|
'http://falsehost/qux' => lambda{}
|
||||||
|
})
|
||||||
|
opt = {:map => true, :Port => 9241, :Host => 'localhost'}
|
||||||
|
Rack::Handler::Mongrel.run(map, opt) { |server|
|
||||||
|
server.should.be.kind_of Mongrel::HttpServer
|
||||||
|
server.classifier.uris.should.include '/'
|
||||||
|
server.classifier.handler_map['/'].size.should.be 2
|
||||||
|
server.classifier.uris.should.include '/foo'
|
||||||
|
server.classifier.handler_map['/foo'].size.should.be 1
|
||||||
|
server.classifier.uris.should.include '/bar'
|
||||||
|
server.classifier.handler_map['/bar'].size.should.be 2
|
||||||
|
server.classifier.uris.should.not.include '/qux'
|
||||||
|
server.classifier.uris.should.not.include '/arf'
|
||||||
|
server.classifier.uris.size.should.be 3
|
||||||
|
block_ran = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep 1
|
||||||
|
block_ran.should.be true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should stream #each part of the response" do
|
||||||
|
body = ''
|
||||||
|
begin
|
||||||
|
Timeout.timeout(1) do
|
||||||
|
Net::HTTP.start(@host, @port) do |http|
|
||||||
|
get = Net::HTTP::Get.new('/stream')
|
||||||
|
http.request(get) do |response|
|
||||||
|
response.read_body { |part| body << part }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Timeout::Error
|
||||||
|
end
|
||||||
|
body.should.not.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
teardown do
|
||||||
|
@acc.raise Mongrel::StopServer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue LoadError
|
||||||
|
$stderr.puts "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again."
|
||||||
|
end
|
77
vendor/plugins/rack/test/spec_rack_recursive.rb
vendored
Normal file
77
vendor/plugins/rack/test/spec_rack_recursive.rb
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/recursive'
|
||||||
|
require 'rack/urlmap'
|
||||||
|
require 'rack/response'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::Recursive" do
|
||||||
|
setup do
|
||||||
|
|
||||||
|
@app1 = lambda { |env|
|
||||||
|
res = Rack::Response.new
|
||||||
|
res["X-Path-Info"] = env["PATH_INFO"]
|
||||||
|
res["X-Query-String"] = env["QUERY_STRING"]
|
||||||
|
res.finish do |res|
|
||||||
|
res.write "App1"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
@app2 = lambda { |env|
|
||||||
|
Rack::Response.new.finish do |res|
|
||||||
|
res.write "App2"
|
||||||
|
_, _, body = env['rack.recursive.include'].call(env, "/app1")
|
||||||
|
body.each { |b|
|
||||||
|
res.write b
|
||||||
|
}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
@app3 = lambda { |env|
|
||||||
|
raise Rack::ForwardRequest.new("/app1")
|
||||||
|
}
|
||||||
|
|
||||||
|
@app4 = lambda { |env|
|
||||||
|
raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh")
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should allow for subrequests" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Recursive.new(
|
||||||
|
Rack::URLMap.new("/app1" => @app1,
|
||||||
|
"/app2" => @app2))).
|
||||||
|
get("/app2")
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.equal "App2App1"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should raise error on requests not below the app" do
|
||||||
|
app = Rack::URLMap.new("/app1" => @app1,
|
||||||
|
"/app" => Rack::Recursive.new(
|
||||||
|
Rack::URLMap.new("/1" => @app1,
|
||||||
|
"/2" => @app2)))
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::MockRequest.new(app).get("/app/2")
|
||||||
|
}.should.raise(ArgumentError).
|
||||||
|
message.should =~ /can only include below/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should support forwarding" do
|
||||||
|
app = Rack::Recursive.new(Rack::URLMap.new("/app1" => @app1,
|
||||||
|
"/app3" => @app3,
|
||||||
|
"/app4" => @app4))
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).get("/app3")
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.equal "App1"
|
||||||
|
|
||||||
|
res = Rack::MockRequest.new(app).get("/app4")
|
||||||
|
res.should.be.ok
|
||||||
|
res.body.should.equal "App1"
|
||||||
|
res["X-Path-Info"].should.equal "/quux"
|
||||||
|
res["X-Query-String"].should.equal "meh"
|
||||||
|
end
|
||||||
|
end
|
504
vendor/plugins/rack/test/spec_rack_request.rb
vendored
Normal file
504
vendor/plugins/rack/test/spec_rack_request.rb
vendored
Normal file
|
@ -0,0 +1,504 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
require 'rack/request'
|
||||||
|
require 'rack/mock'
|
||||||
|
|
||||||
|
context "Rack::Request" do
|
||||||
|
specify "wraps the rack variables" do
|
||||||
|
req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
|
||||||
|
|
||||||
|
req.body.should.respond_to? :gets
|
||||||
|
req.scheme.should.equal "http"
|
||||||
|
req.request_method.should.equal "GET"
|
||||||
|
|
||||||
|
req.should.be.get
|
||||||
|
req.should.not.be.post
|
||||||
|
req.should.not.be.put
|
||||||
|
req.should.not.be.delete
|
||||||
|
req.should.not.be.head
|
||||||
|
|
||||||
|
req.script_name.should.equal ""
|
||||||
|
req.path_info.should.equal "/"
|
||||||
|
req.query_string.should.equal ""
|
||||||
|
|
||||||
|
req.host.should.equal "example.com"
|
||||||
|
req.port.should.equal 8080
|
||||||
|
|
||||||
|
req.content_length.should.equal "0"
|
||||||
|
req.content_type.should.be.nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can figure out the correct host" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
|
||||||
|
req.host.should.equal "www2.example.org"
|
||||||
|
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org:9292")
|
||||||
|
req.host.should.equal "example.org"
|
||||||
|
|
||||||
|
env = Rack::MockRequest.env_for("/")
|
||||||
|
env.delete("SERVER_NAME")
|
||||||
|
req = Rack::Request.new(env)
|
||||||
|
req.host.should.equal ""
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can parse the query string" do
|
||||||
|
req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
|
||||||
|
req.query_string.should.equal "foo=bar&quux=bla"
|
||||||
|
req.GET.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
req.POST.should.be.empty
|
||||||
|
req.params.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can parse POST data" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla")
|
||||||
|
req.content_type.should.be.nil
|
||||||
|
req.media_type.should.be.nil
|
||||||
|
req.query_string.should.equal "foo=quux"
|
||||||
|
req.GET.should.equal "foo" => "quux"
|
||||||
|
req.POST.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
req.params.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can parse POST data with explicit content type" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
|
||||||
|
:input => "foo=bar&quux=bla")
|
||||||
|
req.content_type.should.equal 'application/x-www-form-urlencoded;foo=bar'
|
||||||
|
req.media_type.should.equal 'application/x-www-form-urlencoded'
|
||||||
|
req.media_type_params['foo'].should.equal 'bar'
|
||||||
|
req.POST.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
req.params.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not parse POST data when media type is not form-data" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/?foo=quux",
|
||||||
|
"CONTENT_TYPE" => 'text/plain;charset=utf-8',
|
||||||
|
:input => "foo=bar&quux=bla")
|
||||||
|
req.content_type.should.equal 'text/plain;charset=utf-8'
|
||||||
|
req.media_type.should.equal 'text/plain'
|
||||||
|
req.media_type_params['charset'].should.equal 'utf-8'
|
||||||
|
req.POST.should.be.empty
|
||||||
|
req.params.should.equal "foo" => "quux"
|
||||||
|
req.body.read.should.equal "foo=bar&quux=bla"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "rewinds input after parsing POST data" do
|
||||||
|
input = StringIO.new("foo=bar&quux=bla")
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
|
||||||
|
:input => input)
|
||||||
|
req.params.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
input.read.should.equal "foo=bar&quux=bla"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "cleans up Safari's ajax POST body" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/", :input => "foo=bar&quux=bla\0")
|
||||||
|
req.POST.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can get value by key from params with #[]" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("?foo=quux")
|
||||||
|
req['foo'].should.equal 'quux'
|
||||||
|
req[:foo].should.equal 'quux'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can set value to key on params with #[]=" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("?foo=duh")
|
||||||
|
req['foo'].should.equal 'duh'
|
||||||
|
req[:foo].should.equal 'duh'
|
||||||
|
req.params.should.equal 'foo' => 'duh'
|
||||||
|
|
||||||
|
req['foo'] = 'bar'
|
||||||
|
req.params.should.equal 'foo' => 'bar'
|
||||||
|
req['foo'].should.equal 'bar'
|
||||||
|
req[:foo].should.equal 'bar'
|
||||||
|
|
||||||
|
req[:foo] = 'jaz'
|
||||||
|
req.params.should.equal 'foo' => 'jaz'
|
||||||
|
req['foo'].should.equal 'jaz'
|
||||||
|
req[:foo].should.equal 'jaz'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "values_at answers values by keys in order given" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
|
||||||
|
req.values_at('foo').should.equal ['baz']
|
||||||
|
req.values_at('foo', 'wun').should.equal ['baz', 'der']
|
||||||
|
req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der']
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "referrer should be extracted correct" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
|
||||||
|
req.referer.should.equal "/some/path"
|
||||||
|
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/")
|
||||||
|
req.referer.should.equal "/"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can cache, but invalidates the cache" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla")
|
||||||
|
req.GET.should.equal "foo" => "quux"
|
||||||
|
req.GET.should.equal "foo" => "quux"
|
||||||
|
req.env["QUERY_STRING"] = "bla=foo"
|
||||||
|
req.GET.should.equal "bla" => "foo"
|
||||||
|
req.GET.should.equal "bla" => "foo"
|
||||||
|
|
||||||
|
req.POST.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
req.POST.should.equal "foo" => "bar", "quux" => "bla"
|
||||||
|
req.env["rack.input"] = StringIO.new("foo=bla&quux=bar")
|
||||||
|
req.POST.should.equal "foo" => "bla", "quux" => "bar"
|
||||||
|
req.POST.should.equal "foo" => "bla", "quux" => "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can figure out if called via XHR" do
|
||||||
|
req = Rack::Request.new(Rack::MockRequest.env_for(""))
|
||||||
|
req.should.not.be.xhr
|
||||||
|
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
|
||||||
|
req.should.be.xhr
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can parse cookies" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
|
||||||
|
req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
|
||||||
|
req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
|
||||||
|
req.env.delete("HTTP_COOKIE")
|
||||||
|
req.cookies.should.equal({})
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "parses cookies according to RFC 2109" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
|
||||||
|
req.cookies.should.equal 'foo' => 'bar'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "provides setters" do
|
||||||
|
req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
|
||||||
|
req.script_name.should.equal ""
|
||||||
|
req.script_name = "/foo"
|
||||||
|
req.script_name.should.equal "/foo"
|
||||||
|
e["SCRIPT_NAME"].should.equal "/foo"
|
||||||
|
|
||||||
|
req.path_info.should.equal "/"
|
||||||
|
req.path_info = "/foo"
|
||||||
|
req.path_info.should.equal "/foo"
|
||||||
|
e["PATH_INFO"].should.equal "/foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "provides the original env" do
|
||||||
|
req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
|
||||||
|
req.env.should.be e
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can restore the URL" do
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("")).url.
|
||||||
|
should.equal "http://example.org/"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
|
||||||
|
should.equal "http://example.org/foo/"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("/foo")).url.
|
||||||
|
should.equal "http://example.org/foo"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("?foo")).url.
|
||||||
|
should.equal "http://example.org/?foo"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url.
|
||||||
|
should.equal "http://example.org:8080/"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url.
|
||||||
|
should.equal "https://example.org/"
|
||||||
|
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
|
||||||
|
should.equal "https://example.com:8080/foo?foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can restore the full path" do
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("")).fullpath.
|
||||||
|
should.equal "/"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
|
||||||
|
should.equal "/foo/"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath.
|
||||||
|
should.equal "/foo"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath.
|
||||||
|
should.equal "/?foo"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
|
||||||
|
should.equal "/"
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath.
|
||||||
|
should.equal "/"
|
||||||
|
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
|
||||||
|
should.equal "/foo?foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can handle multiple media type parameters" do
|
||||||
|
req = Rack::Request.new \
|
||||||
|
Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam')
|
||||||
|
req.should.not.be.form_data
|
||||||
|
req.media_type_params.should.include 'foo'
|
||||||
|
req.media_type_params['foo'].should.equal 'BAR'
|
||||||
|
req.media_type_params.should.include 'baz'
|
||||||
|
req.media_type_params['baz'].should.equal 'bizzle dizzle'
|
||||||
|
req.media_type_params.should.not.include 'BLING'
|
||||||
|
req.media_type_params.should.include 'bling'
|
||||||
|
req.media_type_params['bling'].should.equal 'bam'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can parse multipart form data" do
|
||||||
|
# Adapted from RFC 1867.
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="reply"\r
|
||||||
|
\r
|
||||||
|
yes\r
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
|
||||||
|
Content-Type: image/jpeg\r
|
||||||
|
Content-Transfer-Encoding: base64\r
|
||||||
|
\r
|
||||||
|
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
|
||||||
|
--AaB03x--\r
|
||||||
|
EOF
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => input)
|
||||||
|
|
||||||
|
req.POST.should.include "fileupload"
|
||||||
|
req.POST.should.include "reply"
|
||||||
|
|
||||||
|
req.should.be.form_data
|
||||||
|
req.content_length.should.equal input.size
|
||||||
|
req.media_type.should.equal 'multipart/form-data'
|
||||||
|
req.media_type_params.should.include 'boundary'
|
||||||
|
req.media_type_params['boundary'].should.equal 'AaB03x'
|
||||||
|
|
||||||
|
req.POST["reply"].should.equal "yes"
|
||||||
|
|
||||||
|
f = req.POST["fileupload"]
|
||||||
|
f.should.be.kind_of Hash
|
||||||
|
f[:type].should.equal "image/jpeg"
|
||||||
|
f[:filename].should.equal "dj.jpg"
|
||||||
|
f.should.include :tempfile
|
||||||
|
f[:tempfile].size.should.equal 76
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can parse big multipart form data" do
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
||||||
|
\r
|
||||||
|
#{"x"*32768}\r
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="mean"; filename="mean"\r
|
||||||
|
\r
|
||||||
|
--AaB03xha\r
|
||||||
|
--AaB03x--\r
|
||||||
|
EOF
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => input)
|
||||||
|
|
||||||
|
req.POST["huge"][:tempfile].size.should.equal 32768
|
||||||
|
req.POST["mean"][:tempfile].size.should.equal 10
|
||||||
|
req.POST["mean"][:tempfile].read.should.equal "--AaB03xha"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can detect invalid multipart form data" do
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
||||||
|
EOF
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => input)
|
||||||
|
|
||||||
|
lambda { req.POST }.should.raise(EOFError)
|
||||||
|
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
||||||
|
\r
|
||||||
|
foo\r
|
||||||
|
EOF
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => input)
|
||||||
|
|
||||||
|
lambda { req.POST }.should.raise(EOFError)
|
||||||
|
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
||||||
|
\r
|
||||||
|
foo\r
|
||||||
|
EOF
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => input)
|
||||||
|
|
||||||
|
lambda { req.POST }.should.raise(EOFError)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "shouldn't try to interpret binary as utf8" do
|
||||||
|
begin
|
||||||
|
original_kcode = $KCODE
|
||||||
|
$KCODE='UTF8'
|
||||||
|
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="fileupload"; filename="junk.a"\r
|
||||||
|
content-type: application/octet-stream\r
|
||||||
|
\r
|
||||||
|
#{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
|
||||||
|
--AaB03x--\r
|
||||||
|
EOF
|
||||||
|
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => input)
|
||||||
|
|
||||||
|
lambda{req.POST}.should.not.raise(EOFError)
|
||||||
|
req.POST["fileupload"][:tempfile].size.should.equal 4
|
||||||
|
ensure
|
||||||
|
$KCODE = original_kcode
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
specify "should work around buggy 1.8.* Tempfile equality" do
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
||||||
|
\r
|
||||||
|
foo\r
|
||||||
|
--AaB03x--
|
||||||
|
EOF
|
||||||
|
|
||||||
|
rack_input = Tempfile.new("rackspec")
|
||||||
|
rack_input.write(input)
|
||||||
|
rack_input.rewind
|
||||||
|
|
||||||
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size,
|
||||||
|
:input => rack_input)
|
||||||
|
|
||||||
|
lambda {req.POST}.should.not.raise
|
||||||
|
lambda {req.POST}.should.blaming("input re-processed!").not.raise
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does conform to the Rack spec" do
|
||||||
|
app = lambda { |env|
|
||||||
|
content = Rack::Request.new(env).POST["file"].inspect
|
||||||
|
size = content.respond_to?(:bytesize) ? content.bytesize : content.size
|
||||||
|
[200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
|
||||||
|
}
|
||||||
|
|
||||||
|
input = <<EOF
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="reply"\r
|
||||||
|
\r
|
||||||
|
yes\r
|
||||||
|
--AaB03x\r
|
||||||
|
content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
|
||||||
|
Content-Type: image/jpeg\r
|
||||||
|
Content-Transfer-Encoding: base64\r
|
||||||
|
\r
|
||||||
|
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
|
||||||
|
--AaB03x--\r
|
||||||
|
EOF
|
||||||
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
|
||||||
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
||||||
|
"CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
|
||||||
|
|
||||||
|
res.should.be.ok
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should parse Accept-Encoding correctly" do
|
||||||
|
parser = lambda do |x|
|
||||||
|
Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.call(nil).should.equal([])
|
||||||
|
|
||||||
|
parser.call("compress, gzip").should.equal([["compress", 1.0], ["gzip", 1.0]])
|
||||||
|
parser.call("").should.equal([])
|
||||||
|
parser.call("*").should.equal([["*", 1.0]])
|
||||||
|
parser.call("compress;q=0.5, gzip;q=1.0").should.equal([["compress", 0.5], ["gzip", 1.0]])
|
||||||
|
parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").should.equal([["gzip", 1.0], ["identity", 0.5], ["*", 0] ])
|
||||||
|
|
||||||
|
lambda { parser.call("gzip ; q=1.0") }.should.raise(RuntimeError)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify 'should provide ip information' do
|
||||||
|
app = lambda { |env|
|
||||||
|
request = Rack::Request.new(env)
|
||||||
|
response = Rack::Response.new
|
||||||
|
response.write request.ip
|
||||||
|
response.finish
|
||||||
|
}
|
||||||
|
|
||||||
|
mock = Rack::MockRequest.new(Rack::Lint.new(app))
|
||||||
|
res = mock.get '/', 'REMOTE_ADDR' => '123.123.123.123'
|
||||||
|
res.body.should.equal '123.123.123.123'
|
||||||
|
|
||||||
|
res = mock.get '/',
|
||||||
|
'REMOTE_ADDR' => '123.123.123.123',
|
||||||
|
'HTTP_X_FORWARDED_FOR' => '234.234.234.234'
|
||||||
|
|
||||||
|
res.body.should.equal '234.234.234.234'
|
||||||
|
|
||||||
|
res = mock.get '/',
|
||||||
|
'REMOTE_ADDR' => '123.123.123.123',
|
||||||
|
'HTTP_X_FORWARDED_FOR' => '234.234.234.234,212.212.212.212'
|
||||||
|
|
||||||
|
res.body.should.equal '212.212.212.212'
|
||||||
|
end
|
||||||
|
|
||||||
|
class MyRequest < Rack::Request
|
||||||
|
def params
|
||||||
|
{:foo => "bar"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should allow subclass request to be instantiated after parent request" do
|
||||||
|
env = Rack::MockRequest.env_for("/?foo=bar")
|
||||||
|
|
||||||
|
req1 = Rack::Request.new(env)
|
||||||
|
req1.GET.should.equal "foo" => "bar"
|
||||||
|
req1.params.should.equal "foo" => "bar"
|
||||||
|
|
||||||
|
req2 = MyRequest.new(env)
|
||||||
|
req2.GET.should.equal "foo" => "bar"
|
||||||
|
req2.params.should.equal :foo => "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should allow parent request to be instantiated after subclass request" do
|
||||||
|
env = Rack::MockRequest.env_for("/?foo=bar")
|
||||||
|
|
||||||
|
req1 = MyRequest.new(env)
|
||||||
|
req1.GET.should.equal "foo" => "bar"
|
||||||
|
req1.params.should.equal :foo => "bar"
|
||||||
|
|
||||||
|
req2 = Rack::Request.new(env)
|
||||||
|
req2.GET.should.equal "foo" => "bar"
|
||||||
|
req2.params.should.equal "foo" => "bar"
|
||||||
|
end
|
||||||
|
end
|
218
vendor/plugins/rack/test/spec_rack_response.rb
vendored
Normal file
218
vendor/plugins/rack/test/spec_rack_response.rb
vendored
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'set'
|
||||||
|
|
||||||
|
require 'rack/response'
|
||||||
|
|
||||||
|
context "Rack::Response" do
|
||||||
|
specify "has sensible default values" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
status, header, body = response.finish
|
||||||
|
status.should.equal 200
|
||||||
|
header.should.equal "Content-Type" => "text/html"
|
||||||
|
body.each { |part|
|
||||||
|
part.should.equal ""
|
||||||
|
}
|
||||||
|
|
||||||
|
response = Rack::Response.new
|
||||||
|
status, header, body = *response
|
||||||
|
status.should.equal 200
|
||||||
|
header.should.equal "Content-Type" => "text/html"
|
||||||
|
body.each { |part|
|
||||||
|
part.should.equal ""
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can be written to" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
|
||||||
|
status, header, body = response.finish do
|
||||||
|
response.write "foo"
|
||||||
|
response.write "bar"
|
||||||
|
response.write "baz"
|
||||||
|
end
|
||||||
|
|
||||||
|
parts = []
|
||||||
|
body.each { |part| parts << part }
|
||||||
|
|
||||||
|
parts.should.equal ["foo", "bar", "baz"]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can set and read headers" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
response["Content-Type"].should.equal "text/html"
|
||||||
|
response["Content-Type"] = "text/plain"
|
||||||
|
response["Content-Type"].should.equal "text/plain"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can set cookies" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
|
||||||
|
response.set_cookie "foo", "bar"
|
||||||
|
response["Set-Cookie"].should.equal "foo=bar"
|
||||||
|
response.set_cookie "foo2", "bar2"
|
||||||
|
response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2"]
|
||||||
|
response.set_cookie "foo3", "bar3"
|
||||||
|
response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "formats the Cookie expiration date accordingly to RFC 2109" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
|
||||||
|
response.set_cookie "foo", {:value => "bar", :expires => Time.now+10}
|
||||||
|
response["Set-Cookie"].should.match(
|
||||||
|
/expires=..., \d\d-...-\d\d\d\d \d\d:\d\d:\d\d .../)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can set secure cookies" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
response.set_cookie "foo", {:value => "bar", :secure => true}
|
||||||
|
response["Set-Cookie"].should.equal "foo=bar; secure"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can set http only cookies" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
response.set_cookie "foo", {:value => "bar", :httponly => true}
|
||||||
|
response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can delete cookies" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
response.set_cookie "foo", "bar"
|
||||||
|
response.set_cookie "foo2", "bar2"
|
||||||
|
response.delete_cookie "foo"
|
||||||
|
response["Set-Cookie"].should.equal ["foo2=bar2",
|
||||||
|
"foo=; expires=Thu, 01-Jan-1970 00:00:00 GMT"]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "can do redirects" do
|
||||||
|
response = Rack::Response.new
|
||||||
|
response.redirect "/foo"
|
||||||
|
status, header, body = response.finish
|
||||||
|
|
||||||
|
status.should.equal 302
|
||||||
|
header["Location"].should.equal "/foo"
|
||||||
|
|
||||||
|
response = Rack::Response.new
|
||||||
|
response.redirect "/foo", 307
|
||||||
|
status, header, body = response.finish
|
||||||
|
|
||||||
|
status.should.equal 307
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "has a useful constructor" do
|
||||||
|
r = Rack::Response.new("foo")
|
||||||
|
status, header, body = r.finish
|
||||||
|
str = ""; body.each { |part| str << part }
|
||||||
|
str.should.equal "foo"
|
||||||
|
|
||||||
|
r = Rack::Response.new(["foo", "bar"])
|
||||||
|
status, header, body = r.finish
|
||||||
|
str = ""; body.each { |part| str << part }
|
||||||
|
str.should.equal "foobar"
|
||||||
|
|
||||||
|
r = Rack::Response.new(["foo", "bar"].to_set)
|
||||||
|
r.write "foo"
|
||||||
|
status, header, body = r.finish
|
||||||
|
str = ""; body.each { |part| str << part }
|
||||||
|
str.should.equal "foobarfoo"
|
||||||
|
|
||||||
|
r = Rack::Response.new([], 500)
|
||||||
|
r.status.should.equal 500
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "has a constructor that can take a block" do
|
||||||
|
r = Rack::Response.new { |res|
|
||||||
|
res.status = 404
|
||||||
|
res.write "foo"
|
||||||
|
}
|
||||||
|
status, header, body = r.finish
|
||||||
|
str = ""; body.each { |part| str << part }
|
||||||
|
str.should.equal "foo"
|
||||||
|
status.should.equal 404
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "doesn't return invalid responses" do
|
||||||
|
r = Rack::Response.new(["foo", "bar"], 204)
|
||||||
|
status, header, body = r.finish
|
||||||
|
str = ""; body.each { |part| str << part }
|
||||||
|
str.should.be.empty
|
||||||
|
header["Content-Type"].should.equal nil
|
||||||
|
|
||||||
|
lambda {
|
||||||
|
Rack::Response.new(Object.new)
|
||||||
|
}.should.raise(TypeError).
|
||||||
|
message.should =~ /stringable or iterable required/
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "knows if it's empty" do
|
||||||
|
r = Rack::Response.new
|
||||||
|
r.should.be.empty
|
||||||
|
r.write "foo"
|
||||||
|
r.should.not.be.empty
|
||||||
|
|
||||||
|
r = Rack::Response.new
|
||||||
|
r.should.be.empty
|
||||||
|
r.finish
|
||||||
|
r.should.be.empty
|
||||||
|
|
||||||
|
r = Rack::Response.new
|
||||||
|
r.should.be.empty
|
||||||
|
r.finish { }
|
||||||
|
r.should.not.be.empty
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide access to the HTTP status" do
|
||||||
|
res = Rack::Response.new
|
||||||
|
res.status = 200
|
||||||
|
res.should.be.successful
|
||||||
|
res.should.be.ok
|
||||||
|
|
||||||
|
res.status = 404
|
||||||
|
res.should.not.be.successful
|
||||||
|
res.should.be.client_error
|
||||||
|
res.should.be.not_found
|
||||||
|
|
||||||
|
res.status = 501
|
||||||
|
res.should.not.be.successful
|
||||||
|
res.should.be.server_error
|
||||||
|
|
||||||
|
res.status = 307
|
||||||
|
res.should.be.redirect
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should provide access to the HTTP headers" do
|
||||||
|
res = Rack::Response.new
|
||||||
|
res["Content-Type"] = "text/yaml"
|
||||||
|
|
||||||
|
res.should.include "Content-Type"
|
||||||
|
res.headers["Content-Type"].should.equal "text/yaml"
|
||||||
|
res["Content-Type"].should.equal "text/yaml"
|
||||||
|
res.content_type.should.equal "text/yaml"
|
||||||
|
res.content_length.should.be.nil
|
||||||
|
res.location.should.be.nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "does not add or change Content-Length when #finish()ing" do
|
||||||
|
res = Rack::Response.new
|
||||||
|
res.status = 200
|
||||||
|
res.finish
|
||||||
|
res.headers["Content-Length"].should.be.nil
|
||||||
|
|
||||||
|
res = Rack::Response.new
|
||||||
|
res.status = 200
|
||||||
|
res.headers["Content-Length"] = "10"
|
||||||
|
res.finish
|
||||||
|
res.headers["Content-Length"].should.equal "10"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "updates Content-Length when body appended to using #write" do
|
||||||
|
res = Rack::Response.new
|
||||||
|
res.status = 200
|
||||||
|
res.headers["Content-Length"].should.be.nil
|
||||||
|
res.write "Hi"
|
||||||
|
res.headers["Content-Length"].should.equal "2"
|
||||||
|
res.write " there"
|
||||||
|
res.headers["Content-Length"].should.equal "8"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
118
vendor/plugins/rack/test/spec_rack_rewindable_input.rb
vendored
Normal file
118
vendor/plugins/rack/test/spec_rack_rewindable_input.rb
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
require 'test/spec'
|
||||||
|
require 'stringio'
|
||||||
|
require 'rack/rewindable_input'
|
||||||
|
|
||||||
|
shared_context "a rewindable IO object" do
|
||||||
|
setup do
|
||||||
|
@rio = Rack::RewindableInput.new(@io)
|
||||||
|
end
|
||||||
|
|
||||||
|
teardown do
|
||||||
|
@rio.close
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle to read()" do
|
||||||
|
@rio.read.should.equal "hello world"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle to read(nil)" do
|
||||||
|
@rio.read(nil).should.equal "hello world"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle to read(length)" do
|
||||||
|
@rio.read(1).should.equal "h"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle to read(length, buffer)" do
|
||||||
|
buffer = ""
|
||||||
|
result = @rio.read(1, buffer)
|
||||||
|
result.should.equal "h"
|
||||||
|
result.object_id.should.equal buffer.object_id
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle to read(nil, buffer)" do
|
||||||
|
buffer = ""
|
||||||
|
result = @rio.read(nil, buffer)
|
||||||
|
result.should.equal "hello world"
|
||||||
|
result.object_id.should.equal buffer.object_id
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should rewind to the beginning when #rewind is called" do
|
||||||
|
@rio.read(1)
|
||||||
|
@rio.rewind
|
||||||
|
@rio.read.should.equal "hello world"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle gets" do
|
||||||
|
@rio.gets.should == "hello world"
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be able to handle each" do
|
||||||
|
array = []
|
||||||
|
@rio.each do |data|
|
||||||
|
array << data
|
||||||
|
end
|
||||||
|
array.should.equal(["hello world"])
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should not buffer into a Tempfile if no data has been read yet" do
|
||||||
|
@rio.instance_variable_get(:@rewindable_io).should.be.nil
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should buffer into a Tempfile when data has been consumed for the first time" do
|
||||||
|
@rio.read(1)
|
||||||
|
tempfile = @rio.instance_variable_get(:@rewindable_io)
|
||||||
|
tempfile.should.not.be.nil
|
||||||
|
@rio.read(1)
|
||||||
|
tempfile2 = @rio.instance_variable_get(:@rewindable_io)
|
||||||
|
tempfile2.should.equal tempfile
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should close the underlying tempfile upon calling #close" do
|
||||||
|
@rio.read(1)
|
||||||
|
tempfile = @rio.instance_variable_get(:@rewindable_io)
|
||||||
|
@rio.close
|
||||||
|
tempfile.should.be.closed
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be possibel to call #close when no data has been buffered yet" do
|
||||||
|
@rio.close
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should be possible to call #close multiple times" do
|
||||||
|
@rio.close
|
||||||
|
@rio.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Rack::RewindableInput" do
|
||||||
|
context "given an IO object that is already rewindable" do
|
||||||
|
setup do
|
||||||
|
@io = StringIO.new("hello world")
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like "a rewindable IO object"
|
||||||
|
end
|
||||||
|
|
||||||
|
context "given an IO object that is not rewindable" do
|
||||||
|
setup do
|
||||||
|
@io = StringIO.new("hello world")
|
||||||
|
@io.instance_eval do
|
||||||
|
undef :rewind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like "a rewindable IO object"
|
||||||
|
end
|
||||||
|
|
||||||
|
context "given an IO object whose rewind method raises Errno::ESPIPE" do
|
||||||
|
setup do
|
||||||
|
@io = StringIO.new("hello world")
|
||||||
|
def @io.rewind
|
||||||
|
raise Errno::ESPIPE, "You can't rewind this!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like "a rewindable IO object"
|
||||||
|
end
|
||||||
|
end
|
82
vendor/plugins/rack/test/spec_rack_session_cookie.rb
vendored
Normal file
82
vendor/plugins/rack/test/spec_rack_session_cookie.rb
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
require 'test/spec'
|
||||||
|
|
||||||
|
require 'rack/session/cookie'
|
||||||
|
require 'rack/mock'
|
||||||
|
require 'rack/response'
|
||||||
|
|
||||||
|
context "Rack::Session::Cookie" do
|
||||||
|
incrementor = lambda { |env|
|
||||||
|
env["rack.session"]["counter"] ||= 0
|
||||||
|
env["rack.session"]["counter"] += 1
|
||||||
|
Rack::Response.new(env["rack.session"].inspect).to_a
|
||||||
|
}
|
||||||
|
|
||||||
|
specify "creates a new cookie" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
|
||||||
|
res["Set-Cookie"].should.match("rack.session=")
|
||||||
|
res.body.should.equal '{"counter"=>1}'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "loads from a cookie" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
|
||||||
|
cookie = res["Set-Cookie"]
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
|
||||||
|
get("/", "HTTP_COOKIE" => cookie)
|
||||||
|
res.body.should.equal '{"counter"=>2}'
|
||||||
|
cookie = res["Set-Cookie"]
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
|
||||||
|
get("/", "HTTP_COOKIE" => cookie)
|
||||||
|
res.body.should.equal '{"counter"=>3}'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "survives broken cookies" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
|
||||||
|
get("/", "HTTP_COOKIE" => "rack.session=blarghfasel")
|
||||||
|
res.body.should.equal '{"counter"=>1}'
|
||||||
|
end
|
||||||
|
|
||||||
|
bigcookie = lambda { |env|
|
||||||
|
env["rack.session"]["cookie"] = "big" * 3000
|
||||||
|
Rack::Response.new(env["rack.session"].inspect).to_a
|
||||||
|
}
|
||||||
|
|
||||||
|
specify "barks on too big cookies" do
|
||||||
|
lambda {
|
||||||
|
Rack::MockRequest.new(Rack::Session::Cookie.new(bigcookie)).
|
||||||
|
get("/", :fatal => true)
|
||||||
|
}.should.raise(Rack::MockRequest::FatalWarning)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "creates a new cookie with integrity hash" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
|
||||||
|
if RUBY_VERSION < "1.9"
|
||||||
|
res["Set-Cookie"].should.match("rack.session=BAh7BiIMY291bnRlcmkG%0A--1439b4d37b9d4b04c603848382f712d6fcd31088")
|
||||||
|
else
|
||||||
|
res["Set-Cookie"].should.match("rack.session=BAh7BkkiDGNvdW50ZXIGOg1lbmNvZGluZyINVVMtQVNDSUlpBg%3D%3D%0A--d7a6637b94d2728194a96c18484e1f7ed9074a83")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "loads from a cookie wih integrity hash" do
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
|
||||||
|
cookie = res["Set-Cookie"]
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
|
||||||
|
get("/", "HTTP_COOKIE" => cookie)
|
||||||
|
res.body.should.equal '{"counter"=>2}'
|
||||||
|
cookie = res["Set-Cookie"]
|
||||||
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
|
||||||
|
get("/", "HTTP_COOKIE" => cookie)
|
||||||
|
res.body.should.equal '{"counter"=>3}'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "ignores tampered with session cookies" do
|
||||||
|
app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
|
||||||
|
response1 = Rack::MockRequest.new(app).get("/")
|
||||||
|
_, digest = response1["Set-Cookie"].split("--")
|
||||||
|
tampered_with_cookie = "hackerman-was-here" + "--" + digest
|
||||||
|
response2 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" =>
|
||||||
|
tampered_with_cookie)
|
||||||
|
|
||||||
|
# The tampered-with cookie is ignored, so we get back an identical Set-Cookie
|
||||||
|
response2["Set-Cookie"].should.equal(response1["Set-Cookie"])
|
||||||
|
end
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue