pax_global_header00006660000000000000000000000064125436511350014517gustar00rootroot0000000000000052 comment=d780285afb26638fec9c797266dea14f79b75bbf rack-contrib-1.3.0/000077500000000000000000000000001254365113500140765ustar00rootroot00000000000000rack-contrib-1.3.0/.gitignore000066400000000000000000000001361254365113500160660ustar00rootroot00000000000000/pkg /doc /RDOX ChangeLog /Gemfile.lock /.bundle /test/gemfiles/.bundle /test/gemfiles/*.lock rack-contrib-1.3.0/.travis.yml000066400000000000000000000004041254365113500162050ustar00rootroot00000000000000language: ruby rvm: - 2.2.2 - 2.1.6 - 1.9.3 gemfile: - Gemfile - test/gemfiles/minimum_versions matrix: include: - rvm: 1.8.7 gemfile: test/gemfiles/1.8.7-compatible - rvm: 1.8.7 gemfile: test/gemfiles/1.8.7-minimum_versions rack-contrib-1.3.0/AUTHORS000066400000000000000000000017001254365113500151440ustar00rootroot00000000000000Ryan Tomayko Joshua Peek Jeremy Kemper mynyml Cameron Walters Jon Crosby Matt Todd Pirmin Kalberer Rune Botten Pratik Naik Paul Sadauskas Jeremy Evans Michael Fellinger Geoff Buesing Nicolas Mérouze Cyril Rohr Harry Vangberg James Rosen Mislav Marohnić Ben Brinckerhoff Rafael Souza Stephen Delano TJ Holowaychuk anupom syam ichverstehe kubicek Jordi Polo rack-contrib-1.3.0/CHANGELOG.md000066400000000000000000000001711254365113500157060ustar00rootroot00000000000000# rack-contrib Changelog ## 1.2.1 ### Project * we have a changelog! * README converted to markdown * added a Gemfile rack-contrib-1.3.0/CONTRIBUTING.md000066400000000000000000000066511254365113500163370ustar00rootroot00000000000000After a long hiatus, `rack-contrib` is back under active maintenance! # Reporting bugs If you think you've found a problem in a `rack-contrib` middleware, please accept our apologies. Nothing's perfect, although with your help, we can get closer. When reporting a bug, please provide: * The version of the `rack-contrib` gem (or git hash of the repo) that you are running; * A tiny `config.ru` which demonstrates how you're using the middleware; * An example request which triggers the bug; * A description of what you're seeing happen (so that we can tell that we're seeing the same problem when we reproduce it); and * A description of what you expect to see happen. # Submitting patches New functionality and bug fixes are always welcome. To maintain the quality of the codebase, however, there are a number of things that all patches must have before they can be landed: * Test cases. A bugfix must have a test case which fails prior to the bugfix being applied, and which passes afterwards. Feature additions must have test cases which exercise all features and edge cases. * Documentation. Most bugfixes won't require documentation changes (although some will), but all feature enhancements and new middleware will *definitely* need to have documentation written. Many existing middlewares aren't well documented, we know that, but we're trying to make sure things don't get any *worse* as new things get added. If you're adding a new middleware, please be especially careful to document any additional gems that are required in order for your middleware to work. * Adhere to existing coding conventions. The existing code isn't in a great place, but if you diverge from how things are done at the moment the patch won't get accepted as-is. * Support Ruby 1.8.7. We're going to keep supporting 1.8 as long as Rack proper does. Some existing middleware requires 1.9, but all new middleware, and existing 1.8-compatible middleware, must continue to be 1.8 compatible. We use [Travis CI test runs](https://travis-ci.org/rack/rack-contrib) to validate this. * Require no external dependencies. Some existing middleware depends on additional gems in order to function; we feel that this is an anti-pattern, and so no patches will be accepted which add additional external gems. If your middleware or other change has a dependency on a gem, please discuss it with us; we may accept it and split it out into a separate gem. We will not reject patches which do not meet these standards, however *someone* will have to do the work to bring the patch up to scratch before it can be landed. The maintainers *might* do it, eventually, but it may not happen any time soon. # Release frequency * Bugfix releases (incrementing `Z` in version `X.Y.Z`), which do not change documented behaviour in any way, should be released as soon as the bugfix is landed. * Minor releases (incrementing `Y` in version `X.Y.Z`), which change documented behaviour in ways which are entirely backwards compatible, should be released each month, in the first few days of the month (assuming there are any features outstanding). * Major releases (incrementing `X` in version `X.Y.Z`), which make changes to documented behaviour in ways which mean that existing users of the gem may have to change something about the way they use the gem, should be released no less than six months apart, and ideally far less often than that. rack-contrib-1.3.0/COPYING000066400000000000000000000020441254365113500151310ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2008 The Committers 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. rack-contrib-1.3.0/Gemfile000066400000000000000000000000471254365113500153720ustar00rootroot00000000000000source 'https://rubygems.org' gemspec rack-contrib-1.3.0/README.md000066400000000000000000000126621254365113500153640ustar00rootroot00000000000000# Contributed Rack Middleware and Utilities This package includes a variety of add-on components for Rack, a Ruby web server interface: * `Rack::AcceptFormat` - Adds a format extension at the end of the URI when there is none, corresponding to the mime-type given in the Accept HTTP header. * `Rack::Access` - Limits access based on IP address * `Rack::Backstage` - Returns content of specified file if it exists, which makes it convenient for putting up maintenance pages. * `Rack::CSSHTTPRequest` - Adds CSSHTTPRequest support by encoding responses as CSS for cross-site AJAX-style data loading * `Rack::Callbacks` - Implements DSL for pure before/after filter like Middlewares. * `Rack::Config` - Shared configuration for cooperative middleware. * `Rack::Cookies` - Adds simple cookie jar hash to env * `Rack::Deflect` - Helps protect against DoS attacks. * `Rack::Evil` - Lets the rack application return a response to the client from any place. * `Rack::HostMeta` - Configures `/host-meta` using a block * `Rack::JSONP` - Adds JSON-P support by stripping out the callback param and padding the response with the appropriate callback format. * `Rack::LighttpdScriptNameFix` - Fixes how lighttpd sets the `SCRIPT_NAME` and `PATH_INFO` variables in certain configurations. * `Rack::Locale` - Detects the client locale using the Accept-Language request header and sets a `rack.locale` variable in the environment. * `Rack::MailExceptions` - Rescues exceptions raised from the app and sends a useful email with the exception, stacktrace, and contents of the environment. * `Rack::NestedParams` - parses form params with subscripts (e.g., * "`post[title]=Hello`") into a nested/recursive Hash structure (based on Rails' implementation). * `Rack::NotFound` - A default 404 application. * `Rack::PostBodyContentTypeParser` - Adds support for JSON request bodies. The Rack parameter hash is populated by deserializing the JSON data provided in the request body when the Content-Type is application/json. * `Rack::Printout` - Prints the environment and the response per request * `Rack::ProcTitle` - Displays request information in process title (`$0`) for monitoring/inspection with ps(1). * `Rack::Profiler` - Uses ruby-prof to measure request time. * `Rack::RelativeRedirect` - Transforms relative paths in redirects to absolute URLs. * `Rack::ResponseCache` - Caches responses to requests without query strings to Disk or a user provider Ruby object. Similar to Rails' page caching. * `Rack::ResponseHeaders` - Manipulates response headers object at runtime * `Rack::Sendfile` - Enables `X-Sendfile` support for bodies that can be served from file. * `Rack::Signals` - Installs signal handlers that are safely processed after a request * `Rack::SimpleEndpoint` - Creates simple endpoints with routing rules, similar to Sinatra actions * `Rack::StaticCache` - Modifies the response headers to facilitiate client and proxy caching for static files that minimizes http requests and improves overall load times for second time visitors. * `Rack::TimeZone` - Detects the client's timezone using JavaScript and sets a variable in Rack's environment with the offset from UTC. * `Rack::TryStatic` - Tries to match request to a static file ### Use Git is the quickest way to the rack-contrib sources: git clone git://github.com/rack/rack-contrib.git Gems are available too: gem install rack-contrib Requiring `'rack/contrib'` will add autoloads to the Rack modules for all of the components included. The following example shows what a simple rackup (`config.ru`) file might look like: ```ruby require 'rack' require 'rack/contrib' use Rack::Profiler if ENV['RACK_ENV'] == 'development' use Rack::ETag use Rack::MailExceptions run theapp ``` ### Testing To contribute to the project, begin by cloning the repo and installing the necessary gems: gem install json rack ruby-prof test-spec test-unit To run the entire test suite, run rake test To run a specific component's tests run specrb -Ilib:test -w test/spec_rack_thecomponent.rb This works on ruby 1.8.7 but has problems under ruby 1.9.x. TODO: instructions for 1.9.x and include bundler ### Criteria for inclusion The criteria for middleware being included in this project are roughly as follows: * For patterns that are very common, provide a reference implementation so that other projects do not have to reinvent the wheel. * For patterns that are very useful or interesting, provide a well-done implementation. * The middleware fits in 1 code file and is relatively small. Currently all middleware in the project are < 150 LOC. * The middleware doesn't have any dependencies other than rack and the ruby standard library. These criteria were introduced several years after the start of the project, so some of the included middleware may not meet all of them. In particular, several middleware have external dependencies. It is possible that in some future release of rack-contrib, middleware with external depencies will be removed from the project. When submitting code keep the above criteria in mind and also see the code guidelines in CONTRIBUTING.md. ### Links * rack-contrib on GitHub:: * Rack:: * Rack On GitHub:: * rack-devel mailing list:: * [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/rack/rack-contrib?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) rack-contrib-1.3.0/Rakefile000066400000000000000000000020071254365113500155420ustar00rootroot00000000000000# -*-ruby-*- exec(*(["bundle", "exec", $PROGRAM_NAME] + ARGV)) if ENV['BUNDLE_GEMFILE'].nil? begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => ex $stderr.puts e.message $stderr.puts "Run `bundle install` to install missing gems" exit e.status_code end require 'rdoc/task' require 'rake/testtask' desc "Run all the tests" task :default => [:test] desc "Run specs" Rake::TestTask.new do |t| t.libs << "test" t.test_files = FileList['test/spec_*.rb'] end desc "Generate RDoc documentation" RDoc::Task.new(:rdoc) do |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README' << '--title' << 'Rack Contrib Documentation' << '--charset' << 'utf-8' rdoc.rdoc_dir = "doc" rdoc.rdoc_files.include 'README.rdoc' rdoc.rdoc_files.include('lib/rack/*.rb') rdoc.rdoc_files.include('lib/rack/*/*.rb') end # PACKAGING ================================================================= Bundler::GemHelper.install_tasks task :release do sh "git release" end rack-contrib-1.3.0/lib/000077500000000000000000000000001254365113500146445ustar00rootroot00000000000000rack-contrib-1.3.0/lib/rack/000077500000000000000000000000001254365113500155645ustar00rootroot00000000000000rack-contrib-1.3.0/lib/rack/contrib.rb000066400000000000000000000044741254365113500175620ustar00rootroot00000000000000require 'rack' require 'git-version-bump' module Rack module Contrib def self.release GVB.version end end autoload :AcceptFormat, "rack/contrib/accept_format" autoload :Access, "rack/contrib/access" autoload :BounceFavicon, "rack/contrib/bounce_favicon" autoload :Cookies, "rack/contrib/cookies" autoload :CSSHTTPRequest, "rack/contrib/csshttprequest" autoload :Deflect, "rack/contrib/deflect" autoload :EnforceValidEncoding, "rack/contrib/enforce_valid_encoding" autoload :ExpectationCascade, "rack/contrib/expectation_cascade" autoload :HostMeta, "rack/contrib/host_meta" autoload :GarbageCollector, "rack/contrib/garbagecollector" autoload :JSONP, "rack/contrib/jsonp" autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix" autoload :Locale, "rack/contrib/locale" autoload :MailExceptions, "rack/contrib/mailexceptions" autoload :PostBodyContentTypeParser, "rack/contrib/post_body_content_type_parser" autoload :ProcTitle, "rack/contrib/proctitle" autoload :Profiler, "rack/contrib/profiler" autoload :ResponseHeaders, "rack/contrib/response_headers" autoload :Runtime, "rack/contrib/runtime" autoload :Sendfile, "rack/contrib/sendfile" autoload :Signals, "rack/contrib/signals" autoload :SimpleEndpoint, "rack/contrib/simple_endpoint" autoload :TimeZone, "rack/contrib/time_zone" autoload :Evil, "rack/contrib/evil" autoload :Callbacks, "rack/contrib/callbacks" autoload :NestedParams, "rack/contrib/nested_params" autoload :Config, "rack/contrib/config" autoload :NotFound, "rack/contrib/not_found" autoload :ResponseCache, "rack/contrib/response_cache" autoload :RelativeRedirect, "rack/contrib/relative_redirect" autoload :StaticCache, "rack/contrib/static_cache" autoload :TryStatic, "rack/contrib/try_static" autoload :Printout, "rack/contrib/printout" end rack-contrib-1.3.0/lib/rack/contrib/000077500000000000000000000000001254365113500172245ustar00rootroot00000000000000rack-contrib-1.3.0/lib/rack/contrib/accept_format.rb000066400000000000000000000025421254365113500223630ustar00rootroot00000000000000module Rack # # A Rack middleware for automatically adding a format token at the end of the request path # when there is none. It can detect formats passed in the HTTP_ACCEPT header to populate this token. # # e.g.: # GET /some/resource HTTP/1.1 # Accept: application/json # -> # GET /some/resource.json HTTP/1.1 # Accept: application/json # # You can add custom types with this kind of function (taken from sinatra): # def mime(ext, type) # ext = ".#{ext}" unless ext.to_s[0] == ?. # Rack::Mime::MIME_TYPES[ext.to_s] = type # end # and then: # mime :json, 'application/json' # # Note: it does not take into account multiple media types in the Accept header. # The first media type takes precedence over all the others. # # MIT-License - Cyril Rohr # class AcceptFormat def initialize(app, default_extention = '.html') @ext = default_extention.to_s.strip @ext = ".#{@ext}" unless @ext[0] == ?. @app = app end def call(env) req = Rack::Request.new(env) if ::File.extname(req.path_info).empty? accept = env['HTTP_ACCEPT'].to_s.scan(/[^;,\s]*\/[^;,\s]*/)[0].to_s extension = Rack::Mime::MIME_TYPES.invert[accept] || @ext req.path_info = req.path_info.chomp('/') << "#{extension}" end @app.call(env) end end end rack-contrib-1.3.0/lib/rack/contrib/access.rb000066400000000000000000000042521254365113500210150ustar00rootroot00000000000000require "ipaddr" module Rack ## # Rack middleware for limiting access based on IP address # # # === Options: # # path => ipmasks ipmasks: Array of remote addresses which are allowed to access # # === Examples: # # use Rack::Access, '/backend' => [ '127.0.0.1', '192.168.1.0/24' ] # # class Access attr_reader :options def initialize(app, options = {}) @app = app mapping = options.empty? ? {"/" => ["127.0.0.1"]} : options @mapping = remap(mapping) end def remap(mapping) mapping.map { |location, ipmasks| 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') ipmasks.collect! do |ipmask| ipmask.is_a?(IPAddr) ? ipmask : IPAddr.new(ipmask) end [host, location, match, ipmasks] }.sort_by { |(h, l, m, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first end def call(env) @original_request = Request.new(env) ipmasks = ipmasks_for_path(env) return forbidden! unless ip_authorized?(ipmasks) status, headers, body = @app.call(env) [status, headers, body] end def ipmasks_for_path(env) path = env["PATH_INFO"].to_s hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') @mapping.each do |host, location, match, ipmasks| next unless (hHost == host || sName == host \ || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) next unless path =~ match && rest = $1 next unless rest.empty? || rest[0] == ?/ return ipmasks end nil end def forbidden! [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []] end def ip_authorized?(ipmasks) return true if ipmasks.nil? ipmasks.any? do |ip_mask| ip_mask.include?(IPAddr.new(@original_request.ip)) end end end end rack-contrib-1.3.0/lib/rack/contrib/backstage.rb000066400000000000000000000007131254365113500214760ustar00rootroot00000000000000module Rack class Backstage File = ::File def initialize(app, path) @app = app @file = File.expand_path(path) end def call(env) if File.exists?(@file) content = File.read(@file) length = "".respond_to?(:bytesize) ? content.bytesize.to_s : content.size.to_s [503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]] else @app.call(env) end end end end rack-contrib-1.3.0/lib/rack/contrib/bounce_favicon.rb000066400000000000000000000005131254365113500225300ustar00rootroot00000000000000module Rack # Bounce those annoying favicon.ico requests class BounceFavicon def initialize(app) @app = app end def call(env) if env["PATH_INFO"] == "/favicon.ico" [404, {"Content-Type" => "text/html", "Content-Length" => "0"}, []] else @app.call(env) end end end end rack-contrib-1.3.0/lib/rack/contrib/callbacks.rb000066400000000000000000000013121254365113500214650ustar00rootroot00000000000000module Rack class Callbacks def initialize(&block) @before = [] @after = [] instance_eval(&block) if block_given? end def before(middleware, *args, &block) if block_given? @before << middleware.new(*args, &block) else @before << middleware.new(*args) end end def after(middleware, *args, &block) if block_given? @after << middleware.new(*args, &block) else @after << middleware.new(*args) end end def run(app) @app = app end def call(env) @before.each {|c| c.call(env) } response = @app.call(env) @after.inject(response) {|r, c| c.call(r) } end end end rack-contrib-1.3.0/lib/rack/contrib/common_cookies.rb000066400000000000000000000016471254365113500225650ustar00rootroot00000000000000module Rack # Rack middleware to use common cookies across domain and subdomains. class CommonCookies DOMAIN_REGEXP = /([^.]*)\.([^.]*|..\...|...\...|..\....)$/ LOCALHOST_OR_IP_REGEXP = /^([\d.]+|localhost)$/ PORT = /:\d+$/ def initialize(app) @app = app end def call(env) @app.call(env).tap do |(status, headers, response)| @host = env['HTTP_HOST'].sub PORT, '' share_cookie headers end end private def domain @host =~ DOMAIN_REGEXP ".#{$1}.#{$2}" end def share_cookie(headers) headers['Set-Cookie'] &&= common_cookie(headers) if @host !~ LOCALHOST_OR_IP_REGEXP end def cookie(headers) cookies = headers['Set-Cookie'] cookies.is_a?(Array) ? cookies.join("\n") : cookies end def common_cookie(headers) cookie(headers).gsub(/; domain=[^;]*/, '').gsub(/$/, "; domain=#{domain}") end end endrack-contrib-1.3.0/lib/rack/contrib/config.rb000066400000000000000000000004261254365113500210200ustar00rootroot00000000000000module 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 rack-contrib-1.3.0/lib/rack/contrib/cookies.rb000066400000000000000000000021251254365113500212050ustar00rootroot00000000000000module Rack class Cookies class CookieJar < Hash def initialize(cookies) @set_cookies = {} @delete_cookies = {} super() update(cookies) end def [](name) super(name.to_s) end def []=(key, options) unless options.is_a?(Hash) options = { :value => options } end options[:path] ||= '/' @set_cookies[key] = options super(key.to_s, options[:value]) end def delete(key, options = {}) options[:path] ||= '/' @delete_cookies[key] = options super(key.to_s) end def finish!(resp) @set_cookies.each { |k, v| resp.set_cookie(k, v) } @delete_cookies.each { |k, v| resp.delete_cookie(k, v) } end end def initialize(app) @app = app end def call(env) req = Request.new(env) env['rack.cookies'] = cookies = CookieJar.new(req.cookies) status, headers, body = @app.call(env) resp = Response.new(body, status, headers) cookies.finish!(resp) resp.to_a end end end rack-contrib-1.3.0/lib/rack/contrib/csshttprequest.rb000066400000000000000000000020311254365113500226460ustar00rootroot00000000000000require 'csshttprequest' module Rack # A Rack middleware for providing CSSHTTPRequest responses. class CSSHTTPRequest def initialize(app) @app = app end # Proxies the request to the application then encodes the response with # the CSSHTTPRequest encoder def call(env) status, headers, response = @app.call(env) if chr_request?(env) response = encode(response) modify_headers!(headers, response) end [status, headers, response] end def chr_request?(env) env['csshttprequest.chr'] ||= !(/\.chr$/.match(env['PATH_INFO'])).nil? || Rack::Request.new(env).params['_format'] == 'chr' end def encode(response, assembled_body="") response.each { |s| assembled_body << s.to_s } # call down the stack return ::CSSHTTPRequest.encode(assembled_body) end def modify_headers!(headers, encoded_response) headers['Content-Length'] = encoded_response.length.to_s headers['Content-Type'] = 'text/css' nil end end end rack-contrib-1.3.0/lib/rack/contrib/deflect.rb000066400000000000000000000072521254365113500211650ustar00rootroot00000000000000require 'thread' # TODO: optional stats # TODO: performance # TODO: clean up tests module Rack ## # Rack middleware for protecting against Denial-of-service attacks # http://en.wikipedia.org/wiki/Denial-of-service_attack. # # This middleware is designed for small deployments, which most likely # are not utilizing load balancing from other software or hardware. Deflect # current supports the following functionality: # # * Saturation prevention (small DoS attacks, or request abuse) # * Blacklisting of remote addresses # * Whitelisting of remote addresses # * Logging # # === Options: # # :log When false logging will be bypassed, otherwise pass an object responding to #puts # :log_format Alter the logging format # :log_date_format Alter the logging date format # :request_threshold Number of requests allowed within the set :interval. Defaults to 100 # :interval Duration in seconds until the request counter is reset. Defaults to 5 # :block_duration Duration in seconds that a remote address will be blocked. Defaults to 900 (15 minutes) # :whitelist Array of remote addresses which bypass Deflect. NOTE: this does not block others # :blacklist Array of remote addresses immediately considered malicious # # === Examples: # # use Rack::Deflect, :log => $stdout, :request_threshold => 20, :interval => 2, :block_duration => 60 # # CREDIT: TJ Holowaychuk # class Deflect attr_reader :options def initialize app, options = {} @mutex = Mutex.new @remote_addr_map = {} @app, @options = app, { :log => false, :log_format => 'deflect(%s): %s', :log_date_format => '%m/%d/%Y', :request_threshold => 100, :interval => 5, :block_duration => 900, :whitelist => [], :blacklist => [] }.merge(options) end def call env return deflect! if deflect? env status, headers, body = @app.call env [status, headers, body] end def deflect! [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []] end def deflect? env @remote_addr = env['REMOTE_ADDR'] return false if options[:whitelist].include? @remote_addr return true if options[:blacklist].include? @remote_addr sync { watch } end def log message return unless options[:log] options[:log].puts(options[:log_format] % [Time.now.strftime(options[:log_date_format]), message]) end def sync &block @mutex.synchronize(&block) end def map @remote_addr_map[@remote_addr] ||= { :expires => Time.now + options[:interval], :requests => 0 } end def watch increment_requests clear! if watch_expired? and not blocked? clear! if blocked? and block_expired? block! if watching? and exceeded_request_threshold? blocked? end def block! return if blocked? log "blocked #{@remote_addr}" map[:block_expires] = Time.now + options[:block_duration] end def blocked? map.has_key? :block_expires end def block_expired? map[:block_expires] < Time.now rescue false end def watching? @remote_addr_map.has_key? @remote_addr end def clear! return unless watching? log "released #{@remote_addr}" if blocked? @remote_addr_map.delete @remote_addr end def increment_requests map[:requests] += 1 end def exceeded_request_threshold? map[:requests] > options[:request_threshold] end def watch_expired? map[:expires] <= Time.now end end end rack-contrib-1.3.0/lib/rack/contrib/enforce_valid_encoding.rb000066400000000000000000000012211254365113500242130ustar00rootroot00000000000000module Rack # Ensure that the path and query string presented to the application # contains only valid characters. If the validation fails, then a # 400 Bad Request response is returned immediately. # # Requires: Ruby 1.9 # class EnforceValidEncoding def initialize app @app = app end def call env full_path = (env.fetch('PATH_INFO', '') + env.fetch('QUERY_STRING', '')) if full_path.force_encoding("US-ASCII").valid_encoding? && Rack::Utils.unescape(full_path).valid_encoding? @app.call env else [400, {'Content-Type'=>'text/plain'}, ['Bad Request']] end end end end rack-contrib-1.3.0/lib/rack/contrib/evil.rb000066400000000000000000000003741254365113500205140ustar00rootroot00000000000000module Rack class Evil # Lets you return a response to the client immediately from anywhere ( M V or C ) in the code. def initialize(app) @app = app end def call(env) catch(:response) { @app.call(env) } end end end rack-contrib-1.3.0/lib/rack/contrib/expectation_cascade.rb000066400000000000000000000014161254365113500235410ustar00rootroot00000000000000module Rack class ExpectationCascade Expect = "Expect".freeze ContinueExpectation = "100-continue".freeze ExpectationFailed = [417, {"Content-Type" => "text/html"}, []].freeze NotFound = [404, {"Content-Type" => "text/html"}, []].freeze attr_reader :apps def initialize @apps = [] yield self if block_given? end def call(env) set_expectation = env[Expect] != ContinueExpectation env[Expect] = ContinueExpectation if set_expectation @apps.each do |app| result = app.call(env) return result unless result[0].to_i == 417 end set_expectation ? NotFound : ExpectationFailed ensure env.delete(Expect) if set_expectation end def <<(app) @apps << app end end end rack-contrib-1.3.0/lib/rack/contrib/garbagecollector.rb000066400000000000000000000003341254365113500230500ustar00rootroot00000000000000module Rack # Forces garbage collection after each request. class GarbageCollector def initialize(app) @app = app end def call(env) @app.call(env) ensure GC.start end end end rack-contrib-1.3.0/lib/rack/contrib/host_meta.rb000066400000000000000000000026311254365113500215360ustar00rootroot00000000000000module Rack # Rack middleware implementing the IETF draft: "Host Metadata for the Web" # including support for Link-Pattern elements as described in the IETF draft: # "Link-based Resource Descriptor Discovery." # # Usage: # use Rack::HostMeta do # link :uri => '/robots.txt', :rel => 'robots' # link :uri => '/w3c/p3p.xml', :rel => 'privacy', :type => 'application/p3p.xml' # link :pattern => '{uri};json_schema', :rel => 'describedby', :type => 'application/x-schema+json' # end # # See also: # http://tools.ietf.org/html/draft-nottingham-site-meta # http://tools.ietf.org/html/draft-hammer-discovery # # TODO: # Accept POST operations allowing downstream services to register themselves # class HostMeta def initialize(app, &block) @app = app @lines = [] instance_eval(&block) @response = @lines.join("\n") end def call(env) if env['PATH_INFO'] == '/host-meta' [200, {'Content-Type' => 'application/host-meta'}, [@response]] else @app.call(env) end end protected def link(config) line = config[:uri] ? "Link: <#{config[:uri]}>;" : "Link-Pattern: <#{config[:pattern]}>;" fragments = [] fragments << "rel=\"#{config[:rel]}\"" if config[:rel] fragments << "type=\"#{config[:type]}\"" if config[:type] @lines << "#{line} #{fragments.join("; ")}" end end end rack-contrib-1.3.0/lib/rack/contrib/jsonp.rb000066400000000000000000000073251254365113500207110ustar00rootroot00000000000000module Rack # A Rack middleware for providing JSON-P support. # # Full credit to Flinn Mueller (http://actsasflinn.com/) for this contribution. # class JSONP include Rack::Utils VALID_JS_VAR = /[a-zA-Z_$][\w$]*/ VALID_CALLBACK = /\A#{VALID_JS_VAR}(?:\.?#{VALID_JS_VAR})*\z/ # These hold the Unicode characters \u2028 and \u2029. # # They are defined in constants for Ruby 1.8 compatibility. # # In 1.8 # "\u2028" # => "u2028" # "\u2029" # => "u2029" # In 1.9 # "\342\200\250" # => "\u2028" # "\342\200\251" # => "\u2029" U2028, U2029 = ("\u2028" == 'u2028') ? ["\342\200\250", "\342\200\251"] : ["\u2028", "\u2029"] def initialize(app) @app = app end # Proxies the request to the application, stripping out the JSON-P callback # method and padding the response with the appropriate callback format if # the returned body is application/json # # Changes nothing if no callback param is specified. # def call(env) request = Rack::Request.new(env) status, headers, response = @app.call(env) if STATUS_WITH_NO_ENTITY_BODY.include?(status) return status, headers, response end headers = HeaderHash.new(headers) if is_json?(headers) && has_callback?(request) callback = request.params['callback'] return bad_request unless valid_callback?(callback) response = pad(callback, response) # No longer json, its javascript! headers['Content-Type'] = headers['Content-Type'].gsub('json', 'javascript') # Set new Content-Length, if it was set before we mutated the response body if headers['Content-Length'] length = response.to_ary.inject(0) { |len, part| len + bytesize(part) } headers['Content-Length'] = length.to_s end end [status, headers, response] end private def is_json?(headers) headers.key?('Content-Type') && headers['Content-Type'].include?('application/json') end def has_callback?(request) request.params.include?('callback') and not request.params['callback'].to_s.empty? end # See: # http://stackoverflow.com/questions/1661197/valid-characters-for-javascript-variable-names # # NOTE: Supports dots (.) since callbacks are often in objects: # def valid_callback?(callback) callback =~ VALID_CALLBACK end # Pads the response with the appropriate callback format according to the # JSON-P spec/requirements. # # The Rack response spec indicates that it should be enumerable. The # method of combining all of the data into a single string makes sense # since JSON is returned as a full string. # def pad(callback, response, body = "") response.each do |s| # U+2028 and U+2029 are allowed inside strings in JSON (as all literal # Unicode characters) but JavaScript defines them as newline # seperators. Because no literal newlines are allowed in a string, this # causes a ParseError in the browser. We work around this issue by # replacing them with the escaped version. This should be safe because # according to the JSON spec, these characters are *only* valid inside # a string and should therefore not be present any other places. body << s.to_s.gsub(U2028, '\u2028').gsub(U2029, '\u2029') end # https://github.com/rack/rack-contrib/issues/46 response.close if response.respond_to?(:close) ["/**/#{callback}(#{body})"] end def bad_request(body = "Bad Request") [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.size.to_s }, [body] ] end end end rack-contrib-1.3.0/lib/rack/contrib/lighttpd_script_name_fix.rb000066400000000000000000000005771254365113500246330ustar00rootroot00000000000000module Rack # Lighttpd sets the wrong SCRIPT_NAME and PATH_INFO if you mount your # FastCGI app at "/". This middleware fixes this issue. 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 end rack-contrib-1.3.0/lib/rack/contrib/locale.rb000066400000000000000000000017461254365113500210200ustar00rootroot00000000000000require 'i18n' module Rack class Locale def initialize(app) @app = app end def call(env) old_locale = I18n.locale begin locale = accept_locale(env) || I18n.default_locale locale = env['rack.locale'] = I18n.locale = locale.to_s status, headers, body = @app.call(env) headers['Content-Language'] = locale unless headers['Content-Language'] [status, headers, body] ensure I18n.locale = old_locale end end private # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 def accept_locale(env) accept_langs = env["HTTP_ACCEPT_LANGUAGE"] return if accept_langs.nil? languages_and_qvalues = accept_langs.split(",").map { |l| l += ';q=1.0' unless l =~ /;q=\d+(?:\.\d+)?$/ l.split(';q=') } lang = languages_and_qvalues.sort_by { |(locale, qvalue)| qvalue.to_f }.last.first lang == '*' ? nil : lang end end end rack-contrib-1.3.0/lib/rack/contrib/mailexceptions.rb000066400000000000000000000074661254365113500226120ustar00rootroot00000000000000require 'net/smtp' require 'mail' require 'erb' module Rack # Catches all exceptions raised from the app it wraps and # sends a useful email with the exception, stacktrace, and # contents of the environment. # use smtp # use Rack::MailExceptions do |mail| # mail.to 'test@gmail.com' # mail.smtp :address => 'mail.test.com', :user_name => 'test@test.com', :password => 'test' # end # use sendmail # use Rack::MailExceptions do |mail| # mail.to 'test@gmail.com' # mail.smtp false # end class MailExceptions attr_reader :config def initialize(app) @app = app @config = { :to => nil, :from => ENV['USER'] || 'rack@localhost', :subject => '[exception] %s', :smtp => { :address => 'localhost', :domain => 'localhost', :port => 25, :authentication => :login, :user_name => nil, :password => nil } } @template = ERB.new(TEMPLATE) @test_mode = false yield self if block_given? end def call(env) status, headers, body = begin @app.call(env) rescue => boom send_notification boom, env raise end send_notification env['mail.exception'], env if env['mail.exception'] [status, headers, body] end %w[to from subject].each do |meth| define_method(meth) { |value| @config[meth.to_sym] = value } end def smtp(settings={}) if settings @config[:smtp].merge! settings else @config[:smtp] = nil end end def enable_test_mode @test_mode = true end def disable_test_mode @test_mode = false end private def generate_mail(exception, env) Mail.new({ :from => config[:from], :to => config[:to], :subject => config[:subject] % [exception.to_s], :body => @template.result(binding), :charset => "UTF-8" }) end def send_notification(exception, env) mail = generate_mail(exception, env) if @test_mode mail.delivery_method :test elsif config[:smtp] smtp = config[:smtp] # for backward compability, replace the :server key with :address address = smtp.delete :server smtp[:address] = address if address mail.delivery_method :smtp, smtp else mail.delivery_method :sendmail end mail.deliver! env['mail.sent'] = true mail end def extract_body(env) if io = env['rack.input'] io.rewind if io.respond_to?(:rewind) io.read end end TEMPLATE = (<<-'EMAIL').gsub(/^ {4}/, '') A <%= exception.class.to_s %> occured: <%= exception.to_s %> <% if body = extract_body(env) %> =================================================================== Request Body: =================================================================== <%= body.gsub(/^/, ' ') %> <% end %> =================================================================== Rack Environment: =================================================================== PID: <%= $$ %> PWD: <%= Dir.getwd %> <%= env.to_a. sort{|a,b| a.first <=> b.first}. map do |k,v| if k == 'HTTP_AUTHORIZATION' and v =~ /^Basic / v = 'Basic *filtered*' end "%-25s%p" % [k+':', v] end. join("\n ") %> <% if exception.respond_to?(:backtrace) %> =================================================================== Backtrace: =================================================================== <%= exception.backtrace.join("\n ") %> <% end %> EMAIL end end rack-contrib-1.3.0/lib/rack/contrib/nested_params.rb000066400000000000000000000105611254365113500224010ustar00rootroot00000000000000require 'cgi' require 'strscan' module Rack # Rack middleware for parsing POST/PUT body data into nested parameters class NestedParams CONTENT_TYPE = 'CONTENT_TYPE'.freeze POST_BODY = 'rack.input'.freeze FORM_INPUT = 'rack.request.form_input'.freeze FORM_HASH = 'rack.request.form_hash'.freeze FORM_VARS = 'rack.request.form_vars'.freeze # supported content type URL_ENCODED = 'application/x-www-form-urlencoded'.freeze def initialize(app) @app = app end def call(env) if form_vars = env[FORM_VARS] env[FORM_HASH] = parse_query_parameters(form_vars) elsif env[CONTENT_TYPE] == URL_ENCODED post_body = env[POST_BODY] env[FORM_INPUT] = post_body env[FORM_HASH] = parse_query_parameters(post_body.read) post_body.rewind if post_body.respond_to?(:rewind) end @app.call(env) end ## the rest is nabbed from Rails ## def parse_query_parameters(query_string) return {} if query_string.nil? or query_string.empty? pairs = query_string.split('&').collect do |chunk| next if chunk.empty? key, value = chunk.split('=', 2) next if key.empty? value = value.nil? ? nil : CGI.unescape(value) [ CGI.unescape(key), value ] end.compact UrlEncodedPairParser.new(pairs).result end class UrlEncodedPairParser < StringScanner attr_reader :top, :parent, :result def initialize(pairs = []) super('') @result = {} pairs.each { |key, value| parse(key, value) } end KEY_REGEXP = %r{([^\[\]=&]+)} BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]} # Parse the query string def parse(key, value) self.string = key @top, @parent = result, nil # First scan the bare key key = scan(KEY_REGEXP) or return key = post_key_check(key) # Then scan as many nestings as present until eos? r = scan(BRACKETED_KEY_REGEXP) or return key = self[1] key = post_key_check(key) end bind(key, value) end private # After we see a key, we must look ahead to determine our next action. Cases: # # [] follows the key. Then the value must be an array. # = follows the key. (A value comes next) # & or the end of string follows the key. Then the key is a flag. # otherwise, a hash follows the key. def post_key_check(key) if scan(/\[\]/) # a[b][] indicates that b is an array container(key, Array) nil elsif check(/\[[^\]]/) # a[b] indicates that a is a hash container(key, Hash) nil else # End of key? We do nothing. key end end # Add a container to the stack. def container(key, klass) type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass) value = bind(key, klass.new) type_conflict! klass, value unless value.is_a?(klass) push(value) end # Push a value onto the 'stack', which is actually only the top 2 items. def push(value) @parent, @top = @top, value end # Bind a key (which may be nil for items in an array) to the provided value. def bind(key, value) if top.is_a? Array if key if top[-1].is_a?(Hash) && ! top[-1].key?(key) top[-1][key] = value else top << {key => value} end push top.last return top[key] else top << value return value end elsif top.is_a? Hash key = CGI.unescape(key) parent << (@top = {}) if top.key?(key) && parent.is_a?(Array) top[key] ||= value return top[key] else raise ArgumentError, "Don't know what to do: top is #{top.inspect}" end end def type_conflict!(klass, value) raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)" end end end end rack-contrib-1.3.0/lib/rack/contrib/not_found.rb000066400000000000000000000006051254365113500215450ustar00rootroot00000000000000module Rack # Rack::NotFound is a default endpoint. Initialize with the path to # your 404 page. class NotFound F = ::File def initialize(path) file = F.expand_path(path) @content = F.read(file) @length = @content.size.to_s end def call(env) [404, {'Content-Type' => 'text/html', 'Content-Length' => @length}, [@content]] end end end rack-contrib-1.3.0/lib/rack/contrib/post_body_content_type_parser.rb000066400000000000000000000017451254365113500257310ustar00rootroot00000000000000begin require 'json' rescue LoadError => e require 'json/pure' end module Rack # A Rack middleware for parsing POST/PUT body data when Content-Type is # not one of the standard supported types, like application/json. # # TODO: Find a better name. # class PostBodyContentTypeParser # Constants # CONTENT_TYPE = 'CONTENT_TYPE'.freeze POST_BODY = 'rack.input'.freeze FORM_INPUT = 'rack.request.form_input'.freeze FORM_HASH = 'rack.request.form_hash'.freeze # Supported Content-Types # APPLICATION_JSON = 'application/json'.freeze def initialize(app) @app = app end def call(env) if Rack::Request.new(env).media_type == APPLICATION_JSON && (body = env[POST_BODY].read).length != 0 env[POST_BODY].rewind # somebody might try to read this stream env.update(FORM_HASH => JSON.parse(body, :create_additions => false), FORM_INPUT => env[POST_BODY]) end @app.call(env) end end end rack-contrib-1.3.0/lib/rack/contrib/printout.rb000066400000000000000000000011311254365113500214310ustar00rootroot00000000000000module Rack #prints the environment and request for simple debugging class Printout def initialize(app) @app = app end def call(env) # See http://rack.rubyforge.org/doc/SPEC.html for details puts "**********\n Environment\n **************" puts env.inspect puts "**********\n Response\n **************" response = @app.call(env) puts response.inspect puts "**********\n Response contents\n **************" response[2].each do |chunk| puts chunk end puts "\n \n" return response end end end rack-contrib-1.3.0/lib/rack/contrib/proctitle.rb000066400000000000000000000015771254365113500215700ustar00rootroot00000000000000module Rack # Middleware to update the process title ($0) with information about the # current request. Based loosely on: # - http://purefiction.net/mongrel_proctitle/ # - http://github.com/grempe/thin-proctitle/tree/master # # NOTE: This will not work properly in a multi-threaded environment. class ProcTitle F = ::File PROGNAME = F.basename($0) def initialize(app) @app = app @appname = Dir.pwd.split('/').reverse. find { |name| name !~ /^(\d+|current|releases)$/ } || PROGNAME @requests = 0 $0 = "#{PROGNAME} [#{@appname}] init ..." end def call(env) host, port = env['SERVER_NAME'], env['SERVER_PORT'] meth, path = env['REQUEST_METHOD'], env['PATH_INFO'] @requests += 1 $0 = "#{PROGNAME} [#{@appname}/#{port}] (#{@requests}) " \ "#{meth} #{path}" @app.call(env) end end end rack-contrib-1.3.0/lib/rack/contrib/profiler.rb000066400000000000000000000054411254365113500213770ustar00rootroot00000000000000require 'ruby-prof' module Rack # Set the profile=process_time query parameter to download a # calltree profile of the request. # # Pass the :printer option to pick a different result format. class Profiler MODES = %w(process_time wall_time cpu_time allocations memory gc_runs gc_time) DEFAULT_PRINTER = :call_stack CONTENT_TYPES = Hash.new('application/octet-stream').merge( 'RubyProf::FlatPrinter' => 'text/plain', 'RubyProf::GraphPrinter' => 'text/plain', 'RubyProf::GraphHtmlPrinter' => 'text/html', 'RubyProf::CallStackPrinter' => 'text/html') # Accepts a :printer => [:call_stack|:call_tree|:graph_html|:graph|:flat] # option defaulting to :call_stack. def initialize(app, options = {}) @app = app @printer = parse_printer(options[:printer] || DEFAULT_PRINTER) @times = (options[:times] || 1).to_i end def call(env) if mode = profiling?(env) profile(env, mode) else @app.call(env) end end private def profiling?(env) unless ::RubyProf.running? request = Rack::Request.new(env.clone) if mode = request.params.delete('profile') if ::RubyProf.const_defined?(mode.upcase) mode else env['rack.errors'].write "Invalid RubyProf measure_mode: " + "#{mode}. Use one of #{MODES.to_a.join(', ')}" false end end end end def profile(env, mode) ::RubyProf.measure_mode = ::RubyProf.const_get(mode.upcase) GC.enable_stats if GC.respond_to?(:enable_stats) result = ::RubyProf.profile do @times.times { @app.call(env) } end GC.disable_stats if GC.respond_to?(:disable_stats) [200, headers(@printer, env, mode), print(@printer, result)] end def print(printer, result) body = StringIO.new printer.new(result).print(body, :min_percent => 0.01) body.rewind body end def headers(printer, env, mode) headers = { 'Content-Type' => CONTENT_TYPES[printer.name] } if printer == ::RubyProf::CallTreePrinter filename = ::File.basename(env['PATH_INFO']) headers['Content-Disposition'] = %(attachment; filename="#{filename}.#{mode}.tree") end headers end def parse_printer(printer) if printer.is_a?(Class) printer else name = "#{camel_case(printer)}Printer" if ::RubyProf.const_defined?(name) ::RubyProf.const_get(name) else ::RubyProf::FlatPrinter end end end def camel_case(word) word.to_s.gsub(/(?:^|_)(.)/) { $1.upcase } end end end rack-contrib-1.3.0/lib/rack/contrib/relative_redirect.rb000066400000000000000000000037011254365113500232460ustar00rootroot00000000000000require 'rack' # Rack::RelativeRedirect is a simple middleware that converts relative paths in # redirects in absolute urls, so they conform to RFC2616. It allows the user to # specify the absolute path to use (with a sensible default), and handles # relative paths (those that don't start with a slash) as well. class Rack::RelativeRedirect SCHEME_MAP = {'http'=>'80', 'https'=>'443'} # The default proc used if a block is not provided to .new # Just uses the url scheme of the request and the server name. DEFAULT_ABSOLUTE_PROC = proc do |env, res| port = env['SERVER_PORT'] scheme = env['rack.url_scheme'] "#{scheme}://#{env['SERVER_NAME']}#{":#{port}" unless SCHEME_MAP[scheme] == port}" end # Initialize a new RelativeRedirect object with the given arguments. Arguments: # * app : The next middleware in the chain. This is always called. # * &block : If provided, it is called with the environment and the response # from the next middleware. It should return a string representing the scheme # and server name (such as 'http://example.org'). def initialize(app, &block) @app = app @absolute_proc = block || DEFAULT_ABSOLUTE_PROC end # Call the next middleware with the environment. If the request was a # redirect (response status 301, 302, or 303), and the location header does # not start with an http or https url scheme, call the block provided by new # and use that to make the Location header an absolute url. If the Location # does not start with a slash, make location relative to the path requested. def call(env) res = @app.call(env) if [301,302,303, 307,308].include?(res[0]) and loc = res[1]['Location'] and !%r{\Ahttps?://}o.match(loc) absolute = @absolute_proc.call(env, res) res[1]['Location'] = if %r{\A/}.match(loc) "#{absolute}#{loc}" else "#{absolute}#{File.dirname(Rack::Utils.unescape(env['PATH_INFO']))}/#{loc}" end end res end end rack-contrib-1.3.0/lib/rack/contrib/response_cache.rb000066400000000000000000000057411254365113500225410ustar00rootroot00000000000000require 'fileutils' require 'rack' # Rack::ResponseCache is a Rack middleware that caches responses for successful # GET requests with no query string to disk or any ruby object that has an # []= method (so it works with memcached). When caching to disk, it works similar to # Rails' page caching, allowing you to cache dynamic pages to static files that can # be served directly by a front end webserver. class Rack::ResponseCache # The default proc used if a block is not provided to .new # It unescapes the PATH_INFO of the environment, and makes sure that it doesn't # include '..'. If the Content-Type of the response is text/(html|css|xml), # return a path with the appropriate extension (.html, .css, or .xml). # If the path ends with a / and the Content-Type is text/html, change the basename # of the path to index.html. DEFAULT_PATH_PROC = proc do |env, res| path = Rack::Utils.unescape(env['PATH_INFO']) if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(res[1]['Content-Type']) type = match[1] path = "#{path}.#{type}" unless /\.#{type}\z/.match(path) path = File.join(File.dirname(path), 'index.html') if type == 'html' and File.basename(path) == '.html' path end end # Initialize a new ReponseCache object with the given arguments. Arguments: # * app : The next middleware in the chain. This is always called. # * cache : The place to cache responses. If a string is provided, a disk # cache is used, and all cached files will use this directory as the root directory. # If anything other than a string is provided, it should respond to []=, which will # be called with a path string and a body value (the 3rd element of the response). # * &block : If provided, it is called with the environment and the response from the next middleware. # It should return nil or false if the path should not be cached, and should return # the pathname to use as a string if the result should be cached. # If not provided, the DEFAULT_PATH_PROC is used. def initialize(app, cache, &block) @app = app @cache = cache @path_proc = block || DEFAULT_PATH_PROC end # Call the next middleware with the environment. If the request was successful (response status 200), # was a GET request, and had an empty query string, call the block set up in initialize to get the path. # If the cache is a string, create any necessary middle directories, and cache the file in the appropriate # subdirectory of cache. Otherwise, cache the body of the reponse as the value with the path as the key. def call(env) res = @app.call(env) if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and res[0] == 200 and path = @path_proc.call(env, res) if @cache.is_a?(String) path = File.join(@cache, path) if @cache FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'wb'){|f| res[2].each{|c| f.write(c)}} else @cache[path] = res[2] end end res end end rack-contrib-1.3.0/lib/rack/contrib/response_headers.rb000066400000000000000000000010711254365113500231010ustar00rootroot00000000000000module Rack # Allows you to tap into the response headers. Yields a Rack::Utils::HeaderHash # of current response headers to the block. Example: # # use Rack::ResponseHeaders do |headers| # headers['X-Foo'] = 'bar' # headers.delete('X-Baz') # end # class ResponseHeaders def initialize(app, &block) @app = app @block = block end def call(env) response = @app.call(env) headers = Utils::HeaderHash.new(response[1]) @block.call(headers) response[1] = headers response end end end rack-contrib-1.3.0/lib/rack/contrib/route_exceptions.rb000066400000000000000000000020341254365113500231470ustar00rootroot00000000000000module Rack class RouteExceptions ROUTES = [ [Exception, '/error/internal'] ] PATH_INFO = 'rack.route_exceptions.path_info'.freeze EXCEPTION = 'rack.route_exceptions.exception'.freeze RETURNED = 'rack.route_exceptions.returned'.freeze class << self def route(exception, to) ROUTES.delete_if{|k,v| k == exception } ROUTES << [exception, to] end alias []= route end def initialize(app) @app = app end def call(env, try_again = true) returned = @app.call(env) rescue Exception => exception raise(exception) unless try_again ROUTES.each do |klass, to| next unless klass === exception return route(to, env, returned, exception) end raise(exception) end def route(to, env, returned, exception) env.merge!( PATH_INFO => env['PATH_INFO'], EXCEPTION => exception, RETURNED => returned ) env['PATH_INFO'] = to call(env, try_again = false) end end end rack-contrib-1.3.0/lib/rack/contrib/runtime.rb000066400000000000000000000013121254365113500212310ustar00rootroot00000000000000 module Rack # Sets an "X-Runtime" response header, indicating the response # time of the request, in seconds # # You can put it right before the application to see the processing # time, or before all the other middlewares to include time for them, # too. class Runtime def initialize(app, name = nil) @app = app @header_name = "X-Runtime" @header_name << "-#{name}" if name end def call(env) start_time = Time.now status, headers, body = @app.call(env) request_time = Time.now - start_time if !headers.has_key?(@header_name) headers[@header_name] = "%0.6f" % request_time end [status, headers, body] end end end rack-contrib-1.3.0/lib/rack/contrib/sendfile.rb000066400000000000000000000106311254365113500213430ustar00rootroot00000000000000require 'rack/file' module Rack class File #:nodoc: alias :to_path :path end # = Sendfile # # The Sendfile middleware intercepts responses whose body is being # served from a file and replaces it with a server specific X-Sendfile # header. The web server is then responsible for writing the file contents # to the client. This can dramatically reduce the amount of work required # by the Ruby backend and takes advantage of the web servers optimized file # delivery code. # # In order to take advantage of this middleware, the response body must # respond to +to_path+ and the request must include an X-Sendfile-Type # header. Rack::File and other components implement +to_path+ so there's # rarely anything you need to do in your application. The X-Sendfile-Type # header is typically set in your web servers configuration. The following # sections attempt to document # # === Nginx # # Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile # but requires parts of the filesystem to be mapped into a private URL # hierarachy. # # The following example shows the Nginx configuration required to create # a private "/files/" area, enable X-Accel-Redirect, and pass the special # X-Sendfile-Type and X-Accel-Mapping headers to the backend: # # location /files/ { # internal; # alias /var/www/; # } # # location / { # proxy_redirect false; # # proxy_set_header Host $host; # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # # proxy_set_header X-Sendfile-Type X-Accel-Redirect # proxy_set_header X-Accel-Mapping /files/=/var/www/; # # proxy_pass http://127.0.0.1:8080/; # } # # Note that the X-Sendfile-Type header must be set exactly as shown above. The # X-Accel-Mapping header should specify the name of the private URL pattern, # followed by an equals sign (=), followed by the location on the file system # that it maps to. The middleware performs a simple substitution on the # resulting path. # # See Also: http://wiki.codemongers.com/NginxXSendfile # # === lighttpd # # Lighttpd has supported some variation of the X-Sendfile header for some # time, although only recent version support X-Sendfile in a reverse proxy # configuration. # # $HTTP["host"] == "example.com" { # proxy-core.protocol = "http" # proxy-core.balancer = "round-robin" # proxy-core.backends = ( # "127.0.0.1:8000", # "127.0.0.1:8001", # ... # ) # # proxy-core.allow-x-sendfile = "enable" # proxy-core.rewrite-request = ( # "X-Sendfile-Type" => (".*" => "X-Sendfile") # ) # } # # See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore # # === Apache # # X-Sendfile is supported under Apache 2.x using a separate module: # # http://tn123.ath.cx/mod_xsendfile/ # # Once the module is compiled and installed, you can enable it using # XSendFile config directive: # # RequestHeader Set X-Sendfile-Type X-Sendfile # ProxyPassReverse / http://localhost:8001/ # XSendFile on class Sendfile F = ::File def initialize(app, variation=nil) @app = app @variation = variation end def call(env) status, headers, body = @app.call(env) if body.respond_to?(:to_path) case type = variation(env) when 'X-Accel-Redirect' path = F.expand_path(body.to_path) if url = map_accel_path(env, path) headers[type] = url body = [] else env['rack.errors'] << "X-Accel-Mapping header missing" end when 'X-Sendfile', 'X-Lighttpd-Send-File' path = F.expand_path(body.to_path) headers[type] = path body = [] when '', nil else env['rack.errors'] << "Unknown x-sendfile variation: '#{variation}'.\n" end end [status, headers, body] end private def variation(env) @variation || env['sendfile.type'] || env['HTTP_X_SENDFILE_TYPE'] end def map_accel_path(env, file) if mapping = env['HTTP_X_ACCEL_MAPPING'] internal, external = mapping.split('=', 2).map{ |p| p.strip } file.sub(/^#{internal}/i, external) end end end end rack-contrib-1.3.0/lib/rack/contrib/signals.rb000066400000000000000000000025171254365113500212160ustar00rootroot00000000000000module Rack # Installs signal handlers that are safely processed after a request # # NOTE: This middleware should not be used in a threaded environment # # use Rack::Signals.new do # trap 'INT', lambda { # puts "Exiting now" # exit # } # # trap_when_ready 'USR1', lambda { # puts "Exiting when ready" # exit # } # end class Signals class BodyWithCallback def initialize(body, callback) @body, @callback = body, callback end def each(&block) @body.each(&block) @callback.call end end def initialize(app, &block) @app = app @processing = false @when_ready = nil instance_eval(&block) end def call(env) begin @processing, @when_ready = true, nil status, headers, body = @app.call(env) if handler = @when_ready body = BodyWithCallback.new(body, handler) @when_ready = nil end ensure @processing = false end [status, headers, body] end def trap_when_ready(signal, handler) when_ready_handler = lambda { |signal| if @processing @when_ready = lambda { handler.call(signal) } else handler.call(signal) end } trap(signal, when_ready_handler) end end end rack-contrib-1.3.0/lib/rack/contrib/simple_endpoint.rb000066400000000000000000000045621254365113500227510ustar00rootroot00000000000000module Rack # Create simple endpoints with routing rules, similar to Sinatra actions. # # Simplest example: # # use Rack::SimpleEndpoint, '/ping_monitor' do # 'pong' # end # # The value returned from the block will be written to the response body, so # the above example will return "pong" when the request path is /ping_monitor. # # HTTP verb requirements can optionally be specified: # # use Rack::SimpleEndpoint, '/foo' => :get do # 'only GET requests will match' # end # # use Rack::SimpleEndpoint, '/bar' => [:get, :post] do # 'only GET and POST requests will match' # end # # Rack::Request and Rack::Response objects are yielded to block: # # use Rack::SimpleEndpoint, '/json' do |req, res| # res['Content-Type'] = 'application/json' # %({"foo": "#{req[:foo]}"}) # end # # When path is a Regexp, match data object is yielded as third argument to block # # use Rack::SimpleEndpoint, %r{^/(john|paul|george|ringo)} do |req, res, match| # "Hello, #{match[1]}" # end # # A :pass symbol returned from block will not return a response; control will continue down the # Rack stack: # # use Rack::SimpleEndpoint, '/api_key' do |req, res| # req.env['myapp.user'].authorized? ? '12345' : :pass # end # # # Unauthorized access to /api_key will be handled by PublicApp # run PublicApp class SimpleEndpoint def initialize(app, arg, &block) @app = app @path = extract_path(arg) @verbs = extract_verbs(arg) @block = block end def call(env) match = match_path(env['PATH_INFO']) if match && valid_method?(env['REQUEST_METHOD']) req, res = Request.new(env), Response.new body = @block.call(req, res, (match unless match == true)) body == :pass ? @app.call(env) : (res.write(body); res.finish) else @app.call(env) end end private def extract_path(arg) arg.is_a?(Hash) ? arg.keys.first : arg end def extract_verbs(arg) arg.is_a?(Hash) ? [arg.values.first].flatten.map {|verb| verb.to_s.upcase} : [] end def match_path(path) @path.is_a?(Regexp) ? @path.match(path.to_s) : @path == path.to_s end def valid_method?(method) @verbs.empty? || @verbs.include?(method) end end endrack-contrib-1.3.0/lib/rack/contrib/static_cache.rb000066400000000000000000000074651254365113500221770ustar00rootroot00000000000000module Rack # # The Rack::StaticCache middleware automatically adds, removes and modifies # stuffs in response headers to facilitiate client and proxy caching for static files # that minimizes http requests and improves overall load times for second time visitors. # # Once a static content is stored in a client/proxy the only way to enforce the browser # to fetch the latest content and ignore the cache is to rename the static file. # # Alternatively, we can add a version number into the URL to the content to bypass # the caches. Rack::StaticCache by default handles version numbers in the filename. # As an example, # http://yoursite.com/images/test-1.0.0.png and http://yoursite.com/images/test-2.0.0.png # both reffers to the same image file http://yoursite.com/images/test.png # # Another way to bypass the cache is adding the version number in a field-value pair in the # URL query string. As an example, http://yoursite.com/images/test.png?v=1.0.0 # In that case, set the option :versioning to false to avoid unneccessary regexp calculations. # # It's better to keep the current version number in some config file and use it in every static # content's URL. So each time we modify our static contents, we just have to change the version # number to enforce the browser to fetch the latest content. # # You can use Rack::Deflater along with Rack::StaticCache for further improvements in page loading time. # # Examples: # use Rack::StaticCache, :urls => ["/images", "/css", "/js", "/documents*"], :root => "statics" # will serve all requests beginning with /images, /css or /js from the # directory "statics/images", "statics/css", "statics/js". # All the files from these directories will have modified headers to enable client/proxy caching, # except the files from the directory "documents". Append a * (star) at the end of the pattern # if you want to disable caching for any pattern . In that case, plain static contents will be served with # default headers. # # use Rack::StaticCache, :urls => ["/images"], :duration => 2, :versioning => false # will serve all requests begining with /images under the current directory (default for the option :root # is current directory). All the contents served will have cache expiration duration set to 2 years in headers # (default for :duration is 1 year), and StaticCache will not compute any versioning logics (default for # :versioning is true) # class StaticCache def initialize(app, options={}) @app = app @urls = options[:urls] @no_cache = {} @urls.collect! do |url| if url =~ /\*$/ url.sub!(/\*$/, '') @no_cache[url] = 1 end url end root = options[:root] || Dir.pwd @file_server = Rack::File.new(root) @cache_duration = options[:duration] || 1 @versioning_enabled = true @versioning_enabled = options[:versioning] unless options[:versioning].nil? @duration_in_seconds = self.duration_in_seconds @duration_in_words = self.duration_in_words end def call(env) path = env["PATH_INFO"] url = @urls.detect{ |u| path.index(u) == 0 } unless url.nil? path.sub!(/-[\d.]+([.][a-zA-Z][\w]+)?$/, '\1') if @versioning_enabled status, headers, body = @file_server.call(env) if @no_cache[url].nil? headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public" headers['Expires'] = @duration_in_words end [status, headers, body] else @app.call(env) end end def duration_in_words (Time.now + self.duration_in_seconds).strftime '%a, %d %b %Y %H:%M:%S GMT' end def duration_in_seconds (60 * 60 * 24 * 365 * @cache_duration).to_i end end end rack-contrib-1.3.0/lib/rack/contrib/time_zone.rb000066400000000000000000000011151254365113500215400ustar00rootroot00000000000000module Rack class TimeZone Javascript = <<-EOJ function setTimezoneCookie() { var offset = (new Date()).getTimezoneOffset() var date = new Date(); date.setTime(date.getTime()+3600000); document.cookie = "utc_offset="+offset+"; expires="+date.toGMTString();+"; path=/"; } EOJ def initialize(app) @app = app end def call(env) request = Rack::Request.new(env) if utc_offset = request.cookies["utc_offset"] env["rack.timezone.utc_offset"] = -(utc_offset.to_i * 60) end @app.call(env) end end end rack-contrib-1.3.0/lib/rack/contrib/try_static.rb000066400000000000000000000017661254365113500217500ustar00rootroot00000000000000module Rack # The Rack::TryStatic middleware delegates requests to Rack::Static middleware # trying to match a static file # # Examples # # use Rack::TryStatic, # :root => "public", # static files root dir # :urls => %w[/], # match all requests # :try => ['.html', 'index.html', '/index.html'] # try these postfixes sequentially # # uses same options as Rack::Static with extra :try option which is an array # of postfixes to find desired file class TryStatic def initialize(app, options) @app = app @try = ['', *options[:try]] @static = ::Rack::Static.new( lambda { |_| [404, {}, []] }, options) end def call(env) orig_path = env['PATH_INFO'] found = nil @try.each do |path| resp = @static.call(env.merge!({'PATH_INFO' => orig_path + path})) break if !(403..405).include?(resp[0]) && found = resp end found or @app.call(env.merge!('PATH_INFO' => orig_path)) end end end rack-contrib-1.3.0/rack-contrib.gemspec000066400000000000000000000035621254365113500200270ustar00rootroot00000000000000begin require 'git-version-bump' rescue LoadError nil end Gem::Specification.new do |s| s.specification_version = 2 if s.respond_to? :specification_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.name = 'rack-contrib' s.version = GVB.version rescue "0.0.0.1.ENOGVB" s.date = GVB.date rescue Time.now.strftime("%F") s.licenses = ['MIT'] s.description = "Contributed Rack Middleware and Utilities" s.summary = "Contributed Rack Middleware and Utilities" s.authors = ["rack-devel"] s.email = "rack-devel@googlegroups.com" # = MANIFEST = s.files = %w[ AUTHORS COPYING README.md ] + `git ls-files -z lib`.split("\0") s.test_files = s.files.select {|path| path =~ /^test\/spec_.*\.rb/} s.extra_rdoc_files = %w[README.md COPYING] # REMINDER: If you modify any dependencies, please ensure you # update `test/gemfiles/minimum_versions`! # s.add_runtime_dependency 'rack', '~> 1.4' s.add_runtime_dependency 'git-version-bump', '~> 0.15' s.add_development_dependency 'bundler', '~> 1.0' s.add_development_dependency 'github-release', '~> 0.1' s.add_development_dependency 'i18n', '~> 0.4' s.add_development_dependency 'json', '~> 1.8' s.add_development_dependency 'minitest', '~> 5.0' s.add_development_dependency 'minitest-hooks', '~> 1.0' s.add_development_dependency 'mail', '~> 2.3' s.add_development_dependency 'nbio-csshttprequest', '~> 1.0' s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2' s.add_development_dependency 'rdoc', '~> 3.12' s.add_development_dependency 'ruby-prof', '~> 0.13.0' s.has_rdoc = true s.homepage = "http://github.com/rack/rack-contrib/" s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "rack-contrib", "--main", "README"] s.require_paths = %w[lib] s.rubygems_version = '1.1.1' end rack-contrib-1.3.0/test/000077500000000000000000000000001254365113500150555ustar00rootroot00000000000000rack-contrib-1.3.0/test/404.html000066400000000000000000000000111254365113500162420ustar00rootroot00000000000000Not Foundrack-contrib-1.3.0/test/Maintenance.html000066400000000000000000000000221254365113500201570ustar00rootroot00000000000000Under maintenance.rack-contrib-1.3.0/test/documents/000077500000000000000000000000001254365113500170565ustar00rootroot00000000000000rack-contrib-1.3.0/test/documents/existing.html000066400000000000000000000000161254365113500215730ustar00rootroot00000000000000existing.html rack-contrib-1.3.0/test/documents/index.htm000066400000000000000000000000121254365113500206700ustar00rootroot00000000000000index.htm rack-contrib-1.3.0/test/documents/index.html000066400000000000000000000000131254365113500210450ustar00rootroot00000000000000index.html rack-contrib-1.3.0/test/documents/test000066400000000000000000000000071254365113500177550ustar00rootroot00000000000000nocacherack-contrib-1.3.0/test/gemfiles/000077500000000000000000000000001254365113500166505ustar00rootroot00000000000000rack-contrib-1.3.0/test/gemfiles/1.8.7-compatible000066400000000000000000000005041254365113500213620ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rack', '~> 1.1' gem 'git-version-bump', '~> 0.15' gem 'json', '~> 1.5' gem 'minitest', '~> 5.0' gem 'minitest-hooks', '~> 1.0' gem 'nbio-csshttprequest', '~> 1.0' gem 'rdoc', '~> 3.12' gem 'rake', '~> 10.4', '>= 10.4.2' rack-contrib-1.3.0/test/gemfiles/1.8.7-minimum_versions000066400000000000000000000005101254365113500226430ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rack', '= 1.4.0' gem 'git-version-bump', '= 0.15.0' gem 'json', '= 1.5.0' gem 'minitest', '= 5.0.0' gem 'minitest-hooks', '= 1.0.0' gem 'nbio-csshttprequest', '= 1.0.2' gem 'rdoc', '= 3.12' gem 'rake', '= 10.4.2' rack-contrib-1.3.0/test/gemfiles/minimum_versions000066400000000000000000000012701254365113500221760ustar00rootroot00000000000000source 'https://rubygems.org' # These are the minimum available versions of all the gems we need, based on # the version specs in the gemspec. We test against all of these to ensure # we haven't accidentally used a feature of a gem that isn't in the oldest # version we say we need. # gem 'rack', '= 1.4.0' gem 'git-version-bump', '= 0.15.0' gem 'i18n', '= 0.4.0' gem 'json', '= 1.8.1' gem 'minitest', '= 5.0.0' gem 'minitest-hooks', '= 1.0.0' gem 'mail', '= 2.3.0' gem 'nbio-csshttprequest', '= 1.0.2' gem 'rdoc', '= 3.12' gem 'rake', '= 10.4.2' gem 'ruby-prof', '= 0.13.0' rack-contrib-1.3.0/test/mail_settings.rb000066400000000000000000000010021254365113500202350ustar00rootroot00000000000000TEST_SMTP = nil # Enable SMTP tests by providing the following for your SMTP server. # # TEST_SMTP = { # :server => 'localhost', # :domain => 'localhost', # :port => 25, # :authentication => :login, # :user_name => nil, # :password => nil # } #TEST_SMTP_TLS = { # :server => 'smtp.gmail.com', # :domain => 'gmail.com', # :port => 587, # :authentication => 'plain', # :user_name => nil, # :password => nil, #} rack-contrib-1.3.0/test/spec_rack_accept_format.rb000066400000000000000000000052301254365113500222230ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/accept_format' require 'rack/mime' describe "Rack::AcceptFormat" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, env['PATH_INFO']] } specify "should do nothing when a format extension is already provided" do request = Rack::MockRequest.env_for("/resource.json") body = Rack::AcceptFormat.new(app).call(request).last body.must_equal "/resource.json" end describe "default extention" do specify "should allow custom default" do request = Rack::MockRequest.env_for("/resource") body = Rack::AcceptFormat.new(app, '.xml').call(request).last body.must_equal "/resource.xml" end specify "should default to html" do request = Rack::MockRequest.env_for("/resource") body = Rack::AcceptFormat.new(app).call(request).last body.must_equal "/resource.html" end specify "should notmalize custom extention" do request = Rack::MockRequest.env_for("/resource") body = Rack::AcceptFormat.new(app,'xml').call(request).last #no dot prefix body.must_equal "/resource.xml" body = Rack::AcceptFormat.new(app, :xml).call(request).last body.must_equal "/resource.xml" end end describe "there is no format extension" do before do Rack::Mime::MIME_TYPES.clear end def mime(ext, type) ext = ".#{ext}" unless ext.to_s[0] == ?. Rack::Mime::MIME_TYPES[ext.to_s] = type end specify "should add the default extension if no Accept header" do request = Rack::MockRequest.env_for("/resource") body = Rack::AcceptFormat.new(app).call(request).last body.must_equal "/resource.html" end specify "should add the default extension if the Accept header is not registered in the Mime::Types" do request = Rack::MockRequest.env_for("/resource", 'HTTP_ACCEPT' => 'application/json;q=1.0, text/html;q=0.8, */*;q=0.1') body = Rack::AcceptFormat.new(app).call(request).last body.must_equal "/resource.html" end specify "should add the correct extension if the Accept header is registered in the Mime::Types" do mime :json, 'application/json' request = Rack::MockRequest.env_for("/resource", 'HTTP_ACCEPT' => 'application/json;q=1.0, text/html;q=0.8, */*;q=0.1') body = Rack::AcceptFormat.new(app).call(request).last body.must_equal "/resource.json" end end specify "shouldn't confuse extention when there are dots in path" do request = Rack::MockRequest.env_for("/parent.resource/resource") body = Rack::AcceptFormat.new(app, '.html').call(request).last body.must_equal "/parent.resource/resource.html" end end rack-contrib-1.3.0/test/spec_rack_access.rb000066400000000000000000000121011254365113500206500ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/access' describe "Rack::Access" do before do @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['hello']] } @mock_addr_1 = '111.111.111.111' @mock_addr_2 = '192.168.1.222' @mock_addr_localhost = '127.0.0.1' @mock_addr_range = '192.168.1.0/24' end def mock_env(remote_addr, path = '/') Rack::MockRequest.env_for(path, { 'REMOTE_ADDR' => remote_addr }) end def middleware(options = {}) Rack::Access.new(@app, options) end specify "default configuration should deny non-local requests" do app = middleware status, headers, body = app.call(mock_env(@mock_addr_1)) status.must_equal 403 body.must_equal [] end specify "default configuration should allow requests from 127.0.0.1" do app = middleware status, headers, body = app.call(mock_env(@mock_addr_localhost)) status.must_equal 200 body.must_equal ['hello'] end specify "should allow remote addresses in allow_ipmasking" do app = middleware('/' => [@mock_addr_1]) status, headers, body = app.call(mock_env(@mock_addr_1)) status.must_equal 200 body.must_equal ['hello'] end specify "should deny remote addresses not in allow_ipmasks" do app = middleware('/' => [@mock_addr_1]) status, headers, body = app.call(mock_env(@mock_addr_2)) status.must_equal 403 body.must_equal [] end specify "should allow remote addresses in allow_ipmasks range" do app = middleware('/' => [@mock_addr_range]) status, headers, body = app.call(mock_env(@mock_addr_2)) status.must_equal 200 body.must_equal ['hello'] end specify "should deny remote addresses not in allow_ipmasks range" do app = middleware('/' => [@mock_addr_range]) status, headers, body = app.call(mock_env(@mock_addr_1)) status.must_equal 403 body.must_equal [] end specify "should allow remote addresses in one of allow_ipmasking" do app = middleware('/' => [@mock_addr_range, @mock_addr_localhost]) status, headers, body = app.call(mock_env(@mock_addr_2)) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_localhost)) status.must_equal 200 body.must_equal ['hello'] end specify "should deny remote addresses not in one of allow_ipmasks" do app = middleware('/' => [@mock_addr_range, @mock_addr_localhost]) status, headers, body = app.call(mock_env(@mock_addr_1)) status.must_equal 403 body.must_equal [] end specify "handles paths correctly" do app = middleware({ 'http://foo.org/bar' => [@mock_addr_localhost], '/foo' => [@mock_addr_localhost], '/foo/bar' => [@mock_addr_range, @mock_addr_localhost] }) status, headers, body = app.call(mock_env(@mock_addr_1, "/")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/qux")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/foo")) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/")) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/bar")) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/bar")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_2, "/foo/bar")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/bar/")) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/bar/")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/foo///bar//quux")) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo///bar//quux")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/quux")) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/quux")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/bar")) status.must_equal 200 body.must_equal ['hello'] status, headers, body = app.call(mock_env(@mock_addr_1, "/bar").merge('HTTP_HOST' => 'foo.org')) status.must_equal 403 body.must_equal [] status, headers, body = app.call(mock_env(@mock_addr_localhost, "/bar").merge('HTTP_HOST' => 'foo.org')) status.must_equal 200 body.must_equal ['hello'] end end rack-contrib-1.3.0/test/spec_rack_backstage.rb000066400000000000000000000015551254365113500213460ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/builder' require 'rack/mock' require 'rack/contrib/backstage' describe "Rack::Backstage" do specify "shows maintenances page if present" do app = Rack::Builder.new do use Rack::Backstage, 'test/Maintenance.html' run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } end response = Rack::MockRequest.new(app).get('/') response.body.must_equal('Under maintenance.') response.status.must_equal(503) end specify "passes on request if page is not present" do app = Rack::Builder.new do use Rack::Backstage, 'test/Nonsense.html' run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } end response = Rack::MockRequest.new(app).get('/') response.body.must_equal('Hello, World!') response.status.must_equal(200) end end rack-contrib-1.3.0/test/spec_rack_callbacks.rb000066400000000000000000000021421254365113500213320ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' class Flame def call(env) env['flame'] = 'F Lifo..' end end class Pacify def initialize(with) @with = with end def call(env) env['peace'] = @with end end class Finale def call(response) status, headers, body = response headers['last'] = 'Finale' $old_status = status [201, headers, body] end end class TheEnd def call(response) status, headers, body = response headers['last'] = 'TheEnd' [201, headers, body] end end describe "Rack::Callbacks" do specify "works for love and small stack trace" do callback_app = Rack::Callbacks.new do before Flame before Pacify, "with love" run lambda {|env| [200, {}, [env['flame'], env['peace']]] } after Finale after TheEnd end app = Rack::Builder.new do run callback_app end.to_app response = Rack::MockRequest.new(app).get("/") response.body.must_equal 'F Lifo..with love' $old_status.must_equal 200 response.status.must_equal 201 response.headers['last'].must_equal 'TheEnd' end end rack-contrib-1.3.0/test/spec_rack_common_cookies.rb000066400000000000000000000077401254365113500224300ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/builder' require 'rack/contrib/common_cookies' describe Rack::CommonCookies do before do @app = Rack::Builder.new do use Rack::CommonCookies run lambda {|env| [200, {'Set-Cookie' => env['HTTP_COOKIE']}, []] } end end def request Rack::MockRequest.new(@app) end def make_request(domain, cookies='key=value') request.get '/', 'HTTP_COOKIE' => cookies, 'HTTP_HOST' => domain end specify 'should use .domain.com for cookies from domain.com' do response = make_request 'domain.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com' end specify 'should use .domain.com for cookies from www.domain.com' do response = make_request 'www.domain.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com' end specify 'should use .domain.com for cookies from subdomain.domain.com' do response = make_request 'subdomain.domain.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com' end specify 'should use .domain.com for cookies from 0.subdomain1.subdomain2.domain.com' do response = make_request '0.subdomain1.subdomain2.domain.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com' end specify 'should use .domain.local for cookies from domain.local' do response = make_request '0.subdomain1.subdomain2.domain.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com' end specify 'should use .domain.local for cookies from subdomain.domain.local' do response = make_request 'subdomain.domain.local' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.local' end specify 'should use .domain.com.ua for cookies from domain.com.ua' do response = make_request 'domain.com.ua' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com.ua' end specify 'should use .domain.com.ua for cookies from subdomain.domain.com.ua' do response = make_request 'subdomain.domain.com.ua' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.com.ua' end specify 'should use .domain.co.uk for cookies from domain.co.uk' do response = make_request 'domain.co.uk' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.co.uk' end specify 'should use .domain.co.uk for cookies from subdomain.domain.co.uk' do response = make_request 'subdomain.domain.co.uk' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.co.uk' end specify 'should use .domain.eu.com for cookies from domain.eu.com' do response = make_request 'domain.eu.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.eu.com' end specify 'should use .domain.eu.com for cookies from subdomain.domain.eu.com' do response = make_request 'subdomain.domain.eu.com' response.headers['Set-Cookie'].must_equal 'key=value; domain=.domain.eu.com' end specify 'should work with multiple cookies' do response = make_request 'sub.domain.bz', "key=value\nkey1=value2" response.headers['Set-Cookie'].must_equal "key=value; domain=.domain.bz\nkey1=value2; domain=.domain.bz" end specify 'should work with cookies which have explicit domain' do response = make_request 'sub.domain.bz', "key=value; domain=domain.bz" response.headers['Set-Cookie'].must_equal "key=value; domain=.domain.bz" end specify 'should not touch cookies if domain is localhost' do response = make_request 'localhost' response.headers['Set-Cookie'].must_equal "key=value" end specify 'should not touch cookies if domain is ip address' do response = make_request '127.0.0.1' response.headers['Set-Cookie'].must_equal "key=value" end specify 'should use .domain.com for cookies from subdomain.domain.com:3000' do response = make_request 'subdomain.domain.com:3000' response.headers['Set-Cookie'].must_equal "key=value; domain=.domain.com" end endrack-contrib-1.3.0/test/spec_rack_config.rb000066400000000000000000000010411254365113500206550ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/config' describe "Rack::Config" do specify "should accept a block that modifies the environment" do app = Rack::Builder.new do use Rack::Lint use Rack::ContentLength 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.must_equal('hello') end end rack-contrib-1.3.0/test/spec_rack_contrib.rb000066400000000000000000000002521254365113500210530ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/contrib' describe "Rack::Contrib" do specify "should expose release" do Rack::Contrib.must_respond_to(:release) end end rack-contrib-1.3.0/test/spec_rack_cookies.rb000066400000000000000000000041721254365113500210540ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/cookies' describe "Rack::Cookies" do specify "should be able to read received cookies" do app = lambda { |env| cookies = env['rack.cookies'] foo, quux = cookies[:foo], cookies['quux'] [200, {'Content-Type' => 'text/plain'}, ["foo: #{foo}, quux: #{quux}"]] } app = Rack::Cookies.new(app) response = Rack::MockRequest.new(app).get('/', 'HTTP_COOKIE' => 'foo=bar;quux=h&m') response.body.must_equal('foo: bar, quux: h&m') end specify "should be able to set new cookies" do app = lambda { |env| cookies = env['rack.cookies'] cookies[:foo] = 'bar' cookies['quux'] = 'h&m' [200, {'Content-Type' => 'text/plain'}, []] } app = Rack::Cookies.new(app) response = Rack::MockRequest.new(app).get('/') response.headers['Set-Cookie'].split("\n").sort.must_equal(["foo=bar; path=/","quux=h%26m; path=/"]) end specify "should be able to set cookie with options" do app = lambda { |env| cookies = env['rack.cookies'] cookies['foo'] = { :value => 'bar', :path => '/login', :secure => true } [200, {'Content-Type' => 'text/plain'}, []] } app = Rack::Cookies.new(app) response = Rack::MockRequest.new(app).get('/') response.headers['Set-Cookie'].must_equal('foo=bar; path=/login; secure') end specify "should be able to delete received cookies" do app = lambda { |env| cookies = env['rack.cookies'] cookies.delete(:foo) foo, quux = cookies['foo'], cookies[:quux] [200, {'Content-Type' => 'text/plain'}, ["foo: #{foo}, quux: #{quux}"]] } app = Rack::Cookies.new(app) response = Rack::MockRequest.new(app).get('/', 'HTTP_COOKIE' => 'foo=bar;quux=h&m') response.body.must_equal('foo: , quux: h&m') response.headers['Set-Cookie'].must_match(/foo=(;|$)/) # This test is currently failing; I suspect it is due to a bug in a dependent # lib's cookie handling code, but I haven't had time to track it down yet # -- @mpalmer, 2015-06-17 # response.headers['Set-Cookie'].must_match(/expires=Thu, 01 Jan 1970 00:00:00 GMT/) end end rack-contrib-1.3.0/test/spec_rack_csshttprequest.rb000066400000000000000000000045601254365113500225220ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' begin require 'csshttprequest' require 'rack/contrib/csshttprequest' describe "Rack::CSSHTTPRequest" do before(:each) do @test_body = '{"bar":"foo"}' @test_headers = {'Content-Type' => 'text/plain'} @encoded_body = CSSHTTPRequest.encode(@test_body) @app = lambda { |env| [200, @test_headers, [@test_body]] } end specify "env['csshttprequest.chr'] should be set to true when \ PATH_INFO ends with '.chr'" do request = Rack::MockRequest.env_for("/blah.chr", :lint => true, :fatal => true) Rack::CSSHTTPRequest.new(@app).call(request) request['csshttprequest.chr'].must_equal true end specify "env['csshttprequest.chr'] should be set to true when \ request parameter _format == 'chr'" do request = Rack::MockRequest.env_for("/?_format=chr", :lint => true, :fatal => true) Rack::CSSHTTPRequest.new(@app).call(request) request['csshttprequest.chr'].must_equal true end specify "should not change the headers or response when !env['csshttprequest.chr']" do request = Rack::MockRequest.env_for("/", :lint => true, :fatal => true) status, headers, response = Rack::CSSHTTPRequest.new(@app).call(request) headers.must_equal @test_headers response.join.must_equal @test_body end describe "when env['csshttprequest.chr']" do before(:each) do @request = Rack::MockRequest.env_for("/", 'csshttprequest.chr' => true, :lint => true, :fatal => true) end specify "should modify the content length to the correct value" do headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1] headers['Content-Length'].must_equal @encoded_body.length.to_s end specify "should modify the content type to the correct value" do headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1] headers['Content-Type'].must_equal 'text/css' end specify "should not modify any other headers" do headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1] headers.must_equal @test_headers.merge({ 'Content-Type' => 'text/css', 'Content-Length' => @encoded_body.length.to_s }) end end end rescue LoadError => boom STDERR.puts "WARN: Skipping Rack::CSSHTTPRequest tests (nbio-csshttprequest not installed)" end rack-contrib-1.3.0/test/spec_rack_deflect.rb000066400000000000000000000056401254365113500210270ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/deflect' describe "Rack::Deflect" do before do @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['cookies']] } @mock_addr_1 = '111.111.111.111' @mock_addr_2 = '222.222.222.222' @mock_addr_3 = '333.333.333.333' end def mock_env remote_addr, path = '/' Rack::MockRequest.env_for path, { 'REMOTE_ADDR' => remote_addr } end def mock_deflect options = {} Rack::Deflect.new @app, options end specify "should allow regular requests to follow through" do app = mock_deflect status, headers, body = app.call mock_env(@mock_addr_1) status.must_equal 200 body.must_equal ['cookies'] end specify "should deflect requests exceeding the request threshold" do log = StringIO.new app = mock_deflect :request_threshold => 5, :interval => 10, :block_duration => 10, :log => log env = mock_env @mock_addr_1 # First 5 should be fine 5.times do status, headers, body = app.call env status.must_equal 200 body.must_equal ['cookies'] end # Remaining requests should fail for 10 seconds 10.times do status, headers, body = app.call env status.must_equal 403 body.must_equal [] end # Log should reflect that we have blocked an address log.string.must_match(/^deflect\(\d+\/\d+\/\d+\): blocked 111.111.111.111\n/) end specify "should expire blocking" do log = StringIO.new app = mock_deflect :request_threshold => 5, :interval => 2, :block_duration => 2, :log => log env = mock_env @mock_addr_1 # First 5 should be fine 5.times do status, headers, body = app.call env status.must_equal 200 body.must_equal ['cookies'] end # Exceeds request threshold status, headers, body = app.call env status.must_equal 403 body.must_equal [] # Allow block to expire sleep 3 # Another 5 is fine now 5.times do status, headers, body = app.call env status.must_equal 200 body.must_equal ['cookies'] end # Log should reflect block and release log.string.must_match(/deflect.*: blocked 111\.111\.111\.111\ndeflect.*: released 111\.111\.111\.111\n/) end specify "should allow whitelisting of remote addresses" do app = mock_deflect :whitelist => [@mock_addr_1], :request_threshold => 5, :interval => 2 env = mock_env @mock_addr_1 # Whitelisted addresses are always fine 10.times do status, headers, body = app.call env status.must_equal 200 body.must_equal ['cookies'] end end specify "should allow blacklisting of remote addresses" do app = mock_deflect :blacklist => [@mock_addr_2] status, headers, body = app.call mock_env(@mock_addr_1) status.must_equal 200 body.must_equal ['cookies'] status, headers, body = app.call mock_env(@mock_addr_2) status.must_equal 403 body.must_equal [] end end rack-contrib-1.3.0/test/spec_rack_enforce_valid_encoding.rb000066400000000000000000000037271254365113500240730ustar00rootroot00000000000000# -*- encoding : us-ascii -*- if "a string".respond_to?(:valid_encoding?) require 'rack/mock' require 'rack/contrib/enforce_valid_encoding' VALID_PATH = "h%C3%A4ll%C3%B2" INVALID_PATH = "/%D1%A1%D4%F1%D7%A2%B2%E1%D3%C3%BB%A7%C3%FB" describe "Rack::EnforceValidEncoding" do before do @app = Rack::EnforceValidEncoding.new(lambda { |env| [200, {'Content-Type'=>'text/plain'}, ['Hello World']] }) end describe "contstant assertions" do it "INVALID_PATH should not be a valid UTF-8 string when decoded" do Rack::Utils.unescape(INVALID_PATH).valid_encoding?.must_equal false end it "VALID_PATH should be valid when decoded" do Rack::Utils.unescape(VALID_PATH).valid_encoding?.must_equal true end end it "should accept a request with a correctly encoded path" do response = Rack::MockRequest.new(@app).get(VALID_PATH) response.body.must_equal("Hello World") response.status.must_equal(200) end it "should reject a request with a poorly encoded path" do response = Rack::MockRequest.new(@app).get(INVALID_PATH) response.status.must_equal(400) end it "should accept a request with a correctly encoded query string" do response = Rack::MockRequest.new(@app).get('/', 'QUERY_STRING' => VALID_PATH) response.body.must_equal("Hello World") response.status.must_equal(200) end it "should reject a request with a poorly encoded query string" do response = Rack::MockRequest.new(@app).get('/', 'QUERY_STRING' => INVALID_PATH) response.status.must_equal(400) end it "should reject a request containing malformed multibyte characters" do response = Rack::MockRequest.new(@app).get('/', 'QUERY_STRING' => Rack::Utils.unescape(INVALID_PATH, Encoding::ASCII_8BIT)) response.status.must_equal(400) end end else STDERR.puts "WARN: Skipping Rack::EnforceValidEncoding tests (String#valid_encoding? not available)" end rack-contrib-1.3.0/test/spec_rack_evil.rb000066400000000000000000000011431254365113500203520ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/evil' require 'erb' describe "Rack::Evil" do app = lambda do |env| template = ERB.new("<%= throw :response, [404, {'Content-Type' => 'text/html'}, 'Never know where it comes from'] %>") [200, {'Content-Type' => 'text/plain'}, template.result(binding)] end specify "should enable the app to return the response from anywhere" do status, headers, body = Rack::Evil.new(app).call({}) status.must_equal 404 headers['Content-Type'].must_equal 'text/html' body.must_equal 'Never know where it comes from' end end rack-contrib-1.3.0/test/spec_rack_expectation_cascade.rb000066400000000000000000000046551254365113500234140ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/expectation_cascade' describe "Rack::ExpectationCascade" do specify "with no apps returns a 404 if no expectation header was set" do app = Rack::ExpectationCascade.new env = {} response = app.call(env) response[0].must_equal 404 env.must_equal({}) end specify "with no apps returns a 417 if expectation header was set" do app = Rack::ExpectationCascade.new env = {"Expect" => "100-continue"} response = app.call(env) response[0].must_equal 417 env.must_equal({"Expect" => "100-continue"}) end specify "returns first successful response" do app = Rack::ExpectationCascade.new do |cascade| cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] } end response = app.call({}) response[0].must_equal 200 response[2][0].must_equal "OK" end specify "expectation is set if it has not been already" do app = Rack::ExpectationCascade.new do |cascade| cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Expect: #{env["Expect"]}"]] } end response = app.call({}) response[0].must_equal 200 response[2][0].must_equal "Expect: 100-continue" end specify "returns a 404 if no apps where matched and no expectation header was set" do app = Rack::ExpectationCascade.new do |cascade| cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } end response = app.call({}) response[0].must_equal 404 response[2][0].must_equal nil end specify "returns a 417 if no apps where matched and a expectation header was set" do app = Rack::ExpectationCascade.new do |cascade| cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } end response = app.call({"Expect" => "100-continue"}) response[0].must_equal 417 response[2][0].must_equal nil end specify "nests expectation cascades" do app = Rack::ExpectationCascade.new do |c1| c1 << Rack::ExpectationCascade.new do |c2| c2 << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } end c1 << Rack::ExpectationCascade.new do |c2| c2 << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] } end end response = app.call({}) response[0].must_equal 200 response[2][0].must_equal "OK" end end rack-contrib-1.3.0/test/spec_rack_garbagecollector.rb000066400000000000000000000005151254365113500227140ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/garbagecollector' describe 'Rack::GarbageCollector' do specify 'starts the garbage collector after each request' do app = lambda { |env| [200, {'Content-Type'=>'text/plain'}, ['Hello World']] } Rack::GarbageCollector.new(app).call({}) end end rack-contrib-1.3.0/test/spec_rack_host_meta.rb000066400000000000000000000034061254365113500214020ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/host_meta' require 'rack/contrib/not_found' describe "Rack::HostMeta" do before do app = Rack::Builder.new do use Rack::Lint use Rack::ContentLength use Rack::HostMeta do link :uri => '/robots.txt', :rel => 'robots' link :uri => '/w3c/p3p.xml', :rel => 'privacy', :type => 'application/p3p.xml' link :pattern => '{uri};json_schema', :rel => 'describedby', :type => 'application/x-schema+json' end run Rack::NotFound.new('test/404.html') end @response = Rack::MockRequest.new(app).get('/host-meta') end specify "should respond to /host-meta" do @response.status.must_equal 200 end specify "should respond with the correct media type" do @response['Content-Type'].must_equal 'application/host-meta' end specify "should include a Link entry for each Link item in the config block" do @response.body.must_match(/Link:\s*<\/robots.txt>;.*\n/) @response.body.must_match(/Link:\s*<\/w3c\/p3p.xml>;.*/) end specify "should include a Link-Pattern entry for each Link-Pattern item in the config" do @response.body.must_match(/Link-Pattern:\s*<\{uri\};json_schema>;.*/) end specify "should include a rel attribute for each Link or Link-Pattern entry where specified" do @response.body.must_match(/rel="robots"/) @response.body.must_match(/rel="privacy"/) @response.body.must_match(/rel="describedby"/) end specify "should include a type attribute for each Link or Link-Pattern entry where specified" do @response.body.must_match(/Link:\s*<\/w3c\/p3p.xml>;.*type.*application\/p3p.xml/) @response.body.must_match(/Link-Pattern:\s*<\{uri\};json_schema>;.*type.*application\/x-schema\+json/) end end rack-contrib-1.3.0/test/spec_rack_jsonp.rb000066400000000000000000000212141254365113500205450ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/jsonp' describe "Rack::JSONP" do describe "when a callback parameter is provided" do specify "should wrap the response body in the Javascript callback if JSON" do test_body = '{"bar":"foo"}' callback = 'foo' app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = Rack::JSONP.new(app).call(request).last body.must_equal ["/**/#{callback}(#{test_body})"] end specify "should not wrap the response body in a callback if body is not JSON" do test_body = '{"bar":"foo"}' callback = 'foo' app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = Rack::JSONP.new(app).call(request).last body.must_equal ['{"bar":"foo"}'] end specify "should update content length if it was set" do test_body = '{"bar":"foo"}' callback = 'foo' app = lambda { |env| [200, {'Content-Type' => 'application/json', 'Content-Length' => test_body.length}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") headers = Rack::JSONP.new(app).call(request)[1] expected_length = "/**/".length + test_body.length + callback.length + "()".length headers['Content-Length'].must_equal(expected_length.to_s) end specify "should not touch content length if not set" do test_body = '{"bar":"foo"}' callback = 'foo' app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") headers = Rack::JSONP.new(app).call(request)[1] headers['Content-Length'].must_equal nil end specify "should modify the content type to application/javascript" do test_body = '{"bar":"foo"}' callback = 'foo' app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") headers = Rack::JSONP.new(app).call(request)[1] headers['Content-Type'].must_equal('application/javascript') end specify "should not allow literal U+2028 or U+2029" do test_body = unless "\u2028" == 'u2028' "{\"bar\":\"\u2028 and \u2029\"}" else "{\"bar\":\"\342\200\250 and \342\200\251\"}" end callback = 'foo' app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = Rack::JSONP.new(app).call(request).last unless "\u2028" == 'u2028' body.join.wont_match(/\u2028|\u2029/) else body.join.wont_match(/\342\200\250|\342\200\251/) end end describe "but is empty" do specify "with assignment" do test_body = '{"bar":"foo"}' callback = '' app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = Rack::JSONP.new(app).call(request).last body.must_equal ['{"bar":"foo"}'] end specify "without assignment" do test_body = '{"bar":"foo"}' app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback") body = Rack::JSONP.new(app).call(request).last body.must_equal ['{"bar":"foo"}'] end end describe 'but is invalid' do describe 'with content-type application/json' do specify 'should return "Bad Request"' do test_body = '{"bar":"foo"}' callback = '*' content_type = 'application/json' app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = Rack::JSONP.new(app).call(request).last body.must_equal ['Bad Request'] end specify 'should return set the response code to 400' do test_body = '{"bar":"foo"}' callback = '*' content_type = 'application/json' app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") response_code = Rack::JSONP.new(app).call(request).first response_code.must_equal 400 end end describe 'with content-type text/plain' do specify 'should return "Good Request"' do test_body = 'Good Request' callback = '*' content_type = 'text/plain' app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = Rack::JSONP.new(app).call(request).last body.must_equal ['Good Request'] end specify 'should not change the response code from 200' do test_body = '{"bar":"foo"}' callback = '*' content_type = 'text/plain' app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") response_code = Rack::JSONP.new(app).call(request).first response_code.must_equal 200 end end end describe "with XSS vulnerability attempts" do def request(callback, body = '{"bar":"foo"}') app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") Rack::JSONP.new(app).call(request) end def assert_bad_request(response) response.wont_equal nil status, headers, body = response status.must_equal 400 body.must_equal ["Bad Request"] end specify "should return bad request for callback with invalid characters" do assert_bad_request(request("foobaz()$")) end specify "should return bad request for callbacks with ")) end specify "should return bad requests for callbacks with multiple statements" do assert_bad_request(request("foo%3balert(1)//")) # would render: "foo;alert(1)//" end specify "should not return a bad request for callbacks with dots in the callback" do status, headers, body = request(callback = "foo.bar.baz", test_body = '{"foo":"bar"}') status.must_equal 200 body.must_equal ["/**/#{callback}(#{test_body})"] end end end specify "should not change anything if no callback param is provided" do test_body = ['{"bar":"foo"}'] app = lambda { |env| [200, {'Content-Type' => 'application/json'}, test_body] } request = Rack::MockRequest.env_for("/", :params => "foo=bar") body = Rack::JSONP.new(app).call(request).last body.must_equal test_body end specify "should not change anything if it's not a json response" do test_body = '404 Not Found' app = lambda { |env| [404, {'Content-Type' => 'text/html'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "callback=foo", 'HTTP_ACCEPT' => 'application/json') body = Rack::JSONP.new(app).call(request).last body.must_equal [test_body] end specify "should not change anything if there is no Content-Type header" do test_body = '404 Not Found' app = lambda { |env| [404, {}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "callback=foo", 'HTTP_ACCEPT' => 'application/json') body = Rack::JSONP.new(app).call(request).last body.must_equal [test_body] end specify "should not change anything if the request doesn't have a body" do app1 = lambda { |env| [100, {}, []] } app2 = lambda { |env| [204, {}, []] } app3 = lambda { |env| [304, {}, []] } request = Rack::MockRequest.env_for("/", :params => "callback=foo", 'HTTP_ACCEPT' => 'application/json') Rack::JSONP.new(app1).call(request).must_equal app1.call({}) Rack::JSONP.new(app2).call(request).must_equal app2.call({}) Rack::JSONP.new(app3).call(request).must_equal app3.call({}) end end rack-contrib-1.3.0/test/spec_rack_lighttpd_script_name_fix.rb000066400000000000000000000010441254365113500244640ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/lighttpd_script_name_fix' describe "Rack::LighttpdScriptNameFix" do specify "corrects SCRIPT_NAME and PATH_INFO set by lighttpd " do env = { "PATH_INFO" => "/foo/bar/baz", "SCRIPT_NAME" => "/hello" } app = lambda { |_| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } response = Rack::LighttpdScriptNameFix.new(app).call(env) env['SCRIPT_NAME'].empty?.must_equal(true) env['PATH_INFO'].must_equal '/hello/foo/bar/baz' end end rack-contrib-1.3.0/test/spec_rack_locale.rb000066400000000000000000000035371254365113500206630ustar00rootroot00000000000000require 'minitest/autorun' require 'minitest/hooks' require 'rack/mock' begin require './lib/rack/contrib/locale' describe "Rack::Locale" do include Minitest::Hooks before(:all) do # Set the locales that will be used at various points in the tests I18n.config.available_locales = [I18n.default_locale, :dk, :'en-gb', :es, :zh] end def app @app ||= Rack::Builder.new do use Rack::Locale run lambda { |env| [ 200, {}, [ I18n.locale.to_s ] ] } end end def response_with_languages(accept_languages) Rack::MockRequest.new(app).get('/', { 'HTTP_ACCEPT_LANGUAGE' => accept_languages } ) end specify 'should use I18n.default_locale if no languages are requested' do I18n.default_locale = :zh response_with_languages(nil).body.must_equal('zh') end specify 'should treat an empty qvalue as 1.0' do response_with_languages('en,es;q=0.95').body.must_equal('en') end specify 'should set the Content-Language response header' do headers = response_with_languages('de;q=0.7,dk;q=0.9').headers headers['Content-Language'].must_equal('dk') end specify 'should pick the language with the highest qvalue' do response_with_languages('en;q=0.9,es;q=0.95').body.must_equal('es') end specify 'should retain full language codes' do response_with_languages('en-gb,en-us;q=0.95;en').body.must_equal('en-gb') end specify 'should treat a * as "all other languages"' do response_with_languages('*,en;q=0.5').body.must_equal( I18n.default_locale.to_s ) end specify 'should reset the I18n locale after the response' do I18n.locale = :es response_with_languages('en,de;q=0.8') I18n.locale.must_equal(:es) end end rescue LoadError STDERR.puts "WARN: Skipping Rack::Locale tests (i18n not installed)" end rack-contrib-1.3.0/test/spec_rack_mailexceptions.rb000066400000000000000000000055351254365113500224500ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' begin require './lib/rack/contrib/mailexceptions' require './test/mail_settings.rb' class TestError < RuntimeError end def test_exception raise TestError, 'Suffering Succotash!' rescue => boom return boom end describe 'Rack::MailExceptions' do before do @app = lambda { |env| raise TestError, 'Why, I say' } @env = Rack::MockRequest.env_for("/foo", 'FOO' => 'BAR', :method => 'GET', :input => 'THE BODY' ) @smtp_settings = { :server => 'example.com', :domain => 'example.com', :port => 500, :authentication => :login, :user_name => 'joe', :password => 'secret' } end specify 'yields a configuration object to the block when created' do called = false mailer = Rack::MailExceptions.new(@app) do |mail| called = true mail.to 'foo@example.org' mail.from 'bar@example.org' mail.subject '[ERROR] %s' mail.smtp @smtp_settings end called.must_equal(true) end specify 'generates a Mail object with configured settings' do mailer = Rack::MailExceptions.new(@app) do |mail| mail.to 'foo@example.org' mail.from 'bar@example.org' mail.subject '[ERROR] %s' mail.smtp @smtp_settings end mail = mailer.send(:generate_mail, test_exception, @env) mail.to.must_equal ['foo@example.org'] mail.from.must_equal ['bar@example.org'] mail.subject.must_equal '[ERROR] Suffering Succotash!' mail.body.wont_equal(nil) mail.body.to_s.must_match(/FOO:\s+"BAR"/) mail.body.to_s.must_match(/^\s*THE BODY\s*$/) end specify 'filters HTTP_EXCEPTION body' do mailer = Rack::MailExceptions.new(@app) do |mail| mail.to 'foo@example.org' mail.from 'bar@example.org' mail.subject '[ERROR] %s' mail.smtp @smtp_settings end env = @env.dup env['HTTP_AUTHORIZATION'] = 'Basic xyzzy12345' mail = mailer.send(:generate_mail, test_exception, env) mail.body.to_s.must_match /HTTP_AUTHORIZATION:\s+"Basic \*filtered\*"/ end specify 'catches exceptions raised from app, sends mail, and re-raises' do mailer = Rack::MailExceptions.new(@app) do |mail| mail.to 'foo@example.org' mail.from 'bar@example.org' mail.subject '[ERROR] %s' mail.smtp @smtp_settings end mailer.enable_test_mode lambda { mailer.call(@env) }.must_raise(TestError) @env['mail.sent'].must_equal(true) Mail::TestMailer.deliveries.length.must_equal(1) end end rescue LoadError => boom STDERR.puts "WARN: Skipping Rack::MailExceptions tests (mail not installed)" end rack-contrib-1.3.0/test/spec_rack_nested_params.rb000066400000000000000000000032061254365113500222420ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/nested_params' require 'rack/methodoverride' describe Rack::NestedParams do App = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env)] } def env_for_post_with_headers(path, headers, body) Rack::MockRequest.env_for(path, {:method => "POST", :input => body}.merge(headers)) end def form_post(params, content_type = 'application/x-www-form-urlencoded') params = Rack::Utils.build_query(params) if Hash === params env_for_post_with_headers('/', {'CONTENT_TYPE' => content_type}, params) end def middleware Rack::NestedParams.new(App) end specify "should handle requests with POST body Content-Type of application/x-www-form-urlencoded" do req = middleware.call(form_post({'foo[bar][baz]' => 'nested'})).last req.POST.must_equal({"foo" => { "bar" => { "baz" => "nested" }}}) end specify "should not parse requests with other Content-Type" do req = middleware.call(form_post({'foo[bar][baz]' => 'nested'}, 'text/plain')).last req.POST.must_equal({}) end specify "should work even after another middleware already parsed the request" do app = Rack::MethodOverride.new(middleware) req = app.call(form_post({'_method' => 'put', 'foo[bar]' => 'nested'})).last req.POST.must_equal({'_method' => 'put', "foo" => { "bar" => "nested" }}) req.put?.must_equal true end specify "should make first boolean have precedence even after request already parsed" do app = Rack::MethodOverride.new(middleware) req = app.call(form_post("foo=1&foo=0")).last req.POST.must_equal({"foo" => '1'}) end end rack-contrib-1.3.0/test/spec_rack_not_found.rb000066400000000000000000000006561254365113500214160ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/not_found' describe "Rack::NotFound" do specify "should render the file at the given path for all requests" do app = Rack::Builder.new do use Rack::Lint run Rack::NotFound.new('test/404.html') end response = Rack::MockRequest.new(app).get('/') response.body.must_equal('Not Found') response.status.must_equal(404) end end rack-contrib-1.3.0/test/spec_rack_post_body_content_type_parser.rb000066400000000000000000000031661254365113500255730ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' begin require 'rack/contrib/post_body_content_type_parser' describe "Rack::PostBodyContentTypeParser" do specify "should parse 'application/json' requests" do params = params_for_request '{"key":"value"}', "application/json" params['key'].must_equal "value" end specify "should parse 'application/json; charset=utf-8' requests" do params = params_for_request '{"key":"value"}', "application/json; charset=utf-8" params['key'].must_equal "value" end specify "should parse 'application/json' requests with empty body" do params = params_for_request "", "application/json" params.must_equal({}) end specify "shouldn't affect form-urlencoded requests" do params = params_for_request("key=value", "application/x-www-form-urlencoded") params['key'].must_equal "value" end specify "should not create additions" do before = Symbol.all_symbols params_for_request %{{"json_class":"this_should_not_be_added"}}, "application/json" rescue nil result = Symbol.all_symbols - before result.must_be_empty end end def params_for_request(body, content_type) env = Rack::MockRequest.env_for "/", {:method => "POST", :input => body, "CONTENT_TYPE" => content_type} app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST] } Rack::PostBodyContentTypeParser.new(app).call(env).last end rescue LoadError => e # Missing dependency JSON, skipping tests. STDERR.puts "WARN: Skipping Rack::PostBodyContentTypeParser tests (json not installed)" end rack-contrib-1.3.0/test/spec_rack_proctitle.rb000066400000000000000000000013151254365113500214210ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/proctitle' describe "Rack::ProcTitle" do progname = ::File.basename($0) appname = ::File.expand_path(__FILE__).split('/')[-3] def simple_app(body=['Hello World!']) lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] } end specify "should set the process title when created" do Rack::ProcTitle.new(simple_app) $0.must_equal "#{progname} [#{appname}] init ..." end specify "should set the process title on each request" do app = Rack::ProcTitle.new(simple_app) req = Rack::MockRequest.new(app) 10.times { req.get('/hello') } $0.must_equal "#{progname} [#{appname}/80] (10) GET /hello" end end rack-contrib-1.3.0/test/spec_rack_profiler.rb000066400000000000000000000031301254365113500212330ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' begin require 'rack/contrib/profiler' describe 'Rack::Profiler' do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'Oh hai der'] } request = Rack::MockRequest.env_for("/", :params => "profile=process_time") specify 'printer defaults to RubyProf::CallStackPrinter' do profiler = Rack::Profiler.new(nil) profiler.instance_variable_get('@printer').must_equal RubyProf::CallStackPrinter profiler.instance_variable_get('@times').must_equal 1 end specify 'CallStackPrinter has Content-Type test/html' do headers = Rack::Profiler.new(app, :printer => :call_stack).call(request)[1] headers.must_equal "Content-Type"=>"text/html" end specify 'CallTreePrinter has correct headers' do headers = Rack::Profiler.new(app, :printer => :call_tree).call(request)[1] headers.must_equal "Content-Disposition"=>"attachment; filename=\"/.process_time.tree\"", "Content-Type"=>"application/octet-stream" end specify 'FlatPrinter and GraphPrinter has Content-Type text/plain' do %w(flat graph).each do |printer| headers = Rack::Profiler.new(app, :printer => printer.to_sym).call(request)[1] headers.must_equal "Content-Type"=>"text/plain" end end specify 'GraphHtmlPrinter has Content-Type text/html' do headers = Rack::Profiler.new(app, :printer => :graph_html).call(request)[1] headers.must_equal "Content-Type"=>"text/html" end end rescue LoadError => boom $stderr.puts "WARN: Skipping Rack::Profiler tests (ruby-prof not installed)" end rack-contrib-1.3.0/test/spec_rack_relative_redirect.rb000066400000000000000000000066501254365113500231170ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/relative_redirect' require 'fileutils' describe Rack::RelativeRedirect do def request(opts={}, &block) @def_status = opts[:status] if opts[:status] @def_location = opts[:location] if opts[:location] yield Rack::MockRequest.new(Rack::RelativeRedirect.new(@def_app, &opts[:block])).get(opts[:path]||@def_path, opts[:headers]||{}) end before do @def_path = '/path/to/blah' @def_status = 301 @def_location = '/redirect/to/blah' @def_app = lambda { |env| [@def_status, {'Location' => @def_location}, [""]]} end specify "should rewrite Location on all the redirect codes" do [301, 302, 303, 307, 308].each do |status| request(:status => status) do |r| r.status.must_equal(status) r.headers['Location'].must_equal('http://example.org/redirect/to/blah') end end end specify "should not rewrite Location on other status codes" do [200, 201, 300, 304, 305, 306, 404, 500].each do |status| request(:status => status) do |r| r.status.must_equal(status) r.headers['Location'].must_equal('/redirect/to/blah') end end end specify "should make the location url an absolute url if currently a relative url" do request do |r| r.status.must_equal(301) r.headers['Location'].must_equal('http://example.org/redirect/to/blah') end request(:status=>302, :location=>'/redirect') do |r| r.status.must_equal(302) r.headers['Location'].must_equal('http://example.org/redirect') end end specify "should use the request path if the relative url is given and doesn't start with a slash" do request(:status=>303, :location=>'redirect/to/blah') do |r| r.status.must_equal(303) r.headers['Location'].must_equal('http://example.org/path/to/redirect/to/blah') end request(:status=>303, :location=>'redirect') do |r| r.status.must_equal(303) r.headers['Location'].must_equal('http://example.org/path/to/redirect') end end specify "should use a given block to make the url absolute" do request(:block=>proc{|env, res| "https://example.org"}) do |r| r.status.must_equal(301) r.headers['Location'].must_equal('https://example.org/redirect/to/blah') end request(:status=>303, :location=>'/redirect', :block=>proc{|env, res| "https://e.org:9999/blah"}) do |r| r.status.must_equal(303) r.headers['Location'].must_equal('https://e.org:9999/blah/redirect') end end specify "should not modify the location url unless the response is a redirect" do status = 200 @def_app = lambda { |env| [status, {'Content-Type' => "text/html"}, [""]]} request do |r| r.status.must_equal(200) r.headers.wont_include('Location') end status = 404 @def_app = lambda { |env| [status, {'Content-Type' => "text/html", 'Location' => 'redirect'}, [""]]} request do |r| r.status.must_equal(404) r.headers['Location'].must_equal('redirect') end end specify "should not modify the location url if it is already an absolute url" do request(:location=>'https://example.org/') do |r| r.status.must_equal(301) r.headers['Location'].must_equal('https://example.org/') end request(:status=>302, :location=>'https://e.org:9999/redirect') do |r| r.status.must_equal(302) r.headers['Location'].must_equal('https://e.org:9999/redirect') end end end rack-contrib-1.3.0/test/spec_rack_response_cache.rb000066400000000000000000000120571254365113500224020ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/response_cache' require 'fileutils' describe Rack::ResponseCache do def request(opts={}, &block) Rack::MockRequest.new(Rack::ResponseCache.new(block||@def_app, opts[:cache]||@cache, &opts[:rc_block])).send(opts[:meth]||:get, opts[:path]||@def_path, opts[:headers]||{}) end before do @cache = {} @def_disk_cache = ::File.join(::File.dirname(__FILE__), 'response_cache_test_disk_cache') @def_value = ["rack-response-cache"] @def_path = '/path/to/blah' @def_app = lambda { |env| [200, {'Content-Type' => env['CT'] || 'text/html'}, @def_value]} end after do FileUtils.rm_rf(@def_disk_cache) end specify "should cache results to disk if cache is a string" do request(:cache=>@def_disk_cache) ::File.read(::File.join(@def_disk_cache, 'path', 'to', 'blah.html')).must_equal @def_value.first request(:path=>'/path/3', :cache=>@def_disk_cache) ::File.read(::File.join(@def_disk_cache, 'path', '3.html')).must_equal @def_value.first end specify "should cache results to given cache if cache is not a string" do request @cache.must_equal('/path/to/blah.html'=>@def_value) request(:path=>'/path/3') @cache.must_equal('/path/to/blah.html'=>@def_value, '/path/3.html'=>@def_value) end specify "should not CACHE RESults if request method is not GET" do request(:meth=>:post) @cache.must_equal({}) request(:meth=>:put) @cache.must_equal({}) request(:meth=>:delete) @cache.must_equal({}) end specify "should not cache results if there is a query string" do request(:path=>'/path/to/blah?id=1') @cache.must_equal({}) request(:path=>'/path/to/?id=1') @cache.must_equal({}) request(:path=>'/?id=1') @cache.must_equal({}) end specify "should cache results if there is an empty query string" do request(:path=>'/?') @cache.must_equal('/index.html'=>@def_value) end specify "should not cache results if the request is not sucessful (status 200)" do request{|env| [404, {'Content-Type' => 'text/html'}, ['']]} @cache.must_equal({}) request{|env| [500, {'Content-Type' => 'text/html'}, ['']]} @cache.must_equal({}) request{|env| [302, {'Content-Type' => 'text/html'}, ['']]} @cache.must_equal({}) end specify "should not cache results if the block returns nil or false" do request(:rc_block=>proc{false}) @cache.must_equal({}) request(:rc_block=>proc{nil}) @cache.must_equal({}) end specify "should cache results to path returned by block" do request(:rc_block=>proc{"1"}) @cache.must_equal("1"=>@def_value) request(:rc_block=>proc{"2"}) @cache.must_equal("1"=>@def_value, "2"=>@def_value) end specify "should pass the environment and response to the block" do e, r = nil, nil request(:rc_block=>proc{|env,res| e, r = env, res; nil}) e['PATH_INFO'].must_equal @def_path e['REQUEST_METHOD'].must_equal 'GET' e['QUERY_STRING'].must_equal '' r.must_equal([200, {"Content-Type"=>"text/html"}, ["rack-response-cache"]]) end specify "should unescape the path by default" do request(:path=>'/path%20with%20spaces') @cache.must_equal('/path with spaces.html'=>@def_value) request(:path=>'/path%3chref%3e') @cache.must_equal('/path with spaces.html'=>@def_value, '/path.html'=>@def_value) end specify "should cache html, css, and xml responses by default" do request(:path=>'/a') @cache.must_equal('/a.html'=>@def_value) request(:path=>'/b', :headers=>{'CT'=>'text/xml'}) @cache.must_equal('/a.html'=>@def_value, '/b.xml'=>@def_value) request(:path=>'/c', :headers=>{'CT'=>'text/css'}) @cache.must_equal('/a.html'=>@def_value, '/b.xml'=>@def_value, '/c.css'=>@def_value) end specify "should cache responses by default with the extension added if not already present" do request(:path=>'/a.html') @cache.must_equal('/a.html'=>@def_value) request(:path=>'/b.xml', :headers=>{'CT'=>'text/xml'}) @cache.must_equal('/a.html'=>@def_value, '/b.xml'=>@def_value) request(:path=>'/c.css', :headers=>{'CT'=>'text/css'}) @cache.must_equal('/a.html'=>@def_value, '/b.xml'=>@def_value, '/c.css'=>@def_value) end specify "should not delete existing extensions" do request(:path=>'/d.css', :headers=>{'CT'=>'text/html'}) @cache.must_equal('/d.css.html'=>@def_value) end specify "should cache html responses with empty basename to index.html by default" do request(:path=>'/') @cache.must_equal('/index.html'=>@def_value) request(:path=>'/blah/') @cache.must_equal('/index.html'=>@def_value, '/blah/index.html'=>@def_value) request(:path=>'/blah/2/') @cache.must_equal('/index.html'=>@def_value, '/blah/index.html'=>@def_value, '/blah/2/index.html'=>@def_value) end specify "should raise an error if a cache argument is not provided" do app = Rack::Builder.new{use Rack::ResponseCache; run lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST]}} proc{Rack::MockRequest.new(app).get('/')}.must_raise(ArgumentError) end end rack-contrib-1.3.0/test/spec_rack_response_headers.rb000066400000000000000000000020131254365113500227410ustar00rootroot00000000000000require 'minitest/autorun' require 'rack' require 'rack/contrib/response_headers' describe "Rack::ResponseHeaders" do specify "yields a HeaderHash of response headers" do orig_headers = {'X-Foo' => 'foo', 'X-Bar' => 'bar'} app = Proc.new {[200, orig_headers, []]} middleware = Rack::ResponseHeaders.new(app) do |headers| assert_instance_of Rack::Utils::HeaderHash, headers orig_headers.must_equal headers end middleware.call({}) end specify "allows adding headers" do app = Proc.new {[200, {'X-Foo' => 'foo'}, []]} middleware = Rack::ResponseHeaders.new(app) do |headers| headers['X-Bar'] = 'bar' end r = middleware.call({}) r[1].must_equal('X-Foo' => 'foo', 'X-Bar' => 'bar') end specify "allows deleting headers" do app = Proc.new {[200, {'X-Foo' => 'foo', 'X-Bar' => 'bar'}, []]} middleware = Rack::ResponseHeaders.new(app) do |headers| headers.delete('X-Bar') end r = middleware.call({}) r[1].must_equal('X-Foo' => 'foo') end end rack-contrib-1.3.0/test/spec_rack_runtime.rb000066400000000000000000000025021254365113500210760ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/runtime' describe "Rack::Runtime" do specify "sets X-Runtime is none is set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } response = Rack::Runtime.new(app).call({}) response[1]['X-Runtime'].must_match /[\d\.]+/ end specify "does not set the X-Runtime if it is already set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain', "X-Runtime" => "foobar"}, "Hello, World!"] } response = Rack::Runtime.new(app).call({}) response[1]['X-Runtime'].must_equal "foobar" end specify "should allow a suffix to be set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } response = Rack::Runtime.new(app, "Test").call({}) response[1]['X-Runtime-Test'].must_match /[\d\.]+/ end specify "should allow multiple timers to be set" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } runtime1 = Rack::Runtime.new(app, "App") runtime2 = Rack::Runtime.new(runtime1, "All") response = runtime2.call({}) response[1]['X-Runtime-App'].must_match /[\d\.]+/ response[1]['X-Runtime-All'].must_match /[\d\.]+/ (Float(response[1]['X-Runtime-All']) > Float(response[1]['X-Runtime-App'])).must_equal(true) end end rack-contrib-1.3.0/test/spec_rack_sendfile.rb000066400000000000000000000050431254365113500212070ustar00rootroot00000000000000require 'minitest/autorun' require 'rack/mock' require 'rack/contrib/sendfile' describe "Rack::File" do specify "should respond to #to_path" do Rack::File.new(Dir.pwd).must_respond_to :to_path end end describe "Rack::Sendfile" do def sendfile_body res = ['Hello World'] def res.to_path ; "/tmp/hello.txt" ; 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::Sendfile.new(simple_app(body)) end before do @request = Rack::MockRequest.new(sendfile_app) end def request(headers={}) yield @request.get('/', headers) end specify "does nothing when no X-Sendfile-Type header present" do request do |response| response.ok?.must_equal(true) response.body.must_equal 'Hello World' response.headers.wont_include 'X-Sendfile' end end specify "sets X-Sendfile response header and discards body" do request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response| response.ok?.must_equal(true) response.body.empty?.must_equal(true) response.headers['X-Sendfile'].must_equal '/tmp/hello.txt' end end specify "sets X-Lighttpd-Send-File response header and discards body" do request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response| response.ok?.must_equal(true) response.body.empty?.must_equal(true) response.headers['X-Lighttpd-Send-File'].must_equal '/tmp/hello.txt' end end specify "sets X-Accel-Redirect response header and discards body" do headers = { 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect', 'HTTP_X_ACCEL_MAPPING' => '/tmp/=/foo/bar/' } request headers do |response| response.ok?.must_equal(true) response.body.empty?.must_equal(true) response.headers['X-Accel-Redirect'].must_equal '/foo/bar/hello.txt' end end specify 'writes to rack.error when no X-Accel-Mapping is specified' do request 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect' do |response| response.ok?.must_equal(true) response.body.must_equal 'Hello World' response.headers.wont_include 'X-Accel-Redirect' response.errors.must_include 'X-Accel-Mapping' end end specify '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.must_equal 'Not a file...' response.headers.wont_include 'X-Sendfile' end end end rack-contrib-1.3.0/test/spec_rack_simple_endpoint.rb000066400000000000000000000072071254365113500226130ustar00rootroot00000000000000require 'minitest/autorun' require 'rack' require 'rack/contrib/simple_endpoint' describe "Rack::SimpleEndpoint" do before do @app = Proc.new { Rack::Response.new {|r| r.write "Downstream app"}.finish } end specify "calls downstream app when no match" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') { 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/baz')) status.must_equal 200 body.body.must_equal ['Downstream app'] end specify "calls downstream app when path matches but method does not" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo' => :get) { 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo', :method => 'post')) status.must_equal 200 body.body.must_equal ['Downstream app'] end specify "calls downstream app when path matches but block returns :pass" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') { :pass } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo')) status.must_equal 200 body.body.must_equal ['Downstream app'] end specify "returns endpoint response when path matches" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') { 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo')) status.must_equal 200 body.body.must_equal ['bar'] end specify "returns endpoint response when path and single method requirement match" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo' => :get) { 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo')) status.must_equal 200 body.body.must_equal ['bar'] end specify "returns endpoint response when path and one of multiple method requirements match" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo' => [:get, :post]) { 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo', :method => 'post')) status.must_equal 200 body.body.must_equal ['bar'] end specify "returns endpoint response when path matches regex" do endpoint = Rack::SimpleEndpoint.new(@app, /foo/) { 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/bar/foo')) status.must_equal 200 body.body.must_equal ['bar'] end specify "block yields Rack::Request and Rack::Response objects" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') do |req, res| assert_instance_of ::Rack::Request, req assert_instance_of ::Rack::Response, res end endpoint.call(Rack::MockRequest.env_for('/foo')) end specify "block yields MatchData object when Regex path matcher specified" do endpoint = Rack::SimpleEndpoint.new(@app, /foo(.+)/) do |req, res, match| assert_instance_of MatchData, match assert_equal 'bar', match[1] end endpoint.call(Rack::MockRequest.env_for('/foobar')) end specify "block does NOT yield MatchData object when String path matcher specified" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') do |req, res, match| assert_nil match end endpoint.call(Rack::MockRequest.env_for('/foo')) end specify "response honors headers set in block" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') {|req, res| res['X-Foo'] = 'bar'; 'baz' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo')) status.must_equal 200 headers['X-Foo'].must_equal 'bar' body.body.must_equal ['baz'] end specify "sets Content-Length header" do endpoint = Rack::SimpleEndpoint.new(@app, '/foo') {|req, res| 'bar' } status, headers, body = endpoint.call(Rack::MockRequest.env_for('/foo')) headers['Content-Length'].must_equal '3' end end rack-contrib-1.3.0/test/spec_rack_static_cache.rb000066400000000000000000000056371254365113500220410ustar00rootroot00000000000000require 'minitest/autorun' require 'rack' require 'rack/contrib/static_cache' require 'rack/mock' class DummyApp def call(env) [200, {}, ["Hello World"]] end end describe "Rack::StaticCache" do before do @root = ::File.expand_path(::File.dirname(__FILE__)) end it "should serve files with required headers" do default_app_request res = @request.get("/statics/test") res.ok?.must_equal(true) res.body.must_match(/rubyrack/) res.headers['Cache-Control'].must_equal 'max-age=31536000, public' next_year = Time.now().year + 1 res.headers['Expires'].must_match(Regexp.new( "[A-Z][a-z]{2}[,][\s][0-9]{2}[\s][A-Z][a-z]{2}[\s]" << "#{next_year}" << "[\s][0-9]{2}[:][0-9]{2}[:][0-9]{2} GMT$")) end it "should return 404s if url root is known but it can't find the file" do default_app_request res = @request.get("/statics/foo") res.not_found?.must_equal(true) end it "should call down the chain if url root is not known" do default_app_request res = @request.get("/something/else") res.ok?.must_equal(true) res.body.must_equal "Hello World" end it "should serve files if requested with version number and versioning is enabled" do default_app_request res = @request.get("/statics/test-0.0.1") res.ok?.must_equal(true) end it "should change cache duration if specified thorugh option" do configured_app_request res = @request.get("/statics/test") res.ok?.must_equal(true) res.body.must_match(/rubyrack/) next_next_year = Time.now().year + 2 res.headers['Expires'].must_match(Regexp.new("#{next_next_year}")) end it "should round max-age if duration is part of a year" do one_week_duration_app_request res = @request.get("/statics/test") res.ok?.must_equal(true) res.body.must_match(/rubyrack/) res.headers['Cache-Control'].must_equal "max-age=606461, public" end it "should return 404s if requested with version number but versioning is disabled" do configured_app_request res = @request.get("/statics/test-0.0.1") res.not_found?.must_equal(true) end it "should serve files with plain headers when * is added to the directory name" do configured_app_request res = @request.get("/documents/test") res.ok?.must_equal(true) res.body.must_match(/nocache/) next_next_year = Time.now().year + 2 res.headers['Expires'].wont_match(Regexp.new("#{next_next_year}")) end def default_app_request @options = {:urls => ["/statics"], :root => @root} request end def one_week_duration_app_request @options = {:urls => ["/statics"], :root => @root, :duration => 1.fdiv(52)} request end def configured_app_request @options = {:urls => ["/statics", "/documents*"], :root => @root, :versioning => false, :duration => 2} request end def request @request = Rack::MockRequest.new(Rack::StaticCache.new(DummyApp.new, @options)) end end rack-contrib-1.3.0/test/spec_rack_try_static.rb000066400000000000000000000030421254365113500216000ustar00rootroot00000000000000require 'minitest/autorun' require 'rack' require 'rack/contrib/try_static' require 'rack/mock' def build_options(opts) { :urls => %w[/], :root => ::File.expand_path(::File.dirname(__FILE__)), }.merge(opts) end def request(options = {}) @request = Rack::MockRequest.new( Rack::TryStatic.new( lambda { |_| [200, {}, ["Hello World"]]}, options)) end describe "Rack::TryStatic" do describe 'when file cannot be found' do it 'should call call app' do res = request(build_options(:try => ['html'])).get('/documents') res.ok?.must_equal(true) res.body.must_equal "Hello World" end end describe 'when file can be found' do it 'should serve first found' do res = request(build_options(:try => ['.html', '/index.html', '/index.htm'])).get('/documents') res.ok?.must_equal(true) res.body.strip.must_equal "index.html" end end describe 'when path_info maps directly to file' do it 'should serve existing' do res = request(build_options(:try => ['/index.html'])).get('/documents/existing.html') res.ok?.must_equal(true) res.body.strip.must_equal "existing.html" end end describe 'when sharing options' do it 'should not mutate given options' do org_options = build_options :try => ['/index.html'] given_options = org_options.dup request(given_options).get('/documents').ok?.must_equal(true) request(given_options).get('/documents').ok?.must_equal(true) given_options.must_equal org_options end end end rack-contrib-1.3.0/test/statics/000077500000000000000000000000001254365113500165275ustar00rootroot00000000000000rack-contrib-1.3.0/test/statics/test000066400000000000000000000000101254365113500174200ustar00rootroot00000000000000rubyrack