pax_global_header00006660000000000000000000000064122335144210014507gustar00rootroot0000000000000052 comment=53c5c99368e86d89027a6432c537d9fc2f2cfcb5 ruby-rack1.4-1.4.5/000077500000000000000000000000001223351442100136405ustar00rootroot00000000000000ruby-rack1.4-1.4.5/COPYING000066400000000000000000000021161223351442100146730ustar00rootroot00000000000000Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012 Christian Neukirchen 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. ruby-rack1.4-1.4.5/KNOWN-ISSUES000066400000000000000000000020611223351442100154670ustar00rootroot00000000000000= Known issues with Rack and ECMA-262 * Many users expect the escape() function defined in ECMA-262 to be compatible with URI. Confusion is especially strong because the documentation for the escape function includes a reference to the URI specifications. ECMA-262 escape is not however a URI escape function, it is a javascript escape function, and is not fully compatible. Most notably, for characters outside of the BMP. Users should use the more correct encodeURI functions. = 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 "/". Since lighttpd 1.4.23, you also can use the "fix-root-scriptname" flag in fastcgi.server. ruby-rack1.4-1.4.5/README.rdoc000066400000000000000000000561601223351442100154560ustar00rootroot00000000000000= Rack, a modular Ruby webserver interface {Build Status}[http://travis-ci.org/rack/rack] {Dependency Status}[https://gemnasium.com/rack/rack] 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. == 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 * Glassfish v3 * Phusion Passenger (which is mod_rack for Apache and for nginx) * Puma * Rainbows! * Unicorn * unixrack * uWSGI * Zbatery Any valid Rack app will run the same on all these handlers, without changing anything. == Supported web frameworks 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 * ... and many others. 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 at rubygems.org. 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 bacon testing framework: bundle install --without extra # to be able to run the fast tests Or: bundle install # this assumes that you have installed native extensions! 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 bacon. To run the test suite completely, you need: * fcgi * memcache-client * mongrel * 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 * 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. * January 3rd, 2010: Ninth public release 1.1.0. * Moved Auth::OpenID to rack-contrib. * SPEC change that relaxes Lint slightly to allow subclasses of the required types * SPEC change to document rack.input binary mode in greator detail * SPEC define optional rack.logger specification * File servers support X-Cascade header * Imported Config middleware * Imported ETag middleware * Imported Runtime middleware * Imported Sendfile middleware * New Logger and NullLogger middlewares * Added mime type for .ogv and .manifest. * Don't squeeze PATH_INFO slashes * Use Content-Type to determine POST params parsing * Update Rack::Utils::HTTP_STATUS_CODES hash * Add status code lookup utility * Response should call #to_i on the status * Add Request#user_agent * Request#host knows about forwared host * Return an empty string for Request#host if HTTP_HOST and SERVER_NAME are both missing * Allow MockRequest to accept hash params * Optimizations to HeaderHash * Refactored rackup into Rack::Server * Added Utils.build_nested_query to complement Utils.parse_nested_query * Added Utils::Multipart.build_multipart to complement Utils::Multipart.parse_multipart * Extracted set and delete cookie helpers into Utils so they can be used outside Response * Extract parse_query and parse_multipart in Request so subclasses can change their behavior * Enforce binary encoding in RewindableInput * Set correct external_encoding for handlers that don't use RewindableInput * June 13th, 2010: Tenth public release 1.2.0. * Removed Camping adapter: Camping 2.0 supports Rack as-is * Removed parsing of quoted values * Add Request.trace? and Request.options? * Add mime-type for .webm and .htc * Fix HTTP_X_FORWARDED_FOR * Various multipart fixes * Switch test suite to bacon * June 15th, 2010: Eleventh public release 1.2.1. * Make CGI handler rewindable * Rename spec/ to test/ to not conflict with SPEC on lesser operating systems * March 13th, 2011: Twelfth public release 1.2.2/1.1.2. * Security fix in Rack::Auth::Digest::MD5: when authenticator returned nil, permission was granted on empty password. * May 22nd, 2011: Thirteenth public release 1.3.0 * Various performance optimizations * Various multipart fixes * Various multipart refactors * Infinite loop fix for multipart * Test coverage for Rack::Server returns * Allow files with '..', but not path components that are '..' * rackup accepts handler-specific options on the command line * Request#params no longer merges POST into GET (but returns the same) * Use URI.encode_www_form_component instead. Use core methods for escaping. * Allow multi-line comments in the config file * Bug L#94 reported by Nikolai Lugovoi, query parameter unescaping. * Rack::Response now deletes Content-Length when appropriate * Rack::Deflater now supports streaming * Improved Rack::Handler loading and searching * Support for the PATCH verb * env['rack.session.options'] now contains session options * Cookies respect renew * Session middleware uses SecureRandom.hex * May 22nd, 2011: Fourteenth public release 1.2.3 * Pulled in relevant bug fixes from 1.3 * Fixed 1.8.6 support * July 13, 2011: Fifteenth public release 1.3.1 * Fix 1.9.1 support * Fix JRuby support * Properly handle $KCODE in Rack::Utils.escape * Make method_missing/respond_to behavior consistent for Rack::Lock, Rack::Auth::Digest::Request and Rack::Multipart::UploadedFile * Reenable passing rack.session to session middleware * Rack::CommonLogger handles streaming responses correctly * Rack::MockResponse calls close on the body object * Fix a DOS vector from MRI stdlib backport * July 16, 2011: Sixteenth public release 1.3.2 * Fix for Rails and rack-test, Rack::Utils#escape calls to_s * September 16, 2011: Seventeenth public release 1.3.3 * Fix bug with broken query parameters in Rack::ShowExceptions * Rack::Request#cookies no longer swallows exceptions on broken input * Prevents XSS attacks enabled by bug in Ruby 1.8's regexp engine * Rack::ConditionalGet handles broken If-Modified-Since helpers * September 16, 2011: Eighteenth public release 1.2.4 * Fix a bug with MRI regex engine to prevent XSS by malformed unicode * October 1, 2011: Nineteenth public release 1.3.4 * Backport security fix from 1.9.3, also fixes some roundtrip issues in URI * Small documentation update * Fix an issue where BodyProxy could cause an infinite recursion * Add some supporting files for travis-ci * October 17, 2011: Twentieth public release 1.3.5 * Fix annoying warnings caused by the backport in 1.3.4 * December 28th, 2011: Twenty first public release: 1.1.3. * Security fix. http://www.ocert.org/advisories/ocert-2011-003.html Further information here: http://jruby.org/2011/12/27/jruby-1-6-5-1 * December 28th, 2011: Twenty fourth public release 1.4.0 * Ruby 1.8.6 support has officially been dropped. Not all tests pass. * Raise sane error messages for broken config.ru * Allow combining run and map in a config.ru * Rack::ContentType will not set Content-Type for responses without a body * Status code 205 does not send a response body * Rack::Response::Helpers will not rely on instance variables * Rack::Utils.build_query no longer outputs '=' for nil query values * Various mime types added * Rack::MockRequest now supports HEAD * Rack::Directory now supports files that contain RFC3986 reserved chars * Rack::File now only supports GET and HEAD requests * Rack::Server#start now passes the block to Rack::Handler::#run * Rack::Static now supports an index option * Added the Teapot status code * rackup now defaults to Thin instead of Mongrel (if installed) * Support added for HTTP_X_FORWARDED_SCHEME * Numerous bug fixes, including many fixes for new and alternate rubies * January 22nd, 2012: Twenty fifth public release 1.4.1 * Alter the keyspace limit calculations to reduce issues with nested params * Add a workaround for multipart parsing where files contain unescaped "%" * Added Rack::Response::Helpers#method_not_allowed? (code 405) * Rack::File now returns 404 for illegal directory traversals * Rack::File now returns 405 for illegal methods (non HEAD/GET) * Rack::Cascade now catches 405 by default, as well as 404 * Cookies missing '--' no longer cause an exception to be raised * Various style changes and documentation spelling errors * Rack::BodyProxy always ensures to execute its block * Additional test coverage around cookies and secrets * Rack::Session::Cookie can now be supplied either secret or old_secret * Tests are no longer dependent on set order * Rack::Static no longer defaults to serving index files * Rack.release was fixed * January 6th, 2013: Twenty sixth public release 1.1.4 * Add warnings when users do not provide a session secret * January 6th, 2013: Twenty seventh public release 1.2.6 * Add warnings when users do not provide a session secret * Fix parsing performance for unquoted filenames * January 6th, 2013: Twenty eighth public release 1.3.7 * Add warnings when users do not provide a session secret * Fix parsing performance for unquoted filenames * Updated URI backports * Fix URI backport version matching, and silence constant warnings * Correct parameter parsing with empty values * Correct rackup '-I' flag, to allow multiple uses * Correct rackup pidfile handling * Report rackup line numbers correctly * Fix request loops caused by non-stale nonces with time limits * Fix reloader on Windows * Prevent infinite recursions from Response#to_ary * Various middleware better conforms to the body close specification * Updated language for the body close specification * Additional notes regarding ECMA escape compatibility issues * Fix the parsing of multiple ranges in range headers * January 6th, 2013: Twenty ninth public release 1.4.2 * Add warnings when users do not provide a session secret * Fix parsing performance for unquoted filenames * Updated URI backports * Fix URI backport version matching, and silence constant warnings * Correct parameter parsing with empty values * Correct rackup '-I' flag, to allow multiple uses * Correct rackup pidfile handling * Report rackup line numbers correctly * Fix request loops caused by non-stale nonces with time limits * Fix reloader on Windows * Prevent infinite recursions from Response#to_ary * Various middleware better conforms to the body close specification * Updated language for the body close specification * Additional notes regarding ECMA escape compatibility issues * Fix the parsing of multiple ranges in range headers * Prevent errors from empty parameter keys * Added PATCH verb to Rack::Request * Various documentation updates * Fix session merge semantics (fixes rack-test) * Rack::Static :index can now handle multiple directories * All tests now utilize Rack::Lint (special thanks to Lars Gierth) * Rack::File cache_control parameter is now deprecated, and removed by 1.5 * Correct Rack::Directory script name escaping * Rack::Static supports header rules for sophisticated configurations * Multipart parsing now works without a Content-Length header * New logos courtesy of Zachary Scott! * Rack::BodyProxy now explicitly defines #each, useful for C extensions * Cookies that are not URI escaped no longer cause exceptions * January 7th, 2013: Thirtieth public release 1.3.8 * Security: Prevent unbounded reads in large multipart boundaries * January 7th, 2013: Thirty first public release 1.4.3 * Security: Prevent unbounded reads in large multipart boundaries * January 13th, 2013: Thirty second public release 1.4.4, 1.3.9, 1.2.7, 1.1.5 * [SEC] Rack::Auth::AbstractRequest no longer symbolizes arbitrary strings * Fixed erroneous test case in the 1.3.x series * February 7th, Thirty fifth public release 1.1.6, 1.2.8, 1.3.10 * Fix CVE-2013-0263, timing attack against Rack::Session::Cookie * February 7th, Thirty fifth public release 1.4.5 * Fix CVE-2013-0263, timing attack against Rack::Session::Cookie * Fix CVE-2013-0262, symlink path traversal in Rack::File * February 7th, Thirty fifth public release 1.5.2 * Fix CVE-2013-0263, timing attack against Rack::Session::Cookie * Fix CVE-2013-0262, symlink path traversal in Rack::File * Add various methods to Session for enhanced Rails compatibility * Request#trusted_proxy? now only matches whole stirngs * Add JSON cookie coder, to be default in Rack 1.6+ due to security concerns * URLMap host matching in environments that don't set the Host header fixed * Fix a race condition that could result in overwritten pidfiles * Various documentation additions == Contact Please post bugs, suggestions and patches to the bug tracker at . Please post security related bugs and suggestions to the core team at or rack-core@googlegroups.com. Due to wide usage of the library, it is strongly preferred that we manage timing in order to provide viable patches at the time of disclosure. Your assistance in this matter is greatly appreciated. Mailing list archives are available at . Git repository (send Git patches to the mailing list): * http://github.com/rack/rack * http://git.vuxu.org/cgi-bin/gitweb.cgi?p=rack-github.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) * Aaron Patterson (tenderlove) * Konstantin Haase (rkh) 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. * Alex Beregszaszi, Alexander Kahn, Anil Wadghule, Aredridel, Ben Alpert, Dan Kubb, Daniel Roethlisberger, Matt Todd, Tom Robinson, Phil Hagelberg, S. Brent Faulkner, Bosko Milekic, Daniel Rodríguez Troitiño, Genki Takiuchi, Geoffrey Grosenbach, Julien Sanchez, Kamal Fariz Mahyuddin, Masayoshi Takahashi, Patrick Aljordm, Mig, Kazuhiro Nishiyama, Jon Bardin, Konstantin Haase, Larry Siden, Matias Korhonen, Sam Ruby, Simon Chiang, Tim Connor, Timur Batyrshin, and Zach Brock for bug fixing and other improvements. * Eric Wong, Hongli Lai, Jeremy Kemper for their continuous support and API improvements. * Yehuda Katz and Carl Lerche for refactoring rackup. * 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 contributors not mentioned above. == Copyright Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen 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:: Official Rack repositories:: Rack Bug Tracking:: rack-devel mailing list:: Rack's Rubyforge project:: Christian Neukirchen:: ruby-rack1.4-1.4.5/Rakefile000066400000000000000000000065611223351442100153150ustar00rootroot00000000000000# Rakefile for Rack. -*-ruby-*- desc "Run all the tests" task :default => [:test] desc "Install gem dependencies" task :deps do require 'rubygems' spec = Gem::Specification.load('rack.gemspec') spec.dependencies.each do |dep| reqs = dep.requirements_list reqs = (["-v"] * reqs.size).zip(reqs).flatten # Use system over sh, because we want to ignore errors! system Gem.ruby, "-S", "gem", "install", '--conservative', dep.name, *reqs end end desc "Make an archive as .tar.gz" task :dist => %w[chmod ChangeLog SPEC rdoc] do sh "git archive --format=tar --prefix=#{release}/ HEAD^{tree} >#{release}.tar" sh "pax -waf #{release}.tar -s ':^:#{release}/:' 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 => %w[SPEC dist gem] do sh "sha1sum #{release}.tar.gz #{release}.gem" end def release "rack-#{File.read("rack.gemspec")[/s.version *= *"(.*?)"/, 1]}" 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 => %w[ChangeLog] file '.git/index' file "ChangeLog" => '.git/index' 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 file 'lib/rack/lint.rb' desc "Generate Rack Specification" file "SPEC" => 'lib/rack/lint.rb' 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 + platform agnostic tests" task :test => 'SPEC' do opts = ENV['TEST'] || '-a' specopts = ENV['TESTOPTS'] || "-q -t '^(?!Rack::Adapter|Rack::Session::Memcache|Rack::Server|Rack::Handler)'" sh "bacon -I./lib:./test #{opts} #{specopts}" end desc "Run all the tests we run on CI" task :ci => :test desc "Run all the tests" task :fulltest => %w[SPEC chmod] do opts = ENV['TEST'] || '-a' specopts = ENV['TESTOPTS'] || '-q' sh "bacon -r./test/gemloader -I./lib:./test -w #{opts} #{specopts}" end task :gem => ["SPEC"] do sh "gem build rack.gemspec" end task :doc => :rdoc desc "Generate RDoc documentation" task :rdoc => %w[ChangeLog SPEC] do sh(*%w{rdoc --line-numbers --main README.rdoc --title 'Rack\ Documentation' --charset utf-8 -U -o doc} + %w{README.rdoc KNOWN-ISSUES SPEC ChangeLog} + `git ls-files lib/\*\*/\*.rb`.strip.split) cp "contrib/rdoc.css", "doc/rdoc.css" end task :pushdoc => %w[rdoc] do sh "rsync -avz doc/ rack.rubyforge.org:/var/www/gforge-projects/rack/doc/" end task :pushsite => %w[pushdoc] do sh "cd site && git gc" sh "rsync -avz site/ rack.rubyforge.org:/var/www/gforge-projects/rack/" sh "cd site && git push" end ruby-rack1.4-1.4.5/SPEC000066400000000000000000000225601223351442100143220ustar00rootroot00000000000000This specification aims to formalize the Rack protocol. You can (and should) use Rack::Lint to enforce it. When you develop middleware, be sure to add a Lint before and after to catch all mistakes. = Rack applications A Rack application is a Ruby object (not a class) that responds to +call+. It takes exactly one argument, the *environment* and returns an Array of exactly three values: The *status*, the *headers*, and the *body*. == The Environment The environment must be an instance of Hash that includes CGI-like headers. The application is free to modify the environment. The environment is required to include these variables (adopted from PEP333), except when they'd be empty, but see below. REQUEST_METHOD:: The HTTP request method, such as "GET" or "POST". This cannot ever be an empty string, and so is always required. SCRIPT_NAME:: The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server. PATH_INFO:: The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. This value may be percent-encoded when I originating from a URL. QUERY_STRING:: The portion of the request URL that follows the ?, if any. May be empty, but is always required! SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. HTTP_ Variables:: Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with HTTP_). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request. In addition to this, the Rack environment must include these Rack-specific variables: rack.version:: The Array [1,1], representing this version of Rack. rack.url_scheme:: +http+ or +https+, depending on the request URL. rack.input:: See below, the input stream. rack.errors:: See below, the error stream. rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). Additional environment specifications have approved to standardized middleware APIs. None of these are required to be implemented by the server. rack.session:: A hash like interface for storing request session data. The store must implement: store(key, value) (aliased as []=); fetch(key, default = nil) (aliased as []); delete(key); clear; rack.logger:: A common object interface for logging messages. The object must implement: info(message, &block) debug(message, &block) warn(message, &block) error(message, &block) fatal(message, &block) The server or the application can store their own data in the environment, too. The keys must contain at least one dot, and should be prefixed uniquely. The prefix rack. is reserved for use with the Rack core distribution and other accepted specifications and must not be used otherwise. The environment must not contain the keys HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH (use the versions without HTTP_). The CGI keys (named without a period) must have String values. There are the following restrictions: * rack.version must be an array of Integers. * rack.url_scheme must either be +http+ or +https+. * There must be a valid input stream in rack.input. * There must be a valid error stream in rack.errors. * The REQUEST_METHOD must be a valid token. * The SCRIPT_NAME, if non-empty, must start with / * The PATH_INFO, if non-empty, must start with / * The CONTENT_LENGTH, if given, must consist of digits only. * One of SCRIPT_NAME or PATH_INFO must be set. PATH_INFO should be / if SCRIPT_NAME is empty. SCRIPT_NAME never should be /, but instead be empty. === The Input Stream The input stream is an IO-like object which contains the raw HTTP POST data. When applicable, its external encoding must be "ASCII-8BIT" and it must be opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond to +gets+, +each+, +read+ and +rewind+. * +gets+ must be called without arguments and return a string, or +nil+ on EOF. * +read+ behaves like IO#read. Its signature is read([length, [buffer]]). If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must be a String and may not be nil. If +length+ is given and not nil, then this method reads at most +length+ bytes from the input stream. If +length+ is not given or nil, then this method reads all data until EOF. When EOF is reached, this method returns nil if +length+ is given and not nil, or "" if +length+ is not given or is nil. If +buffer+ is given, then the read data will be placed into +buffer+ instead of a newly created String object. * +each+ must be called without arguments and only yield Strings. * +rewind+ must be called without arguments. It rewinds the input stream back to the beginning. It must not raise Errno::ESPIPE: that is, it may not be a pipe or a socket. Therefore, handler developers must buffer the input data into some rewindable object if the underlying input stream is not rewindable. * +close+ must never be called on the input stream. === The Error Stream The error stream must respond to +puts+, +write+ and +flush+. * +puts+ must be called with a single argument that responds to +to_s+. * +write+ must be called with a single argument that is a String. * +flush+ must be called without arguments and must be called in order to make the error appear for sure. * +close+ must never be called on the error stream. == The Response === The Status This is an HTTP status. When parsed as integer (+to_i+), it must be greater than or equal to 100. === The Headers The header must respond to +each+, and yield values of key and value. The header keys must be Strings. The header must not contain a +Status+ key, contain keys with : or newlines in their name, contain keys names that end in - or _, but only contain keys that consist of letters, digits, _ or - and start with a letter. The values of the header must be Strings, consisting of lines (for multiple header values, e.g. multiple Set-Cookie values) seperated by "\n". The lines must not contain characters below 037. === The Content-Type There must be a Content-Type, except when the +Status+ is 1xx, 204, 205 or 304, in which case there must be none given. === The Content-Length There must not be a Content-Length header when the +Status+ is 1xx, 204, 205 or 304. === The Body The Body must respond to +each+ and must only yield String values. The Body itself should not be an instance of String, as this will break in Ruby 1.9. If the Body responds to +close+, it will be called after iteration. If the body is replaced by a middleware after action, the original body must be closed first, if it repsonds to close. If the Body responds to +to_path+, it must return a String identifying the location of a file whose contents are identical to that produced by calling +each+; this may be used by the server as an alternative, possibly more efficient way to transport the response. The Body commonly is an Array of Strings, the application instance itself, or a File-like object. == Thanks Some parts of this specification are adopted from PEP333: Python Web Server Gateway Interface v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank everyone involved in that effort. ruby-rack1.4-1.4.5/bin/000077500000000000000000000000001223351442100144105ustar00rootroot00000000000000ruby-rack1.4-1.4.5/bin/rackup000077500000000000000000000000671223351442100156260ustar00rootroot00000000000000#!/usr/bin/env ruby require "rack" Rack::Server.start ruby-rack1.4-1.4.5/contrib/000077500000000000000000000000001223351442100153005ustar00rootroot00000000000000ruby-rack1.4-1.4.5/contrib/rack.png000066400000000000000000000563751223351442100167460ustar00rootroot00000000000000PNG  IHDR@@e6sRGBbKGD pHYs B(xtIME ,+( IDATxyyhB#mɎ96ƾ %8q9J;n%Zw>;ݑ;#؉LNDxBF 1k@4cʩ{nIT "\\N|pvu7p7-cߔThՄNU7 (V a}v$o0e(""}v4$x_@QvLe_#AtVE9.Q1?F0:wZ]Jhzm\BEʫ 9MjpQ]P.lEC-B"L{.`^NbB*nbh9("chl9%2c Q<)""T23Ż+>Iו%wcqnzHP]1KѴիn]CDD$u^MzC8BiYZ,"" ^fZT&~2Vo~5VQ9ykiӜWOLZa-"Ü4O0TtԎ@cf>R#At}ʌՊn3.~ů:3%WYLe^EFՋN3"~:s43cih\CuF-ИmN-"Rf %p!yI\j&62VϏ""RUTff MXfhգNrAfLCNX}\'cx7kttUU} uGX+Иg9""'H73?p53Ke^M;k9 CJ33v斴h\E:ozS*"1$VB 4I&{_; |S9=S" `!2<~\%}{N/ QI\=U`~p#D8M9A4zթڐDk""`4!Dvl!9㌈Afk)cD$)r+pJzS"r{53V'zwwよTiyj Rȉȟu*иj+HxUjʑ[hE(&j$gSV,SDN|!f9"2nZFH?խctb."` -3~P܇HR ' j&6*ШZD*ÿ"uc6-cƚ ]7Vt@,$2c .u2V|CINHxuy>`D]ɌU;a>y{+|{?Y"YƪR'[13V@$AƯ5Kff9oZ3Az>v3*۲2,D8[VNh7}m*CXAuRGѵwRHgAcuU5:aH8/~e)^Yf >G[kfnS= N4 o&CD 4VԒMD7u4d,U9cJZEe՛Lwg8""NU锱ZTxZfX<p8""MXfh'!^tTofJ03s:DD:Lh@S}BIDD0Z;> :E~ xCx`5U,ފ[EDQ.ѭN+k l<hYa7J]A}~H+ev+Ԑ_-IE |&Hwʫq>gѣ;<. 'S'D(QZ=ܺx<Œ(1~ߗe;"xϋ ^ \ua;jZ۴w^]f Ď(2At914F xO|)*9Ҭٰ.ˎ ;DDDڙFP=PN%-G@ q%:~L+ \F+J&CBDJHCH}XDD$BL]L^%d9 '!3$""9v] ]y\|G }^2At_$""(.S»^` >NH3\ `qrHgLS٭zLB0S΁9¡#]""=/~$^q~`$  Q,u4)Rw0x{|E=n\Bzܟk-|u^zNE0`.!r|F"]+چDD4M=2 4c %f%A-S]b""33Yĩ]43xXޚg ֧DDJa* US|?\)pBzi10X 2\H \?F{ܾS1RTz!7X&m=Dޒ`@sZ@U1PmԿ6 Z!.s1gڋ%?O<"^*Rbkmyx#/ѯ.{IE{Ĺs(ykUD̏ў%>=}ɌYl*"U1c|ޖQ{^uq@M`"k=O3 uvEC ϕUQG{~\RP5DhFm"ȉ05F{nMDgpE. PoX(Nl*"Y`}exCky_OuEz1HϠά躋Ds(Eua7DZHϯcPϙ,SujaH:MYJ3NrEj,B*钟% Ek\: )ÜbYA/!:>47b@v)3y evӝb$=ϖL.aiϏ \ӫ\N 3<=cg^7ۥrhj|m" "uQl/޾?j8f)@RukMqE 1jag 6f)$16)N+( K?E5!X,H&MԔ&EVy>~`5htKP$cmV̏ў%cFMx19ۥfu?ͪMdW3 ;(.\J' Y.K414kVhS,oDZ/5ݓ @4k6_%5Ò1-*0^gT@һ4kVh6=e9D-lC;RiZck6sV\[?*y]?Q_'.f:\ Ѥ/fHX 0V#C̢=n g K$Rue^d`f<6OS`_ {Pix*DNj.WТ@zSgў2{0sBl*0Ƨ7ႇ;"afB`=!Zti\ǫ*E )E6=27WQz> ? |/'9ŵe!s=Vʋ> |2 l)0&MH ҶkWJ'l/y<@fαOu@"U "-6n3jbkS)M`S`"! !%Lj<ԧOc.DHud:.>| / #N;X&` TىўV`~&;\ : vuNw/%]ֈt|y*Hh ڛY_G^8><7F .HSP;5-_Ci=K";}4Mod8o?,yhyByp)DzY _)s^ˋ s7-_`ΊQE);cth9`^J#5% qndU`8\ Py4kc~4F{Nm@ P=毣+9ڳ7ўR<@DD@_⛀KO灚n* 63A+DI<rrMkOs)AO|EG\Vm6)';B s-zHDz=ˉK Q=0/XE4842('DҬR\!tQ;lf G mge@" 9._*XgMM~Bs~O"'HD9 |76l|Z[TpnۀS%0iz9ݩ2(tH$"R \Lk_>K|P#PT0h'8dpO<2 sm\B L5Hr zdc BoQ3iK:D>zHw9x[0 s16肈ȉ| 2& >Nq<2.%D 0T-"?FD5_.ʉ˘x$ 56/:]"r(DzYQd\y_!˾DwcH ș^kFbGCwhn%԰POy%ぷS{?M eh@D4FDv.'x$iz=DH%vLD@ sfG=ZdN<Rcѷ)4Q)DgpuN\y"t # A+]f0-9H?N/u9O&&`vN]C1た x'R+z2TWl! o3QֱV;nv}|\c%`욳H\BKUDx\$*+zb}|<[HcT5)m L$m=}?p;BdO߇`2˻&hoױut!)9sQ|E+i pS<N v"kOE1ܯe馵sV=zI=cC:knåp6-ք_/yOUdE3PcRIמ ]s;.@,ѓ&gۖn_@R6zQn397eWnz&`e<k>al h p[/)DN#@iHy# pdeh|D$3ix6'>J+xs} .P/3zxG9ugvNnOx-좿(e~_$%3b]  #h/^yЏj-?6l>aWN cfل⚓b}<6xxVjӷ)0)IMh#@15=)6rvJ\c)+vGM CzSYOѓLHgKQOm#S"JQIU+Pb Ic:&ua,LIL):\ :p.:E6R5um ӷHfj5I_]&oOULUW nfR}Ds!rEҴٹ߇)I7׭]N"eCsoO]hW]: MR"$}s|3vBhN`<>F0-_/.&xR 0!! 1HwUk pNJbeS=s-0+s*g/&}ȝmH(+&ԫ2A1-Rv Q-2bMEFˡhm2/%|),f|}<c=0%0Me%Iz4XI!WZm&ho1Vu xNQ5S`㩇vi#z1{y!)Y/ a}<~P>m&DN:xVuNtP=p)] ?_6"ݠ.:sꤍ @3iŸMAa.RA+Lψ3bSR90qy$AZM(֘ F+J|֓*^6:\@CKڵ?p.Ut$'8bhs ?vGIL.H߳N]#]P K ic{Ax$P1rlo۳ɢ'u#zx !~eKCwӊc+9N]myU9U^ ܅E8BZسTbB;n p<_I[aԈD>ׯ!$Gosnἂ5PzoĖȉQ$t\ETqIU= 9~n_5:\l3!" P;cg .dxjkbǚ"R4ЋW/vw=Ww @3CO?M(^&"RWg8fවH@\.sPS.n0""Mpg<;[k9 4g&Z{n0 B~D)Ň(CD:LY1`iWοm `K0s >tzD!|D:+pUGn~hUD)(H*wIг ׀x~᪖B|>,("h!8->>A6F0x7$:鲻U.vf&DS~^np.coQPD)GrB IDAT腄{ ˖HZ7y}1z[>r7ގo]m7(" ϸ?VZcrh}Gsoxۍrg㍷씋("ioבr|zT-ZOX'0eQ5Esz+?O_\{6C3#w=yj):l/hg=m:uD3} xSry(J`-r{E/EtaSJ&ӊo 흿 ޢ'ڙ6bZD:C{NHҏ8o@ǺcZD@UR'E=.-бhsEDT%6o=(alިRuԳLecxwa,T\v(NiIꓧ]ԥ]G7(]ZRQu{1v" ^N:'x}w<@xE@ *kQ*"p9(꒧2Q5l O%Ll779 K,O u(Mư#(]RKO6Zs]"G+jM{ ?6JEN(oi1%-becxm 9nsJ@u`&XIߌs&\Dj"s%̈́\u@: :Zׁ/Jz|M@ 2)wώo:m 3 V>|ЄVDj" 9:F:uJNW \'B-u0˩4S*[+z˶u'I gWsOn?M0*o$t D$uf$ 9^$j',v_"x0ĩ$p_U|}k)dP #7MBԻv}+aSJ_7[a(Cу E$e2P$ďT_mwIrGJ9Ck LDS H(`lNj1\f%|ѡQ-%4Ip|  IMM \L+:4?F΋(־AB()"r4sy{Fz #@c7DG/XHa' Бx^,wԔ0$ub_?OZҪ_sN/s agOTzP(&wѣrJ|2Àrb Ds4C[[2S)R;^C\@gv<ѣrJC9Ls>EǛsҩ<->4`Yt`)$E ; պHf쥿hXxVbգn:"1zBZ*P]qtKt"Iuw.a%N5jw>GE6OMT#@"cF$rcLU"S)$.;踉R#@5T!:C)WiY=1T 5V7UFzWQ=2FDnN'*MZCwyL͑"udGu@%=+ #@5D?tC9.UVңHȔ1YGUx { ?|@"1`N^FuLrj)L qTxȉTO4Cwh:rBNF|4+*DO/p-Rt =n?ĩSՄ9S=vm~1oaT%9Y%]eٱNC ˕[բg iW= \&)e1Rrvk#Edpy\ݘw'j:V"]mH7E{+<&DD6(Dt~`&&r^>yஜ k1?fUxKNu=H>㲜$?UBHjTFBz.B:-"=!Ǽu-~ZѡuKNgL~6ߥ~" ЄTNLưmo%lOO#~z/h!ҳbѳ(z(@ 9-VFq1|DseۀoD1t SY"=P]3?[kHHD sQJLK /w_@7Ec[HD:>˦WT[c븝f@6BƼx[UpPC&`T@""Ѷ%r:=ۮ$NX,,/ǵz i4vvWP7m%a\_O<Eu ٗP {B;V{?4x3iCj;vE ԝM#a/޳:cԮcԾx?pFrG=_vQ)Dlk dR?Tvyފ UWyK(@"ҋԵ]f®sn>Q$pNQ>貯 .RvstWmBZYEO LD$QuNUGإ43s߉}3t?`Yέ/M::FDD*d3GК!gl?<(;y5= ~J{RHOJ]d.h,!Ty.®9Q)DDJ.п]5 k>]vP?"'BzB$"R:͉+1P2wFB.Ơ ZD^?",> ?8HHH(P!ydHDN 6o#Ժ,<o>JyO0!u#tTPTW0TDu8qט':4w)".o p)D)Q,%ZKR!tKx﫹9Bتj{IOrѦD:bV7:(fJHޚnύEX˦R=@"=Y[oFB疥mnQ{(Zξ8kSz $tߋFQN0j\#LIL~ 0M{ [4I#lts:۩㰤cK$f7.xl!Bg:]%+z* ne6Z5GH8`S=o!j"YP}$2`*'ȯXop) !uB]=*xud~a\ zsߐ7 ?hWH0`\Sc2x@1Ɲ6"G"e.00= LJ\ߍ⛄,N09q:u'm{ԜǗN+>B]gEx= H0MgB5ᵄG\"@FD`p-Bt?ߍhZBA '1*'Sz6B?W']H3\Aʻ)w~Py-pl}F^]} bUD9*U(ʋ"BQ=s}&;RsIs  me݇)Lę.HJ-&n8rtHd-& uj1ṉ́eWRU3鲽(@Q-&FUcDl1!u=]6P :J*Q)jHB*.-&-&&xpVǟSM(@ZDyedbB鲣1BwI9(_=bB .upnb;PW+۔p3Fo{ϡ*O[imMńH1v>nl:0ٯeydt#,{..h@l1!"h>BlN"9/8 ^@u8 9>:8?~)_K/'|lVv)$K#lsϔǀ@:iIz hck-R<;rg.i ٟfo<|8u ^I #~\bByddy(˶6R%{کl$jDPʡ^`y|qzEz|liVovvNq.U-{)#3vo"tٗՉtN?SՇ+A>O(kS%"5@L~ $1_͟_v@=~PptR=[9Ghїp<۰wٱXQ`\;\ 9 &.%tTH PyDxZBO;\@_7oJ`!0i vFG< # ]ԋ m5,0[YhbB~8/bth10 jtӘ.+*p-lt/H2jM) ˺_ޟt*Ҭ-:å"juK-dl}[wбnT 1ZBi/]97Au/r(D́e#OE.5Nرr1ηnDdKl7tټ kes*Q9^LF\Q5|)Mm.T|9+dģC+ճm*jof);]s.P7NV ^Q5eAѡI{W8AL.Kodvl.de P7o½aQI7C nQ-~%5M|]" Ѿ7gpma08q(R`J.:L7({N-]9u]N(@72Q5|,bGKXӬJI]"萈)]Yj*À7v7F;eR5_$R;: :3Psވ/uDjC7F觜>Pn7?p!5 8{(@c .@w@Me2a).|O9]r& [{RCn'lkOM= !,;)4lFTSE& pIun+p ,z 9cw|@$zBݡofݡRz.QI9yB;-%IӁ[[ӷk_D3K}j1>s@VJ(eDR \+d_)6P #E@ Ium8 9q_&('`%%.03P} <sJ@Fv4 Hb!+ }D@My K陨RIFj؞ K5-%QI5Jn`5+)À>B2߽5*O_y6_v:"RW*,W  SV-Fz `ib\G+:d!F94AƳl poϫu]/{t@R-YyTm:6Ry N(z"`5Օ ܾ\xSئP[ftH@=B|%!TUͳ8gQ17JM`U"<P8*z쎢5JNHä\t!Q5Y~QU4ZZ$͈'6ӝ.(YtUf^O0TqqKwӮfXLCd׫bE *HdHP !imBB<5n-p-W@"'zthy" n2%*;H$R:KTeZ q@r]ѤQC_-4m YKh)BMXG=k:e"n {]Nwx~q5_G5uDh&zG֑ eIDATHDJEQ}_l[qBSŵ;203ET= |9jITWISZ4v I;tZ HEQݟq4zY`,ܔ6$Rߏ\4- ÁZM0U1飚݈ U%`( i':iE*v2 DFןhmS.cW"i!H}3p)<*uMGL~bܾ|}בDdMQWǟF1GZua8FhԘ " ՗1蹚j`= 5fϓNHDz|th (A]Њ! CvIwFs |, y{ME\G(_um'1_$rOQ0߿G.oRҙu.-M t'MH@b߲ f>O u5A&{jOq=e4fp)DzI [_8Ci1pkq/0ޗ4Mȉd"V ~;9za F^jZYtFA:ctS`~I *8_ QQ c>:R<,?PzK=N(RhjHl!=V$\D%T_/3ş7k ߝ/!{V|v'wzSIALISXP+ SK$Dv)XF ̎r,_`._۔6$"u`+!MrPVw2BB4z bEюҍy 1$M3ZAZt佌EN&4{kMU\ÄH.P}K$Ja(&(A: "a+ [ƫ̈́\`-y\_p)D@39z&rBIО kW]}ԗ .~E$(+2tX75AH/`<>N;NBݡI ߔx!\<wHDl&4!(A DY>KP0&uL ۣoE#e*s忕tSR\^k5Hv q@lB5M=v\X/v@",&Ft}u]^>MHU;BDm-n^p)DJybM`ޫ . pi I"QiHфf< o'#>Ja_O?% I Hzs_^i~Tvل(F˚(`V\QXl(u@;UMx#1zSUN<%[R6Y~8Bf}1T$S)HT)Ju-VuTD2J:d=sH@z4@3VEL20ryJ$*SI'(RgBآ 6%HOT ) xL:@iq&P~+i7L7_'LEWǟ\߁}wPhJh8-惉}Y>[һJf iJ( S.Љ2XD0=2\f"4ZMT me>). )z0iJT z?iZ@|lp)/<, Ъ{iUf#W'EvzgvEؖ2s_p.YeUrh9e=Ōm@ >[i%R_=E3!7r5{ پG/ԪʾI$E+Mn5ޠ0WBflԛLk#)5Z;hs_f6Y.F"LDi /gK_ԫGQ6éSIh$`-P7S[0 XKg_M7f׀ޓ"2 KV 2+λ'BYIS #@ P$t[*)ͯ>[Ed Dni!{dF(HgkS("@yFӿkÃmc8jFhP3ct[3uˤW>;6HVt@ M0^_WtYm)o \ >𽄣C x#ZmP(OgkS#" v&ӪLdhCs\gdp)| ;H}Pa1t/^`]#ZrrR`_rzO V`f v&<9+^+ѡTP(#T ݄]""$9cpwI/iI. }W|PHJhsEx 6PfY!B!eeh>(q/L/<0)@|5 M]fhҥbΉQv{"^"jg6A*`gh.;ۘc3 '8S%sjD$3xSZ鼷e 68L׀8E"WJh+QA,S)ͫN0KFzCapS%"=..4ԇ o~~+"vz{0`>i6¬KcJDP;c \o Cs }g8`[M(ҍX;D-&Pl7ԔѹvGT(@N~{9Qq)VJ@\Bs iDqH4Z\I4:+_ʷi ɺ"MVNHԏ?NA?QAXl)='8.3,LHH}S%W0Z<:z3uKEH6{" 96y3 -3KqKEH$g:U" y?Lfn~N)p^Ϲ|$FjQ՗O'Mpa"G#oޞ zS%~ԦV;n 3>ϧ)ӴSY7qD@2 Y; w<Žn כhRk}M,NqD@2 y3ujRQL} i:Ū^y#<{hpD@2 Y)!}@69ҍ%ni:U" )ԩZBh4ؿ,dR&ڔE@2TqZ,R6RS S8`VILїUN4R(\C_""v$FI3ϼ8өQQ95>N)KRF>BzQduW.'I':U" 9*C~- ^jwi1x/g\e?;Rgz-#N(@rL-x;ri n9^ZF곜*Q)䘌N,M%oޏFjԍˀu9|+B*jyJbh-#iN4 MRG6:k^{0O(gT\zziE6V N(a~Vխ[z 't_{ʬwvPfDDzS% ~teўCp)EdO%&v2RtD<ӢyA=" R7R/'4R+@i0$U:)N3}iV)}>,.?B l""]'e#ABj)pSh]=YLy/"Us.iWM@rLYCF*X`YRy&>-MN+#T֨12ĊAQTOaqY'wDgǙs|?ɔdw9s%i$ _%˗6+`NZjxϞn^~I>HZ* :D si۳!VQQ^zxXPC(ib{l<ݓKyA gY =`4HDK Rے7yN>`HREBPYᚺ=u.IjAszJ AhHTr4UzCeTT~Qr$Uz@Cq Fݞe&~67C*6X:_#It8@An,WSN{I*AV3* (֒OdnӤ%F=$HyRҥha9;lGb9%Ij uΝs;]pp=g, XIAAꜧZLF񱥆o)޻$R~RiA QINIJiL>:$YAv>H-$IjjR>-|>vM.IԶE. hE5R$IꆭK,|/:V$IY&ܜ%O1!$IukW5y%IR6D|v0RIc'hmGS($I.nF)9`K$p$k.XiҎԒ$I$I$I$I$I$i nsIENDB`ruby-rack1.4-1.4.5/contrib/rack.svg000066400000000000000000000271371223351442100167530ustar00rootroot00000000000000 image/svg+xml ruby-rack1.4-1.4.5/contrib/rack_logo.svg000066400000000000000000001066571223351442100200000ustar00rootroot00000000000000 image/svg+xml ruby-rack1.4-1.4.5/contrib/rdoc.css000066400000000000000000000162351223351442100167500ustar00rootroot00000000000000/* Forked from the Darkfish templates rdoc.css file, much hacked, probably * imperfect */ html { max-width: 960px; margin: 0 auto; } body { font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif; } body.file-popup { font-size: 90%; margin-left: 0; } h1 { color: #4183C4; } :link, :visited { color: #4183C4; text-decoration: none; } :link:hover, :visited:hover { border-bottom: 1px dotted #4183C4; } pre, pre.description { font: 12px Monaco,"Courier New","DejaVu Sans Mono","Bitstream Vera Sans Mono",monospace; padding: 1em; overflow: auto; line-height: 1.4; } /* @group Generic Classes */ .initially-hidden { display: none; } #search-field { width: 98%; } .missing-docs { font-size: 120%; background: white url(images/wrench_orange.png) no-repeat 4px center; color: #ccc; line-height: 2em; border: 1px solid #d00; opacity: 1; text-indent: 24px; letter-spacing: 3px; font-weight: bold; -webkit-border-radius: 5px; -moz-border-radius: 5px; } .target-section { border: 2px solid #dcce90; border-left-width: 8px; background: #fff3c2; } /* @end */ /* @group Index Page, Standalone file pages */ .indexpage ul { line-height: 160%; list-style: none; } .indexpage ul :link, .indexpage ul :visited { font-size: 16px; } .indexpage li { padding-left: 20px; } .indexpage ul > li { background: url(images/bullet_black.png) no-repeat left 4px; } .indexpage li.method { background: url(images/plugin.png) no-repeat left 4px; } .indexpage li.module { background: url(images/package.png) no-repeat left 4px; } .indexpage li.class { background: url(images/ruby.png) no-repeat left 4px; } .indexpage li.file { background: url(images/page_white_text.png) no-repeat left 4px; } .indexpage li li { background: url(images/tag_blue.png) no-repeat left 4px; } .indexpage li .toc-toggle { width: 16px; height: 16px; background: url(images/add.png) no-repeat; } .indexpage li .toc-toggle.open { background: url(images/delete.png) no-repeat; } /* @end */ /* @group Top-Level Structure */ .project-section, #file-metadata, #class-metadata { display: block; background: #f5f5f5; margin-bottom: 1em; padding: 0.5em; } .project-section h3, #file-metadata h3, #class-metadata h3 { margin: 0; } #metadata { float: left; width: 280px; } #documentation { margin: 2em 1em 2em 300px; } #validator-badges { clear: both; margin: 1em 1em 2em; font-size: smaller; } /* @end */ /* @group Metadata Section */ #metadata ul, #metadata dl, #metadata p { padding: 0px; list-style: none; } dl.svninfo { color: #666; margin: 0; } dl.svninfo dt { font-weight: bold; } ul.link-list li { white-space: nowrap; } ul.link-list .type { font-size: 8px; text-transform: uppercase; color: white; background: #969696; } /* @end */ /* @group Documentation Section */ .note-list { margin: 8px 0; } .label-list { margin: 8px 1.5em; border: 1px solid #ccc; } .description .label-list { font-size: 14px; } .note-list dt { font-weight: bold; } .note-list dd { } .label-list dt { font-weight: bold; background: #ddd; } .label-list dd { } .label-list dd + dt, .note-list dd + dt { margin-top: 0.7em; } #documentation .section { font-size: 90%; } #documentation h2.section-header { color: #333; font-size: 175%; } .documentation-section-title { position: relative; } .documentation-section-title .section-click-top { position: absolute; top: 6px; right: 12px; font-size: 10px; visibility: hidden; } .documentation-section-title:hover .section-click-top { visibility: visible; } #documentation h3.section-header { color: #333; font-size: 150%; } #constants-list > dl, #attributes-list > dl { margin: 1em 0 2em; border: 0; } #constants-list > dl dt, #attributes-list > dl dt { font-weight: bold; font-family: Monaco, "Andale Mono"; background: inherit; } #constants-list > dl dt a, #attributes-list > dl dt a { color: inherit; } #constants-list > dl dd, #attributes-list > dl dd { margin: 0 0 1em 0; color: #666; } .documentation-section h2 { position: relative; } .documentation-section h2 a { position: absolute; top: 8px; right: 10px; font-size: 12px; color: #9b9877; visibility: hidden; } .documentation-section h2:hover a { visibility: visible; } /* @group Method Details */ #documentation .method-source-code { display: none; } #documentation .method-detail { margin: 0.2em 0.2em; padding: 0.5em; } #documentation .method-detail:hover { background-color: #f5f5f5; } #documentation .method-heading { cursor: pointer; position: relative; font-size: 125%; line-height: 125%; font-weight: bold; color: #333; background: url(images/brick.png) no-repeat left bottom; padding-left: 20px; } #documentation .method-heading :link, #documentation .method-heading :visited { color: inherit; } #documentation .method-click-advice { position: absolute; right: 5px; font-size: 10px; color: #aaa; visibility: hidden; background: url(images/zoom.png) no-repeat right 5px; padding-right: 20px; overflow: show; } #documentation .method-heading:hover .method-click-advice { visibility: visible; } #documentation .method-alias .method-heading { color: #666; background: url(images/brick_link.png) no-repeat left bottom; } #documentation .method-description, #documentation .aliases { margin: 0 20px; color: #666; } #documentation .method-description p, #documentation .aliases p { line-height: 1.2em; } #documentation .aliases { font-style: italic; cursor: default; } #documentation .method-description p { margin-bottom: 0.5em; } #documentation .method-description ul { margin-left: 1.5em; } #documentation .attribute-method-heading { background: url(images/tag_green.png) no-repeat left bottom; } #documentation #attribute-method-details .method-detail:hover { background-color: transparent; cursor: default; } #documentation .attribute-access-type { font-size: 60%; text-transform: uppercase; vertical-align: super; } .method-section .method-source-code { background: white; } /* @group Source Code */ .ruby-constant .ruby-keyword .ruby-ivar .ruby-operator .ruby-identifier .ruby-node .ruby-comment .ruby-regexp .ruby-value { background: transparent; } /* Thanks GitHub!!! */ .ruby-constant { color: #458; font-weight: bold; } .ruby-keyword { color: black; font-weight: bold; } .ruby-ivar { color: teal; } .ruby-operator { color: #000; } .ruby-identifier { color: black; } .ruby-node { color: red; } .ruby-comment { color: #998; font-weight: bold; } .ruby-regexp { color: #009926; } .ruby-value { color: #099; } .ruby-string { color: red; } /* @group search results */ #search-section .section-header { margin: 0; padding: 0; } #search-results { width: 100%; list-style: none; margin: 0; padding: 0; } #search-results h1 { font-size: 1em; font-weight: normal; text-shadow: none; } #search-results .current { background: #eee; } #search-results li { list-style: none; line-height: 1em; padding: 0.5em; border-bottom: 1px solid black; } #search-results .search-namespace { font-weight: bold; } #search-results li em { background: yellow; font-style: normal; } #search-results pre { margin: 0.5em; } ruby-rack1.4-1.4.5/example/000077500000000000000000000000001223351442100152735ustar00rootroot00000000000000ruby-rack1.4-1.4.5/example/lobster.ru000066400000000000000000000001071223351442100173130ustar00rootroot00000000000000require 'rack/lobster' use Rack::ShowExceptions run Rack::Lobster.new ruby-rack1.4-1.4.5/example/protectedlobster.rb000066400000000000000000000005651223351442100212120ustar00rootroot00000000000000require '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 ruby-rack1.4-1.4.5/example/protectedlobster.ru000066400000000000000000000002401223351442100212230ustar00rootroot00000000000000require 'rack/lobster' use Rack::ShowExceptions use Rack::Auth::Basic, "Lobster 2.0" do |username, password| 'secret' == password end run Rack::Lobster.new ruby-rack1.4-1.4.5/lib/000077500000000000000000000000001223351442100144065ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack.rb000066400000000000000000000056551223351442100156660ustar00rootroot00000000000000# Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen # # Rack is freely distributable under the terms of an MIT-style license. # See COPYING or http://www.opensource.org/licenses/mit-license.php. # The Rack main module, serving as a namespace for all core Rack # modules and classes. # # All modules meant for use in your application are autoloaded here, # so it should be enough just to require rack.rb in your code. module Rack # The Rack protocol version number implemented. VERSION = [1,1] # Return the Rack protocol version as a dotted string. def self.version VERSION.join(".") end # Return the Rack release as a dotted string. def self.release "1.4" end autoload :Builder, "rack/builder" autoload :BodyProxy, "rack/body_proxy" autoload :Cascade, "rack/cascade" autoload :Chunked, "rack/chunked" autoload :CommonLogger, "rack/commonlogger" autoload :ConditionalGet, "rack/conditionalget" autoload :Config, "rack/config" autoload :ContentLength, "rack/content_length" autoload :ContentType, "rack/content_type" autoload :ETag, "rack/etag" autoload :File, "rack/file" autoload :Deflater, "rack/deflater" autoload :Directory, "rack/directory" autoload :ForwardRequest, "rack/recursive" autoload :Handler, "rack/handler" autoload :Head, "rack/head" autoload :Lint, "rack/lint" autoload :Lock, "rack/lock" autoload :Logger, "rack/logger" autoload :MethodOverride, "rack/methodoverride" autoload :Mime, "rack/mime" autoload :NullLogger, "rack/nulllogger" autoload :Recursive, "rack/recursive" autoload :Reloader, "rack/reloader" autoload :Runtime, "rack/runtime" autoload :Sendfile, "rack/sendfile" autoload :Server, "rack/server" autoload :ShowExceptions, "rack/showexceptions" autoload :ShowStatus, "rack/showstatus" autoload :Static, "rack/static" autoload :URLMap, "rack/urlmap" autoload :Utils, "rack/utils" autoload :Multipart, "rack/multipart" autoload :MockRequest, "rack/mock" autoload :MockResponse, "rack/mock" autoload :Request, "rack/request" autoload :Response, "rack/response" module Auth autoload :Basic, "rack/auth/basic" autoload :AbstractRequest, "rack/auth/abstract/request" autoload :AbstractHandler, "rack/auth/abstract/handler" module Digest autoload :MD5, "rack/auth/digest/md5" autoload :Nonce, "rack/auth/digest/nonce" autoload :Params, "rack/auth/digest/params" autoload :Request, "rack/auth/digest/request" end # Not all of the following schemes are "standards", but they are used often. @schemes = %w[basic digest bearer mac token oauth oauth2] def self.add_scheme scheme @schemes << scheme @schemes.uniq! end def self.schemes @schemes.dup end end module Session autoload :Cookie, "rack/session/cookie" autoload :Pool, "rack/session/pool" autoload :Memcache, "rack/session/memcache" end end ruby-rack1.4-1.4.5/lib/rack/000077500000000000000000000000001223351442100153265ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/auth/000077500000000000000000000000001223351442100162675ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/auth/abstract/000077500000000000000000000000001223351442100200725ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/auth/abstract/handler.rb000066400000000000000000000014251223351442100220360ustar00rootroot00000000000000module Rack module Auth # Rack::Auth::AbstractHandler implements common authentication functionality. # # +realm+ should be set for all handlers. class AbstractHandler attr_accessor :realm def initialize(app, realm=nil, &authenticator) @app, @realm, @authenticator = app, realm, authenticator end private def unauthorized(www_authenticate = challenge) return [ 401, { 'Content-Type' => 'text/plain', 'Content-Length' => '0', 'WWW-Authenticate' => www_authenticate.to_s }, [] ] end def bad_request return [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => '0' }, [] ] end end end end ruby-rack1.4-1.4.5/lib/rack/auth/abstract/request.rb000066400000000000000000000015071223351442100221120ustar00rootroot00000000000000require 'rack/request' module Rack module Auth class AbstractRequest def initialize(env) @env = env end def request @request ||= Request.new(@env) end def provided? !authorization_key.nil? end def parts @parts ||= @env[authorization_key].split(' ', 2) end def scheme @scheme ||= begin s = parts.first.downcase Rack::Auth.schemes.include?(s) ? s.to_sym : s end end def params @params ||= parts.last end private AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] def authorization_key @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } end end end end ruby-rack1.4-1.4.5/lib/rack/auth/basic.rb000066400000000000000000000022711223351442100176770ustar00rootroot00000000000000require 'rack/auth/abstract/handler' require 'rack/auth/abstract/request' module Rack module Auth # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. # # Initialize with the Rack application that you want protecting, # and a block that checks if a username and password pair are valid. # # See also: example/protectedlobster.rb class Basic < AbstractHandler def call(env) auth = Basic::Request.new(env) return unauthorized unless auth.provided? return bad_request unless auth.basic? if valid?(auth) env['REMOTE_USER'] = auth.username return @app.call(env) end unauthorized end private def challenge 'Basic realm="%s"' % realm end def valid?(auth) @authenticator.call(*auth.credentials) end class Request < Auth::AbstractRequest def basic? !parts.first.nil? && :basic == scheme end def credentials @credentials ||= params.unpack("m*").first.split(/:/, 2) end def username credentials.first end end end end end ruby-rack1.4-1.4.5/lib/rack/auth/digest/000077500000000000000000000000001223351442100175465ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/auth/digest/md5.rb000066400000000000000000000061131223351442100205610ustar00rootroot00000000000000require 'rack/auth/abstract/handler' require 'rack/auth/digest/request' require 'rack/auth/digest/params' require 'rack/auth/digest/nonce' require 'digest/md5' module Rack module Auth module Digest # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of # HTTP Digest Authentication, as per RFC 2617. # # Initialize with the [Rack] application that you want protecting, # and a block that looks up a plaintext password for a given username. # # +opaque+ needs to be set to a constant base64/hexadecimal string. # class MD5 < AbstractHandler attr_accessor :opaque attr_writer :passwords_hashed def initialize(app, realm=nil, opaque=nil, &authenticator) @passwords_hashed = nil if opaque.nil? and realm.respond_to? :values_at realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed end super(app, realm, &authenticator) @opaque = opaque end def passwords_hashed? !!@passwords_hashed end def call(env) auth = Request.new(env) unless auth.provided? return unauthorized end if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) return bad_request end if valid?(auth) if auth.nonce.stale? return unauthorized(challenge(:stale => true)) else env['REMOTE_USER'] = auth.username return @app.call(env) end end unauthorized end private QOP = 'auth'.freeze def params(hash = {}) Params.new do |params| params['realm'] = realm params['nonce'] = Nonce.new.to_s params['opaque'] = H(opaque) params['qop'] = QOP hash.each { |k, v| params[k] = v } end end def challenge(hash = {}) "Digest #{params(hash)}" end def valid?(auth) valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) end def valid_qop?(auth) QOP == auth.qop end def valid_opaque?(auth) H(opaque) == auth.opaque end def valid_nonce?(auth) auth.nonce.valid? end def valid_digest?(auth) pw = @authenticator.call(auth.username) pw && digest(auth, pw) == auth.response end def md5(data) ::Digest::MD5.hexdigest(data) end alias :H :md5 def KD(secret, data) H([secret, data] * ':') end def A1(auth, password) [ auth.username, auth.realm, password ] * ':' end def A2(auth) [ auth.method, auth.uri ] * ':' end def digest(auth, password) password_hash = passwords_hashed? ? password : H(A1(auth, password)) KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') end end end end end ruby-rack1.4-1.4.5/lib/rack/auth/digest/nonce.rb000066400000000000000000000023071223351442100211770ustar00rootroot00000000000000require 'digest/md5' module Rack module Auth module Digest # Rack::Auth::Digest::Nonce is the default nonce generator for the # Rack::Auth::Digest::MD5 authentication handler. # # +private_key+ needs to set to a constant string. # # +time_limit+ can be optionally set to an integer (number of seconds), # to limit the validity of the generated nonces. class Nonce class << self attr_accessor :private_key, :time_limit end def self.parse(string) new(*string.unpack("m*").first.split(' ', 2)) end def initialize(timestamp = Time.now, given_digest = nil) @timestamp, @given_digest = timestamp.to_i, given_digest end def to_s [([ @timestamp, digest ] * ' ')].pack("m*").strip end def digest ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') end def valid? digest == @given_digest end def stale? !self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit end def fresh? !stale? end end end end end ruby-rack1.4-1.4.5/lib/rack/auth/digest/params.rb000066400000000000000000000021031223351442100213520ustar00rootroot00000000000000module Rack module Auth module Digest class Params < Hash def self.parse(str) Params[*split_header_value(str).map do |param| k, v = param.split('=', 2) [k, dequote(v)] end.flatten] end def self.dequote(str) # From WEBrick::HTTPUtils ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup ret.gsub!(/\\(.)/, "\\1") ret end def self.split_header_value(str) str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } end def initialize super() yield self if block_given? end def [](k) super k.to_s end def []=(k, v) super k.to_s, v.to_s end UNQUOTED = ['nc', 'stale'] def to_s map do |k, v| "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) end.join(', ') end def quote(str) # From WEBrick::HTTPUtils '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' end end end end end ruby-rack1.4-1.4.5/lib/rack/auth/digest/request.rb000066400000000000000000000016731223351442100215720ustar00rootroot00000000000000require 'rack/auth/abstract/request' require 'rack/auth/digest/params' require 'rack/auth/digest/nonce' module Rack module Auth module Digest class Request < Auth::AbstractRequest def method @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] end def digest? :digest == scheme end def correct_uri? request.fullpath == uri end def nonce @nonce ||= Nonce.parse(params['nonce']) end def params @params ||= Params.parse(parts.last) end def respond_to?(sym, *) super or params.has_key? sym.to_s end def method_missing(sym, *args) return super unless params.has_key?(key = sym.to_s) return params[key] if args.size == 0 raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" end end end end end ruby-rack1.4-1.4.5/lib/rack/backports/000077500000000000000000000000001223351442100173165ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/backports/uri/000077500000000000000000000000001223351442100201155ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/backports/uri/common_18.rb000066400000000000000000000032021223351442100222370ustar00rootroot00000000000000# :stopdoc: # Stolen from ruby core's uri/common.rb, with modifications to support 1.8.x # # https://github.com/ruby/ruby/blob/trunk/lib/uri/common.rb # # module URI TBLENCWWWCOMP_ = {} # :nodoc: 256.times do |i| TBLENCWWWCOMP_[i.chr] = '%%%02X' % i end TBLENCWWWCOMP_[' '] = '+' TBLENCWWWCOMP_.freeze TBLDECWWWCOMP_ = {} # :nodoc: 256.times do |i| h, l = i>>4, i&15 TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr end TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze # Encode given +s+ to URL-encoded form data. # # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP # (ASCII space) to + and converts others to %XX. # # This is an implementation of # http://www.w3.org/TR/html5/forms.html#url-encoded-form-data # # See URI.decode_www_form_component, URI.encode_www_form def self.encode_www_form_component(s) str = s.to_s if RUBY_VERSION < "1.9" && $KCODE =~ /u/i str.gsub(/([^ a-zA-Z0-9_.-]+)/) do '%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase end.tr(' ', '+') else str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]} end end # Decode given +str+ of URL-encoded form data. # # This decods + to SP. # # See URI.encode_www_form_component, URI.decode_www_form def self.decode_www_form_component(str, enc=nil) raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]} end end ruby-rack1.4-1.4.5/lib/rack/backports/uri/common_192.rb000066400000000000000000000032421223351442100223260ustar00rootroot00000000000000# :stopdoc: # Stolen from ruby core's uri/common.rb @32618ba to fix DoS issues in 1.9.2 # # https://github.com/ruby/ruby/blob/32618ba7438a2247042bba9b5d85b5d49070f5e5/lib/uri/common.rb # # Issue: # http://redmine.ruby-lang.org/issues/5149 # # Relevant Fixes: # https://github.com/ruby/ruby/commit/b5f91deee04aa6ccbe07c23c8222b937c22a799b # https://github.com/ruby/ruby/commit/93177c1e5c3906abf14472ae0b905d8b5c72ce1b # # This should probably be removed once there is a Ruby 1.9.2 patch level that # includes this fix. require 'uri/common' module URI TBLDECWWWCOMP_ = {} unless const_defined?(:TBLDECWWWCOMP_) #:nodoc: if TBLDECWWWCOMP_.empty? 256.times do |i| h, l = i>>4, i&15 TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr end TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze end def self.decode_www_form(str, enc=Encoding::UTF_8) return [] if str.empty? unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})" end ary = [] $&.scan(/([^=;&]+)=([^;&]*)/) do ary << [decode_www_form_component($1, enc), decode_www_form_component($2, enc)] end ary end def self.decode_www_form_component(str, enc=Encoding::UTF_8) raise ArgumentError, "invalid %-encoding (#{str})" unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str str.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc) end remove_const :WFKV_ if const_defined?(:WFKV_) WFKV_ = '(?:[^%#=;&]*(?:%\h\h[^%#=;&]*)*)' # :nodoc: end ruby-rack1.4-1.4.5/lib/rack/backports/uri/common_193.rb000066400000000000000000000011501223351442100223230ustar00rootroot00000000000000# :stopdoc: require 'uri/common' # Issue: # http://bugs.ruby-lang.org/issues/5925 # # Relevant commit: # https://github.com/ruby/ruby/commit/edb7cdf1eabaff78dfa5ffedfbc2e91b29fa9ca1 module URI 256.times do |i| TBLENCWWWCOMP_[i.chr] = '%%%02X' % i end TBLENCWWWCOMP_[' '] = '+' TBLENCWWWCOMP_.freeze 256.times do |i| h, l = i>>4, i&15 TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr end TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze end # :startdoc: ruby-rack1.4-1.4.5/lib/rack/body_proxy.rb000066400000000000000000000016761223351442100200630ustar00rootroot00000000000000module Rack class BodyProxy def initialize(body, &block) @body, @block, @closed = body, block, false end def respond_to?(*args) return false if args.first.to_s =~ /^to_ary$/ super or @body.respond_to?(*args) end def close return if @closed @closed = true begin @body.close if @body.respond_to? :close ensure @block.call end end def closed? @closed end # N.B. This method is a special case to address the bug described by #434. # We are applying this special case for #each only. Future bugs of this # class will be handled by requesting users to patch their ruby # implementation, to save adding too many methods in this class. def each(*args, &block) @body.each(*args, &block) end def method_missing(*args, &block) super if args.first.to_s =~ /^to_ary$/ @body.__send__(*args, &block) end end end ruby-rack1.4-1.4.5/lib/rack/builder.rb000066400000000000000000000074611223351442100173110ustar00rootroot00000000000000module Rack # Rack::Builder implements a small DSL to iteratively construct Rack # applications. # # Example: # # require 'rack/lobster' # app = Rack::Builder.new do # use Rack::CommonLogger # use Rack::ShowExceptions # map "/lobster" do # use Rack::Lint # run Rack::Lobster.new # end # end # # run app # # Or # # app = Rack::Builder.app do # use Rack::CommonLogger # run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] } # end # # run app # # +use+ adds a middleware to the stack, +run+ dispatches to an application. # You can use +map+ to construct a Rack::URLMap in a convenient way. class Builder def self.parse_file(config, opts = Server::Options.new) options = {} if config =~ /\.ru$/ cfgfile = ::File.read(config) if cfgfile[/^#\\(.*)/] && opts options = opts.parse! $1.split(/\s+/) end cfgfile.sub!(/^__END__\n.*\Z/m, '') app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app", TOPLEVEL_BINDING, config, 0 else require config app = Object.const_get(::File.basename(config, '.rb').capitalize) end return app, options end def initialize(default_app = nil,&block) @use, @map, @run = [], nil, default_app instance_eval(&block) if block_given? end def self.app(default_app = nil, &block) self.new(default_app, &block).to_app end # Specifies a middleware to use in a stack. # # class Middleware # def initialize(app) # @app = app # end # # def call(env) # env["rack.some_header"] = "setting an example" # @app.call(env) # end # end # # use Middleware # run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] } # # All requests through to this application will first be processed by the middleware class. # The +call+ method in this example sets an additional environment key which then can be # referenced in the application if required. def use(middleware, *args, &block) if @map mapping, @map = @map, nil @use << proc { |app| generate_map app, mapping } end @use << proc { |app| middleware.new(app, *args, &block) } end # Takes an argument that is an object that responds to #call and returns a Rack response. # The simplest form of this is a lambda object: # # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] } # # However this could also be a class: # # class Heartbeat # def self.call(env) # [200, { "Content-Type" => "text/plain" }, ["OK"]] # end # end # # run Heartbeat def run(app) @run = app end # Creates a route within the application. # # Rack::Builder.app do # map '/' do # run Heartbeat # end # end # # The +use+ method can also be used here to specify middleware to run under a specific path: # # Rack::Builder.app do # map '/' do # use Middleware # run Heartbeat # end # end # # This example includes a piece of middleware which will run before requests hit +Heartbeat+. # def map(path, &block) @map ||= {} @map[path] = block end def to_app app = @map ? generate_map(@run, @map) : @run fail "missing run or map statement" unless app @use.reverse.inject(app) { |a,e| e[a] } end def call(env) to_app.call(env) end private def generate_map(default_app, mapping) mapped = default_app ? {'/' => default_app} : {} mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) } URLMap.new(mapped) end end end ruby-rack1.4-1.4.5/lib/rack/cascade.rb000066400000000000000000000025241223351442100172410ustar00rootroot00000000000000module Rack # Rack::Cascade tries an request on several apps, and returns the # first response that is not 404 (or in a list of configurable # status codes). class Cascade NotFound = [404, {"Content-Type" => "text/plain"}, []] attr_reader :apps def initialize(apps, catch=[404, 405]) @apps = []; @has_app = {} apps.each { |app| add app } @catch = {} [*catch].each { |status| @catch[status] = true } end def call(env) result = NotFound last_body = nil @apps.each do |app| # The SPEC says that the body must be closed after it has been iterated # by the server, or if it is replaced by a middleware action. Cascade # replaces the body each time a cascade happens. It is assumed that nil # does not respond to close, otherwise the previous application body # will be closed. The final application body will not be closed, as it # will be passed to the server as a result. last_body.close if last_body.respond_to? :close result = app.call(env) last_body = result[2] break unless @catch.include?(result[0].to_i) end result end def add app @has_app[app] = true @apps << app end def include? app @has_app.include? app end alias_method :<<, :add end end ruby-rack1.4-1.4.5/lib/rack/chunked.rb000066400000000000000000000025171223351442100173010ustar00rootroot00000000000000require 'rack/utils' module Rack # Middleware that applies chunked transfer encoding to response bodies # when the response does not include a Content-Length header. class Chunked include Rack::Utils # A body wrapper that emits chunked responses class Body TERM = "\r\n" TAIL = "0#{TERM}#{TERM}" include Rack::Utils def initialize(body) @body = body end def each term = TERM @body.each do |chunk| size = bytesize(chunk) next if size == 0 chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding) yield [size.to_s(16), term, chunk, term].join end yield TAIL end def close @body.close if @body.respond_to?(:close) end end def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) headers = HeaderHash.new(headers) if env['HTTP_VERSION'] == 'HTTP/1.0' || STATUS_WITH_NO_ENTITY_BODY.include?(status) || headers['Content-Length'] || headers['Transfer-Encoding'] [status, headers, body] else headers.delete('Content-Length') headers['Transfer-Encoding'] = 'chunked' [status, headers, Body.new(body)] end end end end ruby-rack1.4-1.4.5/lib/rack/commonlogger.rb000066400000000000000000000040361223351442100203460ustar00rootroot00000000000000require 'rack/body_proxy' module Rack # Rack::CommonLogger forwards every request to the given +app+, and # logs a line in the # {Apache common log format}[http://httpd.apache.org/docs/1.3/logs.html#common] # to the +logger+. # # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is # an instance of Rack::NullLogger. # # +logger+ can be any class, including the standard library Logger, and is # expected to have a +write+ method, which accepts the CommonLogger::FORMAT. # According to the SPEC, the error stream must also respond to +puts+ # (which takes a single argument that responds to +to_s+), and +flush+ # (which is called without arguments in order to make the error appear for # sure) class CommonLogger # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common # # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - # # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} def initialize(app, logger=nil) @app = app @logger = logger end def call(env) began_at = Time.now status, header, body = @app.call(env) header = Utils::HeaderHash.new(header) body = BodyProxy.new(body) { log(env, status, header, began_at) } [status, header, body] end private def log(env, status, header, began_at) now = Time.now length = extract_content_length(header) logger = @logger || env['rack.errors'] logger.write FORMAT % [ env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-", env["REMOTE_USER"] || "-", now.strftime("%d/%b/%Y %H:%M:%S"), env["REQUEST_METHOD"], env["PATH_INFO"], env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"], env["HTTP_VERSION"], status.to_s[0..3], length, now - began_at ] end def extract_content_length(headers) value = headers['Content-Length'] or return '-' value.to_s == '0' ? '-' : value end end end ruby-rack1.4-1.4.5/lib/rack/conditionalget.rb000066400000000000000000000037421223351442100206640ustar00rootroot00000000000000require 'rack/utils' module Rack # Middleware that enables conditional GET using If-None-Match and # If-Modified-Since. The application should set either or both of the # Last-Modified or Etag response headers according to RFC 2616. When # either of the conditions is met, the response body is set to be zero # length and the response status is set to 304 Not Modified. # # Applications that defer response body generation until the body's each # message is received will avoid response body generation completely when # a conditional GET matches. # # Adapted from Michael Klishin's Merb implementation: # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb class ConditionalGet def initialize(app) @app = app end def call(env) case env['REQUEST_METHOD'] when "GET", "HEAD" status, headers, body = @app.call(env) headers = Utils::HeaderHash.new(headers) if status == 200 && fresh?(env, headers) status = 304 headers.delete('Content-Type') headers.delete('Content-Length') body = [] end [status, headers, body] else @app.call(env) end end private def fresh?(env, headers) modified_since = env['HTTP_IF_MODIFIED_SINCE'] none_match = env['HTTP_IF_NONE_MATCH'] return false unless modified_since || none_match success = true success &&= modified_since?(to_rfc2822(modified_since), headers) if modified_since success &&= etag_matches?(none_match, headers) if none_match success end def etag_matches?(none_match, headers) etag = headers['ETag'] and etag == none_match end def modified_since?(modified_since, headers) last_modified = to_rfc2822(headers['Last-Modified']) and modified_since and modified_since >= last_modified end def to_rfc2822(since) Time.rfc2822(since) rescue nil end end end ruby-rack1.4-1.4.5/lib/rack/config.rb000066400000000000000000000004251223351442100171210ustar00rootroot00000000000000module Rack # Rack::Config modifies the environment using the block given during # initialization. class Config def initialize(app, &block) @app = app @block = block end def call(env) @block.call(env) @app.call(env) end end end ruby-rack1.4-1.4.5/lib/rack/content_length.rb000066400000000000000000000013751223351442100206740ustar00rootroot00000000000000require 'rack/utils' module Rack # Sets the Content-Length header on responses with fixed-length bodies. class ContentLength include Rack::Utils def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) headers = HeaderHash.new(headers) if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) && !headers['Content-Length'] && !headers['Transfer-Encoding'] && body.respond_to?(:to_ary) obody = body body, length = [], 0 obody.each { |part| body << part; length += bytesize(part) } obody.close if obody.respond_to?(:close) headers['Content-Length'] = length.to_s end [status, headers, body] end end end ruby-rack1.4-1.4.5/lib/rack/content_type.rb000066400000000000000000000012401223351442100203630ustar00rootroot00000000000000require 'rack/utils' module Rack # Sets the Content-Type header on responses which don't have one. # # Builder Usage: # use Rack::ContentType, "text/plain" # # When no content type argument is provided, "text/html" is assumed. class ContentType include Rack::Utils def initialize(app, content_type = "text/html") @app, @content_type = app, content_type end def call(env) status, headers, body = @app.call(env) headers = Utils::HeaderHash.new(headers) unless STATUS_WITH_NO_ENTITY_BODY.include?(status) headers['Content-Type'] ||= @content_type end [status, headers, body] end end end ruby-rack1.4-1.4.5/lib/rack/deflater.rb000066400000000000000000000054711223351442100174500ustar00rootroot00000000000000require "zlib" require "stringio" require "time" # for Time.httpdate require 'rack/utils' module Rack class Deflater def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) headers = Utils::HeaderHash.new(headers) # Skip compressing empty entity body responses and responses with # no-transform set. if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || headers['Cache-Control'].to_s =~ /\bno-transform\b/ return [status, headers, body] end request = Request.new(env) encoding = Utils.select_best_encoding(%w(gzip deflate identity), request.accept_encoding) # Set the Vary HTTP header. vary = headers["Vary"].to_s.split(",").map { |v| v.strip } unless vary.include?("*") || vary.include?("Accept-Encoding") headers["Vary"] = vary.push("Accept-Encoding").join(",") end case encoding when "gzip" headers['Content-Encoding'] = "gzip" headers.delete('Content-Length') mtime = headers.key?("Last-Modified") ? Time.httpdate(headers["Last-Modified"]) : Time.now [status, headers, GzipStream.new(body, mtime)] when "deflate" headers['Content-Encoding'] = "deflate" headers.delete('Content-Length') [status, headers, DeflateStream.new(body)] when "identity" [status, headers, body] when nil body.close if body.respond_to?(:close) message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] end end class GzipStream def initialize(body, mtime) @body = body @mtime = mtime end def each(&block) @writer = block gzip =::Zlib::GzipWriter.new(self) gzip.mtime = @mtime @body.each { |part| gzip.write(part) gzip.flush } ensure @body.close if @body.respond_to?(:close) gzip.close @writer = nil end def write(data) @writer.call(data) end end class DeflateStream DEFLATE_ARGS = [ Zlib::DEFAULT_COMPRESSION, # drop the zlib header which causes both Safari and IE to choke -Zlib::MAX_WBITS, Zlib::DEF_MEM_LEVEL, Zlib::DEFAULT_STRATEGY ] def initialize(body) @body = body end def each deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) } yield deflater.finish nil ensure @body.close if @body.respond_to?(:close) deflater.close end end end end ruby-rack1.4-1.4.5/lib/rack/directory.rb000066400000000000000000000100231223351442100176530ustar00rootroot00000000000000require 'time' require 'rack/utils' require 'rack/mime' module Rack # Rack::Directory serves entries below the +root+ given, according to the # path info of the Rack request. If a directory is found, the file's contents # will be presented in an html based index. If a file is found, the env will # be passed to the specified +app+. # # If +app+ is not specified, a Rack::File of the same +root+ will be used. class Directory DIR_FILE = "%s%s%s%s" DIR_PAGE = <<-PAGE %s

%s


%s
Name Size Type Last Modified

PAGE attr_reader :files attr_accessor :root, :path def initialize(root, app=nil) @root = F.expand_path(root) @app = app || Rack::File.new(@root) end def call(env) dup._call(env) end F = ::File def _call(env) @env = env @script_name = env['SCRIPT_NAME'] @path_info = Utils.unescape(env['PATH_INFO']) if forbidden = check_forbidden forbidden else @path = F.join(@root, @path_info) list_path end end def check_forbidden return unless @path_info.include? ".." body = "Forbidden\n" size = Rack::Utils.bytesize(body) return [403, {"Content-Type" => "text/plain", "Content-Length" => size.to_s, "X-Cascade" => "pass"}, [body]] end def list_directory @files = [['../','Parent Directory','','','']] glob = F.join(@path, '*') url_head = (@script_name.split('/') + @path_info.split('/')).map do |part| Rack::Utils.escape part end Dir[glob].sort.each do |node| stat = stat(node) next unless stat basename = F.basename(node) ext = F.extname(node) url = F.join(*url_head + [Rack::Utils.escape(basename)]) size = stat.size type = stat.directory? ? 'directory' : Mime.mime_type(ext) size = stat.directory? ? '-' : filesize_format(size) mtime = stat.mtime.httpdate url << '/' if stat.directory? basename << '/' if stat.directory? @files << [ url, basename, size, type, mtime ] end return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] end def stat(node, max = 10) F.stat(node) rescue Errno::ENOENT, Errno::ELOOP return nil end # TODO: add correct response if not readable, not sure if 404 is the best # option def list_path @stat = F.stat(@path) if @stat.readable? return @app.call(@env) if @stat.file? return list_directory if @stat.directory? else raise Errno::ENOENT, 'No such file or directory' end rescue Errno::ENOENT, Errno::ELOOP return entity_not_found end def entity_not_found body = "Entity not found: #{@path_info}\n" size = Rack::Utils.bytesize(body) return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s, "X-Cascade" => "pass"}, [body]] end def each show_path = @path.sub(/^#{@root}/,'') files = @files.map{|f| DIR_FILE % f }*"\n" page = DIR_PAGE % [ show_path, show_path , files ] page.each_line{|l| yield l } end # Stolen from Ramaze FILESIZE_FORMAT = [ ['%.1fT', 1 << 40], ['%.1fG', 1 << 30], ['%.1fM', 1 << 20], ['%.1fK', 1 << 10], ] def filesize_format(int) FILESIZE_FORMAT.each do |format, size| return format % (int.to_f / size) if int >= size end int.to_s + 'B' end end end ruby-rack1.4-1.4.5/lib/rack/etag.rb000066400000000000000000000036331223351442100166000ustar00rootroot00000000000000require 'digest/md5' module Rack # Automatically sets the ETag header on all String bodies. # # The ETag header is skipped if ETag or Last-Modified headers are sent or if # a sendfile body (body.responds_to :to_path) is given (since such cases # should be handled by apache/nginx). # # On initialization, you can pass two parameters: a Cache-Control directive # used when Etag is absent and a directive when it is present. The first # defaults to nil, while the second defaults to "max-age=0, private, must-revalidate" class ETag DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL) @app = app @cache_control = cache_control @no_cache_control = no_cache_control end def call(env) status, headers, body = @app.call(env) if etag_status?(status) && etag_body?(body) && !skip_caching?(headers) digest, body = digest_body(body) headers['ETag'] = %("#{digest}") if digest end unless headers['Cache-Control'] if digest headers['Cache-Control'] = @cache_control if @cache_control else headers['Cache-Control'] = @no_cache_control if @no_cache_control end end [status, headers, body] end private def etag_status?(status) status == 200 || status == 201 end def etag_body?(body) !body.respond_to?(:to_path) end def skip_caching?(headers) (headers['Cache-Control'] && headers['Cache-Control'].include?('no-cache')) || headers.key?('ETag') || headers.key?('Last-Modified') end def digest_body(body) parts = [] body.each { |part| parts << part } string_body = parts.join digest = Digest::MD5.hexdigest(string_body) unless string_body.empty? [digest, parts] end end end ruby-rack1.4-1.4.5/lib/rack/file.rb000066400000000000000000000074321223351442100166000ustar00rootroot00000000000000require 'time' require 'rack/utils' require 'rack/mime' module Rack # Rack::File serves files below the +root+ directory given, according to the # path info of the Rack request. # e.g. when Rack::File.new("/etc") is used, you can access 'passwd' file # as http://localhost:9292/passwd # # Handlers can detect if bodies are a Rack::File, and use mechanisms # like sendfile on the +path+. class File SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact) ALLOWED_VERBS = %w[GET HEAD] attr_accessor :root attr_accessor :path attr_accessor :cache_control alias :to_path :path def initialize(root, headers={}) @root = root # Allow a cache_control string for backwards compatibility if headers.instance_of? String warn \ "Rack::File headers parameter replaces cache_control after Rack 1.5." @headers = { 'Cache-Control' => headers } else @headers = headers end end def call(env) dup._call(env) end F = ::File def _call(env) unless ALLOWED_VERBS.include? env["REQUEST_METHOD"] return fail(405, "Method Not Allowed") end @path_info = Utils.unescape(env["PATH_INFO"]) parts = @path_info.split SEPS clean = [] parts.each do |part| next if part.empty? || part == '.' part == '..' ? clean.pop : clean << part end @path = F.join(@root, *clean) available = begin F.file?(@path) && F.readable?(@path) rescue SystemCallError false end if available serving(env) else fail(404, "File not found: #{@path_info}") end end def serving(env) last_modified = F.mtime(@path).httpdate return [304, {}, []] if env['HTTP_IF_MODIFIED_SINCE'] == last_modified response = [ 200, { "Last-Modified" => last_modified, "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain') }, env["REQUEST_METHOD"] == "HEAD" ? [] : self ] # Set custom headers @headers.each { |field, content| response[1][field] = content } if @headers # NOTE: # We check via File::size? whether this file provides size info # via stat (e.g. /proc files often don't), otherwise we have to # figure it out by reading the whole file into memory. size = F.size?(@path) || Utils.bytesize(F.read(@path)) ranges = Rack::Utils.byte_ranges(env, size) if ranges.nil? || ranges.length > 1 # No ranges, or multiple ranges (which we don't support): # TODO: Support multiple byte-ranges response[0] = 200 @range = 0..size-1 elsif ranges.empty? # Unsatisfiable. Return error, and file size: response = fail(416, "Byte range unsatisfiable") response[1]["Content-Range"] = "bytes */#{size}" return response else # Partial content: @range = ranges[0] response[0] = 206 response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}" size = @range.end - @range.begin + 1 end response[1]["Content-Length"] = size.to_s response end def each F.open(@path, "rb") do |file| file.seek(@range.begin) remaining_len = @range.end-@range.begin+1 while remaining_len > 0 part = file.read([8192, remaining_len].min) break unless part remaining_len -= part.length yield part end end end private def fail(status, body) body += "\n" [ status, { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s, "X-Cascade" => "pass" }, [body] ] end end end ruby-rack1.4-1.4.5/lib/rack/handler.rb000066400000000000000000000053601223351442100172740ustar00rootroot00000000000000module Rack # *Handlers* connect web servers with Rack. # # Rack includes Handlers for Thin, WEBrick, FastCGI, CGI, SCGI # and LiteSpeed. # # Handlers usually are activated by calling MyHandler.run(myapp). # A second optional hash can be passed to include server-specific # configuration. module Handler def self.get(server) return unless server server = server.to_s unless @handlers.include? server load_error = try_require('rack/handler', server) end if klass = @handlers[server] klass.split("::").inject(Object) { |o, x| o.const_get(x) } else const_get(server) end rescue NameError => name_error raise load_error || name_error end def self.default(options = {}) # Guess. if ENV.include?("PHP_FCGI_CHILDREN") # We already speak FastCGI options.delete :File options.delete :Port Rack::Handler::FastCGI elsif ENV.include?("REQUEST_METHOD") Rack::Handler::CGI else begin Rack::Handler::Thin rescue LoadError Rack::Handler::WEBrick end end end # Transforms server-name constants to their canonical form as filenames, # then tries to require them but silences the LoadError if not found # # Naming convention: # # Foo # => 'foo' # FooBar # => 'foo_bar.rb' # FooBAR # => 'foobar.rb' # FOObar # => 'foobar.rb' # FOOBAR # => 'foobar.rb' # FooBarBaz # => 'foo_bar_baz.rb' def self.try_require(prefix, const_name) file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }. gsub(/[A-Z]+[^A-Z]/, '_\&').downcase require(::File.join(prefix, file)) nil rescue LoadError => error error end def self.register(server, klass) @handlers ||= {} @handlers[server.to_s] = klass.to_s end autoload :CGI, "rack/handler/cgi" autoload :FastCGI, "rack/handler/fastcgi" autoload :Mongrel, "rack/handler/mongrel" autoload :EventedMongrel, "rack/handler/evented_mongrel" autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" autoload :WEBrick, "rack/handler/webrick" autoload :LSWS, "rack/handler/lsws" autoload :SCGI, "rack/handler/scgi" autoload :Thin, "rack/handler/thin" register 'cgi', 'Rack::Handler::CGI' register 'fastcgi', 'Rack::Handler::FastCGI' register 'mongrel', 'Rack::Handler::Mongrel' register 'emongrel', 'Rack::Handler::EventedMongrel' register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' register 'webrick', 'Rack::Handler::WEBrick' register 'lsws', 'Rack::Handler::LSWS' register 'scgi', 'Rack::Handler::SCGI' register 'thin', 'Rack::Handler::Thin' end end ruby-rack1.4-1.4.5/lib/rack/handler/000077500000000000000000000000001223351442100167435ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/handler/cgi.rb000066400000000000000000000030421223351442100200310ustar00rootroot00000000000000require 'rack/content_length' require 'rack/rewindable_input' module Rack module Handler class CGI def self.run(app, options=nil) $stdin.binmode serve app end def self.serve(app) env = ENV.to_hash env.delete "HTTP_CONTENT_LENGTH" env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" env.update({"rack.version" => Rack::VERSION, "rack.input" => Rack::RewindableInput.new($stdin), "rack.errors" => $stderr, "rack.multithread" => false, "rack.multiprocess" => true, "rack.run_once" => true, "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" }) env["QUERY_STRING"] ||= "" env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] env["REQUEST_PATH"] ||= "/" status, headers, body = app.call(env) begin send_headers status, headers send_body body ensure body.close if body.respond_to? :close end end def self.send_headers(status, headers) $stdout.print "Status: #{status}\r\n" headers.each { |k, vs| vs.split("\n").each { |v| $stdout.print "#{k}: #{v}\r\n" } } $stdout.print "\r\n" $stdout.flush end def self.send_body(body) body.each { |part| $stdout.print part $stdout.flush } end end end end ruby-rack1.4-1.4.5/lib/rack/handler/evented_mongrel.rb000066400000000000000000000002001223351442100224350ustar00rootroot00000000000000require 'swiftcore/evented_mongrel' module Rack module Handler class EventedMongrel < Handler::Mongrel end end end ruby-rack1.4-1.4.5/lib/rack/handler/fastcgi.rb000066400000000000000000000051401223351442100207100ustar00rootroot00000000000000require 'fcgi' require 'socket' require 'rack/content_length' require 'rack/rewindable_input' if defined? FCGI::Stream class FCGI::Stream alias _rack_read_without_buffer read def read(n, buffer=nil) buf = _rack_read_without_buffer n buffer.replace(buf.to_s) if buffer buf end end end module Rack module Handler class FastCGI def self.run(app, options={}) if options[:File] STDIN.reopen(UNIXServer.new(options[:File])) elsif options[:Port] STDIN.reopen(TCPServer.new(options[:Host], options[:Port])) end FCGI.each { |request| serve request, app } end def self.valid_options { "Host=HOST" => "Hostname to listen on (default: localhost)", "Port=PORT" => "Port to listen on (default: 8080)", "File=PATH" => "Creates a Domain socket at PATH instead of a TCP socket. Ignores Host and Port if set.", } end def self.serve(request, app) env = request.env env.delete "HTTP_CONTENT_LENGTH" env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" rack_input = RewindableInput.new(request.in) env.update({"rack.version" => Rack::VERSION, "rack.input" => rack_input, "rack.errors" => request.err, "rack.multithread" => false, "rack.multiprocess" => true, "rack.run_once" => false, "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" }) env["QUERY_STRING"] ||= "" env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] env["REQUEST_PATH"] ||= "/" env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" begin status, headers, body = app.call(env) begin send_headers request.out, status, headers send_body request.out, body ensure body.close if body.respond_to? :close end ensure rack_input.close request.finish end end def self.send_headers(out, status, headers) out.print "Status: #{status}\r\n" headers.each { |k, vs| vs.split("\n").each { |v| out.print "#{k}: #{v}\r\n" } } out.print "\r\n" out.flush end def self.send_body(out, body) body.each { |part| out.print part out.flush } end end end end ruby-rack1.4-1.4.5/lib/rack/handler/lsws.rb000066400000000000000000000030511223351442100202570ustar00rootroot00000000000000require 'lsapi' require 'rack/content_length' require 'rack/rewindable_input' module Rack module Handler class LSWS def self.run(app, options=nil) while LSAPI.accept != nil serve app end end def self.serve(app) env = ENV.to_hash env.delete "HTTP_CONTENT_LENGTH" env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" rack_input = RewindableInput.new($stdin.read.to_s) env.update( "rack.version" => Rack::VERSION, "rack.input" => rack_input, "rack.errors" => $stderr, "rack.multithread" => false, "rack.multiprocess" => true, "rack.run_once" => false, "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" ) env["QUERY_STRING"] ||= "" env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] env["REQUEST_PATH"] ||= "/" status, headers, body = app.call(env) begin send_headers status, headers send_body body ensure body.close if body.respond_to? :close end ensure rack_input.close end def self.send_headers(status, headers) print "Status: #{status}\r\n" headers.each { |k, vs| vs.split("\n").each { |v| print "#{k}: #{v}\r\n" } } print "\r\n" STDOUT.flush end def self.send_body(body) body.each { |part| print part STDOUT.flush } end end end end ruby-rack1.4-1.4.5/lib/rack/handler/mongrel.rb000066400000000000000000000064741223351442100207460ustar00rootroot00000000000000require 'mongrel' require 'stringio' require 'rack/content_length' require 'rack/chunked' module Rack module Handler class Mongrel < ::Mongrel::HttpHandler def self.run(app, options={}) server = ::Mongrel::HttpServer.new( options[:Host] || '0.0.0.0', options[:Port] || 8080, options[:num_processors] || 950, options[:throttle] || 0, options[:timeout] || 60) # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. # Use is similar to #run, replacing the app argument with a hash of # { path=>app, ... } or an instance of Rack::URLMap. if options[:map] if app.is_a? Hash app.each do |path, appl| path = '/'+path unless path[0] == ?/ server.register(path, Rack::Handler::Mongrel.new(appl)) end elsif app.is_a? URLMap app.instance_variable_get(:@mapping).each do |(host, path, appl)| next if !host.nil? && !options[:Host].nil? && options[:Host] != host path = '/'+path unless path[0] == ?/ server.register(path, Rack::Handler::Mongrel.new(appl)) end else raise ArgumentError, "first argument should be a Hash or URLMap" end else server.register('/', Rack::Handler::Mongrel.new(app)) end yield server if block_given? server.run.join end def self.valid_options { "Host=HOST" => "Hostname to listen on (default: localhost)", "Port=PORT" => "Port to listen on (default: 8080)", "Processors=N" => "Number of concurrent processors to accept (default: 950)", "Timeout=N" => "Time before a request is dropped for inactivity (default: 60)", "Throttle=N" => "Throttle time between socket.accept calls in hundredths of a second (default: 0)", } end def initialize(app) @app = app end def process(request, response) env = {}.replace(request.params) env.delete "HTTP_CONTENT_TYPE" env.delete "HTTP_CONTENT_LENGTH" 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" => Rack::VERSION, "rack.input" => rack_input, "rack.errors" => $stderr, "rack.multithread" => true, "rack.multiprocess" => false, # ??? "rack.run_once" => false, "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" }) env["QUERY_STRING"] ||= "" status, headers, body = @app.call(env) begin response.status = status.to_i response.send_status(nil) headers.each { |k, vs| vs.split("\n").each { |v| response.header[k] = v } } response.send_header body.each { |part| response.write part response.socket.flush } ensure body.close if body.respond_to? :close end end end end end ruby-rack1.4-1.4.5/lib/rack/handler/scgi.rb000066400000000000000000000042001223351442100202110ustar00rootroot00000000000000require 'scgi' require 'stringio' require 'rack/content_length' require 'rack/chunked' module Rack module Handler class SCGI < ::SCGI::Processor attr_accessor :app def self.run(app, options=nil) options[:Socket] = UNIXServer.new(options[:File]) if options[:File] new(options.merge(:app=>app, :host=>options[:Host], :port=>options[:Port], :socket=>options[:Socket])).listen end def self.valid_options { "Host=HOST" => "Hostname to listen on (default: localhost)", "Port=PORT" => "Port to listen on (default: 8080)", } end def initialize(settings = {}) @app = settings[:app] super(settings) end def process_request(request, input_body, socket) env = {}.replace(request) env.delete "HTTP_CONTENT_TYPE" env.delete "HTTP_CONTENT_LENGTH" env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] env["PATH_INFO"] = env["REQUEST_PATH"] env["QUERY_STRING"] ||= "" env["SCRIPT_NAME"] = "" rack_input = StringIO.new(input_body) rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding) env.update({"rack.version" => Rack::VERSION, "rack.input" => rack_input, "rack.errors" => $stderr, "rack.multithread" => true, "rack.multiprocess" => true, "rack.run_once" => false, "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" }) status, headers, body = app.call(env) begin socket.write("Status: #{status}\r\n") headers.each do |k, vs| vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} end socket.write("\r\n") body.each {|s| socket.write(s)} ensure body.close if body.respond_to? :close end end end end end ruby-rack1.4-1.4.5/lib/rack/handler/swiftiplied_mongrel.rb000066400000000000000000000002101223351442100233270ustar00rootroot00000000000000require 'swiftcore/swiftiplied_mongrel' module Rack module Handler class SwiftipliedMongrel < Handler::Mongrel end end end ruby-rack1.4-1.4.5/lib/rack/handler/thin.rb000066400000000000000000000011221223351442100202260ustar00rootroot00000000000000require "thin" require "rack/content_length" require "rack/chunked" module Rack module Handler class Thin def self.run(app, options={}) server = ::Thin::Server.new(options[:Host] || '0.0.0.0', options[:Port] || 8080, app) yield server if block_given? server.start end def self.valid_options { "Host=HOST" => "Hostname to listen on (default: localhost)", "Port=PORT" => "Port to listen on (default: 8080)", } end end end end ruby-rack1.4-1.4.5/lib/rack/handler/webrick.rb000066400000000000000000000045531223351442100207250ustar00rootroot00000000000000require 'webrick' require 'stringio' require 'rack/content_length' module Rack module Handler class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet def self.run(app, options={}) options[:BindAddress] = options.delete(:Host) if options[:Host] @server = ::WEBrick::HTTPServer.new(options) @server.mount "/", Rack::Handler::WEBrick, app yield @server if block_given? @server.start end def self.valid_options { "Host=HOST" => "Hostname to listen on (default: localhost)", "Port=PORT" => "Port to listen on (default: 8080)", } end def self.shutdown @server.shutdown @server = nil end def initialize(server, app) super server @app = app end def service(req, res) env = req.meta_vars 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" => Rack::VERSION, "rack.input" => rack_input, "rack.errors" => $stderr, "rack.multithread" => true, "rack.multiprocess" => false, "rack.run_once" => false, "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" }) env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] env["QUERY_STRING"] ||= "" unless env["PATH_INFO"] == "" path, n = req.request_uri.path, env["SCRIPT_NAME"].length env["PATH_INFO"] = path[n, path.length-n] end env["REQUEST_PATH"] ||= [env["SCRIPT_NAME"], env["PATH_INFO"]].join status, headers, body = @app.call(env) begin res.status = status.to_i headers.each { |k, vs| if k.downcase == "set-cookie" res.cookies.concat vs.split("\n") else # Since WEBrick won't accept repeated headers, # merge the values per RFC 1945 section 4.2. res[k] = vs.split("\n").join(", ") end } body.each { |part| res.body << part } ensure body.close if body.respond_to? :close end end end end end ruby-rack1.4-1.4.5/lib/rack/head.rb000066400000000000000000000004551223351442100165600ustar00rootroot00000000000000module Rack class Head def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if env["REQUEST_METHOD"] == "HEAD" body.close if body.respond_to? :close [status, headers, []] else [status, headers, body] end end end end ruby-rack1.4-1.4.5/lib/rack/lint.rb000066400000000000000000000522071223351442100166270ustar00rootroot00000000000000require 'rack/utils' module Rack # Rack::Lint validates your application and the requests and # responses according to the Rack spec. class Lint def initialize(app) @app = app @content_length = nil end # :stopdoc: class LintError < RuntimeError; end module Assertion def assert(message, &block) unless block.call raise LintError, message end end end include Assertion ## This specification aims to formalize the Rack protocol. You ## can (and should) use Rack::Lint to enforce it. ## ## When you develop middleware, be sure to add a Lint before and ## after to catch all mistakes. ## = Rack applications ## A Rack application is a Ruby object (not a class) that ## responds to +call+. def call(env=nil) dup._call(env) end def _call(env) ## It takes exactly one argument, the *environment* assert("No env given") { env } check_env env env['rack.input'] = InputWrapper.new(env['rack.input']) env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) ## and returns an Array of exactly three values: status, headers, @body = @app.call(env) ## The *status*, check_status status ## the *headers*, check_headers headers ## and the *body*. check_content_type status, headers check_content_length status, headers @head_request = env["REQUEST_METHOD"] == "HEAD" [status, headers, self] end ## == The Environment def check_env(env) ## The environment must be an instance of Hash that includes ## CGI-like headers. The application is free to modify the ## environment. assert("env #{env.inspect} is not a Hash, but #{env.class}") { env.kind_of? Hash } ## ## The environment is required to include these variables ## (adopted from PEP333), except when they'd be empty, but see ## below. ## REQUEST_METHOD:: The HTTP request method, such as ## "GET" or "POST". This cannot ever ## be an empty string, and so is ## always required. ## SCRIPT_NAME:: The initial portion of the request ## URL's "path" that corresponds to the ## application object, so that the ## application knows its virtual ## "location". This may be an empty ## string, if the application corresponds ## to the "root" of the server. ## PATH_INFO:: The remainder of the request URL's ## "path", designating the virtual ## "location" of the request's target ## within the application. This may be an ## empty string, if the request URL targets ## the application root and does not have a ## trailing slash. This value may be ## percent-encoded when I originating from ## a URL. ## QUERY_STRING:: The portion of the request URL that ## follows the ?, if any. May be ## empty, but is always required! ## SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. ## HTTP_ Variables:: Variables corresponding to the ## client-supplied HTTP request ## headers (i.e., variables whose ## names begin with HTTP_). The ## presence or absence of these ## variables should correspond with ## the presence or absence of the ## appropriate HTTP header in the ## request. ## In addition to this, the Rack environment must include these ## Rack-specific variables: ## rack.version:: The Array [1,1], representing this version of Rack. ## rack.url_scheme:: +http+ or +https+, depending on the request URL. ## rack.input:: See below, the input stream. ## rack.errors:: See below, the error stream. ## rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. ## rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. ## rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). ## ## Additional environment specifications have approved to ## standardized middleware APIs. None of these are required to ## be implemented by the server. ## rack.session:: A hash like interface for storing request session data. ## The store must implement: if session = env['rack.session'] ## store(key, value) (aliased as []=); assert("session #{session.inspect} must respond to store and []=") { session.respond_to?(:store) && session.respond_to?(:[]=) } ## fetch(key, default = nil) (aliased as []); assert("session #{session.inspect} must respond to fetch and []") { session.respond_to?(:fetch) && session.respond_to?(:[]) } ## delete(key); assert("session #{session.inspect} must respond to delete") { session.respond_to?(:delete) } ## clear; assert("session #{session.inspect} must respond to clear") { session.respond_to?(:clear) } end ## rack.logger:: A common object interface for logging messages. ## The object must implement: if logger = env['rack.logger'] ## info(message, &block) assert("logger #{logger.inspect} must respond to info") { logger.respond_to?(:info) } ## debug(message, &block) assert("logger #{logger.inspect} must respond to debug") { logger.respond_to?(:debug) } ## warn(message, &block) assert("logger #{logger.inspect} must respond to warn") { logger.respond_to?(:warn) } ## error(message, &block) assert("logger #{logger.inspect} must respond to error") { logger.respond_to?(:error) } ## fatal(message, &block) assert("logger #{logger.inspect} must respond to fatal") { logger.respond_to?(:fatal) } end ## The server or the application can store their own data in the ## environment, too. The keys must contain at least one dot, ## and should be prefixed uniquely. The prefix rack. ## is reserved for use with the Rack core distribution and other ## accepted specifications and must not be used otherwise. ## %w[REQUEST_METHOD SERVER_NAME SERVER_PORT QUERY_STRING rack.version rack.input rack.errors rack.multithread rack.multiprocess rack.run_once].each { |header| assert("env missing required key #{header}") { env.include? header } } ## The environment must not contain the keys ## HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH ## (use the versions without HTTP_). %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| assert("env contains #{header}, must use #{header[5,-1]}") { not env.include? header } } ## The CGI keys (named without a period) must have String values. env.each { |key, value| next if key.include? "." # Skip extensions assert("env variable #{key} has non-string value #{value.inspect}") { value.kind_of? String } } ## ## There are the following restrictions: ## * rack.version must be an array of Integers. assert("rack.version must be an Array, was #{env["rack.version"].class}") { env["rack.version"].kind_of? Array } ## * rack.url_scheme must either be +http+ or +https+. assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { %w[http https].include? env["rack.url_scheme"] } ## * There must be a valid input stream in rack.input. check_input env["rack.input"] ## * There must be a valid error stream in rack.errors. check_error env["rack.errors"] ## * The REQUEST_METHOD must be a valid token. assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ } ## * The SCRIPT_NAME, if non-empty, must start with / assert("SCRIPT_NAME must start with /") { !env.include?("SCRIPT_NAME") || env["SCRIPT_NAME"] == "" || env["SCRIPT_NAME"] =~ /\A\// } ## * The PATH_INFO, if non-empty, must start with / assert("PATH_INFO must start with /") { !env.include?("PATH_INFO") || env["PATH_INFO"] == "" || env["PATH_INFO"] =~ /\A\// } ## * The CONTENT_LENGTH, if given, must consist of digits only. assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ } ## * One of SCRIPT_NAME or PATH_INFO must be ## set. PATH_INFO should be / if ## SCRIPT_NAME is empty. assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { env["SCRIPT_NAME"] || env["PATH_INFO"] } ## SCRIPT_NAME never should be /, but instead be empty. assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { env["SCRIPT_NAME"] != "/" } end ## === The Input Stream ## ## The input stream is an IO-like object which contains the raw HTTP ## POST data. 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+. [:gets, :each, :read, :rewind].each { |method| assert("rack.input #{input} does not respond to ##{method}") { input.respond_to? method } } end class InputWrapper include Assertion def initialize(input) @input = input end ## * +gets+ must be called without arguments and return a string, ## or +nil+ on EOF. def gets(*args) assert("rack.input#gets called with arguments") { args.size == 0 } v = @input.gets assert("rack.input#gets didn't return a String") { v.nil? or v.kind_of? String } v end ## * +read+ behaves like IO#read. Its signature is read([length, [buffer]]). ## If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must ## be a String and may not be nil. If +length+ is given and not nil, then this method ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil, ## then this method reads all data until EOF. ## When EOF is reached, this method returns nil if +length+ is given and not nil, or "" ## if +length+ is not given or is nil. ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a ## newly created String object. def read(*args) assert("rack.input#read called with too many arguments") { args.size <= 2 } if args.size >= 1 assert("rack.input#read called with non-integer and non-nil length") { args.first.kind_of?(Integer) || args.first.nil? } assert("rack.input#read called with a negative length") { args.first.nil? || args.first >= 0 } end if args.size >= 2 assert("rack.input#read called with non-String buffer") { args[1].kind_of?(String) } end v = @input.read(*args) assert("rack.input#read didn't return nil or a String") { v.nil? or v.kind_of? String } if args[0].nil? assert("rack.input#read(nil) returned nil on EOF") { !v.nil? } end v end ## * +each+ must be called without arguments and only yield Strings. def each(*args) assert("rack.input#each called with arguments") { args.size == 0 } @input.each { |line| assert("rack.input#each didn't yield a String") { line.kind_of? String } yield line } end ## * +rewind+ must be called without arguments. It rewinds the input ## stream back to the beginning. It must not raise Errno::ESPIPE: ## that is, it may not be a pipe or a socket. Therefore, handler ## developers must buffer the input data into some rewindable object ## if the underlying input stream is not rewindable. def rewind(*args) assert("rack.input#rewind called with arguments") { args.size == 0 } assert("rack.input#rewind raised Errno::ESPIPE") { begin @input.rewind true rescue Errno::ESPIPE false end } end ## * +close+ must never be called on the input stream. def close(*args) assert("rack.input#close must not be called") { false } end end ## === The Error Stream def check_error(error) ## The error stream must respond to +puts+, +write+ and +flush+. [:puts, :write, :flush].each { |method| assert("rack.error #{error} does not respond to ##{method}") { error.respond_to? method } } end class ErrorWrapper include Assertion def initialize(error) @error = error end ## * +puts+ must be called with a single argument that responds to +to_s+. def puts(str) @error.puts str end ## * +write+ must be called with a single argument that is a String. def write(str) assert("rack.errors#write not called with a String") { str.kind_of? String } @error.write str end ## * +flush+ must be called without arguments and must be called ## in order to make the error appear for sure. def flush @error.flush end ## * +close+ must never be called on the error stream. def close(*args) assert("rack.errors#close must not be called") { false } end end ## == The Response ## === The Status def check_status(status) ## This is an HTTP status. When parsed as integer (+to_i+), it must be ## greater than or equal to 100. assert("Status must be >=100 seen as integer") { status.to_i >= 100 } end ## === The Headers def check_headers(header) ## The header must respond to +each+, and yield values of key and value. assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { header.respond_to? :each } header.each { |key, value| ## The header keys must be Strings. assert("header key must be a string, was #{key.class}") { key.kind_of? String } ## The header must not contain a +Status+ key, assert("header must not contain Status") { key.downcase != "status" } ## contain keys with : or newlines in their name, assert("header names must not contain : or \\n") { key !~ /[:\n]/ } ## contain keys names that end in - or _, assert("header names must not end in - or _") { key !~ /[-_]\z/ } ## but only contain keys that consist of ## letters, digits, _ or - and start with a letter. assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } ## The values of the header must be Strings, assert("a header value must be a String, but the value of " + "'#{key}' is a #{value.class}") { value.kind_of? String } ## consisting of lines (for multiple header values, e.g. multiple ## Set-Cookie values) seperated by "\n". value.split("\n").each { |item| ## The lines must not contain characters below 037. assert("invalid header value #{key}: #{item.inspect}") { item !~ /[\000-\037]/ } } } end ## === The Content-Type def check_content_type(status, headers) headers.each { |key, value| ## There must be a Content-Type, except when the ## +Status+ is 1xx, 204, 205 or 304, in which case there must be none ## given. if key.downcase == "content-type" assert("Content-Type header found in #{status} response, not allowed") { not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i } return end } assert("No Content-Type header found") { Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i } end ## === The Content-Length def check_content_length(status, headers) headers.each { |key, value| if key.downcase == 'content-length' ## There must not be a Content-Length header when the ## +Status+ is 1xx, 204, 205 or 304. assert("Content-Length header found in #{status} response, not allowed") { not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i } @content_length = value end } end def verify_content_length(bytes) if @head_request assert("Response body was given for HEAD request, but should be empty") { bytes == 0 } elsif @content_length assert("Content-Length header was #{@content_length}, but should be #{bytes}") { @content_length == bytes.to_s } end end ## === The Body def each @closed = false bytes = 0 ## The Body must respond to +each+ assert("Response body must respond to each") do @body.respond_to?(:each) end @body.each { |part| ## and must only yield String values. assert("Body yielded non-string value #{part.inspect}") { part.kind_of? String } bytes += Rack::Utils.bytesize(part) yield part } verify_content_length(bytes) ## ## The Body itself should not be an instance of String, as this will ## break in Ruby 1.9. ## ## If the Body responds to +close+, it will be called after iteration. If ## the body is replaced by a middleware after action, the original body ## must be closed first, if it repsonds to close. # XXX howto: assert("Body has not been closed") { @closed } ## ## If the Body responds to +to_path+, it must return a String ## identifying the location of a file whose contents are identical ## to that produced by calling +each+; this may be used by the ## server as an alternative, possibly more efficient way to ## transport the response. if @body.respond_to?(:to_path) assert("The file identified by body.to_path does not exist") { ::File.exist? @body.to_path } end ## ## The Body commonly is an Array of Strings, the application ## instance itself, or a File-like object. end def close @closed = true @body.close if @body.respond_to?(:close) end # :startdoc: end end ## == Thanks ## Some parts of this specification are adopted from PEP333: Python ## Web Server Gateway Interface ## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank ## everyone involved in that effort. ruby-rack1.4-1.4.5/lib/rack/lobster.rb000066400000000000000000000035311223351442100173270ustar00rootroot00000000000000require 'zlib' require 'rack/request' require 'rack/response' module Rack # Paste has a Pony, Rack has a Lobster! class Lobster LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) LambdaLobster = lambda { |env| if env["QUERY_STRING"].include?("flip") lobster = LobsterString.split("\n"). map { |line| line.ljust(42).reverse }. join("\n") href = "?" else lobster = LobsterString href = "?flip" end content = ["Lobstericious!", "
", lobster, "
", "flip!"] length = content.inject(0) { |a,e| a+e.size }.to_s [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] } def call(env) req = Request.new(env) if req.GET["flip"] == "left" lobster = LobsterString.split("\n"). map { |line| line.ljust(42).reverse }. join("\n") href = "?flip=right" elsif req.GET["flip"] == "crash" raise "Lobster crashed" else lobster = LobsterString href = "?flip=left" end res = Response.new res.write "Lobstericious!" res.write "
"
      res.write lobster
      res.write "
" res.write "

flip!

" res.write "

crash!

" res.finish end end end if $0 == __FILE__ require 'rack' require 'rack/showexceptions' Rack::Handler::WEBrick.run \ Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), :Port => 9292 end ruby-rack1.4-1.4.5/lib/rack/lock.rb000066400000000000000000000007401223351442100166040ustar00rootroot00000000000000require 'thread' require 'rack/body_proxy' module Rack class Lock FLAG = 'rack.multithread'.freeze def initialize(app, mutex = Mutex.new) @app, @mutex = app, mutex end def call(env) old, env[FLAG] = env[FLAG], false @mutex.lock response = @app.call(env) body = BodyProxy.new(response[2]) { @mutex.unlock } response[2] = body response ensure @mutex.unlock unless body env[FLAG] = old end end end ruby-rack1.4-1.4.5/lib/rack/logger.rb000066400000000000000000000005451223351442100171360ustar00rootroot00000000000000require 'logger' module Rack # Sets up rack.logger to write to rack.errors stream class Logger def initialize(app, level = ::Logger::INFO) @app, @level = app, level end def call(env) logger = ::Logger.new(env['rack.errors']) logger.level = @level env['rack.logger'] = logger @app.call(env) end end end ruby-rack1.4-1.4.5/lib/rack/methodoverride.rb000066400000000000000000000014431223351442100206750ustar00rootroot00000000000000module Rack class MethodOverride HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH) METHOD_OVERRIDE_PARAM_KEY = "_method".freeze HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze def initialize(app) @app = app end def call(env) if env["REQUEST_METHOD"] == "POST" method = method_override(env) if HTTP_METHODS.include?(method) env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] env["REQUEST_METHOD"] = method end end @app.call(env) end def method_override(env) req = Request.new(env) method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || env[HTTP_METHOD_OVERRIDE_HEADER] method.to_s.upcase rescue EOFError "" end end end ruby-rack1.4-1.4.5/lib/rack/mime.rb000066400000000000000000000746141223351442100166160ustar00rootroot00000000000000module Rack module Mime # Returns String with mime type if found, otherwise use +fallback+. # +ext+ should be filename extension in the '.ext' format that # File.extname(file) returns. # +fallback+ may be any object # # Also see the documentation for MIME_TYPES # # Usage: # Rack::Mime.mime_type('.foo') # # This is a shortcut for: # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') def mime_type(ext, fallback='application/octet-stream') MIME_TYPES.fetch(ext.to_s.downcase, fallback) end module_function :mime_type # List of most common mime-types, selected various sources # according to their usefulness in a webserving scope for Ruby # users. # # To amend this list with your local mime.types list you can use: # # require 'webrick/httputils' # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') # Rack::Mime::MIME_TYPES.merge!(list) # # N.B. On Ubuntu the mime.types file does not include the leading period, so # users may need to modify the data before merging into the hash. # # To add the list mongrel provides, use: # # require 'mongrel/handlers' # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) MIME_TYPES = { ".123" => "application/vnd.lotus-1-2-3", ".3dml" => "text/vnd.in3d.3dml", ".3g2" => "video/3gpp2", ".3gp" => "video/3gpp", ".a" => "application/octet-stream", ".acc" => "application/vnd.americandynamics.acc", ".ace" => "application/x-ace-compressed", ".acu" => "application/vnd.acucobol", ".aep" => "application/vnd.audiograph", ".afp" => "application/vnd.ibm.modcap", ".ai" => "application/postscript", ".aif" => "audio/x-aiff", ".aiff" => "audio/x-aiff", ".ami" => "application/vnd.amiga.ami", ".appcache" => "text/cache-manifest", ".apr" => "application/vnd.lotus-approach", ".asc" => "application/pgp-signature", ".asf" => "video/x-ms-asf", ".asm" => "text/x-asm", ".aso" => "application/vnd.accpac.simply.aso", ".asx" => "video/x-ms-asf", ".atc" => "application/vnd.acucorp", ".atom" => "application/atom+xml", ".atomcat" => "application/atomcat+xml", ".atomsvc" => "application/atomsvc+xml", ".atx" => "application/vnd.antix.game-component", ".au" => "audio/basic", ".avi" => "video/x-msvideo", ".bat" => "application/x-msdownload", ".bcpio" => "application/x-bcpio", ".bdm" => "application/vnd.syncml.dm+wbxml", ".bh2" => "application/vnd.fujitsu.oasysprs", ".bin" => "application/octet-stream", ".bmi" => "application/vnd.bmi", ".bmp" => "image/bmp", ".box" => "application/vnd.previewsystems.box", ".btif" => "image/prs.btif", ".bz" => "application/x-bzip", ".bz2" => "application/x-bzip2", ".c" => "text/x-c", ".c4g" => "application/vnd.clonk.c4group", ".cab" => "application/vnd.ms-cab-compressed", ".cc" => "text/x-c", ".ccxml" => "application/ccxml+xml", ".cdbcmsg" => "application/vnd.contact.cmsg", ".cdkey" => "application/vnd.mediastation.cdkey", ".cdx" => "chemical/x-cdx", ".cdxml" => "application/vnd.chemdraw+xml", ".cdy" => "application/vnd.cinderella", ".cer" => "application/pkix-cert", ".cgm" => "image/cgm", ".chat" => "application/x-chat", ".chm" => "application/vnd.ms-htmlhelp", ".chrt" => "application/vnd.kde.kchart", ".cif" => "chemical/x-cif", ".cii" => "application/vnd.anser-web-certificate-issue-initiation", ".cil" => "application/vnd.ms-artgalry", ".cla" => "application/vnd.claymore", ".class" => "application/octet-stream", ".clkk" => "application/vnd.crick.clicker.keyboard", ".clkp" => "application/vnd.crick.clicker.palette", ".clkt" => "application/vnd.crick.clicker.template", ".clkw" => "application/vnd.crick.clicker.wordbank", ".clkx" => "application/vnd.crick.clicker", ".clp" => "application/x-msclip", ".cmc" => "application/vnd.cosmocaller", ".cmdf" => "chemical/x-cmdf", ".cml" => "chemical/x-cml", ".cmp" => "application/vnd.yellowriver-custom-menu", ".cmx" => "image/x-cmx", ".com" => "application/x-msdownload", ".conf" => "text/plain", ".cpio" => "application/x-cpio", ".cpp" => "text/x-c", ".cpt" => "application/mac-compactpro", ".crd" => "application/x-mscardfile", ".crl" => "application/pkix-crl", ".crt" => "application/x-x509-ca-cert", ".csh" => "application/x-csh", ".csml" => "chemical/x-csml", ".csp" => "application/vnd.commonspace", ".css" => "text/css", ".csv" => "text/csv", ".curl" => "application/vnd.curl", ".cww" => "application/prs.cww", ".cxx" => "text/x-c", ".daf" => "application/vnd.mobius.daf", ".davmount" => "application/davmount+xml", ".dcr" => "application/x-director", ".dd2" => "application/vnd.oma.dd2+xml", ".ddd" => "application/vnd.fujixerox.ddd", ".deb" => "application/x-debian-package", ".der" => "application/x-x509-ca-cert", ".dfac" => "application/vnd.dreamfactory", ".diff" => "text/x-diff", ".dis" => "application/vnd.mobius.dis", ".djv" => "image/vnd.djvu", ".djvu" => "image/vnd.djvu", ".dll" => "application/x-msdownload", ".dmg" => "application/octet-stream", ".dna" => "application/vnd.dna", ".doc" => "application/msword", ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".dot" => "application/msword", ".dp" => "application/vnd.osgi.dp", ".dpg" => "application/vnd.dpgraph", ".dsc" => "text/prs.lines.tag", ".dtd" => "application/xml-dtd", ".dts" => "audio/vnd.dts", ".dtshd" => "audio/vnd.dts.hd", ".dv" => "video/x-dv", ".dvi" => "application/x-dvi", ".dwf" => "model/vnd.dwf", ".dwg" => "image/vnd.dwg", ".dxf" => "image/vnd.dxf", ".dxp" => "application/vnd.spotfire.dxp", ".ear" => "application/java-archive", ".ecelp4800" => "audio/vnd.nuera.ecelp4800", ".ecelp7470" => "audio/vnd.nuera.ecelp7470", ".ecelp9600" => "audio/vnd.nuera.ecelp9600", ".ecma" => "application/ecmascript", ".edm" => "application/vnd.novadigm.edm", ".edx" => "application/vnd.novadigm.edx", ".efif" => "application/vnd.picsel", ".ei6" => "application/vnd.pg.osasli", ".eml" => "message/rfc822", ".eol" => "audio/vnd.digital-winds", ".eot" => "application/vnd.ms-fontobject", ".eps" => "application/postscript", ".es3" => "application/vnd.eszigno3+xml", ".esf" => "application/vnd.epson.esf", ".etx" => "text/x-setext", ".exe" => "application/x-msdownload", ".ext" => "application/vnd.novadigm.ext", ".ez" => "application/andrew-inset", ".ez2" => "application/vnd.ezpix-album", ".ez3" => "application/vnd.ezpix-package", ".f" => "text/x-fortran", ".f77" => "text/x-fortran", ".f90" => "text/x-fortran", ".fbs" => "image/vnd.fastbidsheet", ".fdf" => "application/vnd.fdf", ".fe_launch" => "application/vnd.denovo.fcselayout-link", ".fg5" => "application/vnd.fujitsu.oasysgp", ".fli" => "video/x-fli", ".flo" => "application/vnd.micrografx.flo", ".flv" => "video/x-flv", ".flw" => "application/vnd.kde.kivio", ".flx" => "text/vnd.fmi.flexstor", ".fly" => "text/vnd.fly", ".fm" => "application/vnd.framemaker", ".fnc" => "application/vnd.frogans.fnc", ".for" => "text/x-fortran", ".fpx" => "image/vnd.fpx", ".fsc" => "application/vnd.fsc.weblaunch", ".fst" => "image/vnd.fst", ".ftc" => "application/vnd.fluxtime.clip", ".fti" => "application/vnd.anser-web-funds-transfer-initiation", ".fvt" => "video/vnd.fvt", ".fzs" => "application/vnd.fuzzysheet", ".g3" => "image/g3fax", ".gac" => "application/vnd.groove-account", ".gdl" => "model/vnd.gdl", ".gem" => "application/octet-stream", ".gemspec" => "text/x-script.ruby", ".ghf" => "application/vnd.groove-help", ".gif" => "image/gif", ".gim" => "application/vnd.groove-identity-message", ".gmx" => "application/vnd.gmx", ".gph" => "application/vnd.flographit", ".gqf" => "application/vnd.grafeq", ".gram" => "application/srgs", ".grv" => "application/vnd.groove-injector", ".grxml" => "application/srgs+xml", ".gtar" => "application/x-gtar", ".gtm" => "application/vnd.groove-tool-message", ".gtw" => "model/vnd.gtw", ".gv" => "text/vnd.graphviz", ".gz" => "application/x-gzip", ".h" => "text/x-c", ".h261" => "video/h261", ".h263" => "video/h263", ".h264" => "video/h264", ".hbci" => "application/vnd.hbci", ".hdf" => "application/x-hdf", ".hh" => "text/x-c", ".hlp" => "application/winhlp", ".hpgl" => "application/vnd.hp-hpgl", ".hpid" => "application/vnd.hp-hpid", ".hps" => "application/vnd.hp-hps", ".hqx" => "application/mac-binhex40", ".htc" => "text/x-component", ".htke" => "application/vnd.kenameaapp", ".htm" => "text/html", ".html" => "text/html", ".hvd" => "application/vnd.yamaha.hv-dic", ".hvp" => "application/vnd.yamaha.hv-voice", ".hvs" => "application/vnd.yamaha.hv-script", ".icc" => "application/vnd.iccprofile", ".ice" => "x-conference/x-cooltalk", ".ico" => "image/vnd.microsoft.icon", ".ics" => "text/calendar", ".ief" => "image/ief", ".ifb" => "text/calendar", ".ifm" => "application/vnd.shana.informed.formdata", ".igl" => "application/vnd.igloader", ".igs" => "model/iges", ".igx" => "application/vnd.micrografx.igx", ".iif" => "application/vnd.shana.informed.interchange", ".imp" => "application/vnd.accpac.simply.imp", ".ims" => "application/vnd.ms-ims", ".ipk" => "application/vnd.shana.informed.package", ".irm" => "application/vnd.ibm.rights-management", ".irp" => "application/vnd.irepository.package+xml", ".iso" => "application/octet-stream", ".itp" => "application/vnd.shana.informed.formtemplate", ".ivp" => "application/vnd.immervision-ivp", ".ivu" => "application/vnd.immervision-ivu", ".jad" => "text/vnd.sun.j2me.app-descriptor", ".jam" => "application/vnd.jam", ".jar" => "application/java-archive", ".java" => "text/x-java-source", ".jisp" => "application/vnd.jisp", ".jlt" => "application/vnd.hp-jlyt", ".jnlp" => "application/x-java-jnlp-file", ".joda" => "application/vnd.joost.joda-archive", ".jp2" => "image/jp2", ".jpeg" => "image/jpeg", ".jpg" => "image/jpeg", ".jpgv" => "video/jpeg", ".jpm" => "video/jpm", ".js" => "application/javascript", ".json" => "application/json", ".karbon" => "application/vnd.kde.karbon", ".kfo" => "application/vnd.kde.kformula", ".kia" => "application/vnd.kidspiration", ".kml" => "application/vnd.google-earth.kml+xml", ".kmz" => "application/vnd.google-earth.kmz", ".kne" => "application/vnd.kinar", ".kon" => "application/vnd.kde.kontour", ".kpr" => "application/vnd.kde.kpresenter", ".ksp" => "application/vnd.kde.kspread", ".ktz" => "application/vnd.kahootz", ".kwd" => "application/vnd.kde.kword", ".latex" => "application/x-latex", ".lbd" => "application/vnd.llamagraphics.life-balance.desktop", ".lbe" => "application/vnd.llamagraphics.life-balance.exchange+xml", ".les" => "application/vnd.hhe.lesson-player", ".link66" => "application/vnd.route66.link66+xml", ".log" => "text/plain", ".lostxml" => "application/lost+xml", ".lrm" => "application/vnd.ms-lrm", ".ltf" => "application/vnd.frogans.ltf", ".lvp" => "audio/vnd.lucent.voice", ".lwp" => "application/vnd.lotus-wordpro", ".m3u" => "audio/x-mpegurl", ".m4a" => "audio/mp4a-latm", ".m4v" => "video/mp4", ".ma" => "application/mathematica", ".mag" => "application/vnd.ecowin.chart", ".man" => "text/troff", ".manifest" => "text/cache-manifest", ".mathml" => "application/mathml+xml", ".mbk" => "application/vnd.mobius.mbk", ".mbox" => "application/mbox", ".mc1" => "application/vnd.medcalcdata", ".mcd" => "application/vnd.mcd", ".mdb" => "application/x-msaccess", ".mdi" => "image/vnd.ms-modi", ".mdoc" => "text/troff", ".me" => "text/troff", ".mfm" => "application/vnd.mfmp", ".mgz" => "application/vnd.proteus.magazine", ".mid" => "audio/midi", ".midi" => "audio/midi", ".mif" => "application/vnd.mif", ".mime" => "message/rfc822", ".mj2" => "video/mj2", ".mlp" => "application/vnd.dolby.mlp", ".mmd" => "application/vnd.chipnuts.karaoke-mmd", ".mmf" => "application/vnd.smaf", ".mml" => "application/mathml+xml", ".mmr" => "image/vnd.fujixerox.edmics-mmr", ".mng" => "video/x-mng", ".mny" => "application/x-msmoney", ".mov" => "video/quicktime", ".movie" => "video/x-sgi-movie", ".mp3" => "audio/mpeg", ".mp4" => "video/mp4", ".mp4a" => "audio/mp4", ".mp4s" => "application/mp4", ".mp4v" => "video/mp4", ".mpc" => "application/vnd.mophun.certificate", ".mpeg" => "video/mpeg", ".mpg" => "video/mpeg", ".mpga" => "audio/mpeg", ".mpkg" => "application/vnd.apple.installer+xml", ".mpm" => "application/vnd.blueice.multipass", ".mpn" => "application/vnd.mophun.application", ".mpp" => "application/vnd.ms-project", ".mpy" => "application/vnd.ibm.minipay", ".mqy" => "application/vnd.mobius.mqy", ".mrc" => "application/marc", ".ms" => "text/troff", ".mscml" => "application/mediaservercontrol+xml", ".mseq" => "application/vnd.mseq", ".msf" => "application/vnd.epson.msf", ".msh" => "model/mesh", ".msi" => "application/x-msdownload", ".msl" => "application/vnd.mobius.msl", ".msty" => "application/vnd.muvee.style", ".mts" => "model/vnd.mts", ".mus" => "application/vnd.musician", ".mvb" => "application/x-msmediaview", ".mwf" => "application/vnd.mfer", ".mxf" => "application/mxf", ".mxl" => "application/vnd.recordare.musicxml", ".mxml" => "application/xv+xml", ".mxs" => "application/vnd.triscape.mxs", ".mxu" => "video/vnd.mpegurl", ".n" => "application/vnd.nokia.n-gage.symbian.install", ".nc" => "application/x-netcdf", ".ngdat" => "application/vnd.nokia.n-gage.data", ".nlu" => "application/vnd.neurolanguage.nlu", ".nml" => "application/vnd.enliven", ".nnd" => "application/vnd.noblenet-directory", ".nns" => "application/vnd.noblenet-sealer", ".nnw" => "application/vnd.noblenet-web", ".npx" => "image/vnd.net-fpx", ".nsf" => "application/vnd.lotus-notes", ".oa2" => "application/vnd.fujitsu.oasys2", ".oa3" => "application/vnd.fujitsu.oasys3", ".oas" => "application/vnd.fujitsu.oasys", ".obd" => "application/x-msbinder", ".oda" => "application/oda", ".odc" => "application/vnd.oasis.opendocument.chart", ".odf" => "application/vnd.oasis.opendocument.formula", ".odg" => "application/vnd.oasis.opendocument.graphics", ".odi" => "application/vnd.oasis.opendocument.image", ".odp" => "application/vnd.oasis.opendocument.presentation", ".ods" => "application/vnd.oasis.opendocument.spreadsheet", ".odt" => "application/vnd.oasis.opendocument.text", ".oga" => "audio/ogg", ".ogg" => "application/ogg", ".ogv" => "video/ogg", ".ogx" => "application/ogg", ".org" => "application/vnd.lotus-organizer", ".otc" => "application/vnd.oasis.opendocument.chart-template", ".otf" => "application/vnd.oasis.opendocument.formula-template", ".otg" => "application/vnd.oasis.opendocument.graphics-template", ".oth" => "application/vnd.oasis.opendocument.text-web", ".oti" => "application/vnd.oasis.opendocument.image-template", ".otm" => "application/vnd.oasis.opendocument.text-master", ".ots" => "application/vnd.oasis.opendocument.spreadsheet-template", ".ott" => "application/vnd.oasis.opendocument.text-template", ".oxt" => "application/vnd.openofficeorg.extension", ".p" => "text/x-pascal", ".p10" => "application/pkcs10", ".p12" => "application/x-pkcs12", ".p7b" => "application/x-pkcs7-certificates", ".p7m" => "application/pkcs7-mime", ".p7r" => "application/x-pkcs7-certreqresp", ".p7s" => "application/pkcs7-signature", ".pas" => "text/x-pascal", ".pbd" => "application/vnd.powerbuilder6", ".pbm" => "image/x-portable-bitmap", ".pcl" => "application/vnd.hp-pcl", ".pclxl" => "application/vnd.hp-pclxl", ".pcx" => "image/x-pcx", ".pdb" => "chemical/x-pdb", ".pdf" => "application/pdf", ".pem" => "application/x-x509-ca-cert", ".pfr" => "application/font-tdpfr", ".pgm" => "image/x-portable-graymap", ".pgn" => "application/x-chess-pgn", ".pgp" => "application/pgp-encrypted", ".pic" => "image/x-pict", ".pict" => "image/pict", ".pkg" => "application/octet-stream", ".pki" => "application/pkixcmp", ".pkipath" => "application/pkix-pkipath", ".pl" => "text/x-script.perl", ".plb" => "application/vnd.3gpp.pic-bw-large", ".plc" => "application/vnd.mobius.plc", ".plf" => "application/vnd.pocketlearn", ".pls" => "application/pls+xml", ".pm" => "text/x-script.perl-module", ".pml" => "application/vnd.ctc-posml", ".png" => "image/png", ".pnm" => "image/x-portable-anymap", ".pntg" => "image/x-macpaint", ".portpkg" => "application/vnd.macports.portpkg", ".ppd" => "application/vnd.cups-ppd", ".ppm" => "image/x-portable-pixmap", ".pps" => "application/vnd.ms-powerpoint", ".ppt" => "application/vnd.ms-powerpoint", ".prc" => "application/vnd.palm", ".pre" => "application/vnd.lotus-freelance", ".prf" => "application/pics-rules", ".ps" => "application/postscript", ".psb" => "application/vnd.3gpp.pic-bw-small", ".psd" => "image/vnd.adobe.photoshop", ".ptid" => "application/vnd.pvi.ptid1", ".pub" => "application/x-mspublisher", ".pvb" => "application/vnd.3gpp.pic-bw-var", ".pwn" => "application/vnd.3m.post-it-notes", ".py" => "text/x-script.python", ".pya" => "audio/vnd.ms-playready.media.pya", ".pyv" => "video/vnd.ms-playready.media.pyv", ".qam" => "application/vnd.epson.quickanime", ".qbo" => "application/vnd.intu.qbo", ".qfx" => "application/vnd.intu.qfx", ".qps" => "application/vnd.publishare-delta-tree", ".qt" => "video/quicktime", ".qtif" => "image/x-quicktime", ".qxd" => "application/vnd.quark.quarkxpress", ".ra" => "audio/x-pn-realaudio", ".rake" => "text/x-script.ruby", ".ram" => "audio/x-pn-realaudio", ".rar" => "application/x-rar-compressed", ".ras" => "image/x-cmu-raster", ".rb" => "text/x-script.ruby", ".rcprofile" => "application/vnd.ipunplugged.rcprofile", ".rdf" => "application/rdf+xml", ".rdz" => "application/vnd.data-vision.rdz", ".rep" => "application/vnd.businessobjects", ".rgb" => "image/x-rgb", ".rif" => "application/reginfo+xml", ".rl" => "application/resource-lists+xml", ".rlc" => "image/vnd.fujixerox.edmics-rlc", ".rld" => "application/resource-lists-diff+xml", ".rm" => "application/vnd.rn-realmedia", ".rmp" => "audio/x-pn-realaudio-plugin", ".rms" => "application/vnd.jcp.javame.midlet-rms", ".rnc" => "application/relax-ng-compact-syntax", ".roff" => "text/troff", ".rpm" => "application/x-redhat-package-manager", ".rpss" => "application/vnd.nokia.radio-presets", ".rpst" => "application/vnd.nokia.radio-preset", ".rq" => "application/sparql-query", ".rs" => "application/rls-services+xml", ".rsd" => "application/rsd+xml", ".rss" => "application/rss+xml", ".rtf" => "application/rtf", ".rtx" => "text/richtext", ".ru" => "text/x-script.ruby", ".s" => "text/x-asm", ".saf" => "application/vnd.yamaha.smaf-audio", ".sbml" => "application/sbml+xml", ".sc" => "application/vnd.ibm.secure-container", ".scd" => "application/x-msschedule", ".scm" => "application/vnd.lotus-screencam", ".scq" => "application/scvp-cv-request", ".scs" => "application/scvp-cv-response", ".sdkm" => "application/vnd.solent.sdkm+xml", ".sdp" => "application/sdp", ".see" => "application/vnd.seemail", ".sema" => "application/vnd.sema", ".semd" => "application/vnd.semd", ".semf" => "application/vnd.semf", ".setpay" => "application/set-payment-initiation", ".setreg" => "application/set-registration-initiation", ".sfd" => "application/vnd.hydrostatix.sof-data", ".sfs" => "application/vnd.spotfire.sfs", ".sgm" => "text/sgml", ".sgml" => "text/sgml", ".sh" => "application/x-sh", ".shar" => "application/x-shar", ".shf" => "application/shf+xml", ".sig" => "application/pgp-signature", ".sit" => "application/x-stuffit", ".sitx" => "application/x-stuffitx", ".skp" => "application/vnd.koan", ".slt" => "application/vnd.epson.salt", ".smi" => "application/smil+xml", ".snd" => "audio/basic", ".so" => "application/octet-stream", ".spf" => "application/vnd.yamaha.smaf-phrase", ".spl" => "application/x-futuresplash", ".spot" => "text/vnd.in3d.spot", ".spp" => "application/scvp-vp-response", ".spq" => "application/scvp-vp-request", ".src" => "application/x-wais-source", ".srx" => "application/sparql-results+xml", ".sse" => "application/vnd.kodak-descriptor", ".ssf" => "application/vnd.epson.ssf", ".ssml" => "application/ssml+xml", ".stf" => "application/vnd.wt.stf", ".stk" => "application/hyperstudio", ".str" => "application/vnd.pg.format", ".sus" => "application/vnd.sus-calendar", ".sv4cpio" => "application/x-sv4cpio", ".sv4crc" => "application/x-sv4crc", ".svd" => "application/vnd.svd", ".svg" => "image/svg+xml", ".svgz" => "image/svg+xml", ".swf" => "application/x-shockwave-flash", ".swi" => "application/vnd.arastra.swi", ".t" => "text/troff", ".tao" => "application/vnd.tao.intent-module-archive", ".tar" => "application/x-tar", ".tbz" => "application/x-bzip-compressed-tar", ".tcap" => "application/vnd.3gpp2.tcap", ".tcl" => "application/x-tcl", ".tex" => "application/x-tex", ".texi" => "application/x-texinfo", ".texinfo" => "application/x-texinfo", ".text" => "text/plain", ".tif" => "image/tiff", ".tiff" => "image/tiff", ".tmo" => "application/vnd.tmobile-livetv", ".torrent" => "application/x-bittorrent", ".tpl" => "application/vnd.groove-tool-template", ".tpt" => "application/vnd.trid.tpt", ".tr" => "text/troff", ".tra" => "application/vnd.trueapp", ".trm" => "application/x-msterminal", ".tsv" => "text/tab-separated-values", ".ttf" => "application/octet-stream", ".twd" => "application/vnd.simtech-mindmapper", ".txd" => "application/vnd.genomatix.tuxedo", ".txf" => "application/vnd.mobius.txf", ".txt" => "text/plain", ".ufd" => "application/vnd.ufdl", ".umj" => "application/vnd.umajin", ".unityweb" => "application/vnd.unity", ".uoml" => "application/vnd.uoml+xml", ".uri" => "text/uri-list", ".ustar" => "application/x-ustar", ".utz" => "application/vnd.uiq.theme", ".uu" => "text/x-uuencode", ".vcd" => "application/x-cdlink", ".vcf" => "text/x-vcard", ".vcg" => "application/vnd.groove-vcard", ".vcs" => "text/x-vcalendar", ".vcx" => "application/vnd.vcx", ".vis" => "application/vnd.visionary", ".viv" => "video/vnd.vivo", ".vrml" => "model/vrml", ".vsd" => "application/vnd.visio", ".vsf" => "application/vnd.vsf", ".vtu" => "model/vnd.vtu", ".vxml" => "application/voicexml+xml", ".war" => "application/java-archive", ".wav" => "audio/x-wav", ".wax" => "audio/x-ms-wax", ".wbmp" => "image/vnd.wap.wbmp", ".wbs" => "application/vnd.criticaltools.wbs+xml", ".wbxml" => "application/vnd.wap.wbxml", ".webm" => "video/webm", ".wm" => "video/x-ms-wm", ".wma" => "audio/x-ms-wma", ".wmd" => "application/x-ms-wmd", ".wmf" => "application/x-msmetafile", ".wml" => "text/vnd.wap.wml", ".wmlc" => "application/vnd.wap.wmlc", ".wmls" => "text/vnd.wap.wmlscript", ".wmlsc" => "application/vnd.wap.wmlscriptc", ".wmv" => "video/x-ms-wmv", ".wmx" => "video/x-ms-wmx", ".wmz" => "application/x-ms-wmz", ".woff" => "application/font-woff", ".wpd" => "application/vnd.wordperfect", ".wpl" => "application/vnd.ms-wpl", ".wps" => "application/vnd.ms-works", ".wqd" => "application/vnd.wqd", ".wri" => "application/x-mswrite", ".wrl" => "model/vrml", ".wsdl" => "application/wsdl+xml", ".wspolicy" => "application/wspolicy+xml", ".wtb" => "application/vnd.webturbo", ".wvx" => "video/x-ms-wvx", ".x3d" => "application/vnd.hzn-3d-crossword", ".xar" => "application/vnd.xara", ".xbd" => "application/vnd.fujixerox.docuworks.binder", ".xbm" => "image/x-xbitmap", ".xdm" => "application/vnd.syncml.dm+xml", ".xdp" => "application/vnd.adobe.xdp+xml", ".xdw" => "application/vnd.fujixerox.docuworks", ".xenc" => "application/xenc+xml", ".xer" => "application/patch-ops-error+xml", ".xfdf" => "application/vnd.adobe.xfdf", ".xfdl" => "application/vnd.xfdl", ".xhtml" => "application/xhtml+xml", ".xif" => "image/vnd.xiff", ".xls" => "application/vnd.ms-excel", ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xml" => "application/xml", ".xo" => "application/vnd.olpc-sugar", ".xop" => "application/xop+xml", ".xpm" => "image/x-xpixmap", ".xpr" => "application/vnd.is-xpr", ".xps" => "application/vnd.ms-xpsdocument", ".xpw" => "application/vnd.intercon.formnet", ".xsl" => "application/xml", ".xslt" => "application/xslt+xml", ".xsm" => "application/vnd.syncml+xml", ".xspf" => "application/xspf+xml", ".xul" => "application/vnd.mozilla.xul+xml", ".xwd" => "image/x-xwindowdump", ".xyz" => "chemical/x-xyz", ".yaml" => "text/yaml", ".yml" => "text/yaml", ".zaz" => "application/vnd.zzazz.deck+xml", ".zip" => "application/zip", ".zmm" => "application/vnd.handheld-entertainment+xml", } end end ruby-rack1.4-1.4.5/lib/rack/mock.rb000066400000000000000000000125041223351442100166060ustar00rootroot00000000000000require 'uri' require 'stringio' require 'rack' require 'rack/lint' require 'rack/utils' require 'rack/response' module Rack # Rack::MockRequest helps testing your Rack application without # actually using HTTP. # # After performing a request on a URL with get/post/put/patch/delete, it # returns a MockResponse with useful helper methods for effective # testing. # # You can pass a hash with additional configuration to the # get/post/put/patch/delete. # :input:: A String or IO-like to be used as rack.input. # :fatal:: Raise a FatalWarning if the app writes to rack.errors. # :lint:: If true, wrap the application in a Rack::Lint. class MockRequest class FatalWarning < RuntimeError end class FatalWarner def puts(warning) raise FatalWarning, warning end def write(warning) raise FatalWarning, warning end def flush end def string "" end end DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "rack.errors" => StringIO.new, "rack.multithread" => true, "rack.multiprocess" => true, "rack.run_once" => false, } def initialize(app) @app = app end def get(uri, opts={}) request("GET", uri, opts) end def post(uri, opts={}) request("POST", uri, opts) end def put(uri, opts={}) request("PUT", uri, opts) end def patch(uri, opts={}) request("PATCH", uri, opts) end def delete(uri, opts={}) request("DELETE", uri, opts) end def head(uri, opts={}) request("HEAD", uri, opts) end def request(method="GET", uri="", opts={}) env = self.class.env_for(uri, opts.merge(:method => method)) if opts[:lint] app = Rack::Lint.new(@app) else app = @app end errors = env["rack.errors"] status, headers, body = app.call(env) MockResponse.new(status, headers, body, errors) ensure body.close if body.respond_to?(:close) end # Return the Rack environment used for a request to +uri+. def self.env_for(uri="", opts={}) uri = URI(uri) uri.path = "/#{uri.path}" unless uri.path[0] == ?/ env = DEFAULT_ENV.dup env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET" env["SERVER_NAME"] = uri.host || "example.org" env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" env["QUERY_STRING"] = uri.query.to_s env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path env["rack.url_scheme"] = uri.scheme || "http" env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off" env["SCRIPT_NAME"] = opts[:script_name] || "" if opts[:fatal] env["rack.errors"] = FatalWarner.new else env["rack.errors"] = StringIO.new 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 empty_str = "" empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding opts[:input] ||= empty_str if String === opts[:input] rack_input = StringIO.new(opts[:input]) else rack_input = opts[:input] 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 opts.each { |field, value| env[field] = value if String === field } env end end # Rack::MockResponse provides useful helpers for testing your apps. # Usually, you don't create the MockResponse on your own, but use # MockRequest. class MockResponse < Rack::Response # Headers attr_reader :original_headers # Errors attr_accessor :errors def initialize(status, headers, body, errors=StringIO.new("")) @original_headers = headers @errors = errors.string if errors.respond_to?(:string) @body_string = nil super(body, status, headers) end def =~(other) body =~ other end def match(other) body.match other end def body # FIXME: apparently users of MockResponse expect the return value of # MockResponse#body to be a string. However, the real response object # returns the body as a list. # # See spec_showstatus.rb: # # should "not replace existing messages" do # ... # res.body.should == "foo!" # end super.join end def empty? [201, 204, 205, 304].include? status end end end ruby-rack1.4-1.4.5/lib/rack/multipart.rb000066400000000000000000000022141223351442100176730ustar00rootroot00000000000000module Rack # A multipart form data parser, adapted from IOWA. # # Usually, Rack::Request#POST takes care of calling this. module Multipart autoload :UploadedFile, 'rack/multipart/uploaded_file' autoload :Parser, 'rack/multipart/parser' autoload :Generator, 'rack/multipart/generator' EOL = "\r\n" MULTIPART_BOUNDARY = "AaB03x" MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/ CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/ RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni class << self def parse_multipart(env) Parser.new(env).parse end def build_multipart(params, first = true) Generator.new(params, first).dump end end end end ruby-rack1.4-1.4.5/lib/rack/multipart/000077500000000000000000000000001223351442100173475ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/multipart/generator.rb000066400000000000000000000044051223351442100216650ustar00rootroot00000000000000module Rack module Multipart class Generator def initialize(params, first = true) @params, @first = params, first if @first && !@params.is_a?(Hash) raise ArgumentError, "value must be a Hash" end end def dump return nil if @first && !multipart? return flattened_params if !@first flattened_params.map do |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) content_for_tempfile(f, file, name) end else content_for_other(file, name) end end.join + "--#{MULTIPART_BOUNDARY}--\r" end private def multipart? multipart = false query = lambda { |value| case value when Array value.each(&query) when Hash value.values.each(&query) when Rack::Multipart::UploadedFile multipart = true end } @params.values.each(&query) multipart end def flattened_params @flattened_params ||= begin h = Hash.new @params.each do |key, value| k = @first ? key.to_s : "[#{key}]" case value when Array value.map { |v| Multipart.build_multipart(v, false).each { |subkey, subvalue| h["#{k}[]#{subkey}"] = subvalue } } when Hash Multipart.build_multipart(value, false).each { |subkey, subvalue| h[k + subkey] = subvalue } else h[k] = value end end h end end def content_for_tempfile(io, file, name) <<-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 #{io.read}\r EOF end def content_for_other(file, name) <<-EOF --#{MULTIPART_BOUNDARY}\r Content-Disposition: form-data; name="#{name}"\r \r #{file}\r EOF end end end endruby-rack1.4-1.4.5/lib/rack/multipart/parser.rb000066400000000000000000000115441223351442100211750ustar00rootroot00000000000000require 'rack/utils' module Rack module Multipart class Parser BUFSIZE = 16384 def initialize(env) @env = env end def parse return nil unless setup_parse fast_forward_to_first_boundary loop do head, filename, content_type, name, body = get_current_head_and_filename_and_content_type_and_name_and_body # Save the rest. if i = @buf.index(rx) body << @buf.slice!(0, i) @buf.slice!(0, @boundary_size+2) @content_length = -1 if $1 == "--" end filename, data = get_data(filename, body, content_type, name, head) Utils.normalize_params(@params, name, data) unless data.nil? # break if we're at the end of a buffer, but not if it is the end of a field break if (@buf.empty? && $1 != EOL) || @content_length == -1 end @io.rewind @params.to_params_hash end private def setup_parse return false unless @env['CONTENT_TYPE'] =~ MULTIPART @boundary = "--#{$1}" @buf = "" @params = Utils::KeySpaceConstrainedParams.new @io = @env['rack.input'] @io.rewind @boundary_size = Utils.bytesize(@boundary) + EOL.size if @content_length = @env['CONTENT_LENGTH'] @content_length = @content_length.to_i @content_length -= @boundary_size end true end def full_boundary @boundary + EOL end def rx @rx ||= /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n end def fast_forward_to_first_boundary loop do content = @io.read(BUFSIZE) raise EOFError, "bad content body" unless content @buf << content while @buf.gsub!(/\A([^\n]*\n)/, '') read_buffer = $1 return if read_buffer == full_boundary end raise EOFError, "bad content body" if Utils.bytesize(@buf) >= BUFSIZE end end def get_current_head_and_filename_and_content_type_and_name_and_body head = nil body = '' filename = content_type = name = nil content = nil until head && @buf =~ rx if !head && i = @buf.index(EOL+EOL) head = @buf.slice!(0, i+2) # First \r\n @buf.slice!(0, 2) # Second \r\n content_type = head[MULTIPART_CONTENT_TYPE, 1] name = head[MULTIPART_CONTENT_DISPOSITION, 1] || head[MULTIPART_CONTENT_ID, 1] filename = get_filename(head) if filename body = Tempfile.new("RackMultipart") body.binmode if body.respond_to?(:binmode) end next end # Save the read body part. if head && (@boundary_size+4 < @buf.size) body << @buf.slice!(0, @buf.size - (@boundary_size+4)) end content = @io.read(@content_length && BUFSIZE >= @content_length ? @content_length : BUFSIZE) raise EOFError, "bad content body" if content.nil? || content.empty? @buf << content @content_length -= content.size if @content_length end [head, filename, content_type, name, body] end def get_filename(head) filename = nil if head =~ RFC2183 filename = Hash[head.scan(DISPPARM)]['filename'] filename = $1 if filename and filename =~ /^"(.*)"$/ elsif head =~ BROKEN_QUOTED filename = $1 elsif head =~ BROKEN_UNQUOTED filename = $1 end if filename && filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ } filename = Utils.unescape(filename) end if filename && filename !~ /\\[^\\"]/ filename = filename.gsub(/\\(.)/, '\1') end filename end def get_data(filename, body, content_type, name, head) data = nil if filename == "" # filename is blank which means no file has been selected return data elsif filename body.rewind # Take the basename of the upload's original filename. # This handles the full Windows paths given by Internet Explorer # (and perhaps other broken user agents) without affecting # those which give the lone filename. filename = filename.split(/[\/\\]/).last data = {:filename => filename, :type => content_type, :name => name, :tempfile => body, :head => head} elsif !filename && content_type && body.is_a?(IO) body.rewind # Generic multipart cases, not coming from a form data = {:type => content_type, :name => name, :tempfile => body, :head => head} else data = body end [filename, data] end end end end ruby-rack1.4-1.4.5/lib/rack/multipart/uploaded_file.rb000066400000000000000000000020111223351442100224620ustar00rootroot00000000000000module Rack 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 respond_to?(*args) super or @tempfile.respond_to?(*args) end def method_missing(method_name, *args, &block) #:nodoc: @tempfile.__send__(method_name, *args, &block) end end end endruby-rack1.4-1.4.5/lib/rack/nulllogger.rb000066400000000000000000000006021223351442100200230ustar00rootroot00000000000000module Rack class NullLogger def initialize(app) @app = app end def call(env) env['rack.logger'] = self @app.call(env) end def info(progname = nil, &block); end def debug(progname = nil, &block); end def warn(progname = nil, &block); end def error(progname = nil, &block); end def fatal(progname = nil, &block); end end end ruby-rack1.4-1.4.5/lib/rack/recursive.rb000066400000000000000000000033411223351442100176630ustar00rootroot00000000000000require 'uri' module Rack # Rack::ForwardRequest gets caught by Rack::Recursive and redirects # the current request to the app at +url+. # # raise ForwardRequest.new("/not-found") # class ForwardRequest < Exception attr_reader :url, :env def initialize(url, env={}) @url = URI(url) @env = env @env["PATH_INFO"] = @url.path @env["QUERY_STRING"] = @url.query if @url.query @env["HTTP_HOST"] = @url.host if @url.host @env["HTTP_PORT"] = @url.port if @url.port @env["rack.url_scheme"] = @url.scheme if @url.scheme super "forwarding to #{url}" end end # Rack::Recursive allows applications called down the chain to # include data from other applications (by using # rack['rack.recursive.include'][...] or raise a # ForwardRequest to redirect internally. class Recursive def initialize(app) @app = app end def call(env) dup._call(env) end def _call(env) @script_name = env["SCRIPT_NAME"] @app.call(env.merge('rack.recursive.include' => method(:include))) rescue ForwardRequest => req call(env.merge(req.env)) end def include(env, path) unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || path[@script_name.size].nil?) raise ArgumentError, "can only include below #{@script_name}, not #{path}" end env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, "REQUEST_METHOD" => "GET", "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", "rack.input" => StringIO.new("")) @app.call(env) end end end ruby-rack1.4-1.4.5/lib/rack/reloader.rb000066400000000000000000000057421223351442100174600ustar00rootroot00000000000000# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com # Rack::Reloader is subject to the terms of an MIT-style license. # See COPYING or http://www.opensource.org/licenses/mit-license.php. require 'pathname' module Rack # High performant source reloader # # This class acts as Rack middleware. # # What makes it especially suited for use in a production environment is that # any file will only be checked once and there will only be made one system # call stat(2). # # Please note that this will not reload files in the background, it does so # only when actively called. # # It is performing a check/reload cycle at the start of every request, but # also respects a cool down time, during which nothing will be done. class Reloader def initialize(app, cooldown = 10, backend = Stat) @app = app @cooldown = cooldown @last = (Time.now - cooldown) @cache = {} @mtimes = {} extend backend end def call(env) if @cooldown and Time.now > @last + @cooldown if Thread.list.size > 1 Thread.exclusive{ reload! } else reload! end @last = Time.now end @app.call(env) end def reload!(stderr = $stderr) rotation do |file, mtime| previous_mtime = @mtimes[file] ||= mtime safe_load(file, mtime, stderr) if mtime > previous_mtime end end # A safe Kernel::load, issuing the hooks depending on the results def safe_load(file, mtime, stderr = $stderr) load(file) stderr.puts "#{self.class}: reloaded `#{file}'" file rescue LoadError, SyntaxError => ex stderr.puts ex ensure @mtimes[file] = mtime end module Stat def rotation files = [$0, *$LOADED_FEATURES].uniq paths = ['./', *$LOAD_PATH].uniq files.map{|file| next if file =~ /\.(so|bundle)$/ # cannot reload compiled files found, stat = figure_path(file, paths) next unless found && stat && mtime = stat.mtime @cache[file] = found yield(found, mtime) }.compact end # Takes a relative or absolute +file+ name, a couple possible +paths+ that # the +file+ might reside in. Returns the full path and File::Stat for the # path. def figure_path(file, paths) found = @cache[file] found = file if !found and Pathname.new(file).absolute? found, stat = safe_stat(found) return found, stat if found paths.find do |possible_path| path = ::File.join(possible_path, file) found, stat = safe_stat(path) return ::File.expand_path(found), stat if found end return false, false end def safe_stat(file) return unless file stat = ::File.stat(file) return file, stat if stat.file? rescue Errno::ENOENT, Errno::ENOTDIR, Errno::ESRCH @cache.delete(file) and false end end end end ruby-rack1.4-1.4.5/lib/rack/request.rb000066400000000000000000000252031223351442100173450ustar00rootroot00000000000000require 'rack/utils' module Rack # Rack::Request provides a convenient interface to a Rack # environment. It is stateless, the environment +env+ passed to the # constructor will be directly modified. # # req = Rack::Request.new(env) # req.post? # req.params["data"] # # The environment hash passed will store a reference to the Request object # instantiated so that it will only instantiate if an instance of the Request # object doesn't already exist. class Request # The environment of the request. attr_reader :env def initialize(env) @env = env end def body; @env["rack.input"] end def script_name; @env["SCRIPT_NAME"].to_s end def path_info; @env["PATH_INFO"].to_s end def request_method; @env["REQUEST_METHOD"] end def query_string; @env["QUERY_STRING"].to_s end def content_length; @env['CONTENT_LENGTH'] end def content_type content_type = @env['CONTENT_TYPE'] content_type.nil? || content_type.empty? ? nil : content_type end def session; @env['rack.session'] ||= {} end def session_options; @env['rack.session.options'] ||= {} end def logger; @env['rack.logger'] end # The media type (type/subtype) portion of the CONTENT_TYPE header # without any media type parameters. e.g., when CONTENT_TYPE is # "text/plain;charset=utf-8", the media-type is "text/plain". # # For more information on the use of media types in HTTP, see: # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 def media_type content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase end # The media type parameters provided in CONTENT_TYPE as a Hash, or # an empty Hash if no CONTENT_TYPE or media-type parameters were # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", # this method responds with the following Hash: # { 'charset' => 'utf-8' } def media_type_params return {} if content_type.nil? Hash[*content_type.split(/\s*[;,]\s*/)[1..-1]. collect { |s| s.split('=', 2) }. map { |k,v| [k.downcase, v] }.flatten] end # The character set of the request body if a "charset" media type # parameter was given, or nil if no "charset" was specified. Note # that, per RFC2616, text/* media types that specify no explicit # charset are to be considered ISO-8859-1. def content_charset media_type_params['charset'] end def scheme if @env['HTTPS'] == 'on' 'https' elsif @env['HTTP_X_FORWARDED_SSL'] == 'on' 'https' elsif @env['HTTP_X_FORWARDED_SCHEME'] @env['HTTP_X_FORWARDED_SCHEME'] elsif @env['HTTP_X_FORWARDED_PROTO'] @env['HTTP_X_FORWARDED_PROTO'].split(',')[0] else @env["rack.url_scheme"] end end def ssl? scheme == 'https' end def host_with_port if forwarded = @env["HTTP_X_FORWARDED_HOST"] forwarded.split(/,\s?/).last else @env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}" end end def port if port = host_with_port.split(/:/)[1] port.to_i elsif port = @env['HTTP_X_FORWARDED_PORT'] port.to_i elsif ssl? 443 elsif @env.has_key?("HTTP_X_FORWARDED_HOST") 80 else @env["SERVER_PORT"].to_i end end def host # Remove port number. host_with_port.to_s.gsub(/:\d+\z/, '') end def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end def path_info=(s); @env["PATH_INFO"] = s.to_s end # Checks the HTTP request method (or verb) to see if it was of type DELETE def delete?; request_method == "DELETE" end # Checks the HTTP request method (or verb) to see if it was of type GET def get?; request_method == "GET" end # Checks the HTTP request method (or verb) to see if it was of type HEAD def head?; request_method == "HEAD" end # Checks the HTTP request method (or verb) to see if it was of type OPTIONS def options?; request_method == "OPTIONS" end # Checks the HTTP request method (or verb) to see if it was of type PATCH def patch?; request_method == "PATCH" end # Checks the HTTP request method (or verb) to see if it was of type POST def post?; request_method == "POST" end # Checks the HTTP request method (or verb) to see if it was of type PUT def put?; request_method == "PUT" end # Checks the HTTP request method (or verb) to see if it was of type TRACE def trace?; request_method == "TRACE" end # The set of form-data media-types. Requests that do not indicate # one of the media types presents in this list will not be eligible # for form-data / param parsing. FORM_DATA_MEDIA_TYPES = [ 'application/x-www-form-urlencoded', 'multipart/form-data' ] # The set of media-types. Requests that do not indicate # one of the media types presents in this list will not be eligible # for param parsing like soap attachments or generic multiparts PARSEABLE_DATA_MEDIA_TYPES = [ 'multipart/related', 'multipart/mixed' ] # Determine whether the request body contains form-data by checking # the request Content-Type for one of the media-types: # "application/x-www-form-urlencoded" or "multipart/form-data". The # list of form-data media types can be modified through the # +FORM_DATA_MEDIA_TYPES+ array. # # A request body is also assumed to contain form-data when no # Content-Type header is provided and the request_method is POST. def form_data? type = media_type meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'] (meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type) end # Determine whether the request body contains data by checking # the request media_type against registered parse-data media-types def parseable_data? PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) end # Returns the data received in the query string. def GET if @env["rack.request.query_string"] == query_string @env["rack.request.query_hash"] else @env["rack.request.query_string"] = query_string @env["rack.request.query_hash"] = parse_query(query_string) end end # Returns the data received in the request body. # # This method support both application/x-www-form-urlencoded and # multipart/form-data. def POST if @env["rack.input"].nil? raise "Missing rack.input" elsif @env["rack.request.form_input"].eql? @env["rack.input"] @env["rack.request.form_hash"] elsif form_data? || parseable_data? @env["rack.request.form_input"] = @env["rack.input"] unless @env["rack.request.form_hash"] = parse_multipart(env) form_vars = @env["rack.input"].read # Fix for Safari Ajax postings that always append \0 # form_vars.sub!(/\0\z/, '') # performance replacement: form_vars.slice!(-1) if form_vars[-1] == ?\0 @env["rack.request.form_vars"] = form_vars @env["rack.request.form_hash"] = parse_query(form_vars) @env["rack.input"].rewind end @env["rack.request.form_hash"] else {} end end # The union of GET and POST data. def params @params ||= self.GET.merge(self.POST) rescue EOFError self.GET end # shortcut for request.params[key] def [](key) params[key.to_s] end # shortcut for request.params[key] = value def []=(key, value) params[key.to_s] = value end # like Hash#values_at def values_at(*keys) keys.map{|key| params[key] } end # the referer of the client def referer @env['HTTP_REFERER'] end alias referrer referer def user_agent @env['HTTP_USER_AGENT'] end def cookies hash = @env["rack.request.cookie_hash"] ||= {} string = @env["HTTP_COOKIE"] return hash if string == @env["rack.request.cookie_string"] hash.clear # According to RFC 2109: # If multiple cookies satisfy the criteria above, they are ordered in # the Cookie header such that those with more specific Path attributes # precede those with less specific. Ordering with respect to other # attributes (e.g., Domain) is unspecified. cookies = Utils.parse_query(string, ';,') { |s| Rack::Utils.unescape(s) rescue s } cookies.each { |k,v| hash[k] = Array === v ? v.first : v } @env["rack.request.cookie_string"] = string hash end def xhr? @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" end def base_url url = scheme + "://" url << host if scheme == "https" && port != 443 || scheme == "http" && port != 80 url << ":#{port}" end url end # Tries to return a remake of the original request URL as a string. def url base_url + fullpath end def path script_name + path_info end def fullpath query_string.empty? ? path : "#{path}?#{query_string}" end def accept_encoding @env["HTTP_ACCEPT_ENCODING"].to_s.split(/\s*,\s*/).map do |part| encoding, parameters = part.split(/\s*;\s*/, 2) quality = 1.0 if parameters and /\Aq=([\d.]+)/ =~ parameters quality = $1.to_f end [encoding, quality] end end def trusted_proxy?(ip) ip =~ /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|^::1$|^fd[0-9a-f]{2}:.+|^localhost$/i end def ip remote_addrs = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : [] remote_addrs.reject! { |addr| trusted_proxy?(addr) } return remote_addrs.first if remote_addrs.any? forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : [] if client_ip = @env['HTTP_CLIENT_IP'] # If forwarded_ips doesn't include the client_ip, it might be an # ip spoofing attempt, so we ignore HTTP_CLIENT_IP return client_ip if forwarded_ips.include?(client_ip) end return forwarded_ips.reject { |ip| trusted_proxy?(ip) }.last || @env["REMOTE_ADDR"] end protected def parse_query(qs) Utils.parse_nested_query(qs) end def parse_multipart(env) Rack::Multipart.parse_multipart(env) end end end ruby-rack1.4-1.4.5/lib/rack/response.rb000066400000000000000000000100261223351442100175100ustar00rootroot00000000000000require 'rack/request' require 'rack/utils' require 'time' module Rack # Rack::Response provides a convenient interface to create a Rack # response. # # It allows setting of headers and cookies, and provides useful # defaults (a OK response containing HTML). # # You can use Response#write to iteratively generate your response, # but note that this is buffered by Rack::Response until you call # +finish+. +finish+ however can take a block inside which calls to # +write+ are synchronous with the Rack response. # # Your application's +call+ should end returning Response#finish. class Response attr_accessor :length def initialize(body=[], status=200, header={}) @status = status.to_i @header = Utils::HeaderHash.new("Content-Type" => "text/html"). merge(header) @chunked = "chunked" == @header['Transfer-Encoding'] @writer = lambda { |x| @body << x } @block = nil @length = 0 @body = [] if body.respond_to? :to_str write body.to_str elsif body.respond_to?(:each) body.each { |part| write part.to_s } else raise TypeError, "stringable or iterable required" end yield self if block_given? end attr_reader :header attr_accessor :status, :body def [](key) header[key] end def []=(key, value) header[key] = value end def set_cookie(key, value) Utils.set_cookie_header!(header, key, value) end def delete_cookie(key, value={}) Utils.delete_cookie_header!(header, key, value) end def redirect(target, status=302) self.status = status self["Location"] = target end def finish(&block) @block = block if [204, 205, 304].include?(status.to_i) header.delete "Content-Type" header.delete "Content-Length" close [status.to_i, header, []] else [status.to_i, header, BodyProxy.new(self){}] end end alias to_a finish # For *response alias to_ary finish # For implicit-splat on Ruby 1.9.2 def each(&callback) @body.each(&callback) @writer = callback @block.call(self) if @block end # Append to body and update Content-Length. # # NOTE: Do not mix #write and direct #body access! # def write(str) s = str.to_s @length += Rack::Utils.bytesize(s) unless @chunked @writer.call s header["Content-Length"] = @length.to_s unless @chunked str end def close body.close if body.respond_to?(:close) end def empty? @block == nil && @body.empty? end alias headers header module Helpers def invalid?; status < 100 || status >= 600; end def informational?; status >= 100 && status < 200; end def successful?; status >= 200 && status < 300; end def redirection?; status >= 300 && status < 400; end def client_error?; status >= 400 && status < 500; end def server_error?; status >= 500 && status < 600; end def ok?; status == 200; end def bad_request?; status == 400; end def forbidden?; status == 403; end def not_found?; status == 404; end def method_not_allowed?; status == 405; end def unprocessable?; status == 422; end def redirect?; [301, 302, 303, 307].include? status; end # Headers attr_reader :headers, :original_headers def include?(header) !!headers[header] end def content_type headers["Content-Type"] end def content_length cl = headers["Content-Length"] cl ? cl.to_i : cl end def location headers["Location"] end end include Helpers end end ruby-rack1.4-1.4.5/lib/rack/rewindable_input.rb000066400000000000000000000063001223351442100212050ustar00rootroot00000000000000# -*- encoding: binary -*- require 'tempfile' require 'rack/utils' module Rack # Class which can make any IO object rewindable, including non-rewindable ones. It does # this by buffering the data into a tempfile, which is rewindable. # # rack.input is required to be rewindable, so if your input stream IO is non-rewindable # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class # to easily make it rewindable. # # Don't forget to call #close when you're done. This frees up temporary resources that # RewindableInput uses, though it does *not* close the original IO object. class RewindableInput def initialize(io) @io = io @rewindable_io = nil @unlinked = false end def gets make_rewindable unless @rewindable_io @rewindable_io.gets end def read(*args) make_rewindable unless @rewindable_io @rewindable_io.read(*args) end def each(&block) make_rewindable unless @rewindable_io @rewindable_io.each(&block) end def rewind make_rewindable unless @rewindable_io @rewindable_io.rewind end # Closes this RewindableInput object without closing the originally # wrapped IO oject. Cleans up any temporary resources that this RewindableInput # has created. # # This method may be called multiple times. It does nothing on subsequent calls. def close if @rewindable_io if @unlinked @rewindable_io.close else @rewindable_io.close! end @rewindable_io = nil end end private # Ruby's Tempfile class has a bug. Subclass it and fix it. class Tempfile < ::Tempfile def _close @tmpfile.close if @tmpfile @data[1] = nil if @data @tmpfile = nil end end def make_rewindable # Buffer all data into a tempfile. Since this tempfile is private to this # RewindableInput object, we chmod it so that nobody else can read or write # it. On POSIX filesystems we also unlink the file so that it doesn't # even have a file entry on the filesystem anymore, though we can still # access it because we have the file handle open. @rewindable_io = Tempfile.new('RackRewindableInput') @rewindable_io.chmod(0000) @rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding) @rewindable_io.binmode if filesystem_has_posix_semantics? # Use ::File.unlink as 1.9.1 Tempfile has a bug where unlink closes the file! ::File.unlink @rewindable_io.path raise 'Unlink failed. IO closed.' if @rewindable_io.closed? @unlinked = true end buffer = "" while @io.read(1024 * 4, buffer) entire_buffer_written_out = false while !entire_buffer_written_out written = @rewindable_io.write(buffer) entire_buffer_written_out = written == Rack::Utils.bytesize(buffer) if !entire_buffer_written_out buffer.slice!(0 .. written - 1) end end end @rewindable_io.rewind end def filesystem_has_posix_semantics? RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/ end end end ruby-rack1.4-1.4.5/lib/rack/runtime.rb000066400000000000000000000013061223351442100173360ustar00rootroot00000000000000module Rack # Sets an "X-Runtime" response header, indicating the response # time of the request, in seconds # # You can put it right before the application to see the processing # time, or before all the other middlewares to include time for them, # too. class Runtime def initialize(app, name = nil) @app = app @header_name = "X-Runtime" @header_name << "-#{name}" if name end def call(env) start_time = Time.now status, headers, body = @app.call(env) request_time = Time.now - start_time if !headers.has_key?(@header_name) headers[@header_name] = "%0.6f" % request_time end [status, headers, body] end end end ruby-rack1.4-1.4.5/lib/rack/sendfile.rb000066400000000000000000000106771223351442100174570ustar00rootroot00000000000000require 'rack/file' module Rack # = Sendfile # # The Sendfile middleware intercepts responses whose body is being # served from a file and replaces it with a server specific X-Sendfile # header. The web server is then responsible for writing the file contents # to the client. This can dramatically reduce the amount of work required # by the Ruby backend and takes advantage of the web server's optimized file # delivery code. # # In order to take advantage of this middleware, the response body must # respond to +to_path+ and the request must include an X-Sendfile-Type # header. Rack::File and other components implement +to_path+ so there's # rarely anything you need to do in your application. The X-Sendfile-Type # header is typically set in your web servers configuration. The following # sections attempt to document # # === Nginx # # Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile # but requires parts of the filesystem to be mapped into a private URL # hierarachy. # # The following example shows the Nginx configuration required to create # a private "/files/" area, enable X-Accel-Redirect, and pass the special # X-Sendfile-Type and X-Accel-Mapping headers to the backend: # # location ~ /files/(.*) { # internal; # alias /var/www/$1; # } # # location / { # proxy_redirect off; # # proxy_set_header Host $host; # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # # proxy_set_header X-Sendfile-Type X-Accel-Redirect; # proxy_set_header X-Accel-Mapping /var/www/=/files/; # # proxy_pass http://127.0.0.1:8080/; # } # # Note that the X-Sendfile-Type header must be set exactly as shown above. # The X-Accel-Mapping header should specify the location on the file system, # followed by an equals sign (=), followed name of the private URL pattern # that it maps to. The middleware performs a simple substitution on the # resulting path. # # See Also: http://wiki.codemongers.com/NginxXSendfile # # === lighttpd # # Lighttpd has supported some variation of the X-Sendfile header for some # time, although only recent version support X-Sendfile in a reverse proxy # configuration. # # $HTTP["host"] == "example.com" { # proxy-core.protocol = "http" # proxy-core.balancer = "round-robin" # proxy-core.backends = ( # "127.0.0.1:8000", # "127.0.0.1:8001", # ... # ) # # proxy-core.allow-x-sendfile = "enable" # proxy-core.rewrite-request = ( # "X-Sendfile-Type" => (".*" => "X-Sendfile") # ) # } # # See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore # # === Apache # # X-Sendfile is supported under Apache 2.x using a separate module: # # https://tn123.org/mod_xsendfile/ # # Once the module is compiled and installed, you can enable it using # XSendFile config directive: # # RequestHeader Set X-Sendfile-Type X-Sendfile # ProxyPassReverse / http://localhost:8001/ # XSendFile on class Sendfile F = ::File def initialize(app, variation=nil) @app = app @variation = variation end def call(env) status, headers, body = @app.call(env) if body.respond_to?(:to_path) case type = variation(env) when 'X-Accel-Redirect' path = F.expand_path(body.to_path) if url = map_accel_path(env, path) headers['Content-Length'] = '0' headers[type] = url body = [] else env['rack.errors'].puts "X-Accel-Mapping header missing" end when 'X-Sendfile', 'X-Lighttpd-Send-File' path = F.expand_path(body.to_path) headers['Content-Length'] = '0' headers[type] = path body = [] when '', nil else env['rack.errors'].puts "Unknown x-sendfile variation: '#{variation}'.\n" end end [status, headers, body] end private def variation(env) @variation || env['sendfile.type'] || env['HTTP_X_SENDFILE_TYPE'] end def map_accel_path(env, file) if mapping = env['HTTP_X_ACCEL_MAPPING'] internal, external = mapping.split('=', 2).map{ |p| p.strip } file.sub(/^#{internal}/i, external) end end end end ruby-rack1.4-1.4.5/lib/rack/server.rb000066400000000000000000000235111223351442100171630ustar00rootroot00000000000000require 'optparse' module Rack class Server class Options def parse!(args) options = {} opt_parser = OptionParser.new("", 24, ' ') do |opts| opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]" opts.separator "" opts.separator "Ruby options:" lineno = 1 opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line| eval line, TOPLEVEL_BINDING, "-e", lineno lineno += 1 } opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") { options[:debug] = true } opts.on("-w", "--warn", "turn warnings on for your script") { options[:warn] = true } opts.on("-I", "--include PATH", "specify $LOAD_PATH (may be used more than once)") { |path| (options[:include] ||= []).concat(path.split(":")) } opts.on("-r", "--require LIBRARY", "require the library, before executing your script") { |library| options[:require] = library } opts.separator "" opts.separator "Rack options:" opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s| options[:server] = s } opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host| options[:Host] = host } opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port| options[:Port] = port } opts.on("-O", "--option NAME[=VALUE]", "pass VALUE to the server as option NAME. If no VALUE, sets it to true. Run '#{$0} -s SERVER -h' to get a list of options for SERVER") { |name| name, value = name.split('=', 2) value = true if value.nil? options[name.to_sym] = value } opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e| options[:environment] = e } opts.on("-D", "--daemonize", "run daemonized in the background") { |d| options[:daemonize] = d ? true : false } opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f| options[:pid] = ::File.expand_path(f) } opts.separator "" opts.separator "Common options:" opts.on_tail("-h", "-?", "--help", "Show this message") do puts opts puts handler_opts(options) exit end opts.on_tail("--version", "Show version") do puts "Rack #{Rack.version} (Release: #{Rack.release})" exit end end begin opt_parser.parse! args rescue OptionParser::InvalidOption => e warn e.message abort opt_parser.to_s end options[:config] = args.last if args.last options end def handler_opts(options) begin info = [] server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options) if server && server.respond_to?(:valid_options) info << "" info << "Server-specific options for #{server.name}:" has_options = false server.valid_options.each do |name, description| next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own. info << " -O %-21s %s" % [name, description] has_options = true end return "" if !has_options end info.join("\n") rescue NameError return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options" end end end # Start a new rack server (like running rackup). This will parse ARGV and # provide standard ARGV rackup options, defaulting to load 'config.ru'. # # Providing an options hash will prevent ARGV parsing and will not include # any default options. # # This method can be used to very easily launch a CGI application, for # example: # # Rack::Server.start( # :app => lambda do |e| # [200, {'Content-Type' => 'text/html'}, ['hello world']] # end, # :server => 'cgi' # ) # # Further options available here are documented on Rack::Server#initialize def self.start(options = nil) new(options).start end attr_writer :options # Options may include: # * :app # a rack application to run (overrides :config) # * :config # a rackup configuration file path to load (.ru) # * :environment # this selects the middleware that will be wrapped around # your application. Default options available are: # - development: CommonLogger, ShowExceptions, and Lint # - deployment: CommonLogger # - none: no extra middleware # note: when the server is a cgi server, CommonLogger is not included. # * :server # choose a specific Rack::Handler, e.g. cgi, fcgi, webrick # * :daemonize # if true, the server will daemonize itself (fork, detach, etc) # * :pid # path to write a pid file after daemonize # * :Host # the host address to bind to (used by supporting Rack::Handler) # * :Port # the port to bind to (used by supporting Rack::Handler) # * :AccessLog # webrick acess log options (or supporting Rack::Handler) # * :debug # turn on debug output ($DEBUG = true) # * :warn # turn on warnings ($-w = true) # * :include # add given paths to $LOAD_PATH # * :require # require the given libraries def initialize(options = nil) @options = options @app = options[:app] if options && options[:app] end def options @options ||= parse_options(ARGV) end def default_options { :environment => ENV['RACK_ENV'] || "development", :pid => nil, :Port => 9292, :Host => "0.0.0.0", :AccessLog => [], :config => "config.ru" } end def app @app ||= begin if !::File.exist? options[:config] abort "configuration #{options[:config]} not found" end app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) self.options.merge! options app end end def self.logging_middleware lambda { |server| server.server.name =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr] } end def self.middleware @middleware ||= begin m = Hash.new {|h,k| h[k] = []} m["deployment"].concat [ [Rack::ContentLength], [Rack::Chunked], logging_middleware ] m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]] m end end def middleware self.class.middleware end def start &blk if options[:warn] $-w = true end if includes = options[:include] $LOAD_PATH.unshift(*includes) end if library = options[:require] require library end if options[:debug] $DEBUG = true require 'pp' p options[:server] pp wrapped_app pp app end check_pid! if options[:pid] # Touch the wrapped app, so that the config.ru is loaded before # daemonization (i.e. before chdir, etc). wrapped_app daemonize_app if options[:daemonize] write_pid if options[:pid] trap(:INT) do if server.respond_to?(:shutdown) server.shutdown else exit end end server.run wrapped_app, options, &blk end def server @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options) end private def parse_options(args) options = default_options # Don't evaluate CGI ISINDEX parameters. # http://www.meb.uni-bonn.de/docs/cgi/cl.html args.clear if ENV.include?("REQUEST_METHOD") options.merge! opt_parser.parse!(args) options[:config] = ::File.expand_path(options[:config]) ENV["RACK_ENV"] = options[:environment] options end def opt_parser Options.new end def build_app(app) middleware[options[:environment]].reverse_each do |middleware| middleware = middleware.call(self) if middleware.respond_to?(:call) next unless middleware klass = middleware.shift app = klass.new(app, *middleware) end app end def wrapped_app @wrapped_app ||= build_app app end def daemonize_app if RUBY_VERSION < "1.9" exit if fork Process.setsid exit if fork Dir.chdir "/" STDIN.reopen "/dev/null" STDOUT.reopen "/dev/null", "a" STDERR.reopen "/dev/null", "a" else Process.daemon end end def write_pid ::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") } at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) } end def check_pid! case pidfile_process_status when :running, :not_owned $stderr.puts "A server is already running. Check #{options[:pid]}." exit(1) when :dead ::File.delete(options[:pid]) end end def pidfile_process_status return :exited unless ::File.exist?(options[:pid]) pid = ::File.read(options[:pid]).to_i Process.kill(0, pid) :running rescue Errno::ESRCH :dead rescue Errno::EPERM :not_owned end end end ruby-rack1.4-1.4.5/lib/rack/session/000077500000000000000000000000001223351442100170115ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/session/abstract/000077500000000000000000000000001223351442100206145ustar00rootroot00000000000000ruby-rack1.4-1.4.5/lib/rack/session/abstract/id.rb000066400000000000000000000263621223351442100215460ustar00rootroot00000000000000# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net # bugrep: Andreas Zehnder require 'time' require 'rack/request' require 'rack/response' begin require 'securerandom' rescue LoadError # We just won't get securerandom end module Rack module Session module Abstract ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze # Thin wrapper around Hash that allows us to lazily load session id into session_options. class OptionsHash < Hash #:nodoc: def initialize(by, env, default_options) @by = by @env = env @session_id_loaded = false merge!(default_options) end def [](key) load_session_id! if key == :id && session_id_not_loaded? super end private def session_id_not_loaded? !(@session_id_loaded || key?(:id)) end def load_session_id! self[:id] = @by.send(:extract_session_id, @env) @session_id_loaded = true end end # SessionHash is responsible to lazily load the session from store. class SessionHash < Hash def initialize(by, env) super() @by = by @env = env @loaded = false end def [](key) load_for_read! super(key.to_s) end def has_key?(key) load_for_read! super(key.to_s) end alias :key? :has_key? alias :include? :has_key? def []=(key, value) load_for_write! super(key.to_s, value) end def clear load_for_write! super end def to_hash load_for_read! h = {}.replace(self) h.delete_if { |k,v| v.nil? } h end def update(hash) load_for_write! super(stringify_keys(hash)) end def delete(key) load_for_write! super(key.to_s) end def inspect if loaded? super else "#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>" end end def exists? return @exists if instance_variable_defined?(:@exists) @exists = @by.send(:session_exists?, @env) end def loaded? @loaded end def empty? load_for_read! super end def merge!(hash) load_for_write! super end private def load_for_read! load! if !loaded? && exists? end def load_for_write! load! unless loaded? end def load! id, session = @by.send(:load_session, @env) @env[ENV_SESSION_OPTIONS_KEY][:id] = id replace(stringify_keys(session)) @loaded = true end def stringify_keys(other) hash = {} other.each do |key, value| hash[key.to_s] = value end hash end end # ID sets up a basic framework for implementing an id based sessioning # service. Cookies sent to the client for maintaining sessions will only # contain an id reference. Only #get_session and #set_session are # required to be overwritten. # # All parameters are optional. # * :key determines the name of the cookie, by default it is # 'rack.session' # * :path, :domain, :expire_after, :secure, and :httponly set the related # cookie options as by Rack::Response#add_cookie # * :skip will not a set a cookie in the response nor update the session state # * :defer will not set a cookie in the response but still update the session # state if it is used with a backend # * :renew (implementation dependent) will prompt the generation of a new # session id, and migration of data to be referenced at the new id. If # :defer is set, it will be overridden and the cookie will be set. # * :sidbits sets the number of bits in length that a generated session # id will be. # # These options can be set on a per request basis, at the location of # env['rack.session.options']. Additionally the id of the session can be # found within the options hash at the key :id. It is highly not # recommended to change its value. # # Is Rack::Utils::Context compatible. # # Not included by default; you must require 'rack/session/abstract/id' # to use. class ID DEFAULT_OPTIONS = { :key => 'rack.session', :path => '/', :domain => nil, :expire_after => nil, :secure => false, :httponly => true, :defer => false, :renew => false, :sidbits => 128, :cookie_only => true, :secure_random => (::SecureRandom rescue false) } attr_reader :key, :default_options def initialize(app, options={}) @app = app @default_options = self.class::DEFAULT_OPTIONS.merge(options) @key = @default_options.delete(:key) @cookie_only = @default_options.delete(:cookie_only) initialize_sid end def call(env) context(env) end def context(env, app=@app) prepare_session(env) status, headers, body = app.call(env) commit_session(env, status, headers, body) end private def initialize_sid @sidbits = @default_options[:sidbits] @sid_secure = @default_options[:secure_random] @sid_length = @sidbits / 4 end # Generate a new session id using Ruby #rand. The size of the # session id is controlled by the :sidbits option. # Monkey patch this to use custom methods for session id generation. def generate_sid(secure = @sid_secure) if secure SecureRandom.hex(@sid_length) else "%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1) end rescue NotImplementedError generate_sid(false) end # Sets the lazy session at 'rack.session' and places options and session # metadata into 'rack.session.options'. def prepare_session(env) session_was = env[ENV_SESSION_KEY] env[ENV_SESSION_KEY] = SessionHash.new(self, env) env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) env[ENV_SESSION_KEY].merge! session_was if session_was end # Extracts the session id from provided cookies and passes it and the # environment to #get_session. def load_session(env) sid = current_session_id(env) sid, session = get_session(env, sid) [sid, session || {}] end # Extract session id from request object. def extract_session_id(env) request = Rack::Request.new(env) sid = request.cookies[@key] sid ||= request.params[@key] unless @cookie_only sid end # Returns the current session id from the OptionsHash. def current_session_id(env) env[ENV_SESSION_OPTIONS_KEY][:id] end # Check if the session exists or not. def session_exists?(env) value = current_session_id(env) value && !value.empty? end # Session should be commited if it was loaded, any of specific options like :renew, :drop # or :expire_after was given and the security permissions match. Skips if skip is given. def commit_session?(env, session, options) if options[:skip] false else has_session = loaded_session?(session) || forced_session_update?(session, options) has_session && security_matches?(env, options) end end def loaded_session?(session) !session.is_a?(SessionHash) || session.loaded? end def forced_session_update?(session, options) force_options?(options) && session && !session.empty? end def force_options?(options) options.values_at(:renew, :drop, :defer, :expire_after).any? end def security_matches?(env, options) return true unless options[:secure] request = Rack::Request.new(env) request.ssl? end # Acquires the session from the environment and the session id from # the session options and passes them to #set_session. If successful # and the :defer option is not true, a cookie will be added to the # response with the session's id. def commit_session(env, status, headers, body) session = env['rack.session'] options = env['rack.session.options'] if options[:drop] || options[:renew] session_id = destroy_session(env, options[:id] || generate_sid, options) return [status, headers, body] unless session_id end return [status, headers, body] unless commit_session?(env, session, options) session.send(:load!) unless loaded_session?(session) session = session.to_hash session_id ||= options[:id] || generate_sid if not data = set_session(env, session_id, session, options) env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") elsif options[:defer] and not options[:renew] env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE else cookie = Hash.new cookie[:value] = data cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after] set_cookie(env, headers, cookie.merge!(options)) end [status, headers, body] end # Sets the cookie back to the client with session id. We skip the cookie # setting if the value didn't change (sid is the same) or expires was given. def set_cookie(env, headers, cookie) request = Rack::Request.new(env) if request.cookies[@key] != cookie[:value] || cookie[:expires] Utils.set_cookie_header!(headers, @key, cookie) end end # All thread safety and session retrival proceedures should occur here. # Should return [session_id, session]. # If nil is provided as the session id, generation of a new valid id # should occur within. def get_session(env, sid) raise '#get_session not implemented.' end # All thread safety and session storage proceedures should occur here. # Should return true or false dependant on whether or not the session # was saved or not. def set_session(env, sid, session, options) raise '#set_session not implemented.' end # All thread safety and session destroy proceedures should occur here. # Should return a new session id or nil if options[:drop] def destroy_session(env, sid, options) raise '#destroy_session not implemented' end end end end end ruby-rack1.4-1.4.5/lib/rack/session/cookie.rb000066400000000000000000000120051223351442100206050ustar00rootroot00000000000000require 'openssl' require 'rack/request' require 'rack/response' require 'rack/session/abstract/id' module Rack module Session # Rack::Session::Cookie provides simple cookie based session management. # By default, the session is a Ruby Hash stored as base64 encoded marshalled # data set to :key (default: rack.session). The object that encodes the # session data is configurable and must respond to +encode+ and +decode+. # Both methods must take a string and return a string. # # When the secret key is set, cookie data is checked for data integrity. # The old secret key is also accepted and allows graceful secret rotation. # # Example: # # use Rack::Session::Cookie, :key => 'rack.session', # :domain => 'foo.com', # :path => '/', # :expire_after => 2592000, # :secret => 'change_me', # :old_secret => 'also_change_me' # # All parameters are optional. # # Example of a cookie with no encoding: # # Rack::Session::Cookie.new(application, { # :coder => Rack::Session::Cookie::Identity.new # }) # # Example of a cookie with custom encoding: # # Rack::Session::Cookie.new(application, { # :coder => Class.new { # def encode(str); str.reverse; end # def decode(str); str.reverse; end # }.new # }) # class Cookie < Abstract::ID # Encode session cookies as Base64 class Base64 def encode(str) [str].pack('m') end def decode(str) str.unpack('m').first end # Encode session cookies as Marshaled Base64 data class Marshal < Base64 def encode(str) super(::Marshal.dump(str)) end def decode(str) ::Marshal.load(super(str)) rescue nil end end end # Use no encoding for session cookies class Identity def encode(str); str; end def decode(str); str; end end # Reverse string encoding. (trollface) class Reverse def encode(str); str.reverse; end def decode(str); str.reverse; end end attr_reader :coder def initialize(app, options={}) @secrets = options.values_at(:secret, :old_secret).compact warn <<-MSG unless @secrets.size >= 1 SECURITY WARNING: No secret option provided to Rack::Session::Cookie. This poses a security threat. It is strongly recommended that you provide a secret to prevent exploits that may be possible from crafted cookies. This will not be supported in future versions of Rack, and future versions will even invalidate your existing user cookies. Called from: #{caller[0]}. MSG @coder = options[:coder] ||= Base64::Marshal.new super(app, options.merge!(:cookie_only => true)) end private def load_session(env) data = unpacked_cookie_data(env) data = persistent_session_id!(data) [data["session_id"], data] end def extract_session_id(env) unpacked_cookie_data(env)["session_id"] end def unpacked_cookie_data(env) env["rack.session.unpacked_cookie_data"] ||= begin request = Rack::Request.new(env) session_data = request.cookies[@key] if @secrets.size > 0 && session_data session_data, digest = session_data.split("--") if session_data && digest ok = @secrets.any? do |secret| secret && Rack::Utils.secure_compare(digest, generate_hmac(session_data, secret)) end end session_data = nil unless ok end coder.decode(session_data) || {} end end def persistent_session_id!(data, sid=nil) data ||= {} data["session_id"] ||= sid || generate_sid data end # Overwrite set cookie to bypass content equality and always stream the cookie. def set_cookie(env, headers, cookie) Utils.set_cookie_header!(headers, @key, cookie) end def set_session(env, session_id, session, options) session = session.merge("session_id" => session_id) session_data = coder.encode(session) if @secrets.first session_data = "#{session_data}--#{generate_hmac(session_data, @secrets.first)}" end if session_data.size > (4096 - @key.size) env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.") nil else session_data end end def destroy_session(env, session_id, options) # Nothing to do here, data is in the client generate_sid unless options[:drop] end def generate_hmac(data, secret) OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data) end end end end ruby-rack1.4-1.4.5/lib/rack/session/memcache.rb000066400000000000000000000053631223351442100211070ustar00rootroot00000000000000# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net require 'rack/session/abstract/id' require 'memcache' module Rack module Session # Rack::Session::Memcache provides simple cookie based session management. # Session data is stored in memcached. The corresponding session key is # maintained in the cookie. # You may treat Session::Memcache as you would Session::Pool with the # following caveats. # # * Setting :expire_after to 0 would note to the Memcache server to hang # onto the session data until it would drop it according to it's own # specifications. However, the cookie sent to the client would expire # immediately. # # Note that memcache does drop data before it may be listed to expire. For # a full description of behaviour, please see memcache's documentation. class Memcache < Abstract::ID attr_reader :mutex, :pool DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ :namespace => 'rack:session', :memcache_server => 'localhost:11211' def initialize(app, options={}) super @mutex = Mutex.new mserv = @default_options[:memcache_server] mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k } @pool = options[:cache] || MemCache.new(mserv, mopts) unless @pool.active? and @pool.servers.any?{|c| c.alive? } raise 'No memcache servers' end end def generate_sid loop do sid = super break sid unless @pool.get(sid, true) end end def get_session(env, sid) with_lock(env, [nil, {}]) do unless sid and session = @pool.get(sid) sid, session = generate_sid, {} unless /^STORED/ =~ @pool.add(sid, session) raise "Session collision on '#{sid.inspect}'" end end [sid, session] end end def set_session(env, session_id, new_session, options) expiry = options[:expire_after] expiry = expiry.nil? ? 0 : expiry + 1 with_lock(env, false) do @pool.set session_id, new_session, expiry session_id end end def destroy_session(env, session_id, options) with_lock(env) do @pool.delete(session_id) generate_sid unless options[:drop] end end def with_lock(env, default=nil) @mutex.lock if env['rack.multithread'] yield rescue MemCache::MemCacheError, Errno::ECONNREFUSED if $VERBOSE warn "#{self} is unable to find memcached server." warn $!.inspect end default ensure @mutex.unlock if @mutex.locked? end end end end ruby-rack1.4-1.4.5/lib/rack/session/pool.rb000066400000000000000000000040671223351442100203160ustar00rootroot00000000000000# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net # THANKS: # apeiros, for session id generation, expiry setup, and threadiness # sergio, threadiness and bugreps require 'rack/session/abstract/id' require 'thread' module Rack module Session # Rack::Session::Pool provides simple cookie based session management. # Session data is stored in a hash held by @pool. # In the context of a multithreaded environment, sessions being # committed to the pool is done in a merging manner. # # The :drop option is available in rack.session.options if you wish to # explicitly remove the session from the session cache. # # Example: # myapp = MyRackApp.new # sessioned = Rack::Session::Pool.new(myapp, # :domain => 'foo.com', # :expire_after => 2592000 # ) # Rack::Handler::WEBrick.run sessioned class Pool < Abstract::ID attr_reader :mutex, :pool DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false def initialize(app, options={}) super @pool = Hash.new @mutex = Mutex.new end def generate_sid loop do sid = super break sid unless @pool.key? sid end end def get_session(env, sid) with_lock(env, [nil, {}]) do unless sid and session = @pool[sid] sid, session = generate_sid, {} @pool.store sid, session end [sid, session] end end def set_session(env, session_id, new_session, options) with_lock(env, false) do @pool.store session_id, new_session session_id end end def destroy_session(env, session_id, options) with_lock(env) do @pool.delete(session_id) generate_sid unless options[:drop] end end def with_lock(env, default=nil) @mutex.lock if env['rack.multithread'] yield rescue default ensure @mutex.unlock if @mutex.locked? end end end end ruby-rack1.4-1.4.5/lib/rack/showexceptions.rb000066400000000000000000000272271223351442100207470ustar00rootroot00000000000000require 'ostruct' require 'erb' require 'rack/request' require 'rack/utils' module Rack # Rack::ShowExceptions catches all exceptions raised from the app it # wraps. It shows a useful backtrace with the sourcefile and # clickable context, the whole Rack environment and the request # data. # # Be careful when you use this on public-facing sites as it could # reveal information helpful to attackers. class ShowExceptions CONTEXT = 7 def initialize(app) @app = app @template = ERB.new(TEMPLATE) end def call(env) @app.call(env) rescue StandardError, LoadError, SyntaxError => e exception_string = dump_exception(e) env["rack.errors"].puts(exception_string) env["rack.errors"].flush if prefers_plain_text?(env) content_type = "text/plain" body = [exception_string] else content_type = "text/html" body = pretty(env, e) end [500, {"Content-Type" => content_type, "Content-Length" => Rack::Utils.bytesize(body.join).to_s}, body] end def prefers_plain_text?(env) env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" && (!env["HTTP_ACCEPT"] || !env["HTTP_ACCEPT"].include?("text/html")) end def dump_exception(exception) string = "#{exception.class}: #{exception.message}\n" string << exception.backtrace.map { |l| "\t#{l}" }.join("\n") string end def pretty(env, exception) req = Rack::Request.new(env) # This double assignment is to prevent an "unused variable" warning on # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me. path = path = (req.script_name + req.path_info).squeeze("/") # This double assignment is to prevent an "unused variable" warning on # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me. frames = frames = exception.backtrace.map { |line| frame = OpenStruct.new if line =~ /(.*?):(\d+)(:in `(.*)')?/ frame.filename = $1 frame.lineno = $2.to_i frame.function = $4 begin lineno = frame.lineno-1 lines = ::File.readlines(frame.filename) frame.pre_context_lineno = [lineno-CONTEXT, 0].max frame.pre_context = lines[frame.pre_context_lineno...lineno] frame.context_line = lines[lineno].chomp frame.post_context_lineno = [lineno+CONTEXT, lines.size].min frame.post_context = lines[lineno+1..frame.post_context_lineno] rescue end frame else nil end }.compact [@template.result(binding)] end def h(obj) # :nodoc: case obj when String Utils.escape_html(obj) else Utils.escape_html(obj.inspect) end end # :stopdoc: # adapted from Django # Copyright (c) 2005, the Lawrence Journal-World # Used under the modified BSD license: # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 TEMPLATE = <<'HTML' <%=h exception.class %> at <%=h path %>

<%=h exception.class %> at <%=h path %>

<%=h exception.message %>

Ruby <% if first = frames.first %> <%=h first.filename %>: in <%=h first.function %>, line <%=h frames.first.lineno %> <% else %> unknown location <% end %>
Web <%=h req.request_method %> <%=h(req.host + path)%>

Jump to:

Traceback (innermost first)

    <% frames.each { |frame| %>
  • <%=h frame.filename %>: in <%=h frame.function %> <% if frame.context_line %>
    <% if frame.pre_context %>
      <% frame.pre_context.each { |line| %>
    1. <%=h line %>
    2. <% } %>
    <% end %>
    1. <%=h frame.context_line %>...
    <% if frame.post_context %>
      <% frame.post_context.each { |line| %>
    1. <%=h line %>
    2. <% } %>
    <% end %>
    <% end %>
  • <% } %>

Request information

GET

<% if req.GET and not req.GET.empty? %> <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No GET data.

<% end %>

POST

<% if req.POST and not req.POST.empty? %> <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No POST data.

<% end %> <% unless req.cookies.empty? %> <% req.cookies.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No cookie data.

<% end %>

Rack ENV

<% env.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val %>

You're seeing this error because you use Rack::ShowExceptions.

HTML # :startdoc: end end ruby-rack1.4-1.4.5/lib/rack/showstatus.rb000066400000000000000000000067321223351442100201070ustar00rootroot00000000000000require 'erb' require 'rack/request' require 'rack/utils' module Rack # Rack::ShowStatus catches all empty responses and replaces them # with a site explaining the error. # # Additional details can be put into rack.showstatus.detail # and will be shown as HTML. If such details exist, the error page # is always rendered, even if the reply was not empty. class ShowStatus def initialize(app) @app = app @template = ERB.new(TEMPLATE) end def call(env) status, headers, body = @app.call(env) headers = Utils::HeaderHash.new(headers) empty = headers['Content-Length'].to_i <= 0 # client or server error, or explicit message if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] # This double assignment is to prevent an "unused variable" warning on # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me. req = req = Rack::Request.new(env) message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s # This double assignment is to prevent an "unused variable" warning on # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me. detail = detail = env["rack.showstatus.detail"] || message body = @template.result(binding) size = Rack::Utils.bytesize(body) [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] else [status, headers, body] end end def h(obj) # :nodoc: case obj when String Utils.escape_html(obj) else Utils.escape_html(obj.inspect) end end # :stopdoc: # adapted from Django # Copyright (c) 2005, the Lawrence Journal-World # Used under the modified BSD license: # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 TEMPLATE = <<'HTML' <%=h message %> at <%=h req.script_name + req.path_info %>

<%=h message %> (<%= status.to_i %>)

Request Method: <%=h req.request_method %>
Request URL: <%=h req.url %>

<%= detail %>

You're seeing this error because you use Rack::ShowStatus.

HTML # :startdoc: end end ruby-rack1.4-1.4.5/lib/rack/static.rb000066400000000000000000000121341223351442100171430ustar00rootroot00000000000000module Rack # The Rack::Static middleware intercepts requests for static files # (javascript files, images, stylesheets, etc) based on the url prefixes or # route mappings passed in the options, and serves them using a Rack::File # object. This allows a Rack stack to serve both static and dynamic content. # # Examples: # # Serve all requests beginning with /media from the "media" folder located # in the current directory (ie media/*): # # use Rack::Static, :urls => ["/media"] # # Serve all requests beginning with /css or /images from the folder "public" # in the current directory (ie public/css/* and public/images/*): # # use Rack::Static, :urls => ["/css", "/images"], :root => "public" # # Serve all requests to / with "index.html" from the folder "public" in the # current directory (ie public/index.html): # # use Rack::Static, :urls => {"/" => 'index.html'}, :root => 'public' # # Serve all requests normally from the folder "public" in the current # directory but uses index.html as default route for "/" # # use Rack::Static, :urls => [""], :root => 'public', :index => # 'index.html' # # Set custom HTTP Headers for based on rules: # # use Rack::Static, :root => 'public', # :header_rules => [ # [rule, {header_field => content, header_field => content}], # [rule, {header_field => content}] # ] # # Rules for selecting files: # # 1) All files # Provide the :all symbol # :all => Matches every file # # 2) Folders # Provide the folder path as a string # '/folder' or '/folder/subfolder' => Matches files in a certain folder # # 3) File Extensions # Provide the file extensions as an array # ['css', 'js'] or %w(css js) => Matches files ending in .css or .js # # 4) Regular Expressions / Regexp # Provide a regular expression # %r{\.(?:css|js)\z} => Matches files ending in .css or .js # /\.(?:eot|ttf|otf|woff|svg)\z/ => Matches files ending in # the most common web font formats (.eot, .ttf, .otf, .woff, .svg) # Note: This Regexp is available as a shortcut, using the :fonts rule # # 5) Font Shortcut # Provide the :fonts symbol # :fonts => Uses the Regexp rule stated right above to match all common web font endings # # Rule Ordering: # Rules are applied in the order that they are provided. # List rather general rules above special ones. # # Complete example use case including HTTP header rules: # # use Rack::Static, :root => 'public', # :header_rules => [ # # Cache all static files in public caches (e.g. Rack::Cache) # # as well as in the browser # [:all, {'Cache-Control' => 'public, max-age=31536000'}], # # # Provide web fonts with cross-origin access-control-headers # # Firefox requires this when serving assets using a Content Delivery Network # [:fonts, {'Access-Control-Allow-Origin' => '*'}] # ] # class Static def initialize(app, options={}) @app = app @urls = options[:urls] || ["/favicon.ico"] @index = options[:index] root = options[:root] || Dir.pwd # HTTP Headers @header_rules = options[:header_rules] || [] # Allow for legacy :cache_control option while prioritizing global header_rules setting @header_rules.insert(0, [:all, {'Cache-Control' => options[:cache_control]}]) if options[:cache_control] @headers = {} @file_server = Rack::File.new(root, @headers) end def overwrite_file_path(path) @urls.kind_of?(Hash) && @urls.key?(path) || @index && path =~ /\/$/ end def route_file(path) @urls.kind_of?(Array) && @urls.any? { |url| path.index(url) == 0 } end def can_serve(path) route_file(path) || overwrite_file_path(path) end def call(env) path = env["PATH_INFO"] if can_serve(path) env["PATH_INFO"] = (path =~ /\/$/ ? path + @index : @urls[path]) if overwrite_file_path(path) @path = env["PATH_INFO"] apply_header_rules @file_server.call(env) else @app.call(env) end end # Convert HTTP header rules to HTTP headers def apply_header_rules @header_rules.each do |rule, headers| apply_rule(rule, headers) end end def apply_rule(rule, headers) case rule when :all # All files set_headers(headers) when :fonts # Fonts Shortcut set_headers(headers) if @path.match(/\.(?:ttf|otf|eot|woff|svg)\z/) when String # Folder path = ::Rack::Utils.unescape(@path) set_headers(headers) if (path.start_with?(rule) || path.start_with?('/' + rule)) when Array # Extension/Extensions extensions = rule.join('|') set_headers(headers) if @path.match(/\.(#{extensions})\z/) when Regexp # Flexible Regexp set_headers(headers) if @path.match(rule) else end end def set_headers(headers) headers.each { |field, content| @headers[field] = content } end end end ruby-rack1.4-1.4.5/lib/rack/urlmap.rb000066400000000000000000000041101223351442100171470ustar00rootroot00000000000000module Rack # Rack::URLMap takes a hash mapping urls or paths to apps, and # dispatches accordingly. Support for HTTP/1.1 host names exists if # the URLs start with http:// or https://. # # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part # relevant for dispatch is in the SCRIPT_NAME, and the rest in the # PATH_INFO. This should be taken care of when you need to # reconstruct the URL in order to create links. # # URLMap dispatches in such a way that the longest paths are tried # first, since they are most specific. class URLMap NEGATIVE_INFINITY = -1.0 / 0.0 def initialize(map = {}) remap(map) end def remap(map) @mapping = map.map { |location, app| if location =~ %r{\Ahttps?://(.*?)(/.*)} host, location = $1, $2 else host = nil end unless location[0] == ?/ raise ArgumentError, "paths need to start with /" end location = location.chomp('/') match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n') [host, location, match, app] }.sort_by do |(host, location, _, _)| [host ? -host.size : NEGATIVE_INFINITY, -location.size] end end def call(env) path = env["PATH_INFO"] script_name = env['SCRIPT_NAME'] hHost = env['HTTP_HOST'] sName = env['SERVER_NAME'] sPort = env['SERVER_PORT'] @mapping.each do |host, location, match, app| unless hHost == host \ || sName == host \ || (!host && (hHost == sName || hHost == sName+':'+sPort)) next end next unless m = match.match(path.to_s) rest = m[1] next unless !rest || rest.empty? || rest[0] == ?/ env['SCRIPT_NAME'] = (script_name + location) env['PATH_INFO'] = rest return app.call(env) end [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]] ensure env['PATH_INFO'] = path env['SCRIPT_NAME'] = script_name end end end ruby-rack1.4-1.4.5/lib/rack/utils.rb000066400000000000000000000403411223351442100170150ustar00rootroot00000000000000# -*- encoding: binary -*- require 'fileutils' require 'set' require 'tempfile' require 'rack/multipart' major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i } if major == 1 && minor < 9 require 'rack/backports/uri/common_18' elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL <= 320 && RUBY_ENGINE != 'jruby' require 'rack/backports/uri/common_192' elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125 require 'rack/backports/uri/common_193' else require 'uri/common' end module Rack # Rack::Utils contains a grab-bag of useful methods for writing web # applications adopted from all kinds of Ruby libraries. module Utils # URI escapes. (CGI style space to +) def escape(s) URI.encode_www_form_component(s) end module_function :escape # Like URI escaping, but with %20 instead of +. Strictly speaking this is # true URI escaping. def escape_path(s) escape(s).gsub('+', '%20') end module_function :escape_path # Unescapes a URI escaped string with +encoding+. +encoding+ will be the # target encoding of the string returned, and it defaults to UTF-8 if defined?(::Encoding) def unescape(s, encoding = Encoding::UTF_8) URI.decode_www_form_component(s, encoding) end else def unescape(s, encoding = nil) URI.decode_www_form_component(s, encoding) end end module_function :unescape DEFAULT_SEP = /[&;] */n class << self attr_accessor :key_space_limit end # The default number of bytes to allow parameter keys to take up. # This helps prevent a rogue client from flooding a Request. self.key_space_limit = 65536 # Stolen from Mongrel, with some small modifications: # Parses a query string by breaking it up at the '&' # and ';' characters. You can also use this to parse # cookies by changing the characters used in the second # parameter (which defaults to '&;'). def parse_query(qs, d = nil, &unescaper) unescaper ||= method(:unescape) params = KeySpaceConstrainedParams.new (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| next if p.empty? k, v = p.split('=', 2).map(&unescaper) next unless k || v if cur = params[k] if cur.class == Array params[k] << v else params[k] = [cur, v] end else params[k] = v end end return params.to_params_hash end module_function :parse_query def parse_nested_query(qs, d = nil) params = KeySpaceConstrainedParams.new (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| k, v = p.split('=', 2).map { |s| unescape(s) } normalize_params(params, k, v) end return params.to_params_hash end module_function :parse_nested_query def normalize_params(params, name, v = nil) name =~ %r(\A[\[\]]*([^\[\]]+)\]*) k = $1 || '' after = $' || '' return if k.empty? if after == "" params[k] = v elsif after == "[]" params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) params[k] << v elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) child_key = $1 params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) if params_hash_type?(params[k].last) && !params[k].last.key?(child_key) normalize_params(params[k].last, child_key, v) else params[k] << normalize_params(params.class.new, child_key, v) end else params[k] ||= params.class.new raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k]) params[k] = normalize_params(params[k], after, v) end return params end module_function :normalize_params def params_hash_type?(obj) obj.kind_of?(KeySpaceConstrainedParams) || obj.kind_of?(Hash) end module_function :params_hash_type? def build_query(params) params.map { |k, v| if v.class == Array build_query(v.map { |x| [k, x] }) else v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}" end }.join("&") end module_function :build_query def build_nested_query(value, prefix = nil) case value when Array value.map { |v| build_nested_query(v, "#{prefix}[]") }.join("&") when Hash value.map { |k, v| build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) }.join("&") when String raise ArgumentError, "value must be a Hash" if prefix.nil? "#{prefix}=#{escape(value)}" else prefix end end module_function :build_nested_query ESCAPE_HTML = { "&" => "&", "<" => "<", ">" => ">", "'" => "'", '"' => """, "/" => "/" } if //.respond_to?(:encoding) ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys) else # On 1.8, there is a kcode = 'u' bug that allows for XSS otherwhise # TODO doesn't apply to jruby, so a better condition above might be preferable? ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n end # Escape ampersands, brackets and quotes to their HTML/XML entities. def escape_html(string) string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] } end module_function :escape_html def select_best_encoding(available_encodings, accept_encoding) # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html expanded_accept_encoding = accept_encoding.map { |m, q| if m == "*" (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } else [[m, q]] end }.inject([]) { |mem, list| mem + list } encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } unless encoding_candidates.include?("identity") encoding_candidates.push("identity") end expanded_accept_encoding.find_all { |m, q| q == 0.0 }.each { |m, _| encoding_candidates.delete(m) } return (encoding_candidates & available_encodings)[0] end module_function :select_best_encoding def set_cookie_header!(header, key, value) case value when Hash domain = "; domain=" + value[:domain] if value[:domain] path = "; path=" + value[:path] if value[:path] # According to RFC 2109, we need dashes here. # N.B.: cgi.rb uses spaces... expires = "; expires=" + rfc2822(value[:expires].clone.gmtime) if value[:expires] secure = "; secure" if value[:secure] httponly = "; HttpOnly" if value[:httponly] value = value[:value] end value = [value] unless Array === value cookie = escape(key) + "=" + value.map { |v| escape v }.join("&") + "#{domain}#{path}#{expires}#{secure}#{httponly}" case header["Set-Cookie"] when nil, '' header["Set-Cookie"] = cookie when String header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n") when Array header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n") end nil end module_function :set_cookie_header! def delete_cookie_header!(header, key, value = {}) case header["Set-Cookie"] when nil, '' cookies = [] when String cookies = header["Set-Cookie"].split("\n") when Array cookies = header["Set-Cookie"] end cookies.reject! { |cookie| if value[:domain] cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/ elsif value[:path] cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/ else cookie =~ /\A#{escape(key)}=/ end } header["Set-Cookie"] = cookies.join("\n") set_cookie_header!(header, key, {:value => '', :path => nil, :domain => nil, :expires => Time.at(0) }.merge(value)) nil end module_function :delete_cookie_header! # Return the bytesize of String; uses String#size under Ruby 1.8 and # String#bytesize under 1.9. if ''.respond_to?(:bytesize) def bytesize(string) string.bytesize end else def bytesize(string) string.size end end module_function :bytesize # Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead # of '% %b %Y'. # It assumes that the time is in GMT to comply to the RFC 2109. # # NOTE: I'm not sure the RFC says it requires GMT, but is ambigous enough # that I'm certain someone implemented only that option. # Do not use %a and %b from Time.strptime, it would use localized names for # weekday and month. # def rfc2822(time) wday = Time::RFC2822_DAY_NAME[time.wday] mon = Time::RFC2822_MONTH_NAME[time.mon - 1] time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT") end module_function :rfc2822 # Parses the "Range:" header, if present, into an array of Range objects. # Returns nil if the header is missing or syntactically invalid. # Returns an empty array if none of the ranges are satisfiable. def byte_ranges(env, size) # See http_range = env['HTTP_RANGE'] return nil unless http_range && http_range =~ /bytes=([^;]+)/ ranges = [] $1.split(/,\s*/).each do |range_spec| return nil unless range_spec =~ /(\d*)-(\d*)/ r0,r1 = $1, $2 if r0.empty? return nil if r1.empty? # suffix-byte-range-spec, represents trailing suffix of file r0 = size - r1.to_i r0 = 0 if r0 < 0 r1 = size - 1 else r0 = r0.to_i if r1.empty? r1 = size - 1 else r1 = r1.to_i return nil if r1 < r0 # backwards range is syntactically invalid r1 = size-1 if r1 >= size end end ranges << (r0..r1) if r0 <= r1 end ranges end module_function :byte_ranges # Constant time string comparison. def secure_compare(a, b) return false unless bytesize(a) == bytesize(b) l = a.unpack("C*") r, i = 0, -1 b.each_byte { |v| r |= v ^ l[i+=1] } r == 0 end module_function :secure_compare # Context allows the use of a compatible middleware at different points # in a request handling stack. A compatible middleware must define # #context which should take the arguments env and app. The first of which # would be the request environment. The second of which would be the rack # application that the request would be forwarded to. class Context attr_reader :for, :app def initialize(app_f, app_r) raise 'running context does not respond to #context' unless app_f.respond_to? :context @for, @app = app_f, app_r end def call(env) @for.context(env, @app) end def recontext(app) self.class.new(@for, app) end def context(env, app=@app) recontext(app).call(env) end end # A case-insensitive Hash that preserves the original case of a # header when set. class HeaderHash < Hash def self.new(hash={}) HeaderHash === hash ? hash : super(hash) end def initialize(hash={}) super() @names = {} hash.each { |k, v| self[k] = v } end def each super do |k, v| yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v) end end def to_hash hash = {} each { |k,v| hash[k] = v } hash end def [](k) super(k) || super(@names[k.downcase]) end def []=(k, v) canonical = k.downcase delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary @names[k] = @names[canonical] = k super k, v end def delete(k) canonical = k.downcase result = super @names.delete(canonical) @names.delete_if { |name,| name.downcase == canonical } result end def include?(k) @names.include?(k) || @names.include?(k.downcase) end alias_method :has_key?, :include? alias_method :member?, :include? alias_method :key?, :include? def merge!(other) other.each { |k, v| self[k] = v } self end def merge(other) hash = dup hash.merge! other end def replace(other) clear other.each { |k, v| self[k] = v } self end end class KeySpaceConstrainedParams def initialize(limit = Utils.key_space_limit) @limit = limit @size = 0 @params = {} end def [](key) @params[key] end def []=(key, value) @size += key.size if key && !@params.key?(key) raise RangeError, 'exceeded available parameter key space' if @size > @limit @params[key] = value end def key?(key) @params.key?(key) end def to_params_hash hash = @params hash.keys.each do |key| value = hash[key] if value.kind_of?(self.class) hash[key] = value.to_params_hash elsif value.kind_of?(Array) value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x} end end hash end end # Every standard HTTP code mapped to the appropriate message. # Generated with: # curl -s http://www.iana.org/assignments/http-status-codes | \ # ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and # puts " #{m[1]} => \x27#{m[2].strip}x27,"' HTTP_STATUS_CODES = { 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 226 => 'IM Used', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Reserved', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 418 => "I'm a Teapot", 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 426 => 'Upgrade Required', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 510 => 'Not Extended', } # Responses with HTTP status codes that should not have an entity body STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304) SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message| [message.downcase.gsub(/\s|-/, '_').to_sym, code] }.flatten] def status_code(status) if status.is_a?(Symbol) SYMBOL_TO_STATUS_CODE[status] || 500 else status.to_i end end module_function :status_code Multipart = Rack::Multipart end end ruby-rack1.4-1.4.5/metadata.yml000066400000000000000000000221451223351442100161470ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: rack version: !ruby/object:Gem::Version hash: 13 prerelease: segments: - 1 - 4 - 5 version: 1.4.5 platform: ruby authors: - Christian Neukirchen autorequire: bindir: bin cert_chain: [] date: 2013-02-08 00:00:00 Z dependencies: - !ruby/object:Gem::Dependency name: bacon prerelease: false requirement: &id001 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" type: :development version_requirements: *id001 - !ruby/object:Gem::Dependency name: rake prerelease: false requirement: &id002 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" type: :development version_requirements: *id002 - !ruby/object:Gem::Dependency name: ruby-fcgi prerelease: false requirement: &id003 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" type: :development version_requirements: *id003 - !ruby/object:Gem::Dependency name: memcache-client prerelease: false requirement: &id004 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" type: :development version_requirements: *id004 - !ruby/object:Gem::Dependency name: mongrel prerelease: false requirement: &id005 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3904189667 segments: - 1 - 2 - 0 - pre - 2 version: 1.2.0.pre2 type: :development version_requirements: *id005 - !ruby/object:Gem::Dependency name: thin prerelease: false requirement: &id006 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" type: :development version_requirements: *id006 description: | 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. Also see http://rack.github.com/. email: chneukirchen@gmail.com executables: - rackup extensions: [] extra_rdoc_files: - README.rdoc - KNOWN-ISSUES files: - bin/rackup - contrib/rack.png - contrib/rack.svg - contrib/rack_logo.svg - contrib/rdoc.css - example/lobster.ru - example/protectedlobster.rb - example/protectedlobster.ru - lib/rack/auth/abstract/handler.rb - lib/rack/auth/abstract/request.rb - lib/rack/auth/basic.rb - lib/rack/auth/digest/md5.rb - lib/rack/auth/digest/nonce.rb - lib/rack/auth/digest/params.rb - lib/rack/auth/digest/request.rb - lib/rack/backports/uri/common_18.rb - lib/rack/backports/uri/common_192.rb - lib/rack/backports/uri/common_193.rb - lib/rack/body_proxy.rb - lib/rack/builder.rb - lib/rack/cascade.rb - lib/rack/chunked.rb - lib/rack/commonlogger.rb - lib/rack/conditionalget.rb - lib/rack/config.rb - lib/rack/content_length.rb - lib/rack/content_type.rb - lib/rack/deflater.rb - lib/rack/directory.rb - lib/rack/etag.rb - lib/rack/file.rb - lib/rack/handler/cgi.rb - lib/rack/handler/evented_mongrel.rb - lib/rack/handler/fastcgi.rb - lib/rack/handler/lsws.rb - lib/rack/handler/mongrel.rb - lib/rack/handler/scgi.rb - lib/rack/handler/swiftiplied_mongrel.rb - lib/rack/handler/thin.rb - lib/rack/handler/webrick.rb - lib/rack/handler.rb - lib/rack/head.rb - lib/rack/lint.rb - lib/rack/lobster.rb - lib/rack/lock.rb - lib/rack/logger.rb - lib/rack/methodoverride.rb - lib/rack/mime.rb - lib/rack/mock.rb - lib/rack/multipart/generator.rb - lib/rack/multipart/parser.rb - lib/rack/multipart/uploaded_file.rb - lib/rack/multipart.rb - lib/rack/nulllogger.rb - lib/rack/recursive.rb - lib/rack/reloader.rb - lib/rack/request.rb - lib/rack/response.rb - lib/rack/rewindable_input.rb - lib/rack/runtime.rb - lib/rack/sendfile.rb - lib/rack/server.rb - lib/rack/session/abstract/id.rb - lib/rack/session/cookie.rb - lib/rack/session/memcache.rb - lib/rack/session/pool.rb - lib/rack/showexceptions.rb - lib/rack/showstatus.rb - lib/rack/static.rb - lib/rack/urlmap.rb - lib/rack/utils.rb - lib/rack.rb - test/builder/anything.rb - test/builder/comment.ru - test/builder/end.ru - test/builder/line.ru - test/builder/options.ru - test/cgi/assets/folder/test.js - test/cgi/assets/fonts/font.eot - test/cgi/assets/images/image.png - test/cgi/assets/index.html - test/cgi/assets/javascripts/app.js - test/cgi/assets/stylesheets/app.css - test/cgi/lighttpd.conf - test/cgi/rackup_stub.rb - test/cgi/sample_rackup.ru - test/cgi/test - test/cgi/test+directory/test+file - test/cgi/test.fcgi - test/cgi/test.ru - test/gemloader.rb - test/multipart/bad_robots - test/multipart/binary - test/multipart/content_type_and_no_filename - test/multipart/empty - test/multipart/fail_16384_nofile - test/multipart/file1.txt - test/multipart/filename_and_modification_param - test/multipart/filename_with_escaped_quotes - test/multipart/filename_with_escaped_quotes_and_modification_param - test/multipart/filename_with_percent_escaped_quotes - test/multipart/filename_with_unescaped_percentages - test/multipart/filename_with_unescaped_percentages2 - test/multipart/filename_with_unescaped_percentages3 - test/multipart/filename_with_unescaped_quotes - test/multipart/ie - test/multipart/mixed_files - test/multipart/nested - test/multipart/none - test/multipart/semicolon - test/multipart/text - test/multipart/webkit - test/rackup/config.ru - test/registering_handler/rack/handler/registering_myself.rb - test/spec_auth.rb - test/spec_auth_basic.rb - test/spec_auth_digest.rb - test/spec_body_proxy.rb - test/spec_builder.rb - test/spec_cascade.rb - test/spec_cgi.rb - test/spec_chunked.rb - test/spec_commonlogger.rb - test/spec_conditionalget.rb - test/spec_config.rb - test/spec_content_length.rb - test/spec_content_type.rb - test/spec_deflater.rb - test/spec_directory.rb - test/spec_etag.rb - test/spec_fastcgi.rb - test/spec_file.rb - test/spec_handler.rb - test/spec_head.rb - test/spec_lint.rb - test/spec_lobster.rb - test/spec_lock.rb - test/spec_logger.rb - test/spec_methodoverride.rb - test/spec_mock.rb - test/spec_mongrel.rb - test/spec_multipart.rb - test/spec_nulllogger.rb - test/spec_recursive.rb - test/spec_request.rb - test/spec_response.rb - test/spec_rewindable_input.rb - test/spec_runtime.rb - test/spec_sendfile.rb - test/spec_server.rb - test/spec_session_abstract_id.rb - test/spec_session_cookie.rb - test/spec_session_memcache.rb - test/spec_session_pool.rb - test/spec_showexceptions.rb - test/spec_showstatus.rb - test/spec_static.rb - test/spec_thin.rb - test/spec_urlmap.rb - test/spec_utils.rb - test/spec_webrick.rb - test/static/another/index.html - test/static/index.html - test/testrequest.rb - test/unregistered_handler/rack/handler/unregistered.rb - test/unregistered_handler/rack/handler/unregistered_long_one.rb - COPYING - KNOWN-ISSUES - rack.gemspec - Rakefile - README.rdoc - SPEC homepage: http://rack.github.com/ licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: rack rubygems_version: 1.8.24 signing_key: specification_version: 3 summary: a modular Ruby webserver interface test_files: - test/spec_auth.rb - test/spec_auth_basic.rb - test/spec_auth_digest.rb - test/spec_body_proxy.rb - test/spec_builder.rb - test/spec_cascade.rb - test/spec_cgi.rb - test/spec_chunked.rb - test/spec_commonlogger.rb - test/spec_conditionalget.rb - test/spec_config.rb - test/spec_content_length.rb - test/spec_content_type.rb - test/spec_deflater.rb - test/spec_directory.rb - test/spec_etag.rb - test/spec_fastcgi.rb - test/spec_file.rb - test/spec_handler.rb - test/spec_head.rb - test/spec_lint.rb - test/spec_lobster.rb - test/spec_lock.rb - test/spec_logger.rb - test/spec_methodoverride.rb - test/spec_mock.rb - test/spec_mongrel.rb - test/spec_multipart.rb - test/spec_nulllogger.rb - test/spec_recursive.rb - test/spec_request.rb - test/spec_response.rb - test/spec_rewindable_input.rb - test/spec_runtime.rb - test/spec_sendfile.rb - test/spec_server.rb - test/spec_session_abstract_id.rb - test/spec_session_cookie.rb - test/spec_session_memcache.rb - test/spec_session_pool.rb - test/spec_showexceptions.rb - test/spec_showstatus.rb - test/spec_static.rb - test/spec_thin.rb - test/spec_urlmap.rb - test/spec_utils.rb - test/spec_webrick.rb has_rdoc: ruby-rack1.4-1.4.5/rack.gemspec000066400000000000000000000025241223351442100161300ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = "rack" s.version = "1.4.5" s.platform = Gem::Platform::RUBY s.summary = "a modular Ruby webserver interface" s.description = <<-EOF 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. Also see http://rack.github.com/. EOF s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] + %w(COPYING KNOWN-ISSUES rack.gemspec Rakefile README.rdoc SPEC) s.bindir = 'bin' s.executables << 'rackup' s.require_path = 'lib' s.extra_rdoc_files = ['README.rdoc', 'KNOWN-ISSUES'] s.test_files = Dir['test/spec_*.rb'] s.author = 'Christian Neukirchen' s.email = 'chneukirchen@gmail.com' s.homepage = 'http://rack.github.com/' s.rubyforge_project = 'rack' s.add_development_dependency 'bacon' s.add_development_dependency 'rake' s.add_development_dependency 'ruby-fcgi' s.add_development_dependency 'memcache-client' s.add_development_dependency 'mongrel', '>= 1.2.0.pre2' s.add_development_dependency 'thin' end ruby-rack1.4-1.4.5/test/000077500000000000000000000000001223351442100146175ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/builder/000077500000000000000000000000001223351442100162455ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/builder/anything.rb000066400000000000000000000001421223351442100204100ustar00rootroot00000000000000class Anything def self.call(env) [200, {'Content-Type' => 'text/plain'}, ['OK']] end end ruby-rack1.4-1.4.5/test/builder/comment.ru000066400000000000000000000001221223351442100202520ustar00rootroot00000000000000=begin =end run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] } ruby-rack1.4-1.4.5/test/builder/end.ru000066400000000000000000000001711223351442100173620ustar00rootroot00000000000000run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] } __END__ Should not be evaluated Neither should This ruby-rack1.4-1.4.5/test/builder/line.ru000066400000000000000000000001151223351442100175410ustar00rootroot00000000000000run lambda{ |env| [200, {'Content-Type' => 'text/plain'}, [__LINE__.to_s]] } ruby-rack1.4-1.4.5/test/builder/options.ru000066400000000000000000000001131223351442100203030ustar00rootroot00000000000000#\ -d run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] } ruby-rack1.4-1.4.5/test/cgi/000077500000000000000000000000001223351442100153615ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/000077500000000000000000000000001223351442100166635ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/folder/000077500000000000000000000000001223351442100201365ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/folder/test.js000066400000000000000000000000211223351442100214440ustar00rootroot00000000000000### TestFile ### ruby-rack1.4-1.4.5/test/cgi/assets/fonts/000077500000000000000000000000001223351442100200145ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/fonts/font.eot000066400000000000000000000000211223351442100214640ustar00rootroot00000000000000### TestFile ### ruby-rack1.4-1.4.5/test/cgi/assets/images/000077500000000000000000000000001223351442100201305ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/images/image.png000066400000000000000000000000211223351442100217110ustar00rootroot00000000000000### TestFile ### ruby-rack1.4-1.4.5/test/cgi/assets/index.html000066400000000000000000000000211223351442100206510ustar00rootroot00000000000000### TestFile ### ruby-rack1.4-1.4.5/test/cgi/assets/javascripts/000077500000000000000000000000001223351442100212145ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/javascripts/app.js000066400000000000000000000000211223351442100223230ustar00rootroot00000000000000### TestFile ### ruby-rack1.4-1.4.5/test/cgi/assets/stylesheets/000077500000000000000000000000001223351442100212375ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/assets/stylesheets/app.css000066400000000000000000000000211223351442100225220ustar00rootroot00000000000000### TestFile ### ruby-rack1.4-1.4.5/test/cgi/lighttpd.conf000077500000000000000000000016711223351442100200570ustar00rootroot00000000000000server.modules = ("mod_fastcgi", "mod_cgi") server.document-root = "." server.errorlog = var.CWD + "/lighttpd.errors" server.port = 9203 server.bind = "127.0.0.1" 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" => CWD + "/rackup_stub.rb test.ru")), "sample_rackup.ru" => ("localhost" => ("min-procs" => 1, "socket" => "/tmp/rack-test-rackup-fcgi", "bin-path" => CWD + "/rackup_stub.rb sample_rackup.ru")), ) ruby-rack1.4-1.4.5/test/cgi/rackup_stub.rb000077500000000000000000000001351223351442100202320ustar00rootroot00000000000000#!/usr/bin/env ruby # -*- ruby -*- $:.unshift '../../lib' require 'rack' Rack::Server.start ruby-rack1.4-1.4.5/test/cgi/sample_rackup.ru000077500000000000000000000001161223351442100205600ustar00rootroot00000000000000# -*- ruby -*- require '../testrequest' run Rack::Lint.new(TestRequest.new) ruby-rack1.4-1.4.5/test/cgi/test000077500000000000000000000003011223351442100162600ustar00rootroot00000000000000#!/usr/bin/env ruby # -*- ruby -*- $: << File.join(File.dirname(__FILE__), "..", "..", "lib") require 'rack' require '../testrequest' Rack::Handler::CGI.run(Rack::Lint.new(TestRequest.new)) ruby-rack1.4-1.4.5/test/cgi/test+directory/000077500000000000000000000000001223351442100203405ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/cgi/test+directory/test+file000066400000000000000000000000271223351442100221540ustar00rootroot00000000000000this file has plusses! ruby-rack1.4-1.4.5/test/cgi/test.fcgi000077500000000000000000000002401223351442100171710ustar00rootroot00000000000000#!/usr/bin/env ruby # -*- ruby -*- $:.unshift '../../lib' require 'rack' require '../testrequest' Rack::Handler::FastCGI.run(Rack::Lint.new(TestRequest.new)) ruby-rack1.4-1.4.5/test/cgi/test.ru000077500000000000000000000001401223351442100167060ustar00rootroot00000000000000#!../../bin/rackup # -*- ruby -*- require '../testrequest' run Rack::Lint.new(TestRequest.new) ruby-rack1.4-1.4.5/test/gemloader.rb000066400000000000000000000004521223351442100171040ustar00rootroot00000000000000require 'rubygems' project = 'rack' gemspec = File.expand_path("#{project}.gemspec", Dir.pwd) Gem::Specification.load(gemspec).dependencies.each do |dep| begin gem dep.name, *dep.requirement.as_list rescue Gem::LoadError warn "Cannot load #{dep.name} #{dep.requirement.to_s}" end end ruby-rack1.4-1.4.5/test/multipart/000077500000000000000000000000001223351442100166405ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/multipart/bad_robots000066400000000000000000000425221223351442100207060ustar00rootroot00000000000000--1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="bbbbbbbbbbbbbbb" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaa --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="ccccccc" ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="file.name" INPUTMSG.gz --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="file.content_type" application/octet-stream --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="file.path" /var/tmp/uploads/4/0001728414 --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="file.md5" aa73198feb4b4c1c3186f5e7466cbbcc --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="file.size" 13212 --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="size" 80892 --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="mail_server_id" <1111111111.22222222.3333333333333.JavaMail.app@ffff-aaaa.dddd> --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="addresses" {"campsy_programmer@pinkedum.com":{"domain":"pinkedum.com","name":"Campsy Programmer","type":["env_sender"],"mailbox":"campsy_programmer"},"tex@rapidcity.com":{"domain":"rapidcity.com","name":"Big Tex","type":["env_recipients","to"],"mailbox":"tex"},"group-digests@linkedin.com":{"domain":"linkedin.com","name":"Group Members","type":["from"],"mailbox":"group-digests"}} --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="received_on" 2009-11-15T14:21:11Z --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="id" dbfd9804d26d11deab24e3037639bf77 --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon Content-Disposition: form-data; name="ip_address" 127.0.0.1 --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon-- ruby-rack1.4-1.4.5/test/multipart/binary000066400000000000000000000640531223351442100200570ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files"; filename="rack-logo.png" Content-Type: image/png PNG  IHDRbKGD pHYsHHFk>fIDATxy|uߓ)fr$mR" iJz @QAA@Et9@T䐴E@( BiZp=6M L&mi&# !陒|G d^>?q@ Dgz A D@@ .@ ] @ @t $ H@% K A D@@ .@ ] @ @t $ H@% KDz NR)H$P @ $ ~p: (..FM~~8iɧBI$k %I q3=xFh[PPplNiz$qry@ 8|*99P(+ a"[T.s$IB3ɗ]. pO H111nlillj&,;~- X,$Hq$ɓ'''_ jz)>1 tQ4d2YN;222( {KK nD!pRZZ:t͋|Qq[T$Q9ȋ D@(z oK#(~FR rSEE0 #6 r/QrFY֡^lbʋ"99&HTDOkK4nh/*xQQ:>`e%Յ1G+м‹IPx*99]Z@zP4L:0XE%KT<]S[pI$>JUrr2%P:1,a' >iK4zE%eZ*eم9W-*XQh!H$HTD[ 1.VPF{2a`4Mi:d&m@ \.7>"IҌ~!_B& H4:EQ@tTiitm0F1 34M+USx B 7T@|DCB ].ac`͋-4M_W/F}" v+,,ThQTzEE%4RAt ;W}E_DߣGr޽{]0HpWowU4|nZh^T ,--΋haF2ܢ=}l}4ɱcp}ՈDDQ)((XY__?#Z0k֬ӦM+W*Ajz5^B*--MTihQ!UWުT*^uzT@-[|/`Pqzꩊ 333/8ތ*/**++QVVQh2\죡?:=- ._ߝqF.[p $ *H&$,]O'}]T233_۲e;"(E ]˗ \OKtF.KA$I{ bh!lƍ'~AjRjG]!.wbe%narZqX)"e V[j,:R CQR___V]]u7D/'$x]dddB+EIJb(7999.hw{ReJV[j+NgVfl6G 3РcYV)ZD@rn\'"Jr%KV[,~`H`f8˲I^==4>}M os5/,ZKXZ慥5!,((H0xш Wk4.:::SF  $ajyx…l$0"0aaxaIdY6j*Y zOT-!MrGq^rY*煥BVR%, K4F㽼hcYV ] 84K,˦A #gDD@Kvvvq:.\, a $ $R5i-`BCD4W{(J/x KP,2$,hIFQϋeYt]48/h4??o֦{@t4 '/, 鄅 z*&Ih2M&S"m60-,e iZFt"L &JKVrC!*((#ruMiiizT*%HgD/&TYX]yZ$Y Efq0LR4ᥥ.+j玁{H4 naRi,[!.$Itw@^4T^44,tsMb\RHOO߫K'A@QzB"D"q$II[0\uXJ[xxEO@^qͭkY*jYΓ$y٫>!)((ŋĆhtgnWbqJ:^Nd2C@QO|;||e_@=[rUXXX,Noa1{IVzI^Dne4'1eA=`qdGSRR̟?(%%R&]ek@ }^@NJ4egg[gDž+WÇ>HpD }Re%8-,ڈJ9jeف=p,Ntz*X͍(&E !@WL&.J%K^U $`P= aYVbXTq͜f֬Y{g͚}\\-^u ms0)==*>>++x7X"_hZz.UbqJ*LOOߣt2F -F@ձ+p%;;2s:|ad/,p/anZ^i愄SNݣt:L&shk@$Pxoy #=p_*s8&i8|@p f|qL&;©SV$$$xE(@Q6mZ +]'_LxH i_p82XT*Wk@ Ht,$I$j8 K^XKKK:wL \nٵkѣGšZ~  iY ;Be$+Ng_0(//OVSS3⣏>z qܩRG[k@ Hʱ{Wyȑ}@ ɭfa~qV pzp83 #}Z0 n!}$!sau@7poo@ 1aY- $O@‘x!У.,76o|ׇ wp@z@  zXkxYR_VVvV\ܭ-b,%%p%%%#].h:eY)[˲"^T *yQH8 @t>% ^VFlQQXʸhk,6\~V"8jh4fXg \1tdii(5j놨X*xTZp"\Dž@ :HvaXI^ -y{VF oeh4MMM=qZ"\.{BQTRZZ:tDCDE wy?RGT)?dž@ A@x+#dLQ.Hfdd|C_Qɧ(*a1/*#xQEQ+|r5EQ 1,@!ᭌ񼕑[BhqN&tG322N2 /*NVT D%T_ N 箃eYaίч K PeX,.WTG322kȨQT2Q9%*v,6EK]] f7@LF儅V2{eLii['t:FsDќtd2YЭ`%*ٍ^GT_Eq\ݻ޽w]SaQAiD%$., \._ʸZͲvX,>[GbccK222Cee?r'PȲ{C˲bQx+*Ӽ%ɕn%ra!}X Fqĉ'9v>nWM[Um p8D6msMwwDT;JgE G ;x ~bgYI$ JUme0^͛W74}p?/0:+k'* GIYC^\ ^lҤI愄V 7i\^H*~W1 Ì`f`0(4 ]*? Z*^ic(@BDoQKK#G8rH}5Msss-~JV[+Mm +""`- ~ĉo6>PvpHD^N(\X3p4'ڔmykiii?֊eY1@ #@ʲb P\^^^ bEDk#I"oҭ,,^4"F] 4MZAmBBwSNݫtjL#ч {V0jA$I$Yt:㭕 A0L'hOQhz˲Q@t MӣrrrXpS~OA$kS̋8h4L2i|A8_d%:n^/:u깄fo10je@Qz Zi8t:_ƪIb ?bH$P᭕@ueY1 p[+$9?NDvx)>>lRmmqo`OOOO?RAB~ ѧ\X ""bcc9&ڵk_t ;w3 3;ث E^A/ԁ|A$ ү`4/D^^ޭF~>~X1qqqg͚o֬Y5vQ4z5@ :M v-Uv@}.+DV,{+i,6-eYAt ,өS/^gP]c:A&uǽV(aUN0 f7;(} D&ZVe ")j:l6xx'>WbqJ:ċw:"ɜA C@Qz ^QBC?;7\997:gƔ 2J :GA.ֈ\gtnDzweS 2UTM!*V(h ɂHǛd2Y)vMzz*b DY );:Q91~|5vzfXuh$ !HgWY]]}z֭' 87TK!m"6M{ +AMcC ݤ'wtF,KwNV[- (JgX-0#XMZ*z~jn]rrr(T-`4Miz _,ǯ H$-:V;EQ'DD8\acz8WXX3􍛧x۶mEDD۸q:$Zb+{} #n_|qݻAP(YSL{NOz1βhKӴd^2bqF9z$##D"i.?O-Be$+0EQ`"p'L&d2y 0@0e١VUgDAqHXX,8 &pRyl|WȰl?pM)G]XXx$@oxw_ܳg"p_} ///)//M !,XG-^gVF+cD 2NK$Xb+mD 6$m$IaBa gf(0 0a,;jƱ,+⻉7siV4=,**[o5!**$F |*j˟Rilláw`5hqa?)iiigOwgP^^.\x"aA83cƌw,XPy a/ ^VF+#:nei4 +#(w=;;~t:pBe  @>V iZ nai+^c418N暽a$I^䅥#\ry_q$:Hd8΁a.3 2aTEiiN*JJJ|6rHؼlCN]]k3˟_OVXR yHYX>V&??.줕q],C,7t]F{DDD@\\nwTSvvvfq\h2$&I`2z P[X~Ͳlb :iǞ8K_RgxX@nmEďiiio222~+ɯ p\?V-q'M}+O,ǭ`[-W I$I^#༰L&S,iӦ;,cй,ˊ-K,pM!/Ix׳hZ+ ~ߓOח]DmΓhUU)))jܶm[.H8hankFRRRBJsllo5kVP(lqu` `&*??hL-..~0܀\AAuֽ#pr>QmB@8 \._UVVeeZ, 2|c!2 /,,$I$IqF< 'H~&gY֓vܖ;"iz$մp M*Vy׳,[).$ygD/#FX;~xL&cТT*+Vl>~g8$`mXc'MI&5V8p9g)5ꫯu:]M0`\p%Xv3]T5cƌ-X=5L(\XhԞ8qbci:VXF ͝;?Oر#nxGïimY4yT뉳L:eEu):\h$ ,Z-KT18 <#ƕ+W>]tv;xv> Dr>|nRRRSG>?ѯ |[֭[7Onm+VlQ*={],/ YYYSC=vV˔H~ܖp2']Zvq-B’H$Xll, rv@+ۙ quSO+|F,5҅Vh`lxoŕ*0==}ĉAFFbp@ӧ$%%uzݒU8t¯=BE[w()++i\Aeر566vUݻ]n]eˎz^JZ*eJ3< ^vp΂~gJ+,@O@v;VXX8h4jyѸS &$$|7uԯz!NW-ɜP]]-q<\[A &ЕvذaCz}}'{@А E"Qq oL\\\[ol6wЗF>!m0÷$hᄈΝvǎC@[nQJJm[8L;zO@a:΂  //||~gZ|sqf4'>0ow\d2I}L:wx̙?kJEGGsg'k: tV\\0eYOk{m,[gwdڎs ޜFDqqqhw׮]+**v\q:h*@zz^_ґ_i.ux+m`JIIVpLjTL ֭[m3f\MtBKKK[-xq oCyyaы/~1%%nYI3PN8@%v1MpIԩSc-G@ZZ \(*;zV $NƚXu]t [hq|ZVX\RNF;hqv(++~ Ç3 38X$c 78η233+rx%e8[+͛]揤ٳg]|D]x7nE_ b1T*}塇^LP߳e'ϴgϞ1۷oz2Mӣ~28%%LIIdWn HWillʺ]ORRRRWWo>>5xyp1a„uap~O=3M8yh4u._ DF/4tҿ@ ٳK6oiyy+}KXr^^ޱ3gV"WPea+6sq5&& h;; _GǸqʋd2o\qL&^yT*|td^D޽{~VVVٚ5k)JomڴIaÆ{iiiִmyyy:NaۍFcT*=qܙd0 b|=_7ܠsȾ=3Smw0v RRRjw_sa @h4Oemg\oOKߵw_⾙5kYf܃Yc}N@pwqeq8?8EQ;wt\#bG"ΝaaHѰ0<1///+(//͛,[l_ 퐀"@ {U㷬%oWbyJU5t:Lv#vkm@ 'y޽ݻw7ï/=IKK|?Ѕ h~QXX8aA8bݺu)))'C=P\Xa + QSߑUpdƔ=/JIId7]Ku+@tyrOH$~{OIIɿBÞ>s-tc_k׮V]yӂ\XңTSu+\4iV^^T3qsaL> 6,//^O\bb+VX__?·dPYA8dݻw_n]ѲeˊC=PMz"$O~||<3r쟖-[VbŊ^1LOΝ;Up b1bDIIJJj={z0pL3.YfB!oIIIٳ?a֭{ 佌BIO Hsa1 #`Y!"~\~llDBC5 mfGf̘ xWq~׿XƎa<#? z补ٳg'&&n ]{6lxn] 9 CbjƲ$I{^[Cʤ[(Jv͚5f̘Tw'脄3fh! #Rl%K裏f.\ms m}=A6X xOzO}AL 0moKgϞjS^ ºkܾ  $3..;Z1 9ŕ85R*׿6tc&eHeLv"%%e߼y 'Nvɔ .A^LNN 333RTo?,s8$%%9^~[zvlqxjjrVKW?: 4?D :g_~=Pk왙3guEE1o޼g- 5}΅ŋFbhL4V d2YN;jdj2!,Z =9ڪm6a\T2 uv>y։8F*6I$p:pň(%(b9(v\+\[.`$~?blq g%9G.]:ܭ9?Z}JlP]]=`0XpS#GX,_X,>R>x6''֓'O m65ǾpႠ8u|ʕ,Y˗!33sY\\b/Pάl֟ .8^kR;n,Ze˖=xb]555ѵ^ziǺtԞ:R ?c^t{ժU111+w䌘cƌF_KLL\P()}111_̚5kEQkjj3F3l6>cJjڹfYHBrCILL\P(v* 駟j<㭪k|g_z'lR({ Ŷ?޽;e6UQQQC=\EPQ(.3.>>scbby,Kx=wqDz>h)S)/]n|/^B_UU%gL&{oժU 4 [l\P|P(|F][[ItIJe>| EB#˷8'111O?[,?z'OܻwovRR#й:vϙ3gX,7mA>==(Jʟϡ ~zX,= b7nܸF$v?Ͽ{겲64yǗ/_d-[l&!`J9sD)LEر֑#G7nvh-..~/xwї^zogϞ}… >*--H$2lv;,\p֭[:thٳ#HZ=:a֭,;r͚5P*EIV]v W*zj._|QFٗ-[VdX"|7i9gΜ'*)RL&Gv A d$ȑA]4N<)gB*pTo?9r… ۵kPүG f:ulʕ×,Y_|7VhpW*wj פ,_ŋ߹k׮555"f3},Z>glUK.uqx˖-`(`GN먮.Zr8eҥxIT*7GEEK., dff.0`@і-[s,bm59bɓ':W555޲'KsXv˖-}k׮{&113gΈ|ބgΜ@.]z'\ti2q#϶n*|;N&O>-歞z,*BP,^-ku8]v >^,`Kv4)nߩS ޤbB?禰@xKCZPP0h4ꋋSư,c8bJ:^M:N&uG-<>;є0h4h4~M?6swO9@^] w @PJwsRyeĉ}?~|qjJKKQ(G׿*Jp}cǎ}m۶L6Nh/X(`׮]0 [o=77w8aHaŋ/]߿ߒHl)))yeفp+9s ] ҶmۦM0$33H$Bp$IZredogfssssM&Sn?7ŋSSSsv#G<l!I ݑoo|G}ǿ(jAN~f͚uJϟ-[͛oqrRI' q<"))q8y9_hYY$s-اOyRR|nycΝ]@%%%V-K.裏6x=""{9JBQTUUE EE`Vp8T|SFﴬL;gΜnRt͟?ˢ9[l!">>ޤwfI||q͛uĉվV)NWT\\|lokwTA$Iҟ~iq|p;>wm'N:o޼'NX aNgOt:Qedd(H.rWFGjR $I^&I<Z  0CM6=hXC%+`4|mK-WHte Z~jaS?ApgoMq\QZT*U 劊^_XPPX^^ވiӦ|ѻ~III &,...d2E^A~ iii{Z˗/~8p;(ׯ_K<״~}X.<@[{l >x<8~}ݺu=%"!sa\.Zd>ݶ#5Nd2qNWT*U x`ή999ptRTt:YVW t eف%R=Ǎm_J͛7wK@Dz2Qcc~f@PW_/\|,OV,+0 $V\iol8^-+W|Pʻ ###A(^'xb%K6.9hl"H'N\W /۷GFu$ʕ+ E.^8D̳n~{/EQŋzH;vlcdd'[d6;P _}wdYV)Hp[sΝ{tʔ)oqVZ5kb0|AW_ɳeFAum]8. ӧWA|(9t r??fff֋D"lݺu&q3\.࿳TVV&2 #dYvHDDD5kv]Oĉ?+xWرo>T$;zp8ݻ9sNhx8~VR}U4: Ans/9(6 v`ǯpbن4=?O4MyX-Njl6po:K0j8ڹs<))jÇr GOю,**J9sfUWW'8vX4jݿ=TZhQǏnTVV y$ IҚsn+V:mD"cݺu eUYY)*>NjN53I?\-_|s=(++#FCMtiiig0 s655^|ŗiii<^v\~&55@cPRRvnٲM6ܙk&++k?ȑ#[6o|'|bBBžӧ{{n2+++V@gTTTdΜ9j#ӧO~[YYYwTRRB==""܎;ƧOMMXRR"]fj/^".qXJ 8:PxpΜ9_O6;FS- [{2t%:kV$)>kXc-RP4+?j(u IIIxǏOb;nbb 55̫x޼y NSt̙3sV^ErssZ,&L0SnnŒ8a„D"^z ÇOV)q8Qo[ppuEW)))k^jU 4M/ohk!H977be„ E>=b̈́ {`ӧOK _,;N z뭿qw)99yfS͛7SPxiH$2Y)SSO=uرcޱcG֝wP---رc+Ϗ~{ Lcc#;& RRR>ݷo¢_tI1cƌWk ޞ/jKa5kr>z޽v\/^W|ӹf͚|A޽{hѢN5Ryeƍ̜9s?XE~饗^;vlcBBBmV3~S'On}_+SRRvDE7Vmزe˞F )=|֯_ՙ3g.XHtzΜ9Jlĉ9uW:9m7)ّ뢙o8t1 =X 2^iQ}Y-Ojĝj罬V^hzd?21>0mv18nd?y>4iҹ!55uǯUwĉb$s`ĴѵvWPb`j9UyV˲e|p}eҥ-[vAb4/..~a,B3Bρ[O5k־Yf]H$yc" yJ$i|ʼF̞hmW0Z~ b-ZvM|e"r׮] Fqbqq񤆆q,*TbBR:^&2cW6_vݽ6 _c- XpEkqvmT vTQQqRAn*[^4t571-a V_Tb ۮr+R!MCOx aЉ *pd))){ϟ0%%R&]A@<_{/ZD ⭖Quk0 s577þ}dFQA5A%LV鿠2 ~e&NZ"(rjYl˗u'N\(JI|:AA?}R($%=bfXD")?p@h|ܽrDе _T*>q<--H$/q 7Y-t :N9" Hs3 oNWFT  "jpgPYe2YN۫N:2!!ųY@t$ 7[-.~xO~A@ ӃV'' QH@˜V"w]OX,ʠ*CT'@ ` 0LrjZyA@ z$ 72@ }$ H@% K A D@@ .@ ] @ ws\tEXtSoftwarewww.inkscape.org<IENDB` --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/content_type_and_no_filename000066400000000000000000000001731223351442100244550ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="text" Content-Type: text/plain; charset=US-ASCII contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/empty000066400000000000000000000002771223351442100177270ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files"; filename="file1.txt" Content-Type: text/plain --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/fail_16384_nofile000066400000000000000000000651721223351442100216120ustar00rootroot00000000000000------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="_method" put ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="authenticity_token" XCUgSyYsZ+iHQunq/yCSKFzjeVmsXV/WcphHQ0J+05I= ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[SESE]" BooBar ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[BBBBBBBBB]" 18 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[CCCCCCCCCCCCCCCCCCC]" 0 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[STARTFOO]" 2009-11-04 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[ENDFOO]" 2009-12-01 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[DDDDDDDD]" 0 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[DDDDDDDD]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[EEEEEEEEEE]" 10000 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[FFFFFFFFF]" boskoizcool ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[GGGGGGGGGGG]" 0 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[GGGGGGGGGGG]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[YYYYYYYYYYYYYYY]" 5.00 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[ZZZZZZZZZZZZZ]" mille ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[XXXXXXXXXXXXXXXXXXXXX]" 0 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][1][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][2][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][3][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][4][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][5][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][6][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][9]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][10]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][11]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][12]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][13]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][14]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][15]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][16]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][17]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][18]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][19]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][20]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][21]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][22]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][23]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][0]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][1]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][2]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][3]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][4]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][5]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][6]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][7]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[VVVVVVVVVVVVVVVVVVVVVVV][0][8]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[WWWWWWWWWWWWWWWWWWWWWWWWW][678][ZEZE]" PLAPLAPLAINCINCINC ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[WWWWWWWWWWWWWWWWWWWWWWWWW][678][123412341234e]" SITE ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[WWWWWWWWWWWWWWWWWWWWWWWWW][678][12345678901]" 56 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_type]" none ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][has_hashashas_has]" 0 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][frefrefre_fre_freee]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][frefrefre_fre_frefre]" forever ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][self_block]" 0 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][GGG_RULES][][COUCOUN]" ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][GGG_RULES][][REGREG]" ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_wizard][GGG_RULES][][c1c1]" ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA_TARTARTAR_wizard_rule" ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[TARTARTAR_rule]" ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[selection_selection]" R ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[PLAPLAPLA_MEMMEMMEMM_ATTRATTRER][new][-1][selection_selection]" 1 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[PLAPLAPLA_MEMMEMMEMM_ATTRATTRER][new][-1][ba_unit_id]" 1015 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[PLAPLAPLA_MEMMEMMEMM_ATTRATTRER][new][-2][selection_selection]" 2 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[PLAPLAPLA_MEMMEMMEMM_ATTRATTRER][new][-2][ba_unit_id]" 1017 ------WebKitFormBoundaryWsY0GnpbI5U7ztzo Content-Disposition: form-data; name="AAAAAAAAAAAAAAAAAAA[tile_name]" ------WebKitFormBoundaryWsY0GnpbI5U7ztzo-- ruby-rack1.4-1.4.5/test/multipart/file1.txt000066400000000000000000000000101223351442100203700ustar00rootroot00000000000000contentsruby-rack1.4-1.4.5/test/multipart/filename_and_modification_param000066400000000000000000000003601223351442100250710ustar00rootroot00000000000000--AaB03x Content-Type: image/jpeg Content-Disposition: attachment; name="files"; filename=genome.jpeg; modification-date="Wed, 12 Feb 1997 16:29:51 -0500"; Content-Description: a complete map of the human genome contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/filename_with_escaped_quotes000066400000000000000000000002241223351442100244600ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="files"; filename="escape \"quotes" Content-Type: application/octet-stream contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/filename_with_escaped_quotes_and_modification_param000066400000000000000000000003721223351442100312130ustar00rootroot00000000000000--AaB03x Content-Type: image/jpeg Content-Disposition: attachment; name="files"; filename=""human" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500"; Content-Description: a complete map of the human genome contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/filename_with_percent_escaped_quotes000066400000000000000000000002251223351442100262010ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="files"; filename="escape %22quotes" Content-Type: application/octet-stream contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/filename_with_unescaped_percentages000066400000000000000000000003321223351442100260030ustar00rootroot00000000000000------WebKitFormBoundary2NHc7OhsgU68l3Al Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg" Content-Type: image/jpeg contents ------WebKitFormBoundary2NHc7OhsgU68l3Al-- ruby-rack1.4-1.4.5/test/multipart/filename_with_unescaped_percentages2000066400000000000000000000003131223351442100260640ustar00rootroot00000000000000------WebKitFormBoundary2NHc7OhsgU68l3Al Content-Disposition: form-data; name="document[attachment]"; filename="100%a" Content-Type: image/jpeg contents ------WebKitFormBoundary2NHc7OhsgU68l3Al-- ruby-rack1.4-1.4.5/test/multipart/filename_with_unescaped_percentages3000066400000000000000000000003121223351442100260640ustar00rootroot00000000000000------WebKitFormBoundary2NHc7OhsgU68l3Al Content-Disposition: form-data; name="document[attachment]"; filename="100%" Content-Type: image/jpeg contents ------WebKitFormBoundary2NHc7OhsgU68l3Al-- ruby-rack1.4-1.4.5/test/multipart/filename_with_unescaped_quotes000066400000000000000000000002231223351442100250220ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="files"; filename="escape "quotes" Content-Type: application/octet-stream contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/ie000066400000000000000000000002601223351442100171560ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="files"; filename="C:\Documents and Settings\Administrator\Desktop\file1.txt" Content-Type: text/plain contents --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/mixed_files000066400000000000000000000006611223351442100210560ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="foo" bar --AaB03x Content-Disposition: form-data; name="files" Content-Type: multipart/mixed, boundary=BbC04y --BbC04y Content-Disposition: attachment; filename="file.txt" Content-Type: text/plain contents --BbC04y Content-Disposition: attachment; filename="flowers.jpg" Content-Type: image/jpeg Content-Transfer-Encoding: binary contents --BbC04y-- --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/nested000066400000000000000000000003211223351442100200410ustar00rootroot00000000000000--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-- ruby-rack1.4-1.4.5/test/multipart/none000066400000000000000000000002341223351442100175210ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files"; filename="" --AaB03x-- ruby-rack1.4-1.4.5/test/multipart/semicolon000066400000000000000000000001771223351442100205600ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="files"; filename="fi;le1.txt" Content-Type: text/plain contents --AaB03x--ruby-rack1.4-1.4.5/test/multipart/text000066400000000000000000000004631223351442100175520ustar00rootroot00000000000000--AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="submit-name-with-content" Content-Type: text/plain Berry --AaB03x Content-Disposition: form-data; name="files"; filename="file1.txt" Content-Type: text/plain contents --AaB03x--ruby-rack1.4-1.4.5/test/multipart/webkit000066400000000000000000000014531223351442100200530ustar00rootroot00000000000000------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="_method" put ------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="profile[blog]" ------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="profile[public_email]" ------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="profile[interests]" ------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="profile[bio]" hello "quote" ------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="media"; filename="" Content-Type: application/octet-stream ------WebKitFormBoundaryWLHCs9qmcJJoyjKR Content-Disposition: form-data; name="commit" Save ------WebKitFormBoundaryWLHCs9qmcJJoyjKR-- ruby-rack1.4-1.4.5/test/rackup/000077500000000000000000000000001223351442100161045ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/rackup/config.ru000066400000000000000000000015131223351442100177210ustar00rootroot00000000000000require "#{File.dirname(__FILE__)}/../testrequest" $stderr = File.open("#{File.dirname(__FILE__)}/log_output", "w") class EnvMiddleware def initialize(app) @app = app end def call(env) # provides a way to test that lint is present if env["PATH_INFO"] == "/broken_lint" return [200, {}, ["Broken Lint"]] # provides a way to kill the process without knowing the pid elsif env["PATH_INFO"] == "/die" exit! end env["test.$DEBUG"] = $DEBUG env["test.$EVAL"] = BUKKIT if defined?(BUKKIT) env["test.$VERBOSE"] = $VERBOSE env["test.$LOAD_PATH"] = $LOAD_PATH env["test.stderr"] = File.expand_path($stderr.path) env["test.Ping"] = defined?(Ping) env["test.pid"] = Process.pid @app.call(env) end end use EnvMiddleware run TestRequest.new ruby-rack1.4-1.4.5/test/registering_handler/000077500000000000000000000000001223351442100206365ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/registering_handler/rack/000077500000000000000000000000001223351442100215565ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/registering_handler/rack/handler/000077500000000000000000000000001223351442100231735ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/registering_handler/rack/handler/registering_myself.rb000066400000000000000000000001771223351442100274260ustar00rootroot00000000000000module Rack module Handler class RegisteringMyself end register :registering_myself, RegisteringMyself end endruby-rack1.4-1.4.5/test/spec_auth.rb000066400000000000000000000035221223351442100171210ustar00rootroot00000000000000require 'rack' describe Rack::Auth do it "should have all common authentication schemes" do Rack::Auth.schemes.should.include? 'basic' Rack::Auth.schemes.should.include? 'digest' Rack::Auth.schemes.should.include? 'bearer' Rack::Auth.schemes.should.include? 'token' end it "should allow registration of new auth schemes" do Rack::Auth.schemes.should.not.include "test" Rack::Auth.add_scheme "test" Rack::Auth.schemes.should.include "test" end end describe Rack::Auth::AbstractRequest do it "should symbolize known auth schemes" do env = Rack::MockRequest.env_for('/') env['HTTP_AUTHORIZATION'] = 'Basic aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :basic env['HTTP_AUTHORIZATION'] = 'Digest aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :digest env['HTTP_AUTHORIZATION'] = 'Bearer aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :bearer env['HTTP_AUTHORIZATION'] = 'MAC aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :mac env['HTTP_AUTHORIZATION'] = 'Token aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :token env['HTTP_AUTHORIZATION'] = 'OAuth aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :oauth env['HTTP_AUTHORIZATION'] = 'OAuth2 aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should.equal :oauth2 end it "should not symbolize unknown auth schemes" do env = Rack::MockRequest.env_for('/') env['HTTP_AUTHORIZATION'] = 'magic aXJyZXNwb25zaWJsZQ==' req = Rack::Auth::AbstractRequest.new(env) req.scheme.should == "magic" end end ruby-rack1.4-1.4.5/test/spec_auth_basic.rb000066400000000000000000000044131223351442100202620ustar00rootroot00000000000000require 'rack/auth/basic' require 'rack/lint' require 'rack/mock' describe Rack::Auth::Basic do def realm 'WallysWorld' end def unprotected_app Rack::Lint.new 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 before 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 should 'challenge correctly when no credentials are specified' do request do |response| assert_basic_auth_challenge response end end should 'rechallenge if incorrect credentials are specified' do request_with_basic_auth 'joe', 'password' do |response| assert_basic_auth_challenge response end end 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 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 should 'return 400 Bad Request for a malformed authorization header' do request 'HTTP_AUTHORIZATION' => '' do |response| response.should.be.a.client_error response.status.should.equal 400 response.should.not.include 'WWW-Authenticate' end end it 'takes realm as optional constructor arg' do app = Rack::Auth::Basic.new(unprotected_app, realm) { true } realm.should == app.realm end end ruby-rack1.4-1.4.5/test/spec_auth_digest.rb000066400000000000000000000201211223351442100204520ustar00rootroot00000000000000require 'rack/auth/digest/md5' require 'rack/lint' require 'rack/mock' describe Rack::Auth::Digest::MD5 do def realm 'WallysWorld' end def unprotected_app Rack::Lint.new lambda { |env| friend = Rack::Utils.parse_query(env["QUERY_STRING"])["friend"] [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}#{friend ? " and #{friend}" : ''}"] ] } end def protected_app Rack::Auth::Digest::MD5.new(unprotected_app, :realm => realm, :opaque => 'this-should-be-secret') do |username| { 'Alice' => 'correct-password' }[username] end 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 before 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 should 'challenge when no credentials are specified' do request 'GET', '/' do |response| assert_digest_auth_challenge response end end 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 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 should 'rechallenge if incorrect username given' do request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response| assert_digest_auth_challenge response end end should 'rechallenge if incorrect password given' do request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response| assert_digest_auth_challenge response end end should 'rechallenge if incorrect user and blank password given' do request_with_digest_auth 'GET', '/', 'Bob', '' do |response| assert_digest_auth_challenge response end end should 'not rechallenge if nonce is not stale' do begin Rack::Auth::Digest::Nonce.time_limit = 10 request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 1 do |response| response.status.should.equal 200 response.body.to_s.should.equal 'Hi Alice' response.headers['WWW-Authenticate'].should.not =~ /\bstale=true\b/ end ensure Rack::Auth::Digest::Nonce.time_limit = nil end end should 'rechallenge with stale parameter if nonce is stale' do begin Rack::Auth::Digest::Nonce.time_limit = 1 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 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 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 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 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 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 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 should 'return application output when used with a query string and path as uri' do @request = Rack::MockRequest.new(partially_protected_app) request_with_digest_auth 'GET', '/protected?friend=Mike', 'Alice', 'correct-password' do |response| response.status.should.equal 200 response.body.to_s.should.equal 'Hi Alice and Mike' end end should 'return application output when used with a query string and fullpath as uri' do @request = Rack::MockRequest.new(partially_protected_app) qs_uri = '/protected?friend=Mike' request_with_digest_auth 'GET', qs_uri, 'Alice', 'correct-password', 'uri' => qs_uri do |response| response.status.should.equal 200 response.body.to_s.should.equal 'Hi Alice and Mike' end end 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 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 it 'takes realm as optional constructor arg' do app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true } realm.should == app.realm end end ruby-rack1.4-1.4.5/test/spec_body_proxy.rb000066400000000000000000000033571223351442100203640ustar00rootroot00000000000000require 'rack/body_proxy' require 'stringio' describe Rack::BodyProxy do should 'call each on the wrapped body' do called = false proxy = Rack::BodyProxy.new(['foo']) { } proxy.each do |str| called = true str.should.equal 'foo' end called.should.equal true end should 'call close on the wrapped body' do body = StringIO.new proxy = Rack::BodyProxy.new(body) { } proxy.close body.should.be.closed end should 'only call close on the wrapped body if it responds to close' do body = [] proxy = Rack::BodyProxy.new(body) { } proc { proxy.close }.should.not.raise end should 'call the passed block on close' do called = false proxy = Rack::BodyProxy.new([]) { called = true } called.should.equal false proxy.close called.should.equal true end should 'call the passed block on close even if there is an exception' do object = Object.new def object.close() raise "No!" end called = false begin proxy = Rack::BodyProxy.new(object) { called = true } called.should.equal false proxy.close rescue RuntimeError => e end raise "Expected exception to have been raised" unless e called.should.equal true end should 'not close more than one time' do count = 0 proxy = Rack::BodyProxy.new([]) { count += 1; raise "Block invoked more than 1 time!" if count > 1 } 2.times { proxy.close } count.should.equal 1 end should 'be closed when the callback is triggered' do closed = false proxy = Rack::BodyProxy.new([]) { closed = proxy.closed? } proxy.close closed.should.equal true end should 'provide an #each method' do Rack::BodyProxy.method_defined?(:each).should.equal true end end ruby-rack1.4-1.4.5/test/spec_builder.rb000066400000000000000000000134611223351442100176110ustar00rootroot00000000000000require 'rack/builder' require 'rack/lint' require 'rack/mock' require 'rack/showexceptions' require 'rack/urlmap' class NothingMiddleware def initialize(app) @app = app end def call(env) @@env = env response = @app.call(env) response end def self.env @@env end end describe Rack::Builder do def builder(&block) Rack::Lint.new Rack::Builder.new(&block) end def builder_to_app(&block) Rack::Lint.new Rack::Builder.new(&block).to_app end it "supports mapping" do app = builder_to_app do map '/' do |outer_env| run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] } end map '/sub' do run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] } end end Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root' Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub' end it "doesn't dupe env even when mapping" do app = builder_to_app do use NothingMiddleware map '/' do |outer_env| run lambda { |inner_env| inner_env['new_key'] = 'new_value' [200, {"Content-Type" => "text/plain"}, ['root']] } end end Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root' NothingMiddleware.env['new_key'].should.equal 'new_value' end it "chains apps by default" do app = builder_to_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 it "has implicit #to_app" do app = builder 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 it "supports blocks on use" do app = builder do use Rack::ShowExceptions use Rack::Auth::Basic do |username, password| 'secret' == password end run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['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 it "has explicit #to_app" do app = builder 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 it "can mix map and run for endpoints" do app = builder do map '/sub' do run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] } end run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] } end Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root' Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub' end it "accepts middleware-only map blocks" do app = builder do map('/foo') { use Rack::ShowExceptions } run lambda { |env| raise "bzzzt" } end proc { Rack::MockRequest.new(app).get("/") }.should.raise(RuntimeError) Rack::MockRequest.new(app).get("/foo").should.be.server_error end should "initialize apps once" do app = builder 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 it "allows use after run" do app = builder do run lambda { |env| raise "bzzzt" } use Rack::ShowExceptions 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 it 'complains about a missing run' do proc do Rack::Lint.new Rack::Builder.app { use Rack::ShowExceptions } end.should.raise(RuntimeError) end describe "parse_file" do def config_file(name) File.join(File.dirname(__FILE__), 'builder', name) end it "parses commented options" do app, options = Rack::Builder.parse_file config_file('options.ru') options[:debug].should.be.true Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK' end it "removes __END__ before evaluating app" do app, options = Rack::Builder.parse_file config_file('end.ru') options = nil # ignored, prevents warning Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK' end it "supports multi-line comments" do lambda { Rack::Builder.parse_file config_file('comment.ru') }.should.not.raise(SyntaxError) end it "requires anything not ending in .ru" do $: << File.dirname(__FILE__) app, * = Rack::Builder.parse_file 'builder/anything' Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK' $:.pop end it "sets __LINE__ correctly" do app, options = Rack::Builder.parse_file config_file('line.ru') options = nil # ignored, prevents warning Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1' end end end ruby-rack1.4-1.4.5/test/spec_cascade.rb000066400000000000000000000041611223351442100175430ustar00rootroot00000000000000require 'rack/cascade' require 'rack/file' require 'rack/lint' require 'rack/urlmap' require 'rack/mock' describe Rack::Cascade do def cascade(*args) Rack::Lint.new Rack::Cascade.new(*args) end 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"}, [""]]}) should "dispatch onward on 404 and 405 by default" do cascade = cascade([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/../..").should.be.client_error # Put is not allowed by Rack::File so it'll 405. Rack::MockRequest.new(cascade).put("/foo").should.be.ok end should "dispatch onward on whatever is passed" do cascade = cascade([app1, app2, app3], [404, 403]) Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found end should "return 404 if empty" do Rack::MockRequest.new(cascade([])).get('/').should.be.not_found end should "append new app" do cascade = Rack::Cascade.new([], [404, 403]) Rack::MockRequest.new(cascade).get('/').should.be.not_found 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/../..').should.be.client_error Rack::MockRequest.new(cascade).get('/foo').should.be.not_found cascade << app3 Rack::MockRequest.new(cascade).get('/foo').should.be.ok end should "close the body on cascade" do body = StringIO.new closer = lambda { |env| [404, {}, body] } cascade = Rack::Cascade.new([closer, app3], [404]) Rack::MockRequest.new(cascade).get("/foo").should.be.ok body.should.be.closed end end ruby-rack1.4-1.4.5/test/spec_cgi.rb000066400000000000000000000056631223351442100167320ustar00rootroot00000000000000begin require File.expand_path('../testrequest', __FILE__) require 'rack/handler/cgi' describe Rack::Handler::CGI do extend TestRequest::Helpers @host = '127.0.0.1' @port = 9203 if `which lighttpd` && !$?.success? raise "lighttpd not found" end # Keep this first. $pid = fork { ENV['RACK_ENV'] = 'deployment' ENV['RUBYLIB'] = [ File.expand_path('../../lib', __FILE__), ENV['RUBYLIB'], ].compact.join(':') Dir.chdir(File.expand_path("../cgi", __FILE__)) do exec "lighttpd -D -f lighttpd.conf" end } should "respond" do sleep 1 GET("/test") response.should.not.be.nil end should "be a lighttpd" do GET("/test") status.should.equal 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.equal @host end should "have rack headers" do GET("/test") response["rack.version"].should.equal([1,1]) response["rack.multithread"].should.be.false response["rack.multiprocess"].should.be.true response["rack.run_once"].should.be.true end 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 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 should "support HTTP auth" do GET("/test", {:user => "ruth", :passwd => "secret"}) response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" end should "set status" do GET("/test?secret") status.should.equal 403 response["rack.url_scheme"].should.equal "http" end # Keep this last. should "shutdown" do Process.kill 15, $pid Process.wait($pid).should == $pid end end rescue RuntimeError $stderr.puts "Skipping Rack::Handler::CGI tests (lighttpd is required). Install lighttpd and try again." rescue NotImplementedError $stderr.puts "Your Ruby implemenation or platform does not support fork. Skipping Rack::Handler::CGI tests." end ruby-rack1.4-1.4.5/test/spec_chunked.rb000066400000000000000000000066001223351442100176010ustar00rootroot00000000000000require 'rack/chunked' require 'rack/lint' require 'rack/mock' describe Rack::Chunked do ::Enumerator = ::Enumerable::Enumerator unless Object.const_defined?(:Enumerator) def chunked(app) proc do |env| app = Rack::Chunked.new(app) response = Rack::Lint.new(app).call(env) # we want to use body like an array, but it only has #each response[2] = Enumerator.new(response[2]).to_a response end end before do @env = Rack::MockRequest. env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET') end should 'chunk responses with no Content-Length' do app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] } response = Rack::MockResponse.new(*chunked(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 should 'chunks empty bodies properly' do app = lambda { |env| [200, {"Content-Type" => "text/plain"}, []] } response = Rack::MockResponse.new(*chunked(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 should 'chunks encoded bodies properly' do body = ["\uFFFEHello", " ", "World"].map {|t| t.encode("UTF-16LE") } app = lambda { |env| [200, {"Content-Type" => "text/plain"}, body] } response = Rack::MockResponse.new(*chunked(app).call(@env)) response.headers.should.not.include 'Content-Length' response.headers['Transfer-Encoding'].should.equal 'chunked' response.body.encoding.to_s.should.equal "ASCII-8BIT" response.body.should.equal "c\r\n\xFE\xFFH\x00e\x00l\x00l\x00o\x00\r\n2\r\n \x00\r\na\r\nW\x00o\x00r\x00l\x00d\x00\r\n0\r\n\r\n" end if RUBY_VERSION >= "1.9" should 'not modify response when Content-Length header present' do app = lambda { |env| [200, {"Content-Type" => "text/plain", 'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] } status, headers, body = chunked(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 should 'not modify response when client is HTTP/1.0' do app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] } @env['HTTP_VERSION'] = 'HTTP/1.0' status, headers, body = chunked(app).call(@env) status.should.equal 200 headers.should.not.include 'Transfer-Encoding' body.join.should.equal 'Hello World!' end should 'not modify response when Transfer-Encoding header already present' do app = lambda { |env| [200, {"Content-Type" => "text/plain", 'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] } status, headers, body = chunked(app).call(@env) status.should.equal 200 headers['Transfer-Encoding'].should.equal 'identity' body.join.should.equal 'Hello World!' end [100, 204, 205, 304].each do |status_code| should "not modify response when status code is #{status_code}" do app = lambda { |env| [status_code, {}, []] } status, headers, _ = chunked(app).call(@env) status.should.equal status_code headers.should.not.include 'Transfer-Encoding' end end end ruby-rack1.4-1.4.5/test/spec_commonlogger.rb000066400000000000000000000026521223351442100206530ustar00rootroot00000000000000require 'rack/commonlogger' require 'rack/lint' require 'rack/mock' describe Rack::CommonLogger do obj = 'foobar' length = obj.size app = Rack::Lint.new lambda { |env| [200, {"Content-Type" => "text/html", "Content-Length" => length.to_s}, [obj]]} app_without_length = Rack::Lint.new lambda { |env| [200, {"Content-Type" => "text/html"}, []]} app_with_zero_length = Rack::Lint.new lambda { |env| [200, {"Content-Type" => "text/html", "Content-Length" => "0"}, []]} should "log to rack.errors by default" do res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/") res.errors.should.not.be.empty res.errors.should =~ /"GET \/ " 200 #{length} / end should "log to anything with +write+" do log = StringIO.new Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/") log.string.should =~ /"GET \/ " 200 #{length} / end should "log - content length if header is missing" do res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/") res.errors.should.not.be.empty res.errors.should =~ /"GET \/ " 200 - / end should "log - content length if header is zero" do res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/") res.errors.should.not.be.empty res.errors.should =~ /"GET \/ " 200 - / end def length 123 end def self.obj "hello world" end end ruby-rack1.4-1.4.5/test/spec_conditionalget.rb000066400000000000000000000064401223351442100211650ustar00rootroot00000000000000require 'time' require 'rack/conditionalget' require 'rack/mock' describe Rack::ConditionalGet do def conditional_get(app) Rack::Lint.new Rack::ConditionalGet.new(app) end should "set a 304 status and truncate body when If-Modified-Since hits" do timestamp = Time.now.httpdate app = conditional_get(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 should "set a 304 status and truncate body when If-Modified-Since hits and is higher than current time" do app = conditional_get(lambda { |env| [200, {'Last-Modified'=>(Time.now - 3600).httpdate}, ['TEST']] }) response = Rack::MockRequest.new(app). get("/", 'HTTP_IF_MODIFIED_SINCE' => Time.now.httpdate) response.status.should.equal 304 response.body.should.be.empty end should "set a 304 status and truncate body when If-None-Match hits" do app = conditional_get(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 should "not set a 304 status if If-Modified-Since hits but Etag does not" do timestamp = Time.now.httpdate app = conditional_get(lambda { |env| [200, {'Last-Modified'=>timestamp, 'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] }) response = Rack::MockRequest.new(app). get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '4321') response.status.should.equal 200 response.body.should.equal 'TEST' end should "set a 304 status and truncate body when both If-None-Match and If-Modified-Since hits" do timestamp = Time.now.httpdate app = conditional_get(lambda { |env| [200, {'Last-Modified'=>timestamp, 'Etag'=>'1234'}, ['TEST']] }) response = Rack::MockRequest.new(app). get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '1234') response.status.should.equal 304 response.body.should.be.empty end should "not affect non-GET/HEAD requests" do app = conditional_get(lambda { |env| [200, {'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] }) response = Rack::MockRequest.new(app). post("/", 'HTTP_IF_NONE_MATCH' => '1234') response.status.should.equal 200 response.body.should.equal 'TEST' end should "not affect non-200 requests" do app = conditional_get(lambda { |env| [302, {'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] }) response = Rack::MockRequest.new(app). get("/", 'HTTP_IF_NONE_MATCH' => '1234') response.status.should.equal 302 response.body.should.equal 'TEST' end should "not affect requests with malformed HTTP_IF_NONE_MATCH" do bad_timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S %z') app = conditional_get(lambda { |env| [200,{'Last-Modified'=>(Time.now - 3600).httpdate, 'Content-Type' => 'text/plain'}, ['TEST']] }) response = Rack::MockRequest.new(app). get("/", 'HTTP_IF_MODIFIED_SINCE' => bad_timestamp) response.status.should.equal 200 response.body.should.equal 'TEST' end end ruby-rack1.4-1.4.5/test/spec_config.rb000066400000000000000000000010401223351442100174160ustar00rootroot00000000000000require 'rack/builder' require 'rack/config' require 'rack/content_length' require 'rack/lint' require 'rack/mock' describe Rack::Config do should "accept a block that modifies the environment" do app = Rack::Builder.new do use Rack::Lint use Rack::Config do |env| env['greeting'] = 'hello' end run lambda { |env| [200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']] } end response = Rack::MockRequest.new(app).get('/') response.body.should.equal('hello') end end ruby-rack1.4-1.4.5/test/spec_content_length.rb000066400000000000000000000055501223351442100211760ustar00rootroot00000000000000require 'enumerator' require 'rack/content_length' require 'rack/lint' require 'rack/mock' describe Rack::ContentLength do ::Enumerator = ::Enumerable::Enumerator unless Object.const_defined?(:Enumerator) def content_length(app) Rack::Lint.new Rack::ContentLength.new(app) end def request Rack::MockRequest.env_for end should "set Content-Length on Array bodies if none is set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = content_length(app).call(request) response[1]['Content-Length'].should.equal '13' end should "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 = content_length(app).call(request) response[1]['Content-Length'].should.be.nil end should "not change Content-Length if it is already set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] } response = content_length(app).call(request) response[1]['Content-Length'].should.equal '1' end should "not set Content-Length on 304 responses" do app = lambda { |env| [304, {}, []] } response = content_length(app).call(request) response[1]['Content-Length'].should.equal nil end should "not set Content-Length when Transfer-Encoding is chunked" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Transfer-Encoding' => 'chunked'}, []] } response = content_length(app).call(request) response[1]['Content-Length'].should.equal nil end # Using "Connection: close" for this is fairly contended. It might be useful # to have some other way to signal this. # # should "not force a Content-Length when Connection:close" do # app = lambda { |env| [200, {'Connection' => 'close'}, []] } # response = content_length(app).call({}) # response[1]['Content-Length'].should.equal nil # end should "close bodies that need to be closed" do body = Struct.new(:body) do attr_reader :closed def each; body.join; end def close; @closed = true; end def to_ary; end end.new(%w[one two three]) app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] } content_length(app).call(request) body.closed.should.equal true end should "support single-execute bodies" do body = Struct.new(:body) do def each yield body.shift until body.empty? end def to_ary; end end.new(%w[one two three]) app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] } response = content_length(app).call(request) expected = %w[one two three] response[1]['Content-Length'].should.equal expected.join.size.to_s Enumerator.new(response[2]).to_a.should.equal expected end end ruby-rack1.4-1.4.5/test/spec_content_type.rb000066400000000000000000000027461223351442100207020ustar00rootroot00000000000000require 'rack/content_type' require 'rack/lint' require 'rack/mock' describe Rack::ContentType do def content_type(app, *args) Rack::Lint.new Rack::ContentType.new(app, *args) end def request Rack::MockRequest.env_for end should "set Content-Type to default text/html if none is set" do app = lambda { |env| [200, {}, "Hello, World!"] } headers = content_type(app).call(request)[1] headers['Content-Type'].should.equal 'text/html' end should "set Content-Type to chosen default if none is set" do app = lambda { |env| [200, {}, "Hello, World!"] } headers = content_type(app, 'application/octet-stream').call(request)[1] headers['Content-Type'].should.equal 'application/octet-stream' end should "not change Content-Type if it is already set" do app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] } headers = content_type(app).call(request)[1] headers['Content-Type'].should.equal 'foo/bar' end should "detect Content-Type case insensitive" do app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] } headers = content_type(app).call(request)[1] headers.to_a.select { |k,v| k.downcase == "content-type" }. should.equal [["CONTENT-Type","foo/bar"]] end should "not set Content-Type on 304 responses" do app = lambda { |env| [304, {}, []] } response = content_type(app, "text/html").call(request) response[1]['Content-Type'].should.equal nil end end ruby-rack1.4-1.4.5/test/spec_deflater.rb000066400000000000000000000141231223351442100177450ustar00rootroot00000000000000require 'enumerator' require 'stringio' require 'time' # for Time#httpdate require 'rack/deflater' require 'rack/lint' require 'rack/mock' require 'zlib' describe Rack::Deflater do ::Enumerator = ::Enumerable::Enumerator unless Object.const_defined?(:Enumerator) def deflater(app) Rack::Lint.new Rack::Deflater.new(app) end def build_response(status, body, accept_encoding, headers = {}) body = [body] if body.respond_to? :to_str app = lambda do |env| res = [status, {}, body] res[1]["Content-Type"] = "text/plain" unless res[0] == 304 res end request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding)) response = deflater(app).call(request) return response end def inflate(buf) inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS) inflater.inflate(buf) << inflater.finish end 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", "Content-Type" => "text/plain" }) buf = '' response[2].each { |part| buf << part } inflate(buf).should.equal("foobar") end should "flush deflated chunks to the client as they become ready" 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", "Content-Type" => "text/plain" }) buf = [] inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS) response[2].each { |part| buf << inflater.inflate(part) } buf << inflater.finish buf.delete_if { |part| part.empty? } buf.join.should.equal("foobar") end # TODO: This is really just a special case of the above... 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", "Content-Type" => "text/plain" }) buf = '' response[2].each { |part| buf << part } inflate(buf).should.equal("Hello world!") end 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", "Content-Type" => "text/plain" }) buf = '' response[2].each { |part| buf << part } io = StringIO.new(buf) gz = Zlib::GzipReader.new(io) gz.read.should.equal("foobar") gz.close end should "flush gzipped chunks to the client as they become ready" 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", "Content-Type" => "text/plain" }) buf = [] inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32) response[2].each { |part| buf << inflater.inflate(part) } buf << inflater.finish buf.delete_if { |part| part.empty? } buf.join.should.equal("foobar") end 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", "Content-Type" => "text/plain" }) Enumerator.new(response[2]).to_a.should.equal(["Hello world!"]) end 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({}) Enumerator.new(response[2]).to_a.should.equal([]) end 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"}) Enumerator.new(response1[2]).to_a.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"}) Enumerator.new(response2[2]).to_a.should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."]) end should "handle gzip response with Last-Modified header" do last_modified = Time.now.httpdate app = lambda { |env| [200, { "Content-Type" => "text/plain", "Last-Modified" => last_modified }, ["Hello World!"]] } request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip") response = deflater(app).call(request) response[0].should.equal(200) response[1].should.equal({ "Content-Encoding" => "gzip", "Vary" => "Accept-Encoding", "Last-Modified" => last_modified, "Content-Type" => "text/plain" }) 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 should "do nothing when no-transform Cache-Control directive present" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Cache-Control' => 'no-transform'}, ['Hello World!']] } request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip") response = deflater(app).call(request) response[0].should.equal(200) response[1].should.not.include "Content-Encoding" Enumerator.new(response[2]).to_a.join.should.equal("Hello World!") end end ruby-rack1.4-1.4.5/test/spec_directory.rb000066400000000000000000000043071223351442100201660ustar00rootroot00000000000000require 'rack/directory' require 'rack/lint' require 'rack/mock' describe Rack::Directory do DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT FILE_CATCH = proc{|env| [200, {'Content-Type'=>'text/plain', "Content-Length" => "7"}, ['passed!']] } app = Rack::Lint.new(Rack::Directory.new(DOCROOT, FILE_CATCH)) should "serve directory indices" do res = Rack::MockRequest.new(Rack::Lint.new(app)). get("/cgi/") res.should.be.ok res.should =~ // end should "pass 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 should "serve 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 =~ // res = Rack::MockRequest.new(Rack::Lint.new(app)). get("/cgi/%74%65%73%74") # "/cgi/test" res.should.be.ok res.should =~ /passed!/ end should "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 should "404 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 should "uri escape path parts" do # #265, properly escape file names mr = Rack::MockRequest.new(Rack::Lint.new(app)) res = mr.get("/cgi/test%2bdirectory") res.should.be.ok res.body.should =~ %r[/cgi/test%2Bdirectory/test%2Bfile] res = mr.get("/cgi/test%2bdirectory/test%2bfile") res.should.be.ok end should "correctly escape script name" do app2 = Rack::Builder.new do map '/script-path' do run app end end mr = Rack::MockRequest.new(Rack::Lint.new(app2)) res = mr.get("/script-path/cgi/test%2bdirectory") res.should.be.ok res.body.should =~ %r[/script-path/cgi/test%2Bdirectory/test%2Bfile] res = mr.get("/script-path/cgi/test%2bdirectory/test%2bfile") res.should.be.ok end end ruby-rack1.4-1.4.5/test/spec_etag.rb000066400000000000000000000071571223351442100171100ustar00rootroot00000000000000require 'rack/etag' require 'rack/lint' require 'rack/mock' require 'time' describe Rack::ETag do def etag(app, *args) Rack::Lint.new Rack::ETag.new(app, *args) end def request Rack::MockRequest.env_for end def sendfile_body res = ['Hello World'] def res.to_path ; "/tmp/hello.txt" ; end res end should "set ETag if none is set if status is 200" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = etag(app).call(request) response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\"" end should "set ETag if none is set if status is 201" do app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = etag(app).call(request) response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\"" end should "set Cache-Control to 'max-age=0, private, must-revalidate' (default) if none is set" do app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = etag(app).call(request) response[1]['Cache-Control'].should.equal 'max-age=0, private, must-revalidate' end should "set Cache-Control to chosen one if none is set" do app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = etag(app, nil, 'public').call(request) response[1]['Cache-Control'].should.equal 'public' end should "set a given Cache-Control even if digest could not be calculated" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, []] } response = etag(app, 'no-cache').call(request) response[1]['Cache-Control'].should.equal 'no-cache' end should "not set Cache-Control if it is already set" do app = lambda { |env| [201, {'Content-Type' => 'text/plain', 'Cache-Control' => 'public'}, ["Hello, World!"]] } response = etag(app).call(request) response[1]['Cache-Control'].should.equal 'public' end should "not set Cache-Control if directive isn't present" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = etag(app, nil, nil).call(request) response[1]['Cache-Control'].should.equal nil end should "not change ETag if it is already set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] } response = etag(app).call(request) response[1]['ETag'].should.equal "\"abc\"" end should "not set ETag if body is empty" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, []] } response = etag(app).call(request) response[1]['ETag'].should.be.nil end should "not set ETag if Last-Modified is set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, ["Hello, World!"]] } response = etag(app).call(request) response[1]['ETag'].should.be.nil end should "not set ETag if a sendfile_body is given" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, sendfile_body] } response = etag(app).call(request) response[1]['ETag'].should.be.nil end should "not set ETag if a status is not 200 or 201" do app = lambda { |env| [401, {'Content-Type' => 'text/plain'}, ['Access denied.']] } response = etag(app).call(request) response[1]['ETag'].should.be.nil end should "not set ETag if no-cache is given" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Cache-Control' => 'no-cache, must-revalidate'}, ['Hello, World!']] } response = etag(app).call(request) response[1]['ETag'].should.be.nil end end ruby-rack1.4-1.4.5/test/spec_fastcgi.rb000066400000000000000000000061221223351442100175770ustar00rootroot00000000000000begin require File.expand_path('../testrequest', __FILE__) require 'rack/handler/fastcgi' describe Rack::Handler::FastCGI do extend TestRequest::Helpers @host = '127.0.0.1' @port = 9203 if `which lighttpd` && !$?.success? raise "lighttpd not found" end # Keep this first. $pid = fork { ENV['RACK_ENV'] = 'deployment' ENV['RUBYLIB'] = [ File.expand_path('../../lib', __FILE__), ENV['RUBYLIB'], ].compact.join(':') Dir.chdir(File.expand_path("../cgi", __FILE__)) do exec "lighttpd -D -f lighttpd.conf" end } should "respond" do sleep 1 GET("/test") response.should.not.be.nil end should "respond via rackup server" do GET("/sample_rackup.ru") status.should.equal 200 end should "be a lighttpd" do GET("/test.fcgi") status.should.equal 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.equal @host end should "have rack headers" do GET("/test.fcgi") response["rack.version"].should.equal [1,1] response["rack.multithread"].should.be.false response["rack.multiprocess"].should.be.true response["rack.run_once"].should.be.false end 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.equal "" 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 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 should "support HTTP auth" do GET("/test.fcgi", {:user => "ruth", :passwd => "secret"}) response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" end should "set status" do GET("/test.fcgi?secret") status.should.equal 403 response["rack.url_scheme"].should.equal "http" end # Keep this last. should "shutdown" do Process.kill 15, $pid Process.wait($pid).should.equal $pid end end rescue RuntimeError $stderr.puts "Skipping Rack::Handler::FastCGI tests (lighttpd is required). Install lighttpd and try again." rescue LoadError $stderr.puts "Skipping Rack::Handler::FastCGI tests (FCGI is required). `gem install fcgi` and try again." end ruby-rack1.4-1.4.5/test/spec_file.rb000066400000000000000000000126711223351442100171040ustar00rootroot00000000000000require 'rack/file' require 'rack/lint' require 'rack/mock' describe Rack::File do DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT def file(*args) Rack::Lint.new Rack::File.new(*args) end should "serve files" do res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test") res.should.be.ok res.should =~ /ruby/ end should "set Last-Modified header" do res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test") path = File.join(DOCROOT, "/cgi/test") res.should.be.ok res["Last-Modified"].should.equal File.mtime(path).httpdate end should "return 304 if file isn't modified since last serve" do path = File.join(DOCROOT, "/cgi/test") res = Rack::MockRequest.new(file(DOCROOT)). get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate) res.status.should.equal 304 res.body.should.be.empty end should "return the file if it's modified since last serve" do path = File.join(DOCROOT, "/cgi/test") res = Rack::MockRequest.new(file(DOCROOT)). get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate) res.should.be.ok end should "serve files with URL encoded filenames" do res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%74%65%73%74") # "/cgi/test" res.should.be.ok res.should =~ /ruby/ end should "allow safe directory traversal" do req = Rack::MockRequest.new(file(DOCROOT)) res = req.get('/cgi/../cgi/test') res.should.be.successful res = req.get('.') res.should.be.not_found res = req.get("test/..") res.should.be.not_found end should "not allow unsafe directory traversal" do req = Rack::MockRequest.new(file(DOCROOT)) res = req.get("/../README") res.should.be.client_error res = req.get("../test") res.should.be.client_error res = req.get("..") res.should.be.client_error res.should.be.not_found end should "allow files with .. in their name" do req = Rack::MockRequest.new(file(DOCROOT)) res = req.get("/cgi/..test") res.should.be.not_found res = req.get("/cgi/test..") res.should.be.not_found res = req.get("/cgi../test..") res.should.be.not_found end should "not allow unsafe directory traversal with encoded periods" do res = Rack::MockRequest.new(file(DOCROOT)).get("/%2E%2E/README") res.should.be.client_error? res.should.be.not_found end should "allow safe directory traversal with encoded periods" do res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%2E%2E/cgi/test") res.should.be.successful end should "404 if it can't find the file" do res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/blubb") res.should.be.not_found end should "detect SystemCallErrors" do res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi") res.should.be.not_found end should "return bodies that respond to #to_path" do env = Rack::MockRequest.env_for("/cgi/test") status, _, 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 should "return correct byte range in body" do env = Rack::MockRequest.env_for("/cgi/test") env["HTTP_RANGE"] = "bytes=22-33" res = Rack::MockResponse.new(*file(DOCROOT).call(env)) res.status.should.equal 206 res["Content-Length"].should.equal "12" res["Content-Range"].should.equal "bytes 22-33/193" res.body.should.equal "-*- ruby -*-" end should "return error for unsatisfiable byte range" do env = Rack::MockRequest.env_for("/cgi/test") env["HTTP_RANGE"] = "bytes=1234-5678" res = Rack::MockResponse.new(*file(DOCROOT).call(env)) res.status.should.equal 416 res["Content-Range"].should.equal "bytes */193" end should "support legacy cache control options provided as string" do env = Rack::MockRequest.env_for("/cgi/test") status, heads, _ = file(DOCROOT, 'public, max-age=38').call(env) status.should.equal 200 heads['Cache-Control'].should.equal 'public, max-age=38' end should "support custom http headers" do env = Rack::MockRequest.env_for("/cgi/test") status, heads, _ = file(DOCROOT, 'Cache-Control' => 'public, max-age=38', 'Access-Control-Allow-Origin' => '*').call(env) status.should.equal 200 heads['Cache-Control'].should.equal 'public, max-age=38' heads['Access-Control-Allow-Origin'].should.equal '*' end should "support not add custom http headers if none are supplied" do env = Rack::MockRequest.env_for("/cgi/test") status, heads, _ = file(DOCROOT).call(env) status.should.equal 200 heads['Cache-Control'].should.equal nil heads['Access-Control-Allow-Origin'].should.equal nil end should "only support GET and HEAD requests" do req = Rack::MockRequest.new(file(DOCROOT)) forbidden = %w[post put patch delete] forbidden.each do |method| res = req.send(method, "/cgi/test") res.should.be.client_error res.should.be.method_not_allowed end allowed = %w[get head] allowed.each do |method| res = req.send(method, "/cgi/test") res.should.be.successful end end should "set Content-Length correctly for HEAD requests" do req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))) res = req.head "/cgi/test" res.should.be.successful res['Content-Length'].should.equal "193" end end ruby-rack1.4-1.4.5/test/spec_handler.rb000066400000000000000000000034611223351442100175770ustar00rootroot00000000000000require 'rack/handler' class Rack::Handler::Lobster; end class RockLobster; end describe Rack::Handler do it "has registered default handlers" do Rack::Handler.get('cgi').should.equal Rack::Handler::CGI Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick begin Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI rescue LoadError end begin Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel rescue LoadError end end should "raise LoadError if handler doesn't exist" do lambda { Rack::Handler.get('boom') }.should.raise(LoadError) end should "get unregistered, but already required, handler by name" do Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster end should "register custom handler" do Rack::Handler.register('rock_lobster', 'RockLobster') Rack::Handler.get('rock_lobster').should.equal RockLobster end should "not need registration for properly coded handlers even if not already required" do begin $LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__) Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered lambda { Rack::Handler.get('UnRegistered') }.should.raise LoadError Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne ensure $LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__) end end should "allow autoloaded handlers to be registered properly while being loaded" do path = File.expand_path('../registering_handler', __FILE__) begin $LOAD_PATH.push path Rack::Handler.get('registering_myself').should.equal Rack::Handler::RegisteringMyself ensure $LOAD_PATH.delete path end end end ruby-rack1.4-1.4.5/test/spec_head.rb000066400000000000000000000026501223351442100170620ustar00rootroot00000000000000require 'enumerator' require 'rack/head' require 'rack/lint' require 'rack/mock' describe Rack::Head do def test_response(headers = {}) body = StringIO.new "foo" app = lambda do |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, body] end request = Rack::MockRequest.env_for("/", headers) response = Rack::Lint.new(Rack::Head.new(app)).call(request) return response, body end def enum defined?(Enumerator) ? Enumerator : Enumerable::Enumerator end should "pass 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"}) enum.new(resp[2]).to_a.should.equal(["foo"]) end end should "remove 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"}) enum.new(resp[2]).to_a.should.equal([]) end should "close the body when it is removed" do resp, body = test_response("REQUEST_METHOD" => "HEAD") resp[0].should.equal(200) resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"}) enum.new(resp[2]).to_a.should.equal([]) body.should.be.closed end end ruby-rack1.4-1.4.5/test/spec_lint.rb000066400000000000000000000430441223351442100171310ustar00rootroot00000000000000require 'stringio' require 'rack/lint' require 'rack/mock' describe Rack::Lint do def env(*args) Rack::MockRequest.env_for("/", *args) end should "pass valid request" do lambda { Rack::Lint.new(lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] }).call(env({})) }.should.not.raise end should "notice fatal errors" do lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError). message.should.match(/No env given/) end should "notice 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("rack.logger" => [])) }.should.raise(Rack::Lint::LintError). message.should.equal("logger [] must respond to info") 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 should "notice 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 should "notice 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 should "notice 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 should "notice 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 should "notice 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, 205, 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 should "notice content-length errors" do [100, 101, 204, 205, 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({}))[2].each { } }.should.raise(Rack::Lint::LintError). message.should.match(/Content-Length header was 1, but should be 0/) end should "notice body errors" do lambda { body = Rack::Lint.new(lambda { |env| [200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]] }).call(env({}))[2] body.each { |part| } }.should.raise(Rack::Lint::LintError). message.should.match(/yielded non-string/) end should "notice 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 should "notice 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 should "notice 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"}))[2].each { } }.should.raise(Rack::Lint::LintError). message.should.match(/body was given for HEAD/) end should "pass valid read calls" do hello_str = "hello world" hello_str.force_encoding("ASCII-8BIT") if hello_str.respond_to? :force_encoding 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_str)})) }.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_str)})) }.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_str)})) }.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_str)})) }.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_str)})) }.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_str)})) }.should.not.raise(Rack::Lint::LintError) end end describe "Rack::Lint::InputWrapper" do should "delegate :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 ruby-rack1.4-1.4.5/test/spec_lobster.rb000066400000000000000000000023561223351442100176360ustar00rootroot00000000000000require 'rack/lobster' require 'rack/lint' require 'rack/mock' module LobsterHelpers def lobster Rack::MockRequest.new Rack::Lint.new(Rack::Lobster.new) end def lambda_lobster Rack::MockRequest.new Rack::Lint.new(Rack::Lobster::LambdaLobster) end end describe Rack::Lobster::LambdaLobster do extend LobsterHelpers should "be a single lambda" do Rack::Lobster::LambdaLobster.should.be.kind_of Proc end should "look like a lobster" do res = lambda_lobster.get("/") res.should.be.ok res.body.should.include "(,(,,(,,,(" res.body.should.include "?flip" end should "be flippable" do res = lambda_lobster.get("/?flip") res.should.be.ok res.body.should.include "(,,,(,,(,(" end end describe Rack::Lobster do extend LobsterHelpers should "look like a lobster" do res = lobster.get("/") res.should.be.ok res.body.should.include "(,(,,(,,,(" res.body.should.include "?flip" res.body.should.include "crash" end should "be flippable" do res = lobster.get("/?flip=left") res.should.be.ok res.body.should.include "(,,,(,,(,(" end should "provide crashing for testing purposes" do lambda { lobster.get("/?flip=crash") }.should.raise end end ruby-rack1.4-1.4.5/test/spec_lock.rb000066400000000000000000000107231223351442100171110ustar00rootroot00000000000000require 'enumerator' require 'rack/lint' require 'rack/lock' require 'rack/mock' class Lock attr_reader :synchronized def initialize @synchronized = false end def synchronize @synchronized = true yield end def lock @synchronized = true end def unlock @synchronized = false end end module LockHelpers def lock_app(app, lock = Lock.new) app = if lock Rack::Lock.new app, lock else Rack::Lock.new app end Rack::Lint.new app end end describe Rack::Lock do ::Enumerator = ::Enumerable::Enumerator unless Object.const_defined?(:Enumerator) extend LockHelpers describe 'Proxy' do extend LockHelpers should 'delegate each' do env = Rack::MockRequest.env_for("/") response = Class.new { attr_accessor :close_called def initialize; @close_called = false; end def each; %w{ hi mom }.each { |x| yield x }; end }.new app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] }) response = app.call(env)[2] list = [] response.each { |x| list << x } list.should.equal %w{ hi mom } end should 'delegate to_path' do lock = Lock.new env = Rack::MockRequest.env_for("/") res = ['Hello World'] def res.to_path ; "/tmp/hello.txt" ; end app = Rack::Lock.new(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, res] }, lock) body = app.call(env)[2] body.should.respond_to :to_path body.to_path.should.equal "/tmp/hello.txt" end should 'not delegate to_path if body does not implement it' do env = Rack::MockRequest.env_for("/") res = ['Hello World'] app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, res] }) body = app.call(env)[2] body.should.not.respond_to :to_path end end should 'call super on close' do env = Rack::MockRequest.env_for("/") response = Class.new { attr_accessor :close_called def initialize; @close_called = false; end def close; @close_called = true; end }.new app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] }) app.call(env) response.close_called.should.equal false response.close response.close_called.should.equal true end should "not unlock until body is closed" do lock = Lock.new env = Rack::MockRequest.env_for("/") response = Object.new app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] }, lock) lock.synchronized.should.equal false response = app.call(env)[2] lock.synchronized.should.equal true response.close lock.synchronized.should.equal false end should "return value from app" do env = Rack::MockRequest.env_for("/") body = [200, {"Content-Type" => "text/plain"}, %w{ hi mom }] app = lock_app(lambda { |inner_env| body }) res = app.call(env) res[0].should.equal body[0] res[1].should.equal body[1] Enumerator.new(res[2]).to_a.should.equal ["hi", "mom"] end should "call synchronize on lock" do lock = Lock.new env = Rack::MockRequest.env_for("/") app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, %w{ a b c }] }, lock) lock.synchronized.should.equal false app.call(env) lock.synchronized.should.equal true end should "unlock if the app raises" do lock = Lock.new env = Rack::MockRequest.env_for("/") app = lock_app(lambda { raise Exception }, lock) lambda { app.call(env) }.should.raise(Exception) lock.synchronized.should.equal false end should "unlock if the app throws" do lock = Lock.new env = Rack::MockRequest.env_for("/") app = lock_app(lambda {|_| throw :bacon }, lock) lambda { app.call(env) }.should.throw(:bacon) lock.synchronized.should.equal false end should "set multithread flag to false" do app = lock_app(lambda { |env| env['rack.multithread'].should.equal false [200, {"Content-Type" => "text/plain"}, %w{ a b c }] }, false) app.call(Rack::MockRequest.env_for("/")) end should "reset original multithread flag when exiting lock" do app = Class.new(Rack::Lock) { def call(env) env['rack.multithread'].should.equal true super end }.new(lambda { |env| [200, {"Content-Type" => "text/plain"}, %w{ a b c }] }) Rack::Lint.new(app).call(Rack::MockRequest.env_for("/")) end end ruby-rack1.4-1.4.5/test/spec_logger.rb000066400000000000000000000011561223351442100174400ustar00rootroot00000000000000require 'stringio' require 'rack/lint' require 'rack/logger' require 'rack/mock' describe Rack::Logger do app = lambda { |env| log = env['rack.logger'] log.debug("Created logger") log.info("Program started") log.warn("Nothing to do!") [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } should "conform to Rack::Lint" do errors = StringIO.new a = Rack::Lint.new(Rack::Logger.new(app)) Rack::MockRequest.new(a).get('/', 'rack.errors' => errors) errors.string.should.match(/INFO -- : Program started/) errors.string.should.match(/WARN -- : Nothing to do/) end end ruby-rack1.4-1.4.5/test/spec_methodoverride.rb000066400000000000000000000041451223351442100212020ustar00rootroot00000000000000require 'stringio' require 'rack/methodoverride' require 'rack/mock' describe Rack::MethodOverride do def app Rack::Lint.new(Rack::MethodOverride.new(lambda {|e| [200, {"Content-Type" => "text/plain"}, []] })) end should "not affect GET requests" do env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET") app.call env env["REQUEST_METHOD"].should.equal "GET" end should "modify REQUEST_METHOD for POST requests when _method parameter is set" do env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put") app.call env env["REQUEST_METHOD"].should.equal "PUT" end should "modify REQUEST_METHOD for POST requests when X-HTTP-Method-Override is set" do env = Rack::MockRequest.env_for("/", :method => "POST", "HTTP_X_HTTP_METHOD_OVERRIDE" => "PATCH" ) app.call env env["REQUEST_METHOD"].should.equal "PATCH" end should "not modify REQUEST_METHOD if the method is unknown" do env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo") app.call env env["REQUEST_METHOD"].should.equal "POST" end should "not modify REQUEST_METHOD when _method is nil" do env = Rack::MockRequest.env_for("/", :method => "POST", :input => "foo=bar") app.call env env["REQUEST_METHOD"].should.equal "POST" end should "store the original REQUEST_METHOD prior to overriding" do env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=options") app.call env env["rack.methodoverride.original_method"].should.equal "POST" end should "not modify REQUEST_METHOD when given invalid multipart form data" do input = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size.to_s, :method => "POST", :input => input) app.call env env["REQUEST_METHOD"].should.equal "POST" end end ruby-rack1.4-1.4.5/test/spec_mock.rb000066400000000000000000000217751223351442100171230ustar00rootroot00000000000000require 'yaml' require 'rack/lint' require 'rack/mock' require 'stringio' app = Rack::Lint.new(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 body = req.head? ? "" : env.to_yaml Rack::Response.new(body, req.GET["status"] || 200, "Content-Type" => "text/yaml").finish }) describe Rack::MockRequest do should "return a MockResponse" do res = Rack::MockRequest.new(app).get("") res.should.be.kind_of Rack::MockResponse end 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 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 should "allow GET/POST/PUT/DELETE/HEAD" 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).patch("", :input => "foo") env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "PATCH" res = Rack::MockRequest.new(app).delete("", :input => "foo") env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "DELETE" Rack::MockRequest.env_for("/", :method => "HEAD")["REQUEST_METHOD"]. should.equal "HEAD" Rack::MockRequest.env_for("/", :method => "OPTIONS")["REQUEST_METHOD"]. should.equal "OPTIONS" end should "set content length" do env = Rack::MockRequest.env_for("/", :input => "foo") env["CONTENT_LENGTH"].should.equal "3" end 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 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 should "set SSL port and HTTP flag on when using https" do res = Rack::MockRequest.new(app). get("https://example.org/foo") res.should.be.kind_of Rack::MockResponse env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "GET" env["SERVER_NAME"].should.equal "example.org" env["SERVER_PORT"].should.equal "443" env["QUERY_STRING"].should.equal "" env["PATH_INFO"].should.equal "/foo" env["rack.url_scheme"].should.equal "https" env["HTTPS"].should.equal "on" end should "prepend slash to uri path" do res = Rack::MockRequest.new(app). get("foo") res.should.be.kind_of Rack::MockResponse 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 "/foo" env["rack.url_scheme"].should.equal "http" end should "properly convert method name to an uppercase string" do res = Rack::MockRequest.new(app).request(:get) env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "GET" end should "accept params and build query string for GET requests" do res = Rack::MockRequest.new(app).get("/foo?baz=2", :params => {:foo => {:bar => "1"}}) env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "GET" env["QUERY_STRING"].should.include "baz=2" env["QUERY_STRING"].should.include "foo[bar]=1" env["PATH_INFO"].should.equal "/foo" env["mock.postdata"].should.equal "" end should "accept raw input in params for GET requests" do res = Rack::MockRequest.new(app).get("/foo?baz=2", :params => "foo[bar]=1") env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "GET" env["QUERY_STRING"].should.include "baz=2" env["QUERY_STRING"].should.include "foo[bar]=1" env["PATH_INFO"].should.equal "/foo" env["mock.postdata"].should.equal "" end should "accept params and build url encoded params for POST requests" do res = Rack::MockRequest.new(app).post("/foo", :params => {:foo => {:bar => "1"}}) env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "POST" env["QUERY_STRING"].should.equal "" env["PATH_INFO"].should.equal "/foo" env["CONTENT_TYPE"].should.equal "application/x-www-form-urlencoded" env["mock.postdata"].should.equal "foo[bar]=1" end should "accept raw input in params for POST requests" do res = Rack::MockRequest.new(app).post("/foo", :params => "foo[bar]=1") env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "POST" env["QUERY_STRING"].should.equal "" env["PATH_INFO"].should.equal "/foo" env["CONTENT_TYPE"].should.equal "application/x-www-form-urlencoded" env["mock.postdata"].should.equal "foo[bar]=1" end should "accept params and build multipart encoded params for POST requests" do files = Rack::Multipart::UploadedFile.new(File.join(File.dirname(__FILE__), "multipart", "file1.txt")) res = Rack::MockRequest.new(app).post("/foo", :params => { "submit-name" => "Larry", "files" => files }) env = YAML.load(res.body) env["REQUEST_METHOD"].should.equal "POST" env["QUERY_STRING"].should.equal "" env["PATH_INFO"].should.equal "/foo" env["CONTENT_TYPE"].should.equal "multipart/form-data; boundary=AaB03x" # The gsub accounts for differences in YAMLs affect on the data. env["mock.postdata"].gsub("\r", "").length.should.equal 206 end should "behave valid according to the Rack spec" do lambda { Rack::MockRequest.new(app). get("https://bla.example.org:9292/meh/foo?bar", :lint => true) }.should.not.raise(Rack::Lint::LintError) end should "call close on the original body object" do called = false body = Rack::BodyProxy.new(['hi']) { called = true } capp = proc { |e| [200, {'Content-Type' => 'text/plain'}, body] } called.should.equal false Rack::MockRequest.new(capp).get('/', :lint => true) called.should.equal true end end describe Rack::MockResponse do 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 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.not.equal 0 res.location.should.be.nil end 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 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 should "allow calling body.close afterwards" do # this is exactly what rack-test does body = StringIO.new("hi") res = Rack::MockResponse.new(200, {}, body) body.close if body.respond_to?(:close) res.body.should == 'hi' end should "optionally make Rack errors fatal" do lambda { Rack::MockRequest.new(app).get("/?error=foo", :fatal => true) }.should.raise(Rack::MockRequest::FatalWarning) end end ruby-rack1.4-1.4.5/test/spec_mongrel.rb000066400000000000000000000133511223351442100176240ustar00rootroot00000000000000begin require 'rack' require 'rack/handler/mongrel' require File.expand_path('../testrequest', __FILE__) require 'timeout' Thread.abort_on_exception = true $tcp_defer_accept_opts = nil $tcp_cork_opts = nil describe Rack::Handler::Mongrel do extend TestRequest::Helpers @server = Mongrel::HttpServer.new(@host='127.0.0.1', @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 should "respond" do lambda { GET("/test") }.should.not.raise end should "be a Mongrel" do GET("/test") status.should.equal 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 "127.0.0.1" end should "have rack headers" do GET("/test") response["rack.version"].should.equal [1,1] response["rack.multithread"].should.be.true response["rack.multiprocess"].should.be.false response["rack.run_once"].should.be.false end 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.equal "" 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 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 should "support HTTP auth" do GET("/test", {:user => "ruth", :passwd => "secret"}) response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" end should "set status" do GET("/test?secret") status.should.equal 403 response["rack.url_scheme"].should.equal "http" end should "provide a .run" do block_ran = false Thread.new { Rack::Handler::Mongrel.run(lambda {}, {:Host => '127.0.0.1', :Port => 9211}) { |server| server.should.be.kind_of Mongrel::HttpServer block_ran = true } } sleep 1 block_ran.should.be.true end 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, :Host => '127.0.0.1', :Port => 9221) { |server| server.should.be.kind_of Mongrel::HttpServer server.classifier.uris.size.should.equal 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 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, :Host => '127.0.0.1', :Port => 9231}) { |server| server.should.be.kind_of Mongrel::HttpServer server.classifier.uris.size.should.equal 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 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://127.0.0.1/' => lambda{}, 'http://127.0.0.1/bar' => lambda{}, 'http://falsehost/arf' => lambda{}, 'http://falsehost/qux' => lambda{} }) opt = {:map => true, :Port => 9241, :Host => '127.0.0.1'} 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.equal 2 server.classifier.uris.should.include '/foo' server.classifier.handler_map['/foo'].size.should.equal 1 server.classifier.uris.should.include '/bar' server.classifier.handler_map['/bar'].size.should.equal 2 server.classifier.uris.should.not.include '/qux' server.classifier.uris.should.not.include '/arf' server.classifier.uris.size.should.equal 3 block_ran = true } } sleep 1 block_ran.should.be.true end 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 @acc.raise Mongrel::StopServer end rescue LoadError warn "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again." end ruby-rack1.4-1.4.5/test/spec_multipart.rb000066400000000000000000000436571223351442100202160ustar00rootroot00000000000000require 'rack/utils' require 'rack/mock' describe Rack::Multipart do def multipart_fixture(name, boundary = "AaB03x") file = multipart_file(name) data = File.open(file, 'rb') { |io| io.read } type = "multipart/form-data; boundary=#{boundary}" length = data.respond_to?(:bytesize) ? data.bytesize : data.size { "CONTENT_TYPE" => type, "CONTENT_LENGTH" => length.to_s, :input => StringIO.new(data) } end def multipart_file(name) File.join(File.dirname(__FILE__), "multipart", name.to_s) end should "return nil if content type is not multipart" do env = Rack::MockRequest.env_for("/", "CONTENT_TYPE" => 'application/x-www-form-urlencoded') Rack::Multipart.parse_multipart(env).should.equal nil end should "parse multipart content when content type present but filename is not" do env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename)) params = Rack::Multipart.parse_multipart(env) params["text"].should.equal "contents" end should "raise RangeError if the key space is exhausted" do env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename)) old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin lambda { Rack::Multipart.parse_multipart(env) }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "parse multipart form webkit style" do env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit) env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR" params = Rack::Multipart.parse_multipart(env) params['profile']['bio'].should.include 'hello' end should "reject insanely long boundaries" do # using a pipe since a tempfile can use up too much space rd, wr = IO.pipe # we only call rewind once at start, so make sure it succeeds # and doesn't hit ESPIPE def rd.rewind; end wr.sync = true # mock out length to make this pipe look like a Tempfile def rd.length 1024 * 1024 * 8 end # write to a pipe in a background thread, this will write a lot # unless Rack (properly) shuts down the read end thr = Thread.new do begin wr.write("--AaB03x") # make the initial boundary a few gigs long longer = "0123456789" * 1024 * 1024 (1024 * 1024).times { wr.write(longer) } wr.write("\r\n") wr.write('Content-Disposition: form-data; name="a"; filename="a.txt"') wr.write("\r\n") wr.write("Content-Type: text/plain\r\n") wr.write("\r\na") wr.write("--AaB03x--\r\n") wr.close rescue => err # this is EPIPE if Rack shuts us down err end end fixture = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", "CONTENT_LENGTH" => rd.length.to_s, :input => rd, } env = Rack::MockRequest.env_for '/', fixture lambda { Rack::Multipart.parse_multipart(env) }.should.raise(EOFError) rd.close err = thr.value err.should.be.instance_of Errno::EPIPE wr.close end should "parse multipart upload with text file" do env = Rack::MockRequest.env_for("/", multipart_fixture(:text)) params = Rack::Multipart.parse_multipart(env) params["submit-name"].should.equal "Larry" params["submit-name-with-content"].should.equal "Berry" params["files"][:type].should.equal "text/plain" params["files"][:filename].should.equal "file1.txt" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; filename=\"file1.txt\"\r\n" + "Content-Type: text/plain\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse multipart upload with nested parameters" do env = Rack::MockRequest.env_for("/", multipart_fixture(:nested)) params = Rack::Multipart.parse_multipart(env) params["foo"]["submit-name"].should.equal "Larry" params["foo"]["files"][:type].should.equal "text/plain" params["foo"]["files"][:filename].should.equal "file1.txt" params["foo"]["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"foo[files]\"; filename=\"file1.txt\"\r\n" + "Content-Type: text/plain\r\n" params["foo"]["files"][:name].should.equal "foo[files]" params["foo"]["files"][:tempfile].read.should.equal "contents" end should "parse multipart upload with binary file" do env = Rack::MockRequest.env_for("/", multipart_fixture(:binary)) params = Rack::Multipart.parse_multipart(env) params["submit-name"].should.equal "Larry" params["files"][:type].should.equal "image/png" params["files"][:filename].should.equal "rack-logo.png" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; filename=\"rack-logo.png\"\r\n" + "Content-Type: image/png\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.length.should.equal 26473 end should "parse multipart upload with empty file" do env = Rack::MockRequest.env_for("/", multipart_fixture(:empty)) params = Rack::Multipart.parse_multipart(env) params["submit-name"].should.equal "Larry" params["files"][:type].should.equal "text/plain" params["files"][:filename].should.equal "file1.txt" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; filename=\"file1.txt\"\r\n" + "Content-Type: text/plain\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "" end should "parse multipart upload with filename with semicolons" do env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "text/plain" params["files"][:filename].should.equal "fi;le1.txt" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; filename=\"fi;le1.txt\"\r\n" + "Content-Type: text/plain\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "not include file params if no file was selected" do env = Rack::MockRequest.env_for("/", multipart_fixture(:none)) params = Rack::Multipart.parse_multipart(env) params["submit-name"].should.equal "Larry" params["files"].should.equal nil params.keys.should.not.include "files" end should "parse multipart/mixed" do env = Rack::MockRequest.env_for("/", multipart_fixture(:mixed_files)) params = Rack::Utils::Multipart.parse_multipart(env) params["foo"].should.equal "bar" params["files"].should.be.instance_of String params["files"].size.should.equal 252 end should "parse IE multipart upload and clean up filename" do env = Rack::MockRequest.env_for("/", multipart_fixture(:ie)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "text/plain" params["files"][:filename].should.equal "file1.txt" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; " + 'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' + "\r\nContent-Type: text/plain\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse filename and modification param" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "image/jpeg" params["files"][:filename].should.equal "genome.jpeg" params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" + "Content-Disposition: attachment; " + "name=\"files\"; " + "filename=genome.jpeg; " + "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" + "Content-Description: a complete map of the human genome\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse filename with escaped quotes" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "application/octet-stream" params["files"][:filename].should.equal "escape \"quotes" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; " + "filename=\"escape \\\"quotes\"\r\n" + "Content-Type: application/octet-stream\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse filename with percent escaped quotes" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "application/octet-stream" params["files"][:filename].should.equal "escape \"quotes" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; " + "filename=\"escape %22quotes\"\r\n" + "Content-Type: application/octet-stream\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse filename with unescaped quotes" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "application/octet-stream" params["files"][:filename].should.equal "escape \"quotes" params["files"][:head].should.equal "Content-Disposition: form-data; " + "name=\"files\"; " + "filename=\"escape \"quotes\"\r\n" + "Content-Type: application/octet-stream\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse filename with escaped quotes and modification param" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param)) params = Rack::Multipart.parse_multipart(env) params["files"][:type].should.equal "image/jpeg" params["files"][:filename].should.equal "\"human\" genome.jpeg" params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" + "Content-Disposition: attachment; " + "name=\"files\"; " + "filename=\"\"human\" genome.jpeg\"; " + "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" + "Content-Description: a complete map of the human genome\r\n" params["files"][:name].should.equal "files" params["files"][:tempfile].read.should.equal "contents" end should "parse filename with unescaped percentage characters" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al")) params = Rack::Multipart.parse_multipart(env) files = params["document"]["attachment"] files[:type].should.equal "image/jpeg" files[:filename].should.equal "100% of a photo.jpeg" files[:head].should.equal <<-MULTIPART Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r Content-Type: image/jpeg\r MULTIPART files[:name].should.equal "document[attachment]" files[:tempfile].read.should.equal "contents" end should "parse filename with unescaped percentage characters that look like partial hex escapes" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al")) params = Rack::Multipart.parse_multipart(env) files = params["document"]["attachment"] files[:type].should.equal "image/jpeg" files[:filename].should.equal "100%a" files[:head].should.equal <<-MULTIPART Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r Content-Type: image/jpeg\r MULTIPART files[:name].should.equal "document[attachment]" files[:tempfile].read.should.equal "contents" end should "parse filename with unescaped percentage characters that look like partial hex escapes" do env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al")) params = Rack::Multipart.parse_multipart(env) files = params["document"]["attachment"] files[:type].should.equal "image/jpeg" files[:filename].should.equal "100%" files[:head].should.equal <<-MULTIPART Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r Content-Type: image/jpeg\r MULTIPART files[:name].should.equal "document[attachment]" files[:tempfile].read.should.equal "contents" end it "rewinds input after parsing upload" do options = multipart_fixture(:text) input = options[:input] env = Rack::MockRequest.env_for("/", options) params = Rack::Multipart.parse_multipart(env) params["submit-name"].should.equal "Larry" params["files"][:filename].should.equal "file1.txt" input.read.length.should.equal 307 end it "builds multipart body" do files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt")) data = Rack::Multipart.build_multipart("submit-name" => "Larry", "files" => files) options = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", "CONTENT_LENGTH" => data.length.to_s, :input => StringIO.new(data) } env = Rack::MockRequest.env_for("/", options) params = Rack::Multipart.parse_multipart(env) params["submit-name"].should.equal "Larry" params["files"][:filename].should.equal "file1.txt" params["files"][:tempfile].read.should.equal "contents" end it "builds nested multipart body" do files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt")) data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}]) options = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", "CONTENT_LENGTH" => data.length.to_s, :input => StringIO.new(data) } env = Rack::MockRequest.env_for("/", options) params = Rack::Multipart.parse_multipart(env) params["people"][0]["submit-name"].should.equal "Larry" params["people"][0]["files"][:filename].should.equal "file1.txt" params["people"][0]["files"][:tempfile].read.should.equal "contents" end it "can parse fields that end at the end of the buffer" do input = File.read(multipart_file("bad_robots")) req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon", "CONTENT_LENGTH" => input.size, :input => input) req.POST['file.path'].should.equal "/var/tmp/uploads/4/0001728414" req.POST['addresses'].should.not.equal nil end it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do data = File.open(multipart_file("fail_16384_nofile")) { |f| f.read }.gsub(/\n/, "\r\n") options = { "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo", "CONTENT_LENGTH" => data.length.to_s, :input => StringIO.new(data) } env = Rack::MockRequest.env_for("/", options) params = Rack::Multipart.parse_multipart(env) params.should.not.equal nil params.keys.should.include "AAAAAAAAAAAAAAAAAAA" params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER" params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new" params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2" params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id" params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017" end should "return nil if no UploadedFiles were used" do data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}]) data.should.equal nil end should "raise ArgumentError if params is not a Hash" do lambda { Rack::Multipart.build_multipart("foo=bar") }. should.raise(ArgumentError). message.should.equal "value must be a Hash" end it "can parse fields with a content type" do data = <<-EOF --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon\r Content-Disposition: form-data; name="description"\r Content-Type: text/plain"\r \r Very very blue\r --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon--\r EOF options = { "CONTENT_TYPE" => "multipart/form-data; boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon", "CONTENT_LENGTH" => data.length.to_s, :input => StringIO.new(data) } env = Rack::MockRequest.env_for("/", options) params = Rack::Utils::Multipart.parse_multipart(env) params.should.equal({"description"=>"Very very blue"}) end should "parse multipart upload with no content-length header" do env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit) env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR" env.delete 'CONTENT_LENGTH' params = Rack::Multipart.parse_multipart(env) params['profile']['bio'].should.include 'hello' end should "parse very long unquoted multipart file names" do data = <<-EOF --AaB03x\r Content-Type: text/plain\r Content-Disposition: attachment; name=file; filename=#{'long' * 100}\r \r contents\r --AaB03x--\r EOF options = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", "CONTENT_LENGTH" => data.length.to_s, :input => StringIO.new(data) } env = Rack::MockRequest.env_for("/", options) params = Rack::Utils::Multipart.parse_multipart(env) params["file"][:filename].should.equal('long' * 100) end end ruby-rack1.4-1.4.5/test/spec_nulllogger.rb000066400000000000000000000011761223351442100203350ustar00rootroot00000000000000require 'enumerator' require 'rack/lint' require 'rack/mock' require 'rack/nulllogger' describe Rack::NullLogger do ::Enumerator = ::Enumerable::Enumerator unless Object.const_defined?(:Enumerator) should "act as a noop logger" do app = lambda { |env| env['rack.logger'].warn "b00m" [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } logger = Rack::Lint.new(Rack::NullLogger.new(app)) res = logger.call(Rack::MockRequest.env_for) res[0..1].should.equal [ 200, {'Content-Type' => 'text/plain'} ] Enumerator.new(res[2]).to_a.should.equal ["Hello, World!"] end end ruby-rack1.4-1.4.5/test/spec_recursive.rb000066400000000000000000000035201223351442100201650ustar00rootroot00000000000000require 'rack/lint' require 'rack/recursive' require 'rack/mock' describe Rack::Recursive 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 |inner_res| inner_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") } def recursive(map) Rack::Lint.new Rack::Recursive.new(Rack::URLMap.new(map)) end should "allow for subrequests" do res = Rack::MockRequest.new(recursive("/app1" => @app1, "/app2" => @app2)). get("/app2") res.should.be.ok res.body.should.equal "App2App1" end should "raise error on requests not below the app" do app = Rack::URLMap.new("/app1" => @app1, "/app" => recursive("/1" => @app1, "/2" => @app2)) lambda { Rack::MockRequest.new(app).get("/app/2") }.should.raise(ArgumentError). message.should =~ /can only include below/ end should "support forwarding" do app = recursive("/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 ruby-rack1.4-1.4.5/test/spec_request.rb000066400000000000000000000770441223351442100176620ustar00rootroot00000000000000require 'stringio' require 'cgi' require 'rack/request' require 'rack/mock' describe Rack::Request do should "wrap 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.should.not.be.patch 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 should "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", "SERVER_PORT" => "9292") req.host.should.equal "example.org" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292") req.host.should.equal "example.org" env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292") env.delete("SERVER_NAME") req = Rack::Request.new(env) req.host.should.equal "192.168.1.1" env = Rack::MockRequest.env_for("/") env.delete("SERVER_NAME") req = Rack::Request.new(env) req.host.should.equal "" end should "figure out the correct port" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org") req.port.should.equal 80 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81") req.port.should.equal 81 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292") req.port.should.equal 9292 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292") req.port.should.equal 9292 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org") req.port.should.equal 80 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on") req.port.should.equal 443 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https") req.port.should.equal 443 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393") req.port.should.equal 9393 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80") req.port.should.equal 9393 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393") req.port.should.equal 80 end should "figure out the correct host with port" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org") req.host_with_port.should.equal "www2.example.org" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81") req.host_with_port.should.equal "localhost:81" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292") req.host_with_port.should.equal "example.org:9292" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292") req.host_with_port.should.equal "example.org:9292" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393") req.host_with_port.should.equal "example.org" end should "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 should "limit the keys from the GET query string" do env = Rack::MockRequest.env_for("/?foo=bar") old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin req = Rack::Request.new(env) lambda { req.GET }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "limit the key size per nested params hash" do nested_query = Rack::MockRequest.env_for("/?foo[bar][baz][qux]=1") plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1") old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3 begin lambda { Rack::Request.new(nested_query).GET }.should.not.raise(RangeError) lambda { Rack::Request.new(plain_query).GET }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "not unify GET and POST when calling params" do mr = Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', :input => "foo=bar&quux=bla" ) req = Rack::Request.new mr req.params req.GET.should.equal "foo" => "quux" req.POST.should.equal "foo" => "bar", "quux" => "bla" req.params.should.equal req.GET.merge(req.POST) end should "raise if rack.input is missing" do req = Rack::Request.new({}) lambda { req.POST }.should.raise(RuntimeError) end should "parse POST data when method is POST and no Content-Type given" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', :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 should "limit the keys from the POST form data" do env = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=bar&quux=bla") old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin req = Rack::Request.new(env) lambda { req.POST }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "parse POST data with explicit content type regardless of method" 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 should "not parse POST data when media type is not form-data" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', "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 should "parse POST data on PUT when media type is form-data" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'PUT', "CONTENT_TYPE" => 'application/x-www-form-urlencoded', :input => "foo=bar&quux=bla") req.POST.should.equal "foo" => "bar", "quux" => "bla" req.body.read.should.equal "foo=bar&quux=bla" end should "rewind 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 should "clean up Safari's ajax POST body" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0") req.POST.should.equal "foo" => "bar", "quux" => "bla" end should "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 should "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 should "return values for the keys in the order given from values_at" 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 should "extract referrer correctly" 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 nil end should "extract user agent correctly" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)") req.user_agent.should.equal "Mozilla/4.0 (compatible)" req = Rack::Request.new \ Rack::MockRequest.env_for("/") req.user_agent.should.equal nil end should "treat missing content type as nil" do req = Rack::Request.new \ Rack::MockRequest.env_for("/") req.content_type.should.equal nil end should "treat empty content type as nil" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "") req.content_type.should.equal nil end should "return nil media type for empty content type" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "") req.media_type.should.equal nil end should "cache, but invalidates the cache" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "CONTENT_TYPE" => "application/x-www-form-urlencoded", :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 should "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 should "ssl detection" do request = Rack::Request.new(Rack::MockRequest.env_for("/")) request.scheme.should.equal "http" request.should.not.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTPS' => 'on')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080')) request.scheme.should.equal "http" request.should.not.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http')) request.scheme.should.equal "https" request.should.be.ssl? end should "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 should "always return the same hash object" do req = Rack::Request.new \ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m") hash = req.cookies req.env.delete("HTTP_COOKIE") req.cookies.should.equal(hash) req.env["HTTP_COOKIE"] = "zoo=m" req.cookies.should.equal(hash) end should "modify the cookies hash in place" do req = Rack::Request.new(Rack::MockRequest.env_for("")) req.cookies.should.equal({}) req.cookies['foo'] = 'bar' req.cookies.should.equal 'foo' => 'bar' end should "pass through non-uri escaped cookies as-is" do req = Rack::Request.new Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%") req.cookies["foo"].should == "%" end should "parse 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 should 'parse cookies with quotes' do req = Rack::Request.new Rack::MockRequest.env_for('', { 'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"' }) req.cookies.should.equal({ '$Version' => '"1"', 'Customer' => '"WILE_E_COYOTE"', '$Path' => '"/acme"', 'Part_Number' => '"Rocket_Launcher_0001"', }) end should "provide 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 should "provide the original env" do req = Rack::Request.new(e = Rack::MockRequest.env_for("")) req.env.should == e end should "restore the base URL" do Rack::Request.new(Rack::MockRequest.env_for("")).base_url. should.equal "http://example.org" Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url. should.equal "http://example.org" end should "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 should "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 should "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 should "parse with junk before boundry" do # Adapted from RFC 1867. input = < "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 should "not infinite loop with a malformed HTTP request" do # Adapted from RFC 1867. input = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda{req.POST}.should.raise(EOFError) end should "parse multipart form data" do # Adapted from RFC 1867. input = < "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 should "parse big multipart form data" do input = < "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 should "detect invalid multipart form data" do input = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) input = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) input = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) end should "correctly parse the part name from Content-Id header" do input = <\r Content-Transfer-Encoding: 7bit\r \r foo\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/related, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) req.params.keys.should.equal [""] end should "not try to interpret binary as utf8" do if /regexp/.respond_to?(:kcode) # < 1.9 begin original_kcode = $KCODE $KCODE='UTF8' input = < "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 else # >= 1.9 input = < "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 end end should "work around buggy 1.8.* Tempfile equality" do input = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => rack_input) lambda{ req.POST }.should.not.raise lambda{ req.POST }.should.not.raise("input re-processed!") end should "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 = < "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input) res.should.be.ok end 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] ]) parser.call("gzip ; q=0.9").should.equal([["gzip", 0.9]]) parser.call("gzip ; deflate").should.equal([["gzip", 1.0]]) end ip_app = lambda { |env| request = Rack::Request.new(env) response = Rack::Response.new response.write request.ip response.finish } should 'provide ip information' do mock = Rack::MockRequest.new(Rack::Lint.new(ip_app)) res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4' res.body.should.equal '1.2.3.4' res = mock.get '/', 'REMOTE_ADDR' => 'fe80::202:b3ff:fe1e:8329' res.body.should.equal 'fe80::202:b3ff:fe1e:8329' res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6' res.body.should.equal '1.2.3.4' end should 'deals with proxies' do mock = Rack::MockRequest.new(Rack::Lint.new(ip_app)) res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' res.body.should.equal '1.2.3.4' res = mock.get '/', 'REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '127.0.0.1, 3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1' res.body.should.equal 'unknown' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'other,unknown,192.168.0.1' res.body.should.equal 'unknown' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,localhost,192.168.0.1' res.body.should.equal 'unknown' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '::1,2620:0:1c00:0:812c:9583:754b:ca11' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,::1' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'fd5b:982e:9130:247f:0000:0000:0000:0000,2620:0:1c00:0:812c:9583:754b:ca11' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,fd5b:982e:9130:247f:0000:0000:0000:0000' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '1.1.1.1, 127.0.0.1', 'HTTP_CLIENT_IP' => '1.1.1.1' res.body.should.equal '1.1.1.1' # Spoofing attempt res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '1.1.1.1', 'HTTP_CLIENT_IP' => '2.2.2.2' res.body.should.equal '1.1.1.1' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9' res.body.should.equal '9.9.9.9' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, fe80::202:b3ff:fe1e:8329' res.body.should.equal 'fe80::202:b3ff:fe1e:8329' end class MyRequest < Rack::Request def params {:foo => "bar"} end end 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 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 (0x20...0x7E).collect { |a| b = a.chr c = CGI.escape(b) should "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do url = "/?foo=#{c}bar#{c}" env = Rack::MockRequest.env_for(url) req2 = Rack::Request.new(env) req2.GET.should.equal "foo" => "#{b}bar#{b}" req2.params.should.equal "foo" => "#{b}bar#{b}" end } end ruby-rack1.4-1.4.5/test/spec_response.rb000066400000000000000000000223011223351442100200120ustar00rootroot00000000000000require 'rack/response' require 'stringio' describe Rack::Response do should "have 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 it "can be written to" do response = Rack::Response.new _, _, 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 it "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 it "can override the initial Content-Type with a different case" do response = Rack::Response.new("", 200, "content-type" => "text/plain") response["Content-Type"].should.equal "text/plain" end it "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"].join("\n") response.set_cookie "foo3", "bar3" response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"].join("\n") end it "can set cookies with the same name for multiple domains" do response = Rack::Response.new response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"} response.set_cookie "foo", {:value => "bar", :domain => ".example.com"} response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n") end it "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 it "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 it "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 it "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" ].join("\n") end it "can delete cookies with the same name from multiple domains" do response = Rack::Response.new response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"} response.set_cookie "foo", {:value => "bar", :domain => ".example.com"} response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n") response.delete_cookie "foo", :domain => ".example.com" response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=; domain=.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n") response.delete_cookie "foo", :domain => "sample.example.com" response["Set-Cookie"].should.equal ["foo=; domain=.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT", "foo=; domain=sample.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n") end it "can delete cookies with the same name with different paths" do response = Rack::Response.new response.set_cookie "foo", {:value => "bar", :path => "/"} response.set_cookie "foo", {:value => "bar", :path => "/path"} response["Set-Cookie"].should.equal ["foo=bar; path=/", "foo=bar; path=/path"].join("\n") response.delete_cookie "foo", :path => "/path" response["Set-Cookie"].should.equal ["foo=bar; path=/", "foo=; path=/path; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n") end it "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 it "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" object_with_each = Object.new def object_with_each.each yield "foo" yield "bar" end r = Rack::Response.new(object_with_each) 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 r = Rack::Response.new([], "200 OK") r.status.should.equal 200 end it "has a constructor that can take a block" do r = Rack::Response.new { |res| res.status = 404 res.write "foo" } status, _, body = r.finish str = ""; body.each { |part| str << part } str.should.equal "foo" status.should.equal 404 end it "doesn't return invalid responses" do r = Rack::Response.new(["foo", "bar"], 204) _, header, body = r.finish str = ""; body.each { |part| str << part } str.should.be.empty header["Content-Type"].should.equal nil header['Content-Length'].should.equal nil lambda { Rack::Response.new(Object.new) }.should.raise(TypeError). message.should =~ /stringable or iterable required/ end it "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 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 = 400 res.should.not.be.successful res.should.be.client_error res.should.be.bad_request res.status = 404 res.should.not.be.successful res.should.be.client_error res.should.be.not_found res.status = 405 res.should.not.be.successful res.should.be.client_error res.should.be.method_not_allowed res.status = 422 res.should.not.be.successful res.should.be.client_error res.should.be.unprocessable res.status = 501 res.should.not.be.successful res.should.be.server_error res.status = 307 res.should.be.redirect end 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 it "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 it "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 it "calls close on #body" do res = Rack::Response.new res.body = StringIO.new res.close res.body.should.be.closed end it "calls close on #body when 204, 205, or 304" do res = Rack::Response.new res.body = StringIO.new res.finish res.body.should.not.be.closed res.status = 204 _, _, b = res.finish res.body.should.be.closed b.should.not == res.body res.body = StringIO.new res.status = 205 _, _, b = res.finish res.body.should.be.closed b.should.not == res.body res.body = StringIO.new res.status = 304 _, _, b = res.finish res.body.should.be.closed b.should.not == res.body end it "wraps the body from #to_ary to prevent infinite loops" do res = Rack::Response.new res.finish.last.should.not.respond_to?(:to_ary) lambda { res.finish.last.to_ary }.should.raise(NoMethodError) end end ruby-rack1.4-1.4.5/test/spec_rewindable_input.rb000066400000000000000000000054331223351442100215160ustar00rootroot00000000000000require 'stringio' require 'rack/rewindable_input' shared "a rewindable IO object" do before do @rio = Rack::RewindableInput.new(@io) end should "be able to handle to read()" do @rio.read.should.equal "hello world" end should "be able to handle to read(nil)" do @rio.read(nil).should.equal "hello world" end should "be able to handle to read(length)" do @rio.read(1).should.equal "h" end 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 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 should "rewind to the beginning when #rewind is called" do @rio.read(1) @rio.rewind @rio.read.should.equal "hello world" end should "be able to handle gets" do @rio.gets.should == "hello world" end should "be able to handle each" do array = [] @rio.each do |data| array << data end array.should.equal(["hello world"]) end should "not buffer into a Tempfile if no data has been read yet" do @rio.instance_variable_get(:@rewindable_io).should.be.nil end 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.path.should == tempfile.path end 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 should "be possible to call #close when no data has been buffered yet" do lambda{ @rio.close }.should.not.raise end should "be possible to call #close multiple times" do lambda{ @rio.close @rio.close }.should.not.raise end @rio.close @rio = nil end describe Rack::RewindableInput do describe "given an IO object that is already rewindable" do before do @io = StringIO.new("hello world") end behaves_like "a rewindable IO object" end describe "given an IO object that is not rewindable" do before do @io = StringIO.new("hello world") @io.instance_eval do undef :rewind end end behaves_like "a rewindable IO object" end describe "given an IO object whose rewind method raises Errno::ESPIPE" do before do @io = StringIO.new("hello world") def @io.rewind raise Errno::ESPIPE, "You can't rewind this!" end end behaves_like "a rewindable IO object" end end ruby-rack1.4-1.4.5/test/spec_runtime.rb000066400000000000000000000030421223351442100176400ustar00rootroot00000000000000require 'rack/lint' require 'rack/mock' require 'rack/runtime' describe Rack::Runtime do def runtime_app(app, *args) Rack::Lint.new Rack::Runtime.new(app, *args) end def request Rack::MockRequest.env_for end it "sets X-Runtime is none is set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } response = runtime_app(app).call(request) response[1]['X-Runtime'].should =~ /[\d\.]+/ end it "doesn't set the X-Runtime if it is already set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', "X-Runtime" => "foobar"}, "Hello, World!"] } response = runtime_app(app).call(request) response[1]['X-Runtime'].should == "foobar" end should "allow a suffix to be set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } response = runtime_app(app, "Test").call(request) response[1]['X-Runtime-Test'].should =~ /[\d\.]+/ end should "allow multiple timers to be set" do app = lambda { |env| sleep 0.1; [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } runtime = runtime_app(app, "App") # wrap many times to guarantee a measurable difference 100.times do |i| runtime = Rack::Runtime.new(runtime, i.to_s) end runtime = Rack::Runtime.new(runtime, "All") response = runtime.call(request) response[1]['X-Runtime-App'].should =~ /[\d\.]+/ response[1]['X-Runtime-All'].should =~ /[\d\.]+/ Float(response[1]['X-Runtime-All']).should > Float(response[1]['X-Runtime-App']) end end ruby-rack1.4-1.4.5/test/spec_sendfile.rb000066400000000000000000000054501223351442100177530ustar00rootroot00000000000000require 'fileutils' require 'rack/lint' require 'rack/sendfile' require 'rack/mock' require 'tmpdir' describe Rack::File do should "respond to #to_path" do Rack::File.new(Dir.pwd).should.respond_to :to_path end end describe Rack::Sendfile do def sendfile_body FileUtils.touch File.join(Dir.tmpdir, "rack_sendfile") res = ['Hello World'] def res.to_path ; File.join(Dir.tmpdir, "rack_sendfile") ; end res end def simple_app(body=sendfile_body) lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] } end def sendfile_app(body=sendfile_body) Rack::Lint.new Rack::Sendfile.new(simple_app(body)) end @request = Rack::MockRequest.new(sendfile_app) def request(headers={}) yield @request.get('/', headers) end it "does nothing when no X-Sendfile-Type header present" do request do |response| response.should.be.ok response.body.should.equal 'Hello World' response.headers.should.not.include 'X-Sendfile' end end it "sets X-Sendfile response header and discards body" do request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response| response.should.be.ok response.body.should.be.empty response.headers['Content-Length'].should.equal '0' response.headers['X-Sendfile'].should.equal File.join(Dir.tmpdir, "rack_sendfile") end end it "sets X-Lighttpd-Send-File response header and discards body" do request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response| response.should.be.ok response.body.should.be.empty response.headers['Content-Length'].should.equal '0' response.headers['X-Lighttpd-Send-File'].should.equal File.join(Dir.tmpdir, "rack_sendfile") end end it "sets X-Accel-Redirect response header and discards body" do headers = { 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect', 'HTTP_X_ACCEL_MAPPING' => "#{Dir.tmpdir}/=/foo/bar/" } request headers do |response| response.should.be.ok response.body.should.be.empty response.headers['Content-Length'].should.equal '0' response.headers['X-Accel-Redirect'].should.equal '/foo/bar/rack_sendfile' end end it 'writes to rack.error when no X-Accel-Mapping is specified' do request 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect' do |response| response.should.be.ok response.body.should.equal 'Hello World' response.headers.should.not.include 'X-Accel-Redirect' response.errors.should.include 'X-Accel-Mapping' end end it 'does nothing when body does not respond to #to_path' do @request = Rack::MockRequest.new(sendfile_app(['Not a file...'])) request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response| response.body.should.equal 'Not a file...' response.headers.should.not.include 'X-Sendfile' end end end ruby-rack1.4-1.4.5/test/spec_server.rb000066400000000000000000000075151223351442100174740ustar00rootroot00000000000000require 'rack' require 'rack/server' require 'tempfile' require 'socket' require 'open-uri' describe Rack::Server do def app lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] } end def with_stderr old, $stderr = $stderr, StringIO.new yield $stderr ensure $stderr = old end it "overrides :config if :app is passed in" do server = Rack::Server.new(:app => "FOO") server.app.should == "FOO" end should "not include Rack::Lint in deployment or none environments" do server = Rack::Server.new(:app => 'foo') server.middleware['deployment'].flatten.should.not.include(Rack::Lint) server.middleware['none'].flatten.should.not.include(Rack::Lint) end should "not include Rack::ShowExceptions in deployment or none environments" do server = Rack::Server.new(:app => 'foo') server.middleware['deployment'].flatten.should.not.include(Rack::ShowExceptions) server.middleware['none'].flatten.should.not.include(Rack::ShowExceptions) end should "support CGI" do begin o, ENV["REQUEST_METHOD"] = ENV["REQUEST_METHOD"], 'foo' server = Rack::Server.new(:app => 'foo') server.server.name =~ /CGI/ Rack::Server.logging_middleware.call(server).should.eql(nil) ensure ENV['REQUEST_METHOD'] = o end end should "not force any middleware under the none configuration" do server = Rack::Server.new(:app => 'foo') server.middleware['none'].should.be.empty end should "use a full path to the pidfile" do # avoids issues with daemonize chdir opts = Rack::Server.new.send(:parse_options, %w[--pid testing.pid]) opts[:pid].should.eql(::File.expand_path('testing.pid')) end should "run a server" do pidfile = Tempfile.open('pidfile') { |f| break f }.path FileUtils.rm pidfile server = Rack::Server.new( :app => app, :environment => 'none', :pid => pidfile, :Port => TCPServer.open('127.0.0.1', 0){|s| s.addr[1] }, :Host => '127.0.0.1', :daemonize => false, :server => 'webrick' ) t = Thread.new { server.start { |s| Thread.current[:server] = s } } t.join(0.01) until t[:server] && t[:server].status != :Stop body = open("http://127.0.0.1:#{server.options[:Port]}/") { |f| f.read } body.should.eql('success') Process.kill(:INT, $$) t.join open(pidfile) { |f| f.read.should.eql $$.to_s } end should "check pid file presence and running process" do pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path server = Rack::Server.new(:pid => pidfile) server.send(:pidfile_process_status).should.eql :running end should "check pid file presence and dead process" do dead_pid = `echo $$`.to_i pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path server = Rack::Server.new(:pid => pidfile) server.send(:pidfile_process_status).should.eql :dead end should "check pid file presence and exited process" do pidfile = Tempfile.open('pidfile') { |f| break f }.path ::File.delete(pidfile) server = Rack::Server.new(:pid => pidfile) server.send(:pidfile_process_status).should.eql :exited end should "check pid file presence and not owned process" do pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path server = Rack::Server.new(:pid => pidfile) server.send(:pidfile_process_status).should.eql :not_owned end should "inform the user about existing pidfiles with running processes" do pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path server = Rack::Server.new(:pid => pidfile) with_stderr do |err| should.raise(SystemExit) do server.start end err.rewind output = err.read output.should.match(/already running/) output.should.include? pidfile end end end ruby-rack1.4-1.4.5/test/spec_session_abstract_id.rb000066400000000000000000000020151223351442100221760ustar00rootroot00000000000000### WARNING: there be hax in this file. require 'rack/session/abstract/id' describe Rack::Session::Abstract::ID do id = Rack::Session::Abstract::ID def silence_warning o, $VERBOSE = $VERBOSE, nil yield ensure $VERBOSE = o end def reload_id $".delete $".find { |part| part =~ %r{session/abstract/id.rb} } silence_warning { require 'rack/session/abstract/id' } end should "use securerandom when available" do begin fake = false silence_warning do ::SecureRandom = fake = true unless defined?(SecureRandom) end reload_id id::DEFAULT_OPTIONS[:secure_random].should.eql(fake || SecureRandom) ensure Object.send(:remove_const, :SecureRandom) if fake end end should "not use securerandom when unavailable" do begin sr = Object.send(:remove_const, :SecureRandom) if defined?(SecureRandom) reload_id id::DEFAULT_OPTIONS[:secure_random].should.eql false ensure ::SecureRandom = sr if defined?(sr) end end endruby-rack1.4-1.4.5/test/spec_session_cookie.rb000066400000000000000000000306631223351442100212020ustar00rootroot00000000000000require 'rack/session/cookie' require 'rack/lint' require 'rack/mock' describe Rack::Session::Cookie do incrementor = lambda do |env| env["rack.session"]["counter"] ||= 0 env["rack.session"]["counter"] += 1 hash = env["rack.session"].dup hash.delete("session_id") Rack::Response.new(hash.inspect).to_a end session_id = lambda do |env| Rack::Response.new(env["rack.session"].to_hash.inspect).to_a end session_option = lambda do |opt| lambda do |env| Rack::Response.new(env["rack.session.options"][opt].inspect).to_a end end nothing = lambda do |env| Rack::Response.new("Nothing").to_a end renewer = lambda do |env| env["rack.session.options"][:renew] = true Rack::Response.new("Nothing").to_a end only_session_id = lambda do |env| Rack::Response.new(env["rack.session"]["session_id"].to_s).to_a end bigcookie = lambda do |env| env["rack.session"]["cookie"] = "big" * 3000 Rack::Response.new(env["rack.session"].inspect).to_a end destroy_session = lambda do |env| env["rack.session"].destroy Rack::Response.new("Nothing").to_a end def response_for(options={}) request_options = options.fetch(:request, {}) cookie = if options[:cookie].is_a?(Rack::Response) options[:cookie]["Set-Cookie"] else options[:cookie] end request_options["HTTP_COOKIE"] = cookie || "" app_with_cookie = Rack::Session::Cookie.new(*options[:app]) app_with_cookie = Rack::Lint.new(app_with_cookie) Rack::MockRequest.new(app_with_cookie).get("/", request_options) end before do @warnings = warnings = [] Rack::Session::Cookie.class_eval do define_method(:warn) { |m| warnings << m } end end after do Rack::Session::Cookie.class_eval { remove_method :warn } end describe 'Base64' do it 'uses base64 to encode' do coder = Rack::Session::Cookie::Base64.new str = 'fuuuuu' coder.encode(str).should.equal [str].pack('m') end it 'uses base64 to decode' do coder = Rack::Session::Cookie::Base64.new str = ['fuuuuu'].pack('m') coder.decode(str).should.equal str.unpack('m').first end describe 'Marshal' do it 'marshals and base64 encodes' do coder = Rack::Session::Cookie::Base64::Marshal.new str = 'fuuuuu' coder.encode(str).should.equal [::Marshal.dump(str)].pack('m') end it 'marshals and base64 decodes' do coder = Rack::Session::Cookie::Base64::Marshal.new str = [::Marshal.dump('fuuuuu')].pack('m') coder.decode(str).should.equal ::Marshal.load(str.unpack('m').first) end it 'rescues failures on decode' do coder = Rack::Session::Cookie::Base64::Marshal.new coder.decode('lulz').should.equal nil end end end it "warns if no secret is given" do cookie = Rack::Session::Cookie.new(incrementor) @warnings.first.should =~ /no secret/i @warnings.clear cookie = Rack::Session::Cookie.new(incrementor, :secret => 'abc') @warnings.should.be.empty? end it 'uses a coder' do identity = Class.new { attr_reader :calls def initialize @calls = [] end def encode(str); @calls << :encode; str; end def decode(str); @calls << :decode; str; end }.new cookie = Rack::Session::Cookie.new(incrementor, :coder => identity) res = Rack::MockRequest.new(cookie).get("/") res["Set-Cookie"].should.include("rack.session=") res.body.should.equal '{"counter"=>1}' identity.calls.should.equal [:decode, :encode] end it "creates a new cookie" do res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/") res["Set-Cookie"].should.include("rack.session=") res.body.should.equal '{"counter"=>1}' end it "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 renewer = lambda do |env| env["rack.session.options"][:renew] = true Rack::Response.new("Nothing").to_a end only_session_id = lambda do |env| Rack::Response.new(env["rack.session"]["session_id"].to_s).to_a end it "renew session id" do res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/") res = Rack::MockRequest.new(Rack::Session::Cookie.new(only_session_id)). get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.not.equal "" old_session_id = res.body res = Rack::MockRequest.new(Rack::Session::Cookie.new(renewer)). get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res = Rack::MockRequest.new(Rack::Session::Cookie.new(only_session_id)). get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.not.equal "" res.body.should.not.equal old_session_id end it "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}' app = Rack::Session::Cookie.new(incrementor, :secret => 'test') res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => "rack.session=") res.body.should.equal '{"counter"=>1}' end bigcookie = lambda do |env| env["rack.session"]["cookie"] = "big" * 3000 Rack::Response.new(env["rack.session"].inspect).to_a end it "barks on too big cookies" do lambda{ Rack::MockRequest.new(Rack::Session::Cookie.new(bigcookie)). get("/", :fatal => true) }.should.raise(Rack::MockRequest::FatalWarning) end it "loads from a cookie with 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}' res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'other')). get("/", "HTTP_COOKIE" => cookie) res.body.should.equal '{"counter"=>1}' end it "loads from a cookie wih accept-only integrity hash for graceful key rotation" 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 => 'test2', :old_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 => 'test3', :old_secret => 'test2')). get("/", "HTTP_COOKIE" => cookie) res.body.should.equal '{"counter"=>3}' end it "ignores tampered with session cookies" do app = Rack::Session::Cookie.new(incrementor, :secret => 'test') response1 = Rack::MockRequest.new(app).get("/") response1.body.should.equal '{"counter"=>1}' response1 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => response1["Set-Cookie"]) response1.body.should.equal '{"counter"=>2}' _, digest = response1["Set-Cookie"].split("--") tampered_with_cookie = "hackerman-was-here" + "--" + digest response2 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => tampered_with_cookie) # Tampered cookie was ignored. Counter is back to 1. response2.body.should.equal '{"counter"=>1}' end it "supports either of secret or old_secret" do app = Rack::Session::Cookie.new(incrementor, :secret => 'test') res = Rack::MockRequest.new(app).get("/") res.body.should.equal '{"counter"=>1}' res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.equal '{"counter"=>2}' app = Rack::Session::Cookie.new(incrementor, :old_secret => 'test') res = Rack::MockRequest.new(app).get("/") res.body.should.equal '{"counter"=>1}' res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.equal '{"counter"=>2}' end describe "1.9 bugs relating to inspecting yet-to-be-loaded from cookie data: Rack::Session::Abstract::SessionHash" do it "can handle Rack::Lint middleware" do app = Rack::Session::Cookie.new(incrementor) res = Rack::MockRequest.new(app).get("/") app = Rack::Session::Cookie.new(Rack::Lint.new(session_id)) res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.not.be.nil end it "can handle a middleware that inspects the env" do class TestEnvInspector def initialize(app) @app = app end def call(env) env.inspect @app.call(env) end end app = Rack::Session::Cookie.new(incrementor) res = Rack::MockRequest.new(app).get("/") app = Rack::Session::Cookie.new(TestEnvInspector.new(session_id)) res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.not.be.nil end end it "returns the session id in the session hash" do app = Rack::Session::Cookie.new(incrementor) res = Rack::MockRequest.new(app).get("/") res.body.should.equal '{"counter"=>1}' app = Rack::Session::Cookie.new(session_id) res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"]) res.body.should.match(/"session_id"=>/) res.body.should.match(/"counter"=>1/) end it "does not return a cookie if set to secure but not using ssl" do app = Rack::Session::Cookie.new(incrementor, :secure => true) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil res = Rack::MockRequest.new(app).get("/", "HTTPS" => "on") res["Set-Cookie"].should.not.be.nil res["Set-Cookie"].should.match(/secure/) end it "does not return a cookie if cookie was not read/written" do app = Rack::Session::Cookie.new(nothing) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil end it "does not return a cookie if cookie was not written (only read)" do app = Rack::Session::Cookie.new(session_id) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil end it "returns even if not read/written if :expire_after is set" do app = Rack::Session::Cookie.new(nothing, :expire_after => 3600) res = Rack::MockRequest.new(app).get("/", 'rack.session' => {'not' => 'empty'}) res["Set-Cookie"].should.not.be.nil end it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do app = Rack::Session::Cookie.new(nothing, :expire_after => 3600) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil end it "exposes :secret in env['rack.session.option']" do app = Rack::Session::Cookie.new(session_option[:secret], :secret => "foo") res = Rack::MockRequest.new(app).get("/") res.body.should == '"foo"' end it "exposes :coder in env['rack.session.option']" do app = Rack::Session::Cookie.new(session_option[:coder]) res = Rack::MockRequest.new(app).get("/") res.body.should.match(/Base64::Marshal/) end it "allows passing in a hash with session data from middleware in front" do app = Rack::Session::Cookie.new(session_id) res = Rack::MockRequest.new(app).get("/", 'rack.session' => {:foo => 'bar'}) res.body.should.match(/foo/) end it "allows modifying session data with session data from middleware in front" do request = { 'rack.session' => { :foo => 'bar' }} response = response_for(:app => incrementor, :request => request) response.body.should.match(/counter/) response.body.should.match(/foo/) end it "allows modifying session data with session data from middleware in front" do request = { 'rack.session' => { :foo => 'bar' }} response = response_for(:app => incrementor, :request => request) response.body.should.match(/counter/) response.body.should.match(/foo/) end end ruby-rack1.4-1.4.5/test/spec_session_memcache.rb000066400000000000000000000261731223351442100214740ustar00rootroot00000000000000begin require 'rack/session/memcache' require 'rack/lint' require 'rack/mock' require 'thread' describe Rack::Session::Memcache do session_key = Rack::Session::Memcache::DEFAULT_OPTIONS[:key] session_match = /#{session_key}=([0-9a-fA-F]+);/ incrementor = lambda do |env| env["rack.session"]["counter"] ||= 0 env["rack.session"]["counter"] += 1 Rack::Response.new(env["rack.session"].inspect).to_a end drop_session = Rack::Lint.new(proc do |env| env['rack.session.options'][:drop] = true incrementor.call(env) end) renew_session = Rack::Lint.new(proc do |env| env['rack.session.options'][:renew] = true incrementor.call(env) end) defer_session = Rack::Lint.new(proc do |env| env['rack.session.options'][:defer] = true incrementor.call(env) end) skip_session = Rack::Lint.new(proc do |env| env['rack.session.options'][:skip] = true incrementor.call(env) end) incrementor = Rack::Lint.new(incrementor) # test memcache connection Rack::Session::Memcache.new(incrementor) it "faults on no connection" do lambda{ Rack::Session::Memcache.new(incrementor, :memcache_server => 'nosuchserver') }.should.raise end it "connects to existing server" do test_pool = MemCache.new(incrementor, :namespace => 'test:rack:session') test_pool.namespace.should.equal 'test:rack:session' end it "passes options to MemCache" do pool = Rack::Session::Memcache.new(incrementor, :namespace => 'test:rack:session') pool.pool.namespace.should.equal 'test:rack:session' end it "creates a new cookie" do pool = Rack::Session::Memcache.new(incrementor) res = Rack::MockRequest.new(pool).get("/") res["Set-Cookie"].should.include("#{session_key}=") res.body.should.equal '{"counter"=>1}' end it "determines session from a cookie" do pool = Rack::Session::Memcache.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get("/") cookie = res["Set-Cookie"] req.get("/", "HTTP_COOKIE" => cookie). body.should.equal '{"counter"=>2}' req.get("/", "HTTP_COOKIE" => cookie). body.should.equal '{"counter"=>3}' end it "determines session only from a cookie by default" do pool = Rack::Session::Memcache.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get("/") sid = res["Set-Cookie"][session_match, 1] req.get("/?rack.session=#{sid}"). body.should.equal '{"counter"=>1}' req.get("/?rack.session=#{sid}"). body.should.equal '{"counter"=>1}' end it "determines session from params" do pool = Rack::Session::Memcache.new(incrementor, :cookie_only => false) req = Rack::MockRequest.new(pool) res = req.get("/") sid = res["Set-Cookie"][session_match, 1] req.get("/?rack.session=#{sid}"). body.should.equal '{"counter"=>2}' req.get("/?rack.session=#{sid}"). body.should.equal '{"counter"=>3}' end it "survives nonexistant cookies" do bad_cookie = "rack.session=blarghfasel" pool = Rack::Session::Memcache.new(incrementor) res = Rack::MockRequest.new(pool). get("/", "HTTP_COOKIE" => bad_cookie) res.body.should.equal '{"counter"=>1}' cookie = res["Set-Cookie"][session_match] cookie.should.not.match(/#{bad_cookie}/) end it "maintains freshness" do pool = Rack::Session::Memcache.new(incrementor, :expire_after => 3) res = Rack::MockRequest.new(pool).get('/') res.body.should.include '"counter"=>1' cookie = res["Set-Cookie"] res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie) res["Set-Cookie"].should.equal cookie res.body.should.include '"counter"=>2' puts 'Sleeping to expire session' if $DEBUG sleep 4 res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie) res["Set-Cookie"].should.not.equal cookie res.body.should.include '"counter"=>1' end it "does not send the same session id if it did not change" do pool = Rack::Session::Memcache.new(incrementor) req = Rack::MockRequest.new(pool) res0 = req.get("/") cookie = res0["Set-Cookie"][session_match] res0.body.should.equal '{"counter"=>1}' res1 = req.get("/", "HTTP_COOKIE" => cookie) res1["Set-Cookie"].should.be.nil res1.body.should.equal '{"counter"=>2}' res2 = req.get("/", "HTTP_COOKIE" => cookie) res2["Set-Cookie"].should.be.nil res2.body.should.equal '{"counter"=>3}' end it "deletes cookies with :drop option" do pool = Rack::Session::Memcache.new(incrementor) req = Rack::MockRequest.new(pool) drop = Rack::Utils::Context.new(pool, drop_session) dreq = Rack::MockRequest.new(drop) res1 = req.get("/") session = (cookie = res1["Set-Cookie"])[session_match] res1.body.should.equal '{"counter"=>1}' res2 = dreq.get("/", "HTTP_COOKIE" => cookie) res2["Set-Cookie"].should.equal nil res2.body.should.equal '{"counter"=>2}' res3 = req.get("/", "HTTP_COOKIE" => cookie) res3["Set-Cookie"][session_match].should.not.equal session res3.body.should.equal '{"counter"=>1}' end it "provides new session id with :renew option" do pool = Rack::Session::Memcache.new(incrementor) req = Rack::MockRequest.new(pool) renew = Rack::Utils::Context.new(pool, renew_session) rreq = Rack::MockRequest.new(renew) res1 = req.get("/") session = (cookie = res1["Set-Cookie"])[session_match] res1.body.should.equal '{"counter"=>1}' res2 = rreq.get("/", "HTTP_COOKIE" => cookie) new_cookie = res2["Set-Cookie"] new_session = new_cookie[session_match] new_session.should.not.equal session res2.body.should.equal '{"counter"=>2}' res3 = req.get("/", "HTTP_COOKIE" => new_cookie) res3.body.should.equal '{"counter"=>3}' # Old cookie was deleted res4 = req.get("/", "HTTP_COOKIE" => cookie) res4.body.should.equal '{"counter"=>1}' end it "omits cookie with :defer option but still updates the state" do pool = Rack::Session::Memcache.new(incrementor) count = Rack::Utils::Context.new(pool, incrementor) defer = Rack::Utils::Context.new(pool, defer_session) dreq = Rack::MockRequest.new(defer) creq = Rack::MockRequest.new(count) res0 = dreq.get("/") res0["Set-Cookie"].should.equal nil res0.body.should.equal '{"counter"=>1}' res0 = creq.get("/") res1 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"]) res1.body.should.equal '{"counter"=>2}' res2 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"]) res2.body.should.equal '{"counter"=>3}' end it "omits cookie and state update with :skip option" do pool = Rack::Session::Memcache.new(incrementor) count = Rack::Utils::Context.new(pool, incrementor) skip = Rack::Utils::Context.new(pool, skip_session) sreq = Rack::MockRequest.new(skip) creq = Rack::MockRequest.new(count) res0 = sreq.get("/") res0["Set-Cookie"].should.equal nil res0.body.should.equal '{"counter"=>1}' res0 = creq.get("/") res1 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"]) res1.body.should.equal '{"counter"=>2}' res2 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"]) res2.body.should.equal '{"counter"=>2}' end it "updates deep hashes correctly" do hash_check = proc do |env| session = env['rack.session'] unless session.include? 'test' session.update :a => :b, :c => { :d => :e }, :f => { :g => { :h => :i} }, 'test' => true else session[:f][:g][:h] = :j end [200, {}, [session.inspect]] end pool = Rack::Session::Memcache.new(hash_check) req = Rack::MockRequest.new(pool) res0 = req.get("/") session_id = (cookie = res0["Set-Cookie"])[session_match, 1] ses0 = pool.pool.get(session_id, true) req.get("/", "HTTP_COOKIE" => cookie) ses1 = pool.pool.get(session_id, true) ses1.should.not.equal ses0 end # anyone know how to do this better? it "cleanly merges sessions when multithreaded" do unless $DEBUG 1.should.equal 1 # fake assertion to appease the mighty bacon next end warn 'Running multithread test for Session::Memcache' pool = Rack::Session::Memcache.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get('/') res.body.should.equal '{"counter"=>1}' cookie = res["Set-Cookie"] session_id = cookie[session_match, 1] delta_incrementor = lambda do |env| # emulate disconjoinment of threading env['rack.session'] = env['rack.session'].dup Thread.stop env['rack.session'][(Time.now.usec*rand).to_i] = true incrementor.call(env) end tses = Rack::Utils::Context.new pool, delta_incrementor treq = Rack::MockRequest.new(tses) tnum = rand(7).to_i+5 r = Array.new(tnum) do Thread.new(treq) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |request| request['Set-Cookie'].should.equal cookie request.body.should.include '"counter"=>2' end session = pool.pool.get(session_id) session.size.should.equal tnum+1 # counter session['counter'].should.equal 2 # meeeh tnum = rand(7).to_i+5 r = Array.new(tnum) do |i| app = Rack::Utils::Context.new pool, time_delta req = Rack::MockRequest.new app Thread.new(req) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |request| request['Set-Cookie'].should.equal cookie request.body.should.include '"counter"=>3' end session = pool.pool.get(session_id) session.size.should.be tnum+1 session['counter'].should.be 3 drop_counter = proc do |env| env['rack.session'].delete 'counter' env['rack.session']['foo'] = 'bar' [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect] end tses = Rack::Utils::Context.new pool, drop_counter treq = Rack::MockRequest.new(tses) tnum = rand(7).to_i+5 r = Array.new(tnum) do Thread.new(treq) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |request| request['Set-Cookie'].should.equal cookie request.body.should.include '"foo"=>"bar"' end session = pool.pool.get(session_id) session.size.should.be r.size+1 session['counter'].should.be.nil? session['foo'].should.equal 'bar' end end rescue RuntimeError $stderr.puts "Skipping Rack::Session::Memcache tests. Start memcached and try again." rescue LoadError $stderr.puts "Skipping Rack::Session::Memcache tests (Memcache is required). `gem install memcache-client` and try again." end ruby-rack1.4-1.4.5/test/spec_session_pool.rb000066400000000000000000000150431223351442100206750ustar00rootroot00000000000000require 'thread' require 'rack/lint' require 'rack/mock' require 'rack/session/pool' describe Rack::Session::Pool do session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key] session_match = /#{session_key}=[0-9a-fA-F]+;/ incrementor = lambda do |env| env["rack.session"]["counter"] ||= 0 env["rack.session"]["counter"] += 1 Rack::Response.new(env["rack.session"].inspect).to_a end session_id = Rack::Lint.new(lambda do |env| Rack::Response.new(env["rack.session"].inspect).to_a end) nothing = Rack::Lint.new(lambda do |env| Rack::Response.new("Nothing").to_a end) drop_session = Rack::Lint.new(lambda do |env| env['rack.session.options'][:drop] = true incrementor.call(env) end) renew_session = Rack::Lint.new(lambda do |env| env['rack.session.options'][:renew] = true incrementor.call(env) end) defer_session = Rack::Lint.new(lambda do |env| env['rack.session.options'][:defer] = true incrementor.call(env) end) incrementor = Rack::Lint.new(incrementor) it "creates a new cookie" do pool = Rack::Session::Pool.new(incrementor) res = Rack::MockRequest.new(pool).get("/") res["Set-Cookie"].should.match session_match res.body.should.equal '{"counter"=>1}' end it "determines session from a cookie" do pool = Rack::Session::Pool.new(incrementor) req = Rack::MockRequest.new(pool) cookie = req.get("/")["Set-Cookie"] req.get("/", "HTTP_COOKIE" => cookie). body.should.equal '{"counter"=>2}' req.get("/", "HTTP_COOKIE" => cookie). body.should.equal '{"counter"=>3}' end it "survives nonexistant cookies" do pool = Rack::Session::Pool.new(incrementor) res = Rack::MockRequest.new(pool). get("/", "HTTP_COOKIE" => "#{session_key}=blarghfasel") res.body.should.equal '{"counter"=>1}' end it "does not send the same session id if it did not change" do pool = Rack::Session::Pool.new(incrementor) req = Rack::MockRequest.new(pool) res0 = req.get("/") cookie = res0["Set-Cookie"][session_match] res0.body.should.equal '{"counter"=>1}' pool.pool.size.should.equal 1 res1 = req.get("/", "HTTP_COOKIE" => cookie) res1["Set-Cookie"].should.be.nil res1.body.should.equal '{"counter"=>2}' pool.pool.size.should.equal 1 res2 = req.get("/", "HTTP_COOKIE" => cookie) res2["Set-Cookie"].should.be.nil res2.body.should.equal '{"counter"=>3}' pool.pool.size.should.equal 1 end it "deletes cookies with :drop option" do pool = Rack::Session::Pool.new(incrementor) req = Rack::MockRequest.new(pool) drop = Rack::Utils::Context.new(pool, drop_session) dreq = Rack::MockRequest.new(drop) res1 = req.get("/") session = (cookie = res1["Set-Cookie"])[session_match] res1.body.should.equal '{"counter"=>1}' pool.pool.size.should.equal 1 res2 = dreq.get("/", "HTTP_COOKIE" => cookie) res2["Set-Cookie"].should.be.nil res2.body.should.equal '{"counter"=>2}' pool.pool.size.should.equal 0 res3 = req.get("/", "HTTP_COOKIE" => cookie) res3["Set-Cookie"][session_match].should.not.equal session res3.body.should.equal '{"counter"=>1}' pool.pool.size.should.equal 1 end it "provides new session id with :renew option" do pool = Rack::Session::Pool.new(incrementor) req = Rack::MockRequest.new(pool) renew = Rack::Utils::Context.new(pool, renew_session) rreq = Rack::MockRequest.new(renew) res1 = req.get("/") session = (cookie = res1["Set-Cookie"])[session_match] res1.body.should.equal '{"counter"=>1}' pool.pool.size.should.equal 1 res2 = rreq.get("/", "HTTP_COOKIE" => cookie) new_cookie = res2["Set-Cookie"] new_session = new_cookie[session_match] new_session.should.not.equal session res2.body.should.equal '{"counter"=>2}' pool.pool.size.should.equal 1 res3 = req.get("/", "HTTP_COOKIE" => new_cookie) res3.body.should.equal '{"counter"=>3}' pool.pool.size.should.equal 1 res4 = req.get("/", "HTTP_COOKIE" => cookie) res4.body.should.equal '{"counter"=>1}' pool.pool.size.should.equal 2 end it "omits cookie with :defer option" do pool = Rack::Session::Pool.new(incrementor) defer = Rack::Utils::Context.new(pool, defer_session) dreq = Rack::MockRequest.new(defer) res1 = dreq.get("/") res1["Set-Cookie"].should.equal nil res1.body.should.equal '{"counter"=>1}' pool.pool.size.should.equal 1 end # anyone know how to do this better? it "should merge sessions when multithreaded" do unless $DEBUG 1.should.equal 1 next end warn 'Running multithread tests for Session::Pool' pool = Rack::Session::Pool.new(incrementor) req = Rack::MockRequest.new(pool) res = req.get('/') res.body.should.equal '{"counter"=>1}' cookie = res["Set-Cookie"] sess_id = cookie[/#{pool.key}=([^,;]+)/,1] delta_incrementor = lambda do |env| # emulate disconjoinment of threading env['rack.session'] = env['rack.session'].dup Thread.stop env['rack.session'][(Time.now.usec*rand).to_i] = true incrementor.call(env) end tses = Rack::Utils::Context.new pool, delta_incrementor treq = Rack::MockRequest.new(tses) tnum = rand(7).to_i+5 r = Array.new(tnum) do Thread.new(treq) do |run| run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) end end.reverse.map{|t| t.run.join.value } r.each do |resp| resp['Set-Cookie'].should.equal cookie resp.body.should.include '"counter"=>2' end session = pool.pool[sess_id] session.size.should.equal tnum+1 # counter session['counter'].should.equal 2 # meeeh end it "does not return a cookie if cookie was not read/written" do app = Rack::Session::Pool.new(nothing) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil end it "does not return a cookie if cookie was not written (only read)" do app = Rack::Session::Pool.new(session_id) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil end it "returns even if not read/written if :expire_after is set" do app = Rack::Session::Pool.new(nothing, :expire_after => 3600) res = Rack::MockRequest.new(app).get("/", 'rack.session' => {'not' => 'empty'}) res["Set-Cookie"].should.not.be.nil end it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do app = Rack::Session::Pool.new(nothing, :expire_after => 3600) res = Rack::MockRequest.new(app).get("/") res["Set-Cookie"].should.be.nil end end ruby-rack1.4-1.4.5/test/spec_showexceptions.rb000066400000000000000000000042021223351442100212360ustar00rootroot00000000000000require 'rack/showexceptions' require 'rack/lint' require 'rack/mock' describe Rack::ShowExceptions do def show_exceptions(app) Rack::Lint.new Rack::ShowExceptions.new(app) end it "catches exceptions" do res = nil req = Rack::MockRequest.new( show_exceptions( lambda{|env| raise RuntimeError } )) lambda{ res = req.get("/") }.should.not.raise res.should.be.a.server_error res.status.should.equal 500 res.should =~ /RuntimeError/ res.should =~ /ShowExceptions/ end it "responds with plain text on AJAX requests accepting anything but HTML" do res = nil req = Rack::MockRequest.new( show_exceptions( lambda{|env| raise RuntimeError, "It was never supposed to work" } )) lambda{ res = req.get("/", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest") }.should.not.raise res.should.be.a.server_error res.status.should.equal 500 res.content_type.should.equal "text/plain" res.body.should.include "RuntimeError: It was never supposed to work\n" res.body.should.include __FILE__ end it "responds with HTML on AJAX requests accepting HTML" do res = nil req = Rack::MockRequest.new( show_exceptions( lambda{|env| raise RuntimeError, "It was never supposed to work" } )) lambda{ res = req.get("/", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", "HTTP_ACCEPT" => "text/html") }.should.not.raise res.should.be.a.server_error res.status.should.equal 500 res.content_type.should.equal "text/html" res.body.should.include "RuntimeError" res.body.should.include "It was never supposed to work" res.body.should.include Rack::Utils.escape_html(__FILE__) end it "handles exceptions without a backtrace" do res = nil req = Rack::MockRequest.new( show_exceptions( lambda{|env| raise RuntimeError, "", [] } ) ) lambda{ res = req.get("/") }.should.not.raise res.should.be.a.server_error res.status.should.equal 500 res.should =~ /RuntimeError/ res.should =~ /ShowExceptions/ res.should =~ /unknown location/ end end ruby-rack1.4-1.4.5/test/spec_showstatus.rb000066400000000000000000000043201223351442100204010ustar00rootroot00000000000000require 'rack/showstatus' require 'rack/lint' require 'rack/mock' describe Rack::ShowStatus do def show_status(app) Rack::Lint.new Rack::ShowStatus.new(app) end should "provide a default status message" do req = Rack::MockRequest.new( show_status(lambda{|env| [404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []] })) res = req.get("/", :lint => true) res.should.be.not_found res.should.be.not.empty res["Content-Type"].should.equal("text/html") res.should =~ /404/ res.should =~ /Not Found/ end should "let the app provide additional information" do req = Rack::MockRequest.new( show_status( lambda{|env| env["rack.showstatus.detail"] = "gone too meta." [404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []] })) res = req.get("/", :lint => true) res.should.be.not_found res.should.be.not.empty res["Content-Type"].should.equal("text/html") res.should =~ /404/ res.should =~ /Not Found/ res.should =~ /too meta/ end should "not replace existing messages" do req = Rack::MockRequest.new( show_status( lambda{|env| [404, {"Content-Type" => "text/plain", "Content-Length" => "4"}, ["foo!"]] })) res = req.get("/", :lint => true) res.should.be.not_found res.body.should == "foo!" end should "pass on original headers" do headers = {"WWW-Authenticate" => "Basic blah"} req = Rack::MockRequest.new( show_status(lambda{|env| [401, headers, []] })) res = req.get("/", :lint => true) res["WWW-Authenticate"].should.equal("Basic blah") end should "replace existing messages if there is detail" do req = Rack::MockRequest.new( show_status( lambda{|env| env["rack.showstatus.detail"] = "gone too meta." [404, {"Content-Type" => "text/plain", "Content-Length" => "4"}, ["foo!"]] })) res = req.get("/", :lint => true) res.should.be.not_found res.should.be.not.empty res["Content-Type"].should.equal("text/html") res["Content-Length"].should.not.equal("4") res.should =~ /404/ res.should =~ /too meta/ res.body.should.not =~ /foo/ end end ruby-rack1.4-1.4.5/test/spec_static.rb000066400000000000000000000111511223351442100174440ustar00rootroot00000000000000require 'rack/static' require 'rack/lint' require 'rack/mock' class DummyApp def call(env) [200, {"Content-Type" => "text/plain"}, ["Hello World"]] end end describe Rack::Static do def static(app, *args) Rack::Lint.new Rack::Static.new(app, *args) end root = File.expand_path(File.dirname(__FILE__)) OPTIONS = {:urls => ["/cgi"], :root => root} STATIC_OPTIONS = {:urls => [""], :root => "#{root}/static", :index => 'index.html'} HASH_OPTIONS = {:urls => {"/cgi/sekret" => 'cgi/test'}, :root => root} @request = Rack::MockRequest.new(static(DummyApp.new, OPTIONS)) @static_request = Rack::MockRequest.new(static(DummyApp.new, STATIC_OPTIONS)) @hash_request = Rack::MockRequest.new(static(DummyApp.new, HASH_OPTIONS)) it "serves files" do res = @request.get("/cgi/test") res.should.be.ok res.body.should =~ /ruby/ end it "404s if url root is known but it can't find the file" do res = @request.get("/cgi/foo") res.should.be.not_found end it "calls down the chain if url root is not known" do res = @request.get("/something/else") res.should.be.ok res.body.should == "Hello World" end it "calls index file when requesting root in the given folder" do res = @static_request.get("/") res.should.be.ok res.body.should =~ /index!/ res = @static_request.get("/other/") res.should.be.not_found res = @static_request.get("/another/") res.should.be.ok res.body.should =~ /another index!/ end it "doesn't call index file if :index option was omitted" do res = @request.get("/") res.body.should == "Hello World" end it "serves hidden files" do res = @hash_request.get("/cgi/sekret") res.should.be.ok res.body.should =~ /ruby/ end it "calls down the chain if the URI is not specified" do res = @hash_request.get("/something/else") res.should.be.ok res.body.should == "Hello World" end it "supports serving fixed cache-control (legacy option)" do opts = OPTIONS.merge(:cache_control => 'public') request = Rack::MockRequest.new(static(DummyApp.new, opts)) res = request.get("/cgi/test") res.should.be.ok res.headers['Cache-Control'].should == 'public' end HEADER_OPTIONS = {:urls => ["/cgi"], :root => root, :header_rules => [ [:all, {'Cache-Control' => 'public, max-age=100'}], [:fonts, {'Cache-Control' => 'public, max-age=200'}], [%w(png jpg), {'Cache-Control' => 'public, max-age=300'}], ['/cgi/assets/folder/', {'Cache-Control' => 'public, max-age=400'}], ['cgi/assets/javascripts', {'Cache-Control' => 'public, max-age=500'}], [/\.(css|erb)\z/, {'Cache-Control' => 'public, max-age=600'}] ]} @header_request = Rack::MockRequest.new(static(DummyApp.new, HEADER_OPTIONS)) it "supports header rule :all" do # Headers for all files via :all shortcut res = @header_request.get('/cgi/assets/index.html') res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=100' end it "supports header rule :fonts" do # Headers for web fonts via :fonts shortcut res = @header_request.get('/cgi/assets/fonts/font.eot') res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=200' end it "supports file extension header rules provided as an Array" do # Headers for file extensions via array res = @header_request.get('/cgi/assets/images/image.png') res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=300' end it "supports folder rules provided as a String" do # Headers for files in folder via string res = @header_request.get('/cgi/assets/folder/test.js') res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=400' end it "supports folder header rules provided as a String not starting with a slash" do res = @header_request.get('/cgi/assets/javascripts/app.js') res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=500' end it "supports flexible header rules provided as Regexp" do # Flexible Headers via Regexp res = @header_request.get('/cgi/assets/stylesheets/app.css') res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=600' end it "prioritizes header rules over fixed cache-control setting (legacy option)" do opts = OPTIONS.merge( :cache_control => 'public, max-age=24', :header_rules => [ [:all, {'Cache-Control' => 'public, max-age=42'}] ]) request = Rack::MockRequest.new(static(DummyApp.new, opts)) res = request.get("/cgi/test") res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=42' end end ruby-rack1.4-1.4.5/test/spec_thin.rb000066400000000000000000000047261223351442100171310ustar00rootroot00000000000000begin require 'rack/handler/thin' require File.expand_path('../testrequest', __FILE__) require 'timeout' describe Rack::Handler::Thin do extend TestRequest::Helpers @app = Rack::Lint.new(TestRequest.new) @server = nil Thin::Logging.silent = true @thread = Thread.new do Rack::Handler::Thin.run(@app, :Host => @host='127.0.0.1', :Port => @port=9204) do |server| @server = server end end Thread.pass until @server && @server.running? should "respond" do GET("/") response.should.not.be.nil end should "be a Thin" do GET("/") status.should.equal 200 response["SERVER_SOFTWARE"].should =~ /thin/ response["HTTP_VERSION"].should.equal "HTTP/1.1" response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" response["SERVER_PORT"].should.equal "9204" response["SERVER_NAME"].should.equal "127.0.0.1" end should "have rack headers" do GET("/") response["rack.version"].should.equal [1,0] response["rack.multithread"].should.equal false response["rack.multiprocess"].should.equal false response["rack.run_once"].should.equal false end should "have CGI headers on GET" do GET("/") response["REQUEST_METHOD"].should.equal "GET" response["REQUEST_PATH"].should.equal "/" response["PATH_INFO"].should.be.equal "/" response["QUERY_STRING"].should.equal "" response["test.postdata"].should.equal "" GET("/test/foo?quux=1") response["REQUEST_METHOD"].should.equal "GET" response["REQUEST_PATH"].should.equal "/test/foo" response["PATH_INFO"].should.equal "/test/foo" response["QUERY_STRING"].should.equal "quux=1" end should "have CGI headers on POST" do POST("/", {"rack-form-data" => "23"}, {'X-test-header' => '42'}) status.should.equal 200 response["REQUEST_METHOD"].should.equal "POST" 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 should "support HTTP auth" do GET("/test", {:user => "ruth", :passwd => "secret"}) response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" end should "set status" do GET("/test?secret") status.should.equal 403 response["rack.url_scheme"].should.equal "http" end @server.stop! @thread.kill end rescue LoadError $stderr.puts "Skipping Rack::Handler::Thin tests (Thin is required). `gem install thin` and try again." end ruby-rack1.4-1.4.5/test/spec_urlmap.rb000066400000000000000000000176761223351442100174770ustar00rootroot00000000000000require 'rack/urlmap' require 'rack/mock' describe Rack::URLMap do it "dispatches paths correctly" do app = lambda { |env| [200, { 'X-ScriptName' => env['SCRIPT_NAME'], 'X-PathInfo' => env['PATH_INFO'], 'Content-Type' => 'text/plain' }, [""]] } map = Rack::Lint.new(Rack::URLMap.new({ 'http://foo.org/bar' => app, '/foo' => app, '/foo/bar' => app })) res = Rack::MockRequest.new(map).get("/") res.should.be.not_found res = Rack::MockRequest.new(map).get("/qux") res.should.be.not_found res = Rack::MockRequest.new(map).get("/foo") res.should.be.ok res["X-ScriptName"].should.equal "/foo" res["X-PathInfo"].should.equal "" res = Rack::MockRequest.new(map).get("/foo/") res.should.be.ok res["X-ScriptName"].should.equal "/foo" res["X-PathInfo"].should.equal "/" res = Rack::MockRequest.new(map).get("/foo/bar") res.should.be.ok res["X-ScriptName"].should.equal "/foo/bar" res["X-PathInfo"].should.equal "" res = Rack::MockRequest.new(map).get("/foo/bar/") res.should.be.ok res["X-ScriptName"].should.equal "/foo/bar" res["X-PathInfo"].should.equal "/" res = Rack::MockRequest.new(map).get("/foo///bar//quux") res.status.should.equal 200 res.should.be.ok res["X-ScriptName"].should.equal "/foo/bar" res["X-PathInfo"].should.equal "//quux" res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh") res.should.be.ok res["X-ScriptName"].should.equal "/bleh/foo" res["X-PathInfo"].should.equal "/quux" res = Rack::MockRequest.new(map).get("/bar", 'HTTP_HOST' => 'foo.org') res.should.be.ok res["X-ScriptName"].should.equal "/bar" res["X-PathInfo"].should.be.empty res = Rack::MockRequest.new(map).get("/bar/", 'HTTP_HOST' => 'foo.org') res.should.be.ok res["X-ScriptName"].should.equal "/bar" res["X-PathInfo"].should.equal '/' end it "dispatches hosts correctly" do map = Rack::Lint.new(Rack::URLMap.new("http://foo.org/" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "foo.org", "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], }, [""]]}, "http://subdomain.foo.org/" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "subdomain.foo.org", "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], }, [""]]}, "http://bar.org/" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "bar.org", "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], }, [""]]}, "/" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "default.org", "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], }, [""]]} )) res = Rack::MockRequest.new(map).get("/") res.should.be.ok res["X-Position"].should.equal "default.org" res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org") res.should.be.ok res["X-Position"].should.equal "bar.org" res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org") res.should.be.ok res["X-Position"].should.equal "foo.org" res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "subdomain.foo.org", "SERVER_NAME" => "foo.org") res.should.be.ok res["X-Position"].should.equal "subdomain.foo.org" res = Rack::MockRequest.new(map).get("http://foo.org/") res.should.be.ok res["X-Position"].should.equal "default.org" res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org") res.should.be.ok res["X-Position"].should.equal "default.org" res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org:9292", "SERVER_PORT" => "9292") res.should.be.ok res["X-Position"].should.equal "default.org" end should "be nestable" do map = Rack::Lint.new(Rack::URLMap.new("/foo" => Rack::URLMap.new("/bar" => Rack::URLMap.new("/quux" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "/foo/bar/quux", "X-PathInfo" => env["PATH_INFO"], "X-ScriptName" => env["SCRIPT_NAME"], }, [""]]} )))) res = Rack::MockRequest.new(map).get("/foo/bar") res.should.be.not_found res = Rack::MockRequest.new(map).get("/foo/bar/quux") res.should.be.ok res["X-Position"].should.equal "/foo/bar/quux" res["X-PathInfo"].should.equal "" res["X-ScriptName"].should.equal "/foo/bar/quux" end should "route root apps correctly" do map = Rack::Lint.new(Rack::URLMap.new("/" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "root", "X-PathInfo" => env["PATH_INFO"], "X-ScriptName" => env["SCRIPT_NAME"] }, [""]]}, "/foo" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "foo", "X-PathInfo" => env["PATH_INFO"], "X-ScriptName" => env["SCRIPT_NAME"] }, [""]]} )) res = Rack::MockRequest.new(map).get("/foo/bar") res.should.be.ok res["X-Position"].should.equal "foo" res["X-PathInfo"].should.equal "/bar" res["X-ScriptName"].should.equal "/foo" res = Rack::MockRequest.new(map).get("/foo") res.should.be.ok res["X-Position"].should.equal "foo" res["X-PathInfo"].should.equal "" res["X-ScriptName"].should.equal "/foo" res = Rack::MockRequest.new(map).get("/bar") res.should.be.ok res["X-Position"].should.equal "root" res["X-PathInfo"].should.equal "/bar" res["X-ScriptName"].should.equal "" res = Rack::MockRequest.new(map).get("") res.should.be.ok res["X-Position"].should.equal "root" res["X-PathInfo"].should.equal "/" res["X-ScriptName"].should.equal "" end should "not squeeze slashes" do map = Rack::Lint.new(Rack::URLMap.new("/" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "root", "X-PathInfo" => env["PATH_INFO"], "X-ScriptName" => env["SCRIPT_NAME"] }, [""]]}, "/foo" => lambda { |env| [200, { "Content-Type" => "text/plain", "X-Position" => "foo", "X-PathInfo" => env["PATH_INFO"], "X-ScriptName" => env["SCRIPT_NAME"] }, [""]]} )) res = Rack::MockRequest.new(map).get("/http://example.org/bar") res.should.be.ok res["X-Position"].should.equal "root" res["X-PathInfo"].should.equal "/http://example.org/bar" res["X-ScriptName"].should.equal "" end end ruby-rack1.4-1.4.5/test/spec_utils.rb000066400000000000000000000517071223351442100173300ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require 'rack/utils' require 'rack/mock' require 'timeout' describe Rack::Utils do # A helper method which checks # if certain query parameters # are equal. def equal_query_to(query) parts = query.split('&') lambda{|other| (parts & other.split('&')) == parts } end def kcodeu one8 = RUBY_VERSION.to_f < 1.9 default_kcode, $KCODE = $KCODE, 'U' if one8 yield ensure $KCODE = default_kcode if one8 end should "round trip binary data" do r = [218, 0].pack 'CC' if defined?(::Encoding) z = Rack::Utils.unescape(Rack::Utils.escape(r), Encoding::BINARY) else z = Rack::Utils.unescape(Rack::Utils.escape(r)) end r.should.equal z end should "escape correctly" do Rack::Utils.escape("fobar").should.equal "fo%3Co%3Ebar" Rack::Utils.escape("a space").should.equal "a+space" Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\"). should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C" end should "escape correctly for multibyte characters" do matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8' matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8' end if RUBY_VERSION[/^\d+\.\d+/] == '1.8' should "escape correctly for multibyte characters if $KCODE is set to 'U'" do kcodeu do matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8' matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8' end end should "unescape multibyte characters correctly if $KCODE is set to 'U'" do kcodeu do Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal( "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0]) end end end should "escape objects that responds to to_s" do kcodeu do Rack::Utils.escape(:id).should.equal "id" end end if "".respond_to?(:encode) should "escape non-UTF8 strings" do Rack::Utils.escape("ø".encode("ISO-8859-1")).should.equal "%F8" end end should "not hang on escaping long strings that end in % (http://redmine.ruby-lang.org/issues/5149)" do lambda { timeout(1) do lambda { URI.decode_www_form_component "A string that causes catastrophic backtracking as it gets longer %" }.should.raise(ArgumentError) end }.should.not.raise(Timeout::Error) end should "escape path spaces with %20" do Rack::Utils.escape_path("foo bar").should.equal "foo%20bar" end should "unescape correctly" do Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fobar" Rack::Utils.unescape("a+space").should.equal "a space" Rack::Utils.unescape("a%20space").should.equal "a space" Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C"). should.equal "q1!2\"'w$5&7/z8)?\\" end should "parse query strings correctly" do Rack::Utils.parse_query("foo=bar"). should.equal "foo" => "bar" Rack::Utils.parse_query("foo=\"bar\""). should.equal "foo" => "\"bar\"" Rack::Utils.parse_query("foo=bar&foo=quux"). should.equal "foo" => ["bar", "quux"] Rack::Utils.parse_query("foo=1&bar=2"). should.equal "foo" => "1", "bar" => "2" Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"). should.equal "my weird field" => "q1!2\"'w$5&7/z8)?" Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar" Rack::Utils.parse_query("=").should.equal "" => "" Rack::Utils.parse_query("=value").should.equal "" => "value" Rack::Utils.parse_query("key=").should.equal "key" => "" Rack::Utils.parse_query("&key&").should.equal "key" => nil Rack::Utils.parse_query(";key;", ";,").should.equal "key" => nil Rack::Utils.parse_query(",key,", ";,").should.equal "key" => nil Rack::Utils.parse_query(";foo=bar,;", ";,").should.equal "foo" => "bar" Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar" end should "parse nested query strings correctly" do Rack::Utils.parse_nested_query("foo"). should.equal "foo" => nil Rack::Utils.parse_nested_query("foo="). should.equal "foo" => "" Rack::Utils.parse_nested_query("foo=bar"). should.equal "foo" => "bar" Rack::Utils.parse_nested_query("foo=\"bar\""). should.equal "foo" => "\"bar\"" Rack::Utils.parse_nested_query("foo=bar&foo=quux"). should.equal "foo" => "quux" Rack::Utils.parse_nested_query("foo&foo="). should.equal "foo" => "" Rack::Utils.parse_nested_query("foo=1&bar=2"). should.equal "foo" => "1", "bar" => "2" Rack::Utils.parse_nested_query("&foo=1&&bar=2"). should.equal "foo" => "1", "bar" => "2" Rack::Utils.parse_nested_query("foo&bar="). should.equal "foo" => nil, "bar" => "" Rack::Utils.parse_nested_query("foo=bar&baz="). should.equal "foo" => "bar", "baz" => "" Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"). should.equal "my weird field" => "q1!2\"'w$5&7/z8)?" Rack::Utils.parse_nested_query("a=b&pid%3D1234=1023"). should.equal "pid=1234" => "1023", "a" => "b" Rack::Utils.parse_nested_query("foo[]"). should.equal "foo" => [nil] Rack::Utils.parse_nested_query("foo[]="). should.equal "foo" => [""] Rack::Utils.parse_nested_query("foo[]=bar"). should.equal "foo" => ["bar"] Rack::Utils.parse_nested_query("foo[]=1&foo[]=2"). should.equal "foo" => ["1", "2"] Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3"). should.equal "foo" => "bar", "baz" => ["1", "2", "3"] Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3"). should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"] Rack::Utils.parse_nested_query("x[y][z]=1"). should.equal "x" => {"y" => {"z" => "1"}} Rack::Utils.parse_nested_query("x[y][z][]=1"). should.equal "x" => {"y" => {"z" => ["1"]}} Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2"). should.equal "x" => {"y" => {"z" => "2"}} Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2"). should.equal "x" => {"y" => {"z" => ["1", "2"]}} Rack::Utils.parse_nested_query("x[y][][z]=1"). should.equal "x" => {"y" => [{"z" => "1"}]} Rack::Utils.parse_nested_query("x[y][][z][]=1"). should.equal "x" => {"y" => [{"z" => ["1"]}]} Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2"). should.equal "x" => {"y" => [{"z" => "1", "w" => "2"}]} Rack::Utils.parse_nested_query("x[y][][v][w]=1"). should.equal "x" => {"y" => [{"v" => {"w" => "1"}}]} Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2"). should.equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]} Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2"). should.equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]} Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3"). should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]} lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }. should.raise(TypeError). message.should.equal "expected Hash (got String) for param `y'" lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }. should.raise(TypeError). message.should.match(/expected Array \(got [^)]*\) for param `x'/) lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }. should.raise(TypeError). message.should.equal "expected Array (got String) for param `y'" end should "build query strings correctly" do Rack::Utils.build_query("foo" => "bar").should.be equal_query_to("foo=bar") Rack::Utils.build_query("foo" => ["bar", "quux"]). should.be equal_query_to("foo=bar&foo=quux") Rack::Utils.build_query("foo" => "1", "bar" => "2"). should.be equal_query_to("foo=1&bar=2") Rack::Utils.build_query("my weird field" => "q1!2\"'w$5&7/z8)?"). should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F") end should "build nested query strings correctly" do Rack::Utils.build_nested_query("foo" => nil).should.equal "foo" Rack::Utils.build_nested_query("foo" => "").should.equal "foo=" Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar" Rack::Utils.build_nested_query("foo" => "1", "bar" => "2"). should.be equal_query_to("foo=1&bar=2") Rack::Utils.build_nested_query("my weird field" => "q1!2\"'w$5&7/z8)?"). should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F") Rack::Utils.build_nested_query("foo" => [nil]). should.equal "foo[]" Rack::Utils.build_nested_query("foo" => [""]). should.equal "foo[]=" Rack::Utils.build_nested_query("foo" => ["bar"]). should.equal "foo[]=bar" # The ordering of the output query string is unpredictable with 1.8's # unordered hash. Test that build_nested_query performs the inverse # function of parse_nested_query. [{"foo" => nil, "bar" => ""}, {"foo" => "bar", "baz" => ""}, {"foo" => ["1", "2"]}, {"foo" => "bar", "baz" => ["1", "2", "3"]}, {"foo" => ["bar"], "baz" => ["1", "2", "3"]}, {"foo" => ["1", "2"]}, {"foo" => "bar", "baz" => ["1", "2", "3"]}, {"x" => {"y" => {"z" => "1"}}}, {"x" => {"y" => {"z" => ["1"]}}}, {"x" => {"y" => {"z" => ["1", "2"]}}}, {"x" => {"y" => [{"z" => "1"}]}}, {"x" => {"y" => [{"z" => ["1"]}]}}, {"x" => {"y" => [{"z" => "1", "w" => "2"}]}}, {"x" => {"y" => [{"v" => {"w" => "1"}}]}}, {"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}}, {"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}}, {"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}} ].each { |params| qs = Rack::Utils.build_nested_query(params) Rack::Utils.parse_nested_query(qs).should.equal params } lambda { Rack::Utils.build_nested_query("foo=bar") }. should.raise(ArgumentError). message.should.equal "value must be a Hash" end should "parse query strings that have a non-existent value" do key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit" Rack::Utils.parse_query(key).should.equal Rack::Utils.unescape(key) => nil end should "build query strings without = with non-existent values" do key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit" key = Rack::Utils.unescape(key) Rack::Utils.build_query(key => nil).should.equal Rack::Utils.escape(key) end should "escape html entities [&><'\"/]" do Rack::Utils.escape_html("foo").should.equal "foo" Rack::Utils.escape_html("f&o").should.equal "f&o" Rack::Utils.escape_html("fo").should.equal "f>o" Rack::Utils.escape_html("f'o").should.equal "f'o" Rack::Utils.escape_html('f"o').should.equal "f"o" Rack::Utils.escape_html("f/o").should.equal "f/o" Rack::Utils.escape_html("").should.equal "<foo></foo>" end should "escape html entities even on MRI when it's bugged" do test_escape = lambda do kcodeu do Rack::Utils.escape_html("\300<").should.equal "\300<" end end if RUBY_VERSION.to_f < 1.9 test_escape.call else test_escape.should.raise(ArgumentError) end end if "".respond_to?(:encode) should "escape html entities in unicode strings" do # the following will cause warnings if the regex is poorly encoded: Rack::Utils.escape_html("☃").should.equal "☃" end end should "figure out which encodings are acceptable" do helper = lambda do |a, b| Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a)) Rack::Utils.select_best_encoding(a, b) end helper.call(%w(), [["x", 1]]).should.equal(nil) helper.call(%w(identity), [["identity", 0.0]]).should.equal(nil) helper.call(%w(identity), [["*", 0.0]]).should.equal(nil) helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("identity") helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("compress") helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).should.equal("gzip") helper.call(%w(foo bar identity), []).should.equal("identity") helper.call(%w(foo bar identity), [["*", 1.0]]).should.equal("foo") helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).should.equal("bar") helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).should.equal("identity") helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity") end should "return the bytesize of String" do Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6 end should "should perform constant time string comparison" do Rack::Utils.secure_compare('a', 'a').should.equal true Rack::Utils.secure_compare('a', 'b').should.equal false end should "return status code for integer" do Rack::Utils.status_code(200).should.equal 200 end should "return status code for string" do Rack::Utils.status_code("200").should.equal 200 end should "return status code for symbol" do Rack::Utils.status_code(:ok).should.equal 200 end end describe Rack::Utils, "byte_range" do should "ignore missing or syntactically invalid byte ranges" do Rack::Utils.byte_ranges({},500).should.equal nil Rack::Utils.byte_ranges({"HTTP_RANGE" => "foobar"},500).should.equal nil Rack::Utils.byte_ranges({"HTTP_RANGE" => "furlongs=123-456"},500).should.equal nil Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes="},500).should.equal nil Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-"},500).should.equal nil Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123,456"},500).should.equal nil # A range of non-positive length is syntactically invalid and ignored: Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-123"},500).should.equal nil Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-455"},500).should.equal nil end should "parse simple byte ranges" do Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},500).should.equal [(123..456)] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-"},500).should.equal [(123..499)] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},500).should.equal [(400..499)] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},500).should.equal [(0..0)] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).should.equal [(499..499)] end should "parse several byte ranges" do Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-600,601-999"},1000).should.equal [(500..600),(601..999)] end should "truncate byte ranges" do Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).should.equal [(123..499)] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-999"},500).should.equal [(0..499)] end should "ignore unsatisfiable byte ranges" do Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-501"},500).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-"},500).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=999-"},500).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},500).should.equal [] end should "handle byte ranges of empty files" do Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},0).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-"},0).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},0).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},0).should.equal [] Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},0).should.equal [] end end describe Rack::Utils::HeaderHash do should "retain header case" do h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...") h['ETag'] = 'Boo!' h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!' end should "check existence of keys case insensitively" do h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...") h.should.include 'content-md5' h.should.not.include 'ETag' end should "merge case-insensitively" do h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123') merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR') merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR' end should "overwrite case insensitively and assume the new key's case" do h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz") h["foo-bar"] = "bizzle" h["FOO-BAR"].should.equal "bizzle" h.length.should.equal 1 h.to_hash.should.equal "foo-bar" => "bizzle" end should "be converted to real Hash" do h = Rack::Utils::HeaderHash.new("foo" => "bar") h.to_hash.should.be.instance_of Hash end should "convert Array values to Strings when converting to Hash" do h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"]) h.to_hash.should.equal({ "foo" => "bar\nbaz" }) end should "replace hashes correctly" do h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz") j = {"foo" => "bar"} h.replace(j) h["foo"].should.equal "bar" end should "be able to delete the given key case-sensitively" do h = Rack::Utils::HeaderHash.new("foo" => "bar") h.delete("foo") h["foo"].should.be.nil h["FOO"].should.be.nil end should "be able to delete the given key case-insensitively" do h = Rack::Utils::HeaderHash.new("foo" => "bar") h.delete("FOO") h["foo"].should.be.nil h["FOO"].should.be.nil end should "return the deleted value when #delete is called on an existing key" do h = Rack::Utils::HeaderHash.new("foo" => "bar") h.delete("Foo").should.equal("bar") end should "return nil when #delete is called on a non-existant key" do h = Rack::Utils::HeaderHash.new("foo" => "bar") h.delete("Hello").should.be.nil end should "avoid unnecessary object creation if possible" do a = Rack::Utils::HeaderHash.new("foo" => "bar") b = Rack::Utils::HeaderHash.new(a) b.object_id.should.equal(a.object_id) b.should.equal(a) end should "convert Array values to Strings when responding to #each" do h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"]) h.each do |k,v| k.should.equal("foo") v.should.equal("bar\nbaz") end end should "not create headers out of thin air" do h = Rack::Utils::HeaderHash.new h['foo'] h['foo'].should.be.nil h.should.not.include 'foo' end end describe Rack::Utils::Context do class ContextTest attr_reader :app def initialize app; @app=app; end def call env; context env; end def context env, app=@app; app.call(env); end end test_target1 = proc{|e| e.to_s+' world' } test_target2 = proc{|e| e.to_i+2 } test_target3 = proc{|e| nil } test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] } test_app = ContextTest.new test_target4 should "set context correctly" do test_app.app.should.equal test_target4 c1 = Rack::Utils::Context.new(test_app, test_target1) c1.for.should.equal test_app c1.app.should.equal test_target1 c2 = Rack::Utils::Context.new(test_app, test_target2) c2.for.should.equal test_app c2.app.should.equal test_target2 end should "alter app on recontexting" do c1 = Rack::Utils::Context.new(test_app, test_target1) c2 = c1.recontext(test_target2) c2.for.should.equal test_app c2.app.should.equal test_target2 c3 = c2.recontext(test_target3) c3.for.should.equal test_app c3.app.should.equal test_target3 end should "run different apps" do c1 = Rack::Utils::Context.new test_app, test_target1 c2 = c1.recontext test_target2 c3 = c2.recontext test_target3 c4 = c3.recontext test_target4 a4 = Rack::Lint.new c4 a5 = Rack::Lint.new test_app r1 = c1.call('hello') r1.should.equal 'hello world' r2 = c2.call(2) r2.should.equal 4 r3 = c3.call(:misc_symbol) r3.should.be.nil r4 = Rack::MockRequest.new(a4).get('/') r4.status.should.equal 200 r5 = Rack::MockRequest.new(a5).get('/') r5.status.should.equal 200 r4.body.should.equal r5.body end end ruby-rack1.4-1.4.5/test/spec_webrick.rb000066400000000000000000000110431223351442100176030ustar00rootroot00000000000000require 'rack/mock' require File.expand_path('../testrequest', __FILE__) Thread.abort_on_exception = true describe Rack::Handler::WEBrick do extend TestRequest::Helpers @server = WEBrick::HTTPServer.new(:Host => @host='127.0.0.1', :Port => @port=9202, :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN), :AccessLog => []) @server.mount "/test", Rack::Handler::WEBrick, Rack::Lint.new(TestRequest.new) Thread.new { @server.start } trap(:INT) { @server.shutdown } should "respond" do lambda { GET("/test") }.should.not.raise end should "be a WEBrick" do GET("/test") status.should.equal 200 response["SERVER_SOFTWARE"].should =~ /WEBrick/ response["HTTP_VERSION"].should.equal "HTTP/1.1" response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" response["SERVER_PORT"].should.equal "9202" response["SERVER_NAME"].should.equal "127.0.0.1" end should "have rack headers" do GET("/test") response["rack.version"].should.equal [1,1] response["rack.multithread"].should.be.true response["rack.multiprocess"].should.be.false response["rack.run_once"].should.be.false end 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.equal "" 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" GET("/test/foo%25encoding?quux=1") response["REQUEST_METHOD"].should.equal "GET" response["SCRIPT_NAME"].should.equal "/test" response["REQUEST_PATH"].should.equal "/test/foo%25encoding" response["PATH_INFO"].should.equal "/foo%25encoding" response["QUERY_STRING"].should.equal "quux=1" end 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["PATH_INFO"].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 should "support HTTP auth" do GET("/test", {:user => "ruth", :passwd => "secret"}) response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" end should "set status" do GET("/test?secret") status.should.equal 403 response["rack.url_scheme"].should.equal "http" end should "correctly set cookies" do @server.mount "/cookie-test", Rack::Handler::WEBrick, Rack::Lint.new(lambda { |req| res = Rack::Response.new res.set_cookie "one", "1" res.set_cookie "two", "2" res.finish }) Net::HTTP.start(@host, @port) { |http| res = http.get("/cookie-test") res.code.to_i.should.equal 200 res.get_fields("set-cookie").should.equal ["one=1", "two=2"] } end should "provide a .run" do block_ran = false catch(:done) { Rack::Handler::WEBrick.run(lambda {}, { :Host => '127.0.0.1', :Port => 9210, :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN), :AccessLog => []}) { |server| block_ran = true server.should.be.kind_of WEBrick::HTTPServer @s = server throw :done } } block_ran.should.be.true @s.shutdown end should "return repeated headers" do @server.mount "/headers", Rack::Handler::WEBrick, Rack::Lint.new(lambda { |req| [ 401, { "Content-Type" => "text/plain", "WWW-Authenticate" => "Bar realm=X\nBaz realm=Y" }, [""] ] }) Net::HTTP.start(@host, @port) { |http| res = http.get("/headers") res.code.to_i.should.equal 401 res["www-authenticate"].should.equal "Bar realm=X, Baz realm=Y" } end @server.shutdown end ruby-rack1.4-1.4.5/test/static/000077500000000000000000000000001223351442100161065ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/static/another/000077500000000000000000000000001223351442100175465ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/static/another/index.html000066400000000000000000000000171223351442100215410ustar00rootroot00000000000000another index! ruby-rack1.4-1.4.5/test/static/index.html000066400000000000000000000000071223351442100201000ustar00rootroot00000000000000index! ruby-rack1.4-1.4.5/test/testrequest.rb000066400000000000000000000037341223351442100175430ustar00rootroot00000000000000require 'yaml' require 'net/http' require 'rack/lint' class TestRequest NOSERIALIZE = [Method, Proc, Rack::Lint::InputWrapper] def call(env) status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200 env["test.postdata"] = env["rack.input"].read minienv = env.dup # This may in the future want to replace with a dummy value instead. minienv.delete_if { |k,v| NOSERIALIZE.any? { |c| v.kind_of?(c) } } body = minienv.to_yaml size = body.respond_to?(:bytesize) ? body.bytesize : body.size [status, {"Content-Type" => "text/yaml", "Content-Length" => size.to_s}, [body]] end module Helpers attr_reader :status, :response ROOT = File.expand_path(File.dirname(__FILE__) + "/..") ENV["RUBYOPT"] = "-I#{ROOT}/lib -rubygems" def root ROOT end def rackup "#{ROOT}/bin/rackup" end def GET(path, header={}) Net::HTTP.start(@host, @port) { |http| user = header.delete(:user) passwd = header.delete(:passwd) get = Net::HTTP::Get.new(path, header) get.basic_auth user, passwd if user && passwd http.request(get) { |response| @status = response.code.to_i begin @response = YAML.load(response.body) rescue TypeError, ArgumentError @response = nil end } } end def POST(path, formdata={}, header={}) Net::HTTP.start(@host, @port) { |http| user = header.delete(:user) passwd = header.delete(:passwd) post = Net::HTTP::Post.new(path, header) post.form_data = formdata post.basic_auth user, passwd if user && passwd http.request(post) { |response| @status = response.code.to_i @response = YAML.load(response.body) } } end end end class StreamingRequest def self.call(env) [200, {"Content-Type" => "text/plain"}, new] end def each yield "hello there!\n" sleep 5 yield "that is all.\n" end end ruby-rack1.4-1.4.5/test/unregistered_handler/000077500000000000000000000000001223351442100210145ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/unregistered_handler/rack/000077500000000000000000000000001223351442100217345ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/unregistered_handler/rack/handler/000077500000000000000000000000001223351442100233515ustar00rootroot00000000000000ruby-rack1.4-1.4.5/test/unregistered_handler/rack/handler/unregistered.rb000066400000000000000000000002131223351442100263720ustar00rootroot00000000000000module Rack module Handler # this class doesn't do anything, we're just seeing if we get it. class Unregistered end end endruby-rack1.4-1.4.5/test/unregistered_handler/rack/handler/unregistered_long_one.rb000066400000000000000000000002221223351442100302520ustar00rootroot00000000000000module Rack module Handler # this class doesn't do anything, we're just seeing if we get it. class UnregisteredLongOne end end end