Merge branch 'bzr/golem' of /Users/distler/Sites/code/instiki

master
Jacques Distler 2010-06-13 23:21:22 -05:00
commit 65fd56888f
107 changed files with 1463 additions and 2008 deletions

View File

@ -8,12 +8,12 @@ module Rack
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
def self.run(app, options={})
options[:BindAddress] = options.delete(:Host) if options[:Host]
server = ::WEBrick::HTTPServer.new(options)
server.mount "/", Rack::Handler::WEBrick, app
trap(:INT) { server.shutdown }
trap(:TERM) { server.shutdown }
yield server if block_given?
server.start
@server = ::WEBrick::HTTPServer.new(options)
@server.mount "/", Rack::Handler::WEBrick, app
trap(:INT) { @server.shutdown }
trap(:TERM) { @server.shutdown }
yield @server if block_given?
@server.start
end
end
end
@ -39,7 +39,7 @@ module Rack
entire_buffer_written_out = false
while !entire_buffer_written_out
written = @rewindable_io.write(buffer)
entire_buffer_written_out = written == buffer.size
entire_buffer_written_out = written == Rack::Utils.bytesize(buffer)
if !entire_buffer_written_out
buffer.slice!(0 .. written - 1)
end

View File

@ -226,6 +226,15 @@ END_THM
def test_have_latest_itex2mml
assert_markup_parsed_as(
%{<p>boxed equation <math class='maruku-mathml' } +
%{display='inline' xmlns='http://www.w3.org/1998} +
%{/Math/MathML'><menclose notation='box'><mrow><} +
%{menclose notation='updiagonalstrike'><mi>D</mi} +
%{></menclose><mi>\317\210</mi><mo>=</mo><mn>0</} +
%{mn></mrow></menclose></math></p>},
"boxed equation $\\boxed{\\slash{D}\\psi=0}$")
assert_markup_parsed_as(
%{<p>equation <math class='maruku-mathml' displa} +
%{y='inline' xmlns='http://www.w3.org/1998/Math/} +

View File

@ -1,4 +1,4 @@
Copyright (c) 2007, 2008, 2009 Christian Neukirchen <purl.org/net/chneukirchen>
Copyright (c) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to

View File

@ -9,16 +9,6 @@ middleware) into a single method call.
The exact details of this are described in the Rack specification,
which all Rack applications should conform to.
== Specification changes in this release
With Rack 1.1, the Rack specification (found in SPEC) changed in the
following backward-incompatible ways.
* Rack::VERSION has been pushed to [1,1].
* rack.logger is now specified.
* The SPEC now allows subclasses of the required types.
* rack.input has to be opened in binary mode.
== Supported web servers
The included *handlers* connect all kinds of web servers to Rack:
@ -46,9 +36,6 @@ changing anything.
== Supported web frameworks
The included *adapters* connect Rack with existing Ruby web frameworks:
* Camping
These frameworks include Rack adapters in their distributions:
* Camping
* Coset
@ -143,9 +130,9 @@ at my site:
== Running the tests
Testing Rack requires the test/spec testing framework:
Testing Rack requires the bacon testing framework:
gem install test-spec
gem install bacon
There are two rake-based test tasks:
@ -153,11 +140,10 @@ There are two rake-based test tasks:
rake fulltest runs all the tests
The fast testsuite has no dependencies outside of the core Ruby
installation and test-spec.
installation and bacon.
To run the test suite completely, you need:
* camping
* fcgi
* memcache-client
* mongrel
@ -278,7 +264,7 @@ run on port 11211) and memcache-client installed.
* Make sure WEBrick respects the :Host option
* Many Ruby 1.9 fixes.
* December ??th, 2009: Ninth public release 1.1.0.
* January 3rd, 2010: Ninth public release 1.1.0.
* Moved Auth::OpenID to rack-contrib.
* SPEC change that relaxes Lint slightly to allow subclasses of the
required types
@ -313,17 +299,26 @@ run on port 11211) and memcache-client installed.
* Enforce binary encoding in RewindableInput
* Set correct external_encoding for handlers that don't use RewindableInput
* June 13th, 2010: Tenth public release 1.2.0.
* Removed Camping adapter: Camping 2.0 supports Rack as-is
* Removed parsing of quoted values
* Add Request.trace? and Request.options?
* Add mime-type for .webm and .htc
* Fix HTTP_X_FORWARDED_FOR
* Various multipart fixes
* Switch test suite to bacon
== Contact
Please post bugs, suggestions and patches to
the bug tracker at <http://rack.lighthouseapp.com/>.
the bug tracker at <http://github.com/rack/rack/issues>.
Mailing list archives are available at
<http://groups.google.com/group/rack-devel>.
Git repository (send Git patches to the mailing list):
* http://github.com/rack/rack
* http://git.vuxu.org/cgi-bin/gitweb.cgi?p=rack.git
* http://git.vuxu.org/cgi-bin/gitweb.cgi?p=rack-github.git
You are also welcome to join the #rack channel on irc.freenode.net.
@ -345,12 +340,14 @@ would like to thank:
* Tim Fletcher, for the HTTP authentication code.
* Luc Heinrich for the Cookie sessions, the static file handler and bugfixes.
* Armin Ronacher, for the logo and racktools.
* Aredridel, Ben Alpert, Dan Kubb, Daniel Roethlisberger, Matt Todd,
Tom Robinson, Phil Hagelberg, S. Brent Faulkner, Bosko Milekic,
Daniel Rodríguez Troitiño, Genki Takiuchi, Geoffrey Grosenbach,
Julien Sanchez, Kamal Fariz Mahyuddin, Masayoshi Takahashi, Patrick
Aljordm, Mig, and Kazuhiro Nishiyama for bug fixing and other
improvements.
* Alex Beregszaszi, Alexander Kahn, Anil Wadghule, Aredridel, Ben
Alpert, Dan Kubb, Daniel Roethlisberger, Matt Todd, Tom Robinson,
Phil Hagelberg, S. Brent Faulkner, Bosko Milekic, Daniel Rodríguez
Troitiño, Genki Takiuchi, Geoffrey Grosenbach, Julien Sanchez, Kamal
Fariz Mahyuddin, Masayoshi Takahashi, Patrick Aljordm, Mig, Kazuhiro
Nishiyama, Jon Bardin, Konstantin Haase, Larry Siden, Matias
Korhonen, Sam Ruby, Simon Chiang, Tim Connor, Timur Batyrshin, and
Zach Brock for bug fixing and other improvements.
* Eric Wong, Hongli Lai, Jeremy Kemper for their continuous support
and API improvements.
* Yehuda Katz and Carl Lerche for refactoring rackup.
@ -368,7 +365,7 @@ would like to thank:
== Copyright
Copyright (C) 2007, 2008, 2009 Christian Neukirchen <http://purl.org/net/chneukirchen>
Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <http://purl.org/net/chneukirchen>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to

View File

@ -1,16 +1,13 @@
# Rakefile for Rack. -*-ruby-*-
require 'rake/rdoctask'
require 'rake/testtask'
desc "Run all the tests"
task :default => [:test]
desc "Make an archive as .tar.gz"
task :dist => [:chmod, :changelog, :rdoc, "SPEC"] do
FileUtils.touch("RDOX")
sh "git archive --format=tar --prefix=#{release}/ HEAD^{tree} >#{release}.tar"
sh "pax -waf #{release}.tar -s ':^:#{release}/:' RDOX SPEC ChangeLog doc rack.gemspec"
sh "pax -waf #{release}.tar -s ':^:#{release}/:' SPEC ChangeLog doc rack.gemspec"
sh "gzip -f -9 #{release}.tar"
end
@ -23,19 +20,19 @@ task :officialrelease do
sh "mv stage/#{release}.tar.gz stage/#{release}.gem ."
end
task :officialrelease_really => [:fulltest, "RDOX", "SPEC", :dist, :gem] do
task :officialrelease_really => ["SPEC", :dist, :gem] do
sh "sha1sum #{release}.tar.gz #{release}.gem"
end
def release
require File.dirname(__FILE__) + "/lib/rack"
"rack-#{Rack.release}"
"rack-#{Rack.release}.0"
end
desc "Make binaries executable"
task :chmod do
Dir["bin/*"].each { |binary| File.chmod(0775, binary) }
Dir["test/cgi/test*"].each { |binary| File.chmod(0775, binary) }
Dir["spec/cgi/spec*"].each { |binary| File.chmod(0775, binary) }
end
desc "Generate a ChangeLog"
@ -57,11 +54,6 @@ task :changelog do
end
desc "Generate RDox"
task "RDOX" do
sh "specrb -Ilib:test -a --rdox >RDOX"
end
desc "Generate Rack Specification"
task "SPEC" do
File.open("SPEC", "wb") { |file|
@ -75,24 +67,29 @@ end
desc "Run all the fast tests"
task :test do
sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS'] || '-t "^(?!Rack::Handler|Rack::Adapter|Rack::Session::Memcache|rackup)"'}"
opts = ENV['TEST'] || '-a'
specopts = ENV['TESTOPTS'] ||
"-q -t '^(?!Rack::Handler|Rack::Adapter|Rack::Session::Memcache|rackup)'"
sh "bacon -I./lib:./spec -w #{opts} #{specopts}"
end
desc "Run all the tests"
task :fulltest => [:chmod] do
sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
opts = ENV['TEST'] || '-a'
specopts = ENV['TESTOPTS'] || '-q'
sh "bacon -I./lib:./spec -w #{opts} #{specopts}"
end
task :gem => ["SPEC"] do
FileUtils.touch("RDOX")
sh "gem build rack.gemspec"
end
desc "Generate RDoc documentation"
task :rdoc do
task :rdoc => ["SPEC"] do
sh(*%w{rdoc --line-numbers --main README
--title 'Rack\ Documentation' --charset utf-8 -U -o doc} +
%w{README KNOWN-ISSUES SPEC RDOX} +
%w{README KNOWN-ISSUES SPEC} +
Dir["lib/**/*.rb"])
end

View File

@ -1,2 +1,4 @@
#!/usr/bin/env ruby
require "rack"
Rack::Server.start

View File

@ -1,4 +1,4 @@
# Copyright (C) 2007, 2008, 2009 Christian Neukirchen <purl.org/net/chneukirchen>
# Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
#
# Rack is freely distributable under the terms of an MIT-style license.
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -20,7 +20,7 @@ module Rack
# Return the Rack release as a dotted string.
def self.release
"1.1"
"1.2"
end
autoload :Builder, "rack/builder"
@ -78,15 +78,4 @@ module Rack
autoload :Pool, "rack/session/pool"
autoload :Memcache, "rack/session/memcache"
end
# *Adapters* connect Rack with third party web frameworks.
#
# Rack includes an adapter for Camping, see README for other
# frameworks supporting Rack in their code bases.
#
# Refer to the submodules for framework-specific calling details.
module Adapter
autoload :Camping, "rack/adapter/camping"
end
end

View File

@ -1,22 +0,0 @@
module Rack
module Adapter
class Camping
def initialize(app)
@app = app
end
def call(env)
env["PATH_INFO"] ||= ""
env["SCRIPT_NAME"] ||= ""
controller = @app.run(env['rack.input'], env)
h = controller.headers
h.each_pair do |k,v|
if v.kind_of? URI
h[k] = v.to_s
end
end
[controller.status, controller.headers, [controller.body.to_s]]
end
end
end
end

View File

@ -35,7 +35,7 @@ module Rack
super k.to_s, v.to_s
end
UNQUOTED = ['qop', 'nc', 'stale']
UNQUOTED = ['nc', 'stale']
def to_s
inject([]) do |parts, (k, v)|

View File

@ -1,480 +0,0 @@
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
gem 'ruby-openid', '~> 2' if defined? Gem
require 'rack/request'
require 'rack/utils'
require 'rack/auth/abstract/handler'
require 'uri'
require 'openid' #gem
require 'openid/extension' #gem
require 'openid/store/memory' #gem
module Rack
class Request
def openid_request
@env['rack.auth.openid.request']
end
def openid_response
@env['rack.auth.openid.response']
end
end
module Auth
# Rack::Auth::OpenID provides a simple method for setting up an OpenID
# Consumer. It requires the ruby-openid library from janrain to operate,
# as well as a rack method of session management.
#
# The ruby-openid home page is at http://openidenabled.com/ruby-openid/.
#
# The OpenID specifications can be found at
# http://openid.net/specs/openid-authentication-1_1.html
# and
# http://openid.net/specs/openid-authentication-2_0.html. Documentation
# for published OpenID extensions and related topics can be found at
# http://openid.net/developers/specs/.
#
# It is recommended to read through the OpenID spec, as well as
# ruby-openid's documentation, to understand what exactly goes on. However
# a setup as simple as the presented examples is enough to provide
# Consumer functionality.
#
# This library strongly intends to utilize the OpenID 2.0 features of the
# ruby-openid library, which provides OpenID 1.0 compatiblity.
#
# NOTE: Due to the amount of data that this library stores in the
# session, Rack::Session::Cookie may fault.
class OpenID
class NoSession < RuntimeError; end
class BadExtension < RuntimeError; end
# Required for ruby-openid
ValidStatus = [:success, :setup_needed, :cancel, :failure]
# = Arguments
#
# The first argument is the realm, identifying the site they are trusting
# with their identity. This is required, also treated as the trust_root
# in OpenID 1.x exchanges.
#
# The optional second argument is a hash of options.
#
# == Options
#
# <tt>:return_to</tt> defines the url to return to after the client
# authenticates with the openid service provider. This url should point
# to where Rack::Auth::OpenID is mounted. If <tt>:return_to</tt> is not
# provided, return_to will be the current url which allows flexibility
# with caveats.
#
# <tt>:session_key</tt> defines the key to the session hash in the env.
# It defaults to 'rack.session'.
#
# <tt>:openid_param</tt> defines at what key in the request parameters to
# find the identifier to resolve. As per the 2.0 spec, the default is
# 'openid_identifier'.
#
# <tt>:store</tt> defined what OpenID Store to use for persistant
# information. By default a Store::Memory will be used.
#
# <tt>:immediate</tt> as true will make initial requests to be of an
# immediate type. This is false by default. See OpenID specification
# documentation.
#
# <tt>:extensions</tt> should be a hash of openid extension
# implementations. The key should be the extension main module, the value
# should be an array of arguments for extension::Request.new.
# The hash is iterated over and passed to #add_extension for processing.
# Please see #add_extension for further documentation.
#
# == Examples
#
# simple_oid = OpenID.new('http://mysite.com/')
#
# return_oid = OpenID.new('http://mysite.com/', {
# :return_to => 'http://mysite.com/openid'
# })
#
# complex_oid = OpenID.new('http://mysite.com/',
# :immediate => true,
# :extensions => {
# ::OpenID::SReg => [['email'],['nickname']]
# }
# )
#
# = Advanced
#
# Most of the functionality of this library is encapsulated such that
# expansion and overriding functions isn't difficult nor tricky.
# Alternately, to avoid opening up singleton objects or subclassing, a
# wrapper rack middleware can be composed to act upon Auth::OpenID's
# responses. See #check and #finish for locations of pertinent data.
#
# == Responses
#
# To change the responses that Auth::OpenID returns, override the methods
# #redirect, #bad_request, #unauthorized, #access_denied, and
# #foreign_server_failure.
#
# Additionally #confirm_post_params is used when the URI would exceed
# length limits on a GET request when doing the initial verification
# request.
#
# == Processing
#
# To change methods of processing completed transactions, override the
# methods #success, #setup_needed, #cancel, and #failure. Please ensure
# the returned object is a rack compatible response.
#
# The first argument is an OpenID::Response, the second is a
# Rack::Request of the current request, the last is the hash used in
# ruby-openid handling, which can be found manually at
# env['rack.session'][:openid].
#
# This is useful if you wanted to expand the processing done, such as
# setting up user accounts.
#
# oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to
# def oid_app.success oid, request, session
# user = Models::User[oid.identity_url]
# user ||= Models::User.create_from_openid oid
# request['rack.session'][:user] = user.id
# redirect MyApp.site_home
# end
#
# site_map['/openid'] = oid_app
# map = Rack::URLMap.new site_map
# ...
def initialize(realm, options={})
realm = URI(realm)
raise ArgumentError, "Invalid realm: #{realm}" \
unless realm.absolute? \
and realm.fragment.nil? \
and realm.scheme =~ /^https?$/ \
and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/
realm.path = '/' if realm.path.empty?
@realm = realm.to_s
if ruri = options[:return_to]
ruri = URI(ruri)
raise ArgumentError, "Invalid return_to: #{ruri}" \
unless ruri.absolute? \
and ruri.scheme =~ /^https?$/ \
and ruri.fragment.nil?
raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \
unless self.within_realm?(ruri)
@return_to = ruri.to_s
end
@session_key = options[:session_key] || 'rack.session'
@openid_param = options[:openid_param] || 'openid_identifier'
@store = options[:store] || ::OpenID::Store::Memory.new
@immediate = !!options[:immediate]
@extensions = {}
if extensions = options.delete(:extensions)
extensions.each do |ext, args|
add_extension ext, *args
end
end
# Undocumented, semi-experimental
@anonymous = !!options[:anonymous]
end
attr_reader :realm, :return_to, :session_key, :openid_param, :store,
:immediate, :extensions
# Sets up and uses session data at <tt>:openid</tt> within the session.
# Errors in this setup will raise a NoSession exception.
#
# If the parameter 'openid.mode' is set, which implies a followup from
# the openid server, processing is passed to #finish and the result is
# returned. However, if there is no appropriate openid information in the
# session, a 400 error is returned.
#
# If the parameter specified by <tt>options[:openid_param]</tt> is
# present, processing is passed to #check and the result is returned.
#
# If neither of these conditions are met, #unauthorized is called.
def call(env)
env['rack.auth.openid'] = self
env_session = env[@session_key]
unless env_session and env_session.is_a?(Hash)
raise NoSession, 'No compatible session'
end
# let us work in our own namespace...
session = (env_session[:openid] ||= {})
unless session and session.is_a?(Hash)
raise NoSession, 'Incompatible openid session'
end
request = Rack::Request.new(env)
consumer = ::OpenID::Consumer.new(session, @store)
if mode = request.GET['openid.mode']
if session.key?(:openid_param)
finish(consumer, session, request)
else
bad_request
end
elsif request.GET[@openid_param]
check(consumer, session, request)
else
unauthorized
end
end
# As the first part of OpenID consumer action, #check retrieves the data
# required for completion.
#
# If all parameters fit within the max length of a URI, a 303 redirect
# will be returned. Otherwise #confirm_post_params will be called.
#
# Any messages from OpenID's request are logged to env['rack.errors']
#
# <tt>env['rack.auth.openid.request']</tt> is the openid checkid request
# instance.
#
# <tt>session[:openid_param]</tt> is set to the openid identifier
# provided by the user.
#
# <tt>session[:return_to]</tt> is set to the return_to uri given to the
# identity provider.
def check(consumer, session, req)
oid = consumer.begin(req.GET[@openid_param], @anonymous)
req.env['rack.auth.openid.request'] = oid
req.env['rack.errors'].puts(oid.message)
p oid if $DEBUG
## Extension support
extensions.each do |ext,args|
oid.add_extension(ext::Request.new(*args))
end
session[:openid_param] = req.GET[openid_param]
return_to_uri = return_to ? return_to : req.url
session[:return_to] = return_to_uri
immediate = session.key?(:setup_needed) ? false : immediate
if oid.send_redirect?(realm, return_to_uri, immediate)
uri = oid.redirect_url(realm, return_to_uri, immediate)
redirect(uri)
else
confirm_post_params(oid, realm, return_to_uri, immediate)
end
rescue ::OpenID::DiscoveryFailure => e
# thrown from inside OpenID::Consumer#begin by yadis stuff
req.env['rack.errors'].puts([e.message, *e.backtrace]*"\n")
return foreign_server_failure
end
# This is the final portion of authentication.
# If successful, a redirect to the realm is be returned.
# Data gathered from extensions are stored in session[:openid] with the
# extension's namespace uri as the key.
#
# Any messages from OpenID's response are logged to env['rack.errors']
#
# <tt>env['rack.auth.openid.response']</tt> will contain the openid
# response.
def finish(consumer, session, req)
oid = consumer.complete(req.GET, req.url)
req.env['rack.auth.openid.response'] = oid
req.env['rack.errors'].puts(oid.message)
p oid if $DEBUG
raise unless ValidStatus.include?(oid.status)
__send__(oid.status, oid, req, session)
end
# The first argument should be the main extension module.
# The extension module should contain the constants:
# * class Request, should have OpenID::Extension as an ancestor
# * class Response, should have OpenID::Extension as an ancestor
# * string NS_URI, which defining the namespace of the extension
#
# All trailing arguments will be passed to extension::Request.new in
# #check.
# The openid response will be passed to
# extension::Response#from_success_response, #get_extension_args will be
# called on the result to attain the gathered data.
#
# This method returns the key at which the response data will be found in
# the session, which is the namespace uri by default.
def add_extension(ext, *args)
raise BadExtension unless valid_extension?(ext)
extensions[ext] = args
return ext::NS_URI
end
# Checks the validitity, in the context of usage, of a submitted
# extension.
def valid_extension?(ext)
if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) }
raise ArgumentError, 'Extension is missing constants.'
elsif not ext::Response.respond_to?(:from_success_response)
raise ArgumentError, 'Response is missing required method.'
end
return true
rescue
return false
end
# Checks the provided uri to ensure it'd be considered within the realm.
# is currently not compatible with wildcard realms.
def within_realm? uri
uri = URI.parse(uri.to_s)
realm = URI.parse(self.realm)
return false unless uri.absolute?
return false unless uri.path[0, realm.path.size] == realm.path
return false unless uri.host == realm.host or realm.host[/^\*\./]
# for wildcard support, is awkward with URI limitations
realm_match = Regexp.escape(realm.host).
sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$'
return false unless uri.host.match(realm_match)
return true
end
alias_method :include?, :within_realm?
protected
### These methods define some of the boilerplate responses.
# Returns an html form page for posting to an Identity Provider if the
# GET request would exceed the upper URI length limit.
def confirm_post_params(oid, realm, return_to, immediate)
Rack::Response.new.finish do |r|
r.write '<html><head><title>Confirm...</title></head><body>'
r.write oid.form_markup(realm, return_to, immediate)
r.write '</body></html>'
end
end
# Returns a 303 redirect with the destination of that provided by the
# argument.
def redirect(uri)
[ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain',
'Location' => uri},
[] ]
end
# Returns an empty 400 response.
def bad_request
[ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'},
[''] ]
end
# Returns a basic unauthorized 401 response.
def unauthorized
[ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'},
['Unauthorized.'] ]
end
# Returns a basic access denied 403 response.
def access_denied
[ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'},
['Access denied.'] ]
end
# Returns a 503 response to be used if communication with the remote
# OpenID server fails.
def foreign_server_failure
[ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'},
['Foreign server failure.'] ]
end
private
### These methods are called after a transaction is completed, depending
# on its outcome. These should all return a rack compatible response.
# You'd want to override these to provide additional functionality.
# Called to complete processing on a successful transaction.
# Within the openid session, :openid_identity and :openid_identifier are
# set to the user friendly and the standard representation of the
# validated identity. All other data in the openid session is cleared.
def success(oid, request, session)
session.clear
session[:openid_identity] = oid.display_identifier
session[:openid_identifier] = oid.identity_url
extensions.keys.each do |ext|
label = ext.name[/[^:]+$/].downcase
response = ext::Response.from_success_response(oid)
session[label] = response.data
end
redirect(realm)
end
# Called if the Identity Provider indicates further setup by the user is
# required.
# The identifier is retrived from the openid session at :openid_param.
# And :setup_needed is set to true to prevent looping.
def setup_needed(oid, request, session)
identifier = session[:openid_param]
session[:setup_needed] = true
redirect req.script_name + '?' + openid_param + '=' + identifier
end
# Called if the user indicates they wish to cancel identification.
# Data within openid session is cleared.
def cancel(oid, request, session)
session.clear
access_denied
end
# Called if the Identity Provider indicates the user is unable to confirm
# their identity. Data within the openid session is left alone, in case
# of swarm auth attacks.
def failure(oid, request, session)
unauthorized
end
end
# A class developed out of the request to use OpenID as an authentication
# middleware. The request will be sent to the OpenID instance unless the
# block evaluates to true. For example in rackup, you can use it as such:
#
# use Rack::Session::Pool
# use Rack::Auth::OpenIDAuth, realm, openid_options do |env|
# env['rack.session'][:authkey] == a_string
# end
# run RackApp
#
# Or simply:
#
# app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth
class OpenIDAuth < Rack::Auth::AbstractHandler
attr_reader :oid
def initialize(app, realm, options={}, &auth)
@oid = OpenID.new(realm, options)
super(app, &auth)
end
def call(env)
to = auth.call(env) ? @app : @oid
to.call env
end
end
end
end

View File

@ -13,7 +13,7 @@ module Rack
status, headers, body = @app.call(env)
headers = HeaderHash.new(headers)
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
!headers['Content-Length'] &&
!headers['Transfer-Encoding'] &&
(body.respond_to?(:to_ary) || body.respond_to?(:to_str))

View File

@ -11,13 +11,22 @@ module Rack
status, headers, body = @app.call(env)
if !headers.has_key?('ETag')
parts = []
body.each { |part| parts << part.to_s }
headers['ETag'] = %("#{Digest::MD5.hexdigest(parts.join(""))}")
[status, headers, parts]
else
[status, headers, body]
digest, body = digest_body(body)
headers['ETag'] = %("#{digest}")
end
[status, headers, body]
end
private
def digest_body(body)
digest = Digest::MD5.new
parts = []
body.each do |part|
digest << part
parts << part
end
[digest.hexdigest, parts]
end
end
end

View File

@ -3,8 +3,10 @@ require 'rack/utils'
require 'rack/mime'
module Rack
# Rack::File serves files below the +root+ given, according to the
# Rack::File serves files below the +root+ directory given, according to the
# path info of the Rack request.
# e.g. when Rack::File.new("/etc") is used, you can access 'passwd' file
# as http://localhost:9292/passwd
#
# Handlers can detect if bodies are a Rack::File, and use mechanisms
# like sendfile on the +path+.

View File

@ -1,9 +1,11 @@
require 'rack/content_length'
require 'rack/rewindable_input'
module Rack
module Handler
class CGI
def self.run(app, options=nil)
$stdin.binmode
serve app
end
@ -15,8 +17,8 @@ module Rack
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [1,1],
"rack.input" => $stdin,
env.update({"rack.version" => Rack::VERSION,
"rack.input" => Rack::RewindableInput.new($stdin),
"rack.errors" => $stderr,
"rack.multithread" => false,
@ -40,20 +42,20 @@ module Rack
end
def self.send_headers(status, headers)
STDOUT.print "Status: #{status}\r\n"
$stdout.print "Status: #{status}\r\n"
headers.each { |k, vs|
vs.split("\n").each { |v|
STDOUT.print "#{k}: #{v}\r\n"
$stdout.print "#{k}: #{v}\r\n"
}
}
STDOUT.print "\r\n"
STDOUT.flush
$stdout.print "\r\n"
$stdout.flush
end
def self.send_body(body)
body.each { |part|
STDOUT.print part
STDOUT.flush
$stdout.print part
$stdout.flush
}
end
end

View File

@ -36,7 +36,7 @@ module Rack
rack_input = RewindableInput.new(request.in)
env.update({"rack.version" => [1,1],
env.update({"rack.version" => Rack::VERSION,
"rack.input" => rack_input,
"rack.errors" => request.err,

View File

@ -20,7 +20,7 @@ module Rack
rack_input = RewindableInput.new($stdin.read.to_s)
env.update(
"rack.version" => [1,1],
"rack.version" => Rack::VERSION,
"rack.input" => rack_input,
"rack.errors" => $stderr,
"rack.multithread" => false,

View File

@ -52,7 +52,7 @@ module Rack
rack_input = request.body || StringIO.new('')
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
env.update({"rack.version" => [1,1],
env.update({"rack.version" => Rack::VERSION,
"rack.input" => rack_input,
"rack.errors" => $stderr,

View File

@ -17,9 +17,6 @@ module Rack
def initialize(settings = {})
@app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
@log = Object.new
def @log.info(*args); end
def @log.error(*args); end
super(settings)
end
@ -36,7 +33,7 @@ module Rack
rack_input = StringIO.new(input_body)
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
env.update({"rack.version" => [1,1],
env.update({"rack.version" => Rack::VERSION,
"rack.input" => rack_input,
"rack.errors" => $stderr,
"rack.multithread" => true,

View File

@ -7,12 +7,15 @@ module Rack
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
def self.run(app, options={})
options[:BindAddress] = options.delete(:Host) if options[:Host]
server = ::WEBrick::HTTPServer.new(options)
server.mount "/", Rack::Handler::WEBrick, app
trap(:INT) { server.shutdown }
trap(:TERM) { server.shutdown }
yield server if block_given?
server.start
@server = ::WEBrick::HTTPServer.new(options)
@server.mount "/", Rack::Handler::WEBrick, app
yield @server if block_given?
@server.start
end
def self.shutdown
@server.shutdown
@server = nil
end
def initialize(server, app)
@ -27,7 +30,7 @@ module Rack
rack_input = StringIO.new(req.body.to_s)
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
env.update({"rack.version" => [1,1],
env.update({"rack.version" => Rack::VERSION,
"rack.input" => rack_input,
"rack.errors" => $stderr,

View File

@ -7,6 +7,7 @@ module Rack
class Lint
def initialize(app)
@app = app
@content_length = nil
end
# :stopdoc:
@ -51,15 +52,16 @@ module Rack
check_headers headers
## and the *body*.
check_content_type status, headers
check_content_length status, headers, env
check_content_length status, headers
@head_request = env["REQUEST_METHOD"] == "HEAD"
[status, headers, self]
end
## == The Environment
def check_env(env)
## The environment must be an true instance of Hash (no
## subclassing allowed) that includes CGI-like headers.
## The application is free to modify the environment.
## The environment must be an instance of Hash that includes
## CGI-like headers. The application is free to modify the
## environment.
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
env.kind_of? Hash
}
@ -288,10 +290,6 @@ module Rack
@input = input
end
def size
@input.size
end
## * +gets+ must be called without arguments and return a string,
## or +nil+ on EOF.
def gets(*args)
@ -481,7 +479,7 @@ module Rack
end
## === The Content-Length
def check_content_length(status, headers, env)
def check_content_length(status, headers)
headers.each { |key, value|
if key.downcase == 'content-length'
## There must not be a <tt>Content-Length</tt> header when the
@ -489,49 +487,43 @@ module Rack
assert("Content-Length header found in #{status} response, not allowed") {
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
bytes = 0
string_body = true
if @body.respond_to?(:to_ary)
@body.each { |part|
unless part.kind_of?(String)
string_body = false
break
end
bytes += Rack::Utils.bytesize(part)
}
if env["REQUEST_METHOD"] == "HEAD"
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
}
else
if string_body
assert("Content-Length header was #{value}, but should be #{bytes}") {
value == bytes.to_s
}
end
end
end
return
@content_length = value
end
}
end
def verify_content_length(bytes)
if @head_request
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
}
elsif @content_length
assert("Content-Length header was #{@content_length}, but should be #{bytes}") {
@content_length == bytes.to_s
}
end
end
## === The Body
def each
@closed = false
bytes = 0
## The Body must respond to +each+
assert("Response body must respond to each") do
@body.respond_to?(:each)
end
@body.each { |part|
## and must only yield String values.
assert("Body yielded non-string value #{part.inspect}") {
part.kind_of? String
}
bytes += Rack::Utils.bytesize(part)
yield part
}
verify_content_length(bytes)
##
## The Body itself should not be an instance of String, as this will
## break in Ruby 1.9.

View File

@ -87,6 +87,7 @@ module Rack
".gif" => "image/gif",
".gz" => "application/x-gzip",
".h" => "text/x-c",
".htc" => "text/x-component",
".hh" => "text/x-c",
".htm" => "text/html",
".html" => "text/html",
@ -186,6 +187,7 @@ module Rack
".vrml" => "model/vrml",
".war" => "application/java-archive",
".wav" => "audio/x-wav",
".webm" => "video/webm",
".wma" => "audio/x-ms-wma",
".wmv" => "video/x-ms-wmv",
".wmx" => "video/x-ms-wmx",

View File

@ -1,5 +1,6 @@
require 'uri'
require 'stringio'
require 'rack'
require 'rack/lint'
require 'rack/utils'
require 'rack/response'
@ -40,7 +41,7 @@ module Rack
end
DEFAULT_ENV = {
"rack.version" => [1,1],
"rack.version" => Rack::VERSION,
"rack.input" => StringIO.new,
"rack.errors" => StringIO.new,
"rack.multithread" => true,

View File

@ -35,6 +35,10 @@ module Rack
end
def call(env)
dup._call(env)
end
def _call(env)
@script_name = env["SCRIPT_NAME"]
@app.call(env.merge('rack.recursive.include' => method(:include)))
rescue ForwardRequest => req

View File

@ -80,11 +80,13 @@ module Rack
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
def path_info=(s); @env["PATH_INFO"] = s.to_s end
def get?; request_method == "GET" end
def post?; request_method == "POST" end
def put?; request_method == "PUT" end
def delete?; request_method == "DELETE" end
def head?; request_method == "HEAD" end
def delete?; request_method == "DELETE" end
def get?; request_method == "GET" end
def head?; request_method == "HEAD" end
def options?; request_method == "OPTIONS" end
def post?; request_method == "POST" end
def put?; request_method == "PUT" end
def trace?; request_method == "TRACE" end
# The set of form-data media-types. Requests that do not indicate
# one of the media types presents in this list will not be eligible
@ -253,7 +255,7 @@ module Rack
def ip
if addr = @env['HTTP_X_FORWARDED_FOR']
addr.split(',').last.strip
(addr.split(',').grep(/\d\./).first || @env['REMOTE_ADDR']).to_s.strip
else
@env['REMOTE_ADDR']
end

View File

@ -1,5 +1,6 @@
require 'rack/request'
require 'rack/utils'
require 'time'
module Rack
# Rack::Response provides a convenient interface to create a Rack

View File

@ -1,4 +1,6 @@
# -*- encoding: binary -*-
require 'tempfile'
require 'rack/utils'
module Rack
# Class which can make any IO object rewindable, including non-rewindable ones. It does
@ -16,27 +18,27 @@ module Rack
@rewindable_io = nil
@unlinked = false
end
def gets
make_rewindable unless @rewindable_io
@rewindable_io.gets
end
def read(*args)
make_rewindable unless @rewindable_io
@rewindable_io.read(*args)
end
def each(&block)
make_rewindable unless @rewindable_io
@rewindable_io.each(&block)
end
def rewind
make_rewindable unless @rewindable_io
@rewindable_io.rewind
end
# Closes this RewindableInput object without closing the originally
# wrapped IO oject. Cleans up any temporary resources that this RewindableInput
# has created.
@ -52,9 +54,9 @@ module Rack
@rewindable_io = nil
end
end
private
# Ruby's Tempfile class has a bug. Subclass it and fix it.
class Tempfile < ::Tempfile
def _close
@ -74,17 +76,18 @@ module Rack
@rewindable_io.chmod(0000)
@rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
@rewindable_io.binmode
if filesystem_has_posix_semantics? && !tempfile_unlink_contains_bug?
if filesystem_has_posix_semantics?
@rewindable_io.unlink
raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
@unlinked = true
end
buffer = ""
while @io.read(1024 * 4, buffer)
entire_buffer_written_out = false
while !entire_buffer_written_out
written = @rewindable_io.write(buffer)
entire_buffer_written_out = written == buffer.size
entire_buffer_written_out = written == Rack::Utils.bytesize(buffer)
if !entire_buffer_written_out
buffer.slice!(0 .. written - 1)
end
@ -92,19 +95,9 @@ module Rack
end
@rewindable_io.rewind
end
def filesystem_has_posix_semantics?
RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
end
def tempfile_unlink_contains_bug?
# The tempfile library as included in Ruby 1.9.1-p152 and later
# contains a bug: unlinking an open Tempfile object also closes
# it, which breaks our expected POSIX semantics. This problem
# has been fixed in Ruby 1.9.2, but the Ruby team chose not to
# include the bug fix in later versions of the 1.9.1 series.
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
ruby_engine == "ruby" && RUBY_VERSION == "1.9.1" && RUBY_PATCHLEVEL >= 152
end
end
end

View File

@ -2,7 +2,9 @@ require 'rack/file'
module Rack
class File #:nodoc:
alias :to_path :path
unless instance_methods(false).include?('to_path')
alias :to_path :path
end
end
# = Sendfile
@ -11,7 +13,7 @@ module Rack
# 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
# by the Ruby backend and takes advantage of the web server's optimized file
# delivery code.
#
# In order to take advantage of this middleware, the response body must
@ -31,19 +33,19 @@ module Rack
# 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/ {
# location ~ /files/(.*) {
# internal;
# alias /var/www/;
# alias /var/www/$1;
# }
#
# location / {
# proxy_redirect false;
# proxy_redirect off;
#
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#
# proxy_set_header X-Sendfile-Type X-Accel-Redirect
# proxy_set_header X-Sendfile-Type X-Accel-Redirect;
# proxy_set_header X-Accel-Mapping /files/=/var/www/;
#
# proxy_pass http://127.0.0.1:8080/;

View File

@ -57,7 +57,7 @@ module Rack
}
opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
options[:pid] = ::File.expand_path(f)
options[:pid] = f
}
opts.separator ""
@ -74,17 +74,66 @@ module Rack
end
end
opt_parser.parse! args
options[:rack_file] = args.last if args.last
options[:config] = args.last if args.last
options
end
end
def self.start
new.start
# Start a new rack server (like running rackup). This will parse ARGV and
# provide standard ARGV rackup options, defaulting to load 'config.ru'.
#
# Providing an options hash will prevent ARGV parsing and will not include
# any default options.
#
# This method can be used to very easily launch a CGI application, for
# example:
#
# Rack::Server.start(
# :app => lambda do |e|
# [200, {'Content-Type' => 'text/html'}, ['hello world']]
# end,
# :server => 'cgi'
# )
#
# Further options available here are documented on Rack::Server#initialize
def self.start(options = nil)
new(options).start
end
attr_accessor :options
attr_writer :options
# Options may include:
# * :app
# a rack application to run (overrides :config)
# * :config
# a rackup configuration file path to load (.ru)
# * :environment
# this selects the middleware that will be wrapped around
# your application. Default options available are:
# - development: CommonLogger, ShowExceptions, and Lint
# - deployment: CommonLogger
# - none: no extra middleware
# note: when the server is a cgi server, CommonLogger is not included.
# * :server
# choose a specific Rack::Handler, e.g. cgi, fcgi, webrick
# * :daemonize
# if true, the server will daemonize itself (fork, detach, etc)
# * :pid
# path to write a pid file after daemonize
# * :Host
# the host address to bind to (used by supporting Rack::Handler)
# * :Port
# the port to bind to (used by supporting Rack::Handler)
# * :AccessLog
# webrick acess log options (or supporting Rack::Handler)
# * :debug
# turn on debug output ($DEBUG = true)
# * :warn
# turn on warnings ($-w = true)
# * :include
# add given paths to $LOAD_PATH
# * :require
# require the given libraries
def initialize(options = nil)
@options = options
end
@ -100,17 +149,17 @@ module Rack
:Port => 9292,
:Host => "0.0.0.0",
:AccessLog => [],
:rack_file => ::File.expand_path("config.ru")
:config => "config.ru"
}
end
def app
@app ||= begin
if !::File.exist? options[:rack_file]
abort "configuration #{options[:rack_file]} not found"
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
app, options = Rack::Builder.parse_file(self.options[:rack_file], opt_parser)
app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
self.options.merge! options
app
end
@ -119,7 +168,7 @@ module Rack
def self.middleware
@middleware ||= begin
m = Hash.new {|h,k| h[k] = []}
m["deployment"].concat [lambda {|server| server.server =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr] }]
m["deployment"].concat [lambda {|server| server.server.name =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr] }]
m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]]
m
end
@ -143,7 +192,7 @@ module Rack
end
if includes = options[:include]
$LOAD_PATH.unshift *includes
$LOAD_PATH.unshift(*includes)
end
if library = options[:require]
@ -152,11 +201,20 @@ module Rack
daemonize_app if options[:daemonize]
write_pid if options[:pid]
trap(:INT) do
if server.respond_to?(:shutdown)
server.shutdown
else
exit
end
end
server.run wrapped_app, options
end
def server
@_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default
@_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
end
private
@ -168,6 +226,7 @@ module Rack
args.clear if ENV.include?("REQUEST_METHOD")
options.merge! opt_parser.parse! args
ENV["RACK_ENV"] = options[:environment]
options
end

View File

@ -31,7 +31,7 @@ module Rack
@mutex = Mutex.new
mserv = @default_options[:memcache_server]
mopts = @default_options.
reject{|k,v| MemCache::DEFAULT_OPTIONS.include? k }
reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
@pool = MemCache.new mserv, mopts
unless @pool.active? and @pool.servers.any?{|c| c.alive? }
raise 'No memcache servers'

View File

@ -35,21 +35,20 @@ module Rack
end
def call(env)
path = env["PATH_INFO"].to_s
path = env["PATH_INFO"]
script_name = env['SCRIPT_NAME']
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
@mapping.each { |host, location, match, app|
next unless (hHost == host || sName == host \
|| (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
next unless path =~ match && rest = $1
next unless path.to_s =~ match && rest = $1
next unless rest.empty? || rest[0] == ?/
return app.call(
env.merge(
'SCRIPT_NAME' => (script_name + location),
'PATH_INFO' => rest))
env.merge!('SCRIPT_NAME' => (script_name + location), 'PATH_INFO' => rest)
return app.call(env)
}
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
ensure
env.merge! 'PATH_INFO' => path, 'SCRIPT_NAME' => script_name
end
end
end

View File

@ -1,5 +1,6 @@
# -*- encoding: binary -*-
require 'fileutils'
require 'set'
require 'tempfile'
@ -38,9 +39,6 @@ module Rack
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
k, v = p.split('=', 2).map { |x| unescape(x) }
if v =~ /^("|')(.*)\1$/
v = $2.gsub('\\'+$1, $1)
end
if cur = params[k]
if cur.class == Array
params[k] << v
@ -69,9 +67,6 @@ module Rack
module_function :parse_nested_query
def normalize_params(params, name, v = nil)
if v and v =~ /^("|')(.*)\1$/
v = $2.gsub('\\'+$1, $1)
end
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
k = $1 || ''
after = $' || ''
@ -133,13 +128,18 @@ module Rack
end
module_function :build_nested_query
ESCAPE_HTML = {
"&" => "&amp;",
"<" => "&lt;",
">" => "&gt;",
"'" => "&#39;",
'"' => "&quot;",
}
ESCAPE_HTML_PATTERN = Regexp.union(ESCAPE_HTML.keys)
# Escape ampersands, brackets and quotes to their HTML/XML entities.
def escape_html(string)
string.to_s.gsub("&", "&amp;").
gsub("<", "&lt;").
gsub(">", "&gt;").
gsub("'", "&#39;").
gsub('"', "&quot;")
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
end
module_function :escape_html
@ -180,8 +180,8 @@ module Rack
path = "; path=" + value[:path] if value[:path]
# According to RFC 2109, we need dashes here.
# N.B.: cgi.rb uses spaces...
expires = "; expires=" + value[:expires].clone.gmtime.
strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
expires = "; expires=" +
rfc2822(value[:expires].clone.gmtime) if value[:expires]
secure = "; secure" if value[:secure]
httponly = "; HttpOnly" if value[:httponly]
value = value[:value]
@ -192,12 +192,12 @@ module Rack
"#{domain}#{path}#{expires}#{secure}#{httponly}"
case header["Set-Cookie"]
when Array
header["Set-Cookie"] << cookie
when String
header["Set-Cookie"] = [header["Set-Cookie"], cookie]
when nil
when nil, ''
header["Set-Cookie"] = cookie
when String
header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
when Array
header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
end
nil
@ -205,14 +205,25 @@ module Rack
module_function :set_cookie_header!
def delete_cookie_header!(header, key, value = {})
unless Array === header["Set-Cookie"]
header["Set-Cookie"] = [header["Set-Cookie"]].compact
case header["Set-Cookie"]
when nil, ''
cookies = []
when String
cookies = header["Set-Cookie"].split("\n")
when Array
cookies = header["Set-Cookie"]
end
header["Set-Cookie"].reject! { |cookie|
cookie =~ /\A#{escape(key)}=/
cookies.reject! { |cookie|
if value[:domain]
cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
else
cookie =~ /\A#{escape(key)}=/
end
}
header["Set-Cookie"] = cookies.join("\n")
set_cookie_header!(header, key,
{:value => '', :path => nil, :domain => nil,
:expires => Time.at(0) }.merge(value))
@ -234,6 +245,22 @@ module Rack
end
module_function :bytesize
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
# of '% %b %Y'.
# It assumes that the time is in GMT to comply to the RFC 2109.
#
# NOTE: I'm not sure the RFC says it requires GMT, but is ambigous enough
# that I'm certain someone implemented only that option.
# Do not use %a and %b from Time.strptime, it would use localized names for
# weekday and month.
#
def rfc2822(time)
wday = Time::RFC2822_DAY_NAME[time.wday]
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
time.strftime("#{wday}, %d-#{mon}-%Y %T GMT")
end
module_function :rfc2822
# Context allows the use of a compatible middleware at different points
# in a request handling stack. A compatible middleware must define
# #context which should take the arguments env and app. The first of which
@ -291,7 +318,8 @@ module Rack
end
def [](k)
super(@names[k] ||= @names[k.downcase])
super(@names[k]) if @names[k]
super(@names[k.downcase])
end
def []=(k, v)
@ -478,11 +506,31 @@ module Rack
head = buf.slice!(0, i+2) # First \r\n
buf.slice!(0, 2) # Second \r\n
filename = head[/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni, 1]
token = /[^\s()<>,;:\\"\/\[\]?=]+/
condisp = /Content-Disposition:\s*#{token}\s*/i
dispparm = /;\s*(#{token})=("(?:\\"|[^"])*"|#{token})*/
rfc2183 = /^#{condisp}(#{dispparm})+$/i
broken_quoted = /^#{condisp}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{token}=)/i
broken_unquoted = /^#{condisp}.*;\sfilename=(#{token})/i
if head =~ rfc2183
filename = Hash[head.scan(dispparm)]['filename']
filename = $1 if filename and filename =~ /^"(.*)"$/
elsif head =~ broken_quoted
filename = $1
elsif head =~ broken_unquoted
filename = $1
end
if filename && filename !~ /\\[^\\"]/
filename = Utils.unescape(filename).gsub(/\\(.)/, '\1')
end
content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
if content_type || filename
if filename
body = Tempfile.new("RackMultipart")
body.binmode if body.respond_to?(:binmode)
end
@ -519,8 +567,7 @@ module Rack
# This handles the full Windows paths given by Internet Explorer
# (and perhaps other broken user agents) without affecting
# those which give the lone filename.
filename =~ /^(?:.*[:\\\/])?(.*)/m
filename = $1
filename = filename.split(/[\/\\]/).last
data = {:filename => filename, :type => content_type,
:name => name, :tempfile => body, :head => head}

View File

@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "rack"
s.version = "1.1.0"
s.version = "1.2.0"
s.platform = Gem::Platform::RUBY
s.summary = "a modular Ruby webserver interface"
@ -14,23 +14,23 @@ middleware) into a single method call.
Also see http://rack.rubyforge.org.
EOF
s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*}'] +
%w(COPYING KNOWN-ISSUES rack.gemspec RDOX README SPEC)
s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,spec/**/*}'] +
%w(COPYING KNOWN-ISSUES rack.gemspec README SPEC)
s.bindir = 'bin'
s.executables << 'rackup'
s.require_path = 'lib'
s.has_rdoc = true
s.extra_rdoc_files = ['README', 'SPEC', 'KNOWN-ISSUES']
s.test_files = Dir['test/{test,spec}_*.rb']
s.test_files = Dir['spec/spec_*.rb']
s.author = 'Christian Neukirchen'
s.email = 'chneukirchen@gmail.com'
s.homepage = 'http://rack.rubyforge.org'
s.rubyforge_project = 'rack'
s.add_development_dependency 'test-spec'
s.add_development_dependency 'bacon'
s.add_development_dependency 'rake'
s.add_development_dependency 'camping'
s.add_development_dependency 'fcgi'
s.add_development_dependency 'memcache-client'
s.add_development_dependency 'mongrel'

View File

@ -1,6 +1,6 @@
server.modules = ("mod_fastcgi", "mod_cgi")
server.document-root = "."
server.errorlog = "lighttpd.errors"
server.errorlog = var.CWD + "/lighttpd.errors"
server.port = 9203
server.event-handler = "select"
@ -9,7 +9,8 @@ cgi.assign = ("/test" => "",
# ".ru" => ""
)
fastcgi.server = ("test.fcgi" => ("localhost" =>
fastcgi.server = (
"test.fcgi" => ("localhost" =>
("min-procs" => 1,
"socket" => "/tmp/rack-test-fcgi",
"bin-path" => "test.fcgi")),
@ -17,4 +18,8 @@ fastcgi.server = ("test.fcgi" => ("localhost" =>
("min-procs" => 1,
"socket" => "/tmp/rack-test-ru-fcgi",
"bin-path" => "test.ru")),
"sample_rackup.ru" => ("localhost" =>
("min-procs" => 1,
"socket" => "/tmp/rack-test-rackup-fcgi",
"bin-path" => CWD + "/rackup_stub.rb sample_rackup.ru")),
)

6
vendor/plugins/rack/spec/cgi/rackup_stub.rb vendored Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env ruby
# -*- ruby -*-
$:.unshift '../../lib'
require 'rack'
Rack::Server.start

View File

@ -0,0 +1,5 @@
# -*- ruby -*-
require '../testrequest'
run TestRequest.new

5
vendor/plugins/rack/spec/cgi/test.ru vendored Executable file
View File

@ -0,0 +1,5 @@
#!../../bin/rackup
# -*- ruby -*-
require '../testrequest'
run TestRequest.new

View File

@ -0,0 +1,7 @@
--AaB03x
Content-Type: image/jpeg
Content-Disposition: attachment; name="files"; filename=genome.jpeg; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
Content-Description: a complete map of the human genome
contents
--AaB03x--

View File

@ -0,0 +1,6 @@
--AaB03x
Content-Disposition: form-data; name="files"; filename="escape \"quotes"
Content-Type: application/octet-stream
contents
--AaB03x--

View File

@ -0,0 +1,7 @@
--AaB03x
Content-Type: image/jpeg
Content-Disposition: attachment; name="files"; filename=""human" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
Content-Description: a complete map of the human genome
contents
--AaB03x--

View File

@ -0,0 +1,6 @@
--AaB03x
Content-Disposition: form-data; name="files"; filename="escape %22quotes"
Content-Type: application/octet-stream
contents
--AaB03x--

View File

@ -0,0 +1,6 @@
--AaB03x
Content-Disposition: form-data; name="files"; filename="escape "quotes"
Content-Type: application/octet-stream
contents
--AaB03x--

View File

@ -1,25 +1,22 @@
require 'test/spec'
require 'rack/auth/basic'
require 'rack/mock'
context 'Rack::Auth::Basic' do
describe Rack::Auth::Basic do
def realm
'WallysWorld'
end
def unprotected_app
lambda { |env| [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ] }
end
def protected_app
app = Rack::Auth::Basic.new(unprotected_app) { |username, password| 'Boss' == username }
app.realm = realm
app
end
setup do
before do
@request = Rack::MockRequest.new(protected_app)
end
@ -39,26 +36,26 @@ context 'Rack::Auth::Basic' do
response.body.should.be.empty
end
specify 'should challenge correctly when no credentials are specified' do
should 'challenge correctly when no credentials are specified' do
request do |response|
assert_basic_auth_challenge response
end
end
specify 'should rechallenge if incorrect credentials are specified' do
should 'rechallenge if incorrect credentials are specified' do
request_with_basic_auth 'joe', 'password' do |response|
assert_basic_auth_challenge response
end
end
specify 'should return application output if correct credentials are specified' do
should 'return application output if correct credentials are specified' do
request_with_basic_auth 'Boss', 'password' do |response|
response.status.should.equal 200
response.body.to_s.should.equal 'Hi Boss'
end
end
specify 'should return 400 Bad Request if different auth scheme used' do
should 'return 400 Bad Request if different auth scheme used' do
request 'HTTP_AUTHORIZATION' => 'Digest params' do |response|
response.should.be.a.client_error
response.status.should.equal 400
@ -66,8 +63,8 @@ context 'Rack::Auth::Basic' do
end
end
specify 'realm as optional constructor arg' do
it 'takes realm as optional constructor arg' do
app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
assert_equal realm, app.realm
realm.should == app.realm
end
end

View File

@ -1,10 +1,7 @@
require 'test/spec'
require 'rack/auth/digest/md5'
require 'rack/mock'
context 'Rack::Auth::Digest::MD5' do
describe Rack::Auth::Digest::MD5 do
def realm
'WallysWorld'
end
@ -45,7 +42,7 @@ context 'Rack::Auth::Digest::MD5' do
Rack::MethodOverride.new(protected_app)
end
setup do
before do
@request = Rack::MockRequest.new(protected_app)
end
@ -117,20 +114,20 @@ context 'Rack::Auth::Digest::MD5' do
response.should.not.include 'WWW-Authenticate'
end
specify 'should challenge when no credentials are specified' do
should 'challenge when no credentials are specified' do
request 'GET', '/' do |response|
assert_digest_auth_challenge response
end
end
specify 'should return application output if correct credentials given' do
should 'return application output if correct credentials given' do
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
response.status.should.equal 200
response.body.to_s.should.equal 'Hi Alice'
end
end
specify 'should return application output if correct credentials given (hashed passwords)' do
should 'return application output if correct credentials given (hashed passwords)' do
@request = Rack::MockRequest.new(protected_app_with_hashed_passwords)
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
@ -139,19 +136,19 @@ context 'Rack::Auth::Digest::MD5' do
end
end
specify 'should rechallenge if incorrect username given' do
should 'rechallenge if incorrect username given' do
request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response|
assert_digest_auth_challenge response
end
end
specify 'should rechallenge if incorrect password given' do
should 'rechallenge if incorrect password given' do
request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response|
assert_digest_auth_challenge response
end
end
specify 'should rechallenge with stale parameter if nonce is stale' do
should 'rechallenge with stale parameter if nonce is stale' do
begin
Rack::Auth::Digest::Nonce.time_limit = 1
@ -164,39 +161,39 @@ context 'Rack::Auth::Digest::MD5' do
end
end
specify 'should return 400 Bad Request if incorrect qop given' do
should 'return 400 Bad Request if incorrect qop given' do
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response|
assert_bad_request response
end
end
specify 'should return 400 Bad Request if incorrect uri given' do
should 'return 400 Bad Request if incorrect uri given' do
request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response|
assert_bad_request response
end
end
specify 'should return 400 Bad Request if different auth scheme used' do
should 'return 400 Bad Request if different auth scheme used' do
request 'GET', '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response|
assert_bad_request response
end
end
specify 'should not require credentials for unprotected path' do
should 'not require credentials for unprotected path' do
@request = Rack::MockRequest.new(partially_protected_app)
request 'GET', '/' do |response|
response.should.be.ok
end
end
specify 'should challenge when no credentials are specified for protected path' do
should 'challenge when no credentials are specified for protected path' do
@request = Rack::MockRequest.new(partially_protected_app)
request 'GET', '/protected' do |response|
assert_digest_auth_challenge response
end
end
specify 'should return application output if correct credentials given for protected path' do
should 'return application output if correct credentials given for protected path' do
@request = Rack::MockRequest.new(partially_protected_app)
request_with_digest_auth 'GET', '/protected', 'Alice', 'correct-password' do |response|
response.status.should.equal 200
@ -204,14 +201,14 @@ context 'Rack::Auth::Digest::MD5' do
end
end
specify 'should return application output if correct credentials given for POST' do
should 'return application output if correct credentials given for POST' do
request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response|
response.status.should.equal 200
response.body.to_s.should.equal 'Hi Alice'
end
end
specify 'should return application output if correct credentials given for PUT (using method override of POST)' do
should 'return application output if correct credentials given for PUT (using method override of POST)' do
@request = Rack::MockRequest.new(protected_app_with_method_override)
request_with_digest_auth 'POST', '/', 'Alice', 'correct-password', :input => "_method=put" do |response|
response.status.should.equal 200
@ -219,8 +216,8 @@ context 'Rack::Auth::Digest::MD5' do
end
end
specify 'realm as optional constructor arg' do
it 'takes realm as optional constructor arg' do
app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true }
assert_equal realm, app.realm
realm.should == app.realm
end
end

View File

@ -1,12 +1,51 @@
require 'test/spec'
require 'rack/builder'
require 'rack/mock'
require 'rack/showexceptions'
require 'rack/auth/basic'
require 'rack/urlmap'
context "Rack::Builder" do
specify "chains apps by default" do
class NothingMiddleware
def initialize(app)
@app = app
end
def call(env)
@@env = env
response = @app.call(env)
response
end
def self.env
@@env
end
end
describe Rack::Builder do
it "supports mapping" do
app = Rack::Builder.new do
map '/' do |outer_env|
run lambda { |inner_env| [200, {}, ['root']] }
end
map '/sub' do
run lambda { |inner_env| [200, {}, ['sub']] }
end
end.to_app
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
end
it "doesn't dupe env even when mapping" do
app = Rack::Builder.new do
use NothingMiddleware
map '/' do |outer_env|
run lambda { |inner_env|
inner_env['new_key'] = 'new_value'
[200, {}, ['root']]
}
end
end.to_app
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
NothingMiddleware.env['new_key'].should.equal 'new_value'
end
it "chains apps by default" do
app = Rack::Builder.new do
use Rack::ShowExceptions
run lambda { |env| raise "bzzzt" }
@ -17,7 +56,7 @@ context "Rack::Builder" do
Rack::MockRequest.new(app).get("/").should.be.server_error
end
specify "has implicit #to_app" do
it "has implicit #to_app" do
app = Rack::Builder.new do
use Rack::ShowExceptions
run lambda { |env| raise "bzzzt" }
@ -28,7 +67,7 @@ context "Rack::Builder" do
Rack::MockRequest.new(app).get("/").should.be.server_error
end
specify "supports blocks on use" do
it "supports blocks on use" do
app = Rack::Builder.new do
use Rack::ShowExceptions
use Rack::Auth::Basic do |username, password|
@ -43,13 +82,13 @@ context "Rack::Builder" do
response.status.should.equal 401
# with auth...
response = Rack::MockRequest.new(app).get("/",
response = Rack::MockRequest.new(app).get("/",
'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
response.status.should.equal 200
response.body.to_s.should.equal 'Hi Boss'
end
specify "has explicit #to_app" do
it "has explicit #to_app" do
app = Rack::Builder.app do
use Rack::ShowExceptions
run lambda { |env| raise "bzzzt" }
@ -60,7 +99,7 @@ context "Rack::Builder" do
Rack::MockRequest.new(app).get("/").should.be.server_error
end
specify "apps are initialized once" do
should "initialize apps once" do
app = Rack::Builder.new do
class AppClass
def initialize

View File

@ -1,12 +1,9 @@
require 'test/spec'
require 'rack/cascade'
require 'rack/file'
require 'rack/urlmap'
require 'rack/mock'
require 'rack/urlmap'
require 'rack/file'
context "Rack::Cascade" do
describe Rack::Cascade do
docroot = File.expand_path(File.dirname(__FILE__))
app1 = Rack::File.new(docroot)
@ -15,7 +12,7 @@ context "Rack::Cascade" do
app3 = Rack::URLMap.new("/foo" => lambda { |env|
[200, { "Content-Type" => "text/plain"}, [""]]})
specify "should dispatch onward on 404 by default" do
should "dispatch onward on 404 by default" do
cascade = Rack::Cascade.new([app1, app2, app3])
Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
@ -23,16 +20,16 @@ context "Rack::Cascade" do
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.forbidden
end
specify "should dispatch onward on whatever is passed" do
should "dispatch onward on whatever is passed" do
cascade = Rack::Cascade.new([app1, app2, app3], [404, 403])
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
end
specify "should return 404 if empty" do
should "return 404 if empty" do
Rack::MockRequest.new(Rack::Cascade.new([])).get('/').should.be.not_found
end
specify "should append new app" do
should "append new app" do
cascade = Rack::Cascade.new([], [404, 403])
Rack::MockRequest.new(cascade).get('/').should.be.not_found
cascade << app2

View File

@ -1,53 +1,55 @@
require 'test/spec'
require 'testrequest'
require File.expand_path('../testrequest', __FILE__)
require 'rack/handler/cgi'
context "Rack::Handler::CGI" do
include TestRequest::Helpers
describe Rack::Handler::CGI do
extend TestRequest::Helpers
setup do
@host = '0.0.0.0'
@port = 9203
end
@host = '0.0.0.0'
@port = 9203
# Keep this first.
specify "startup" do
$pid = fork {
Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
$pid = fork {
ENV['RACK_ENV'] = 'deployment'
ENV['RUBYLIB'] = [
File.expand_path('../../lib', __FILE__),
ENV['RUBYLIB'],
].compact.join(':')
Dir.chdir(File.expand_path("../cgi", __FILE__)) do
exec "lighttpd -D -f lighttpd.conf"
}
end
end
}
specify "should respond" do
should "respond" do
sleep 1
lambda {
GET("/test")
}.should.not.raise
GET("/test")
response.should.not.be.nil
end
specify "should be a lighttpd" do
should "be a lighttpd" do
GET("/test")
status.should.be 200
status.should.equal 200
response["SERVER_SOFTWARE"].should =~ /lighttpd/
response["HTTP_VERSION"].should.equal "HTTP/1.1"
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
response["SERVER_PORT"].should.equal @port.to_s
response["SERVER_NAME"].should =~ @host
response["SERVER_NAME"].should.equal @host
end
specify "should have rack headers" do
should "have rack headers" do
GET("/test")
response["rack.version"].should.equal [1,1]
response["rack.multithread"].should.be false
response["rack.multiprocess"].should.be true
response["rack.run_once"].should.be true
response["rack.version"].should.equal([1,1])
response["rack.multithread"].should.be.false
response["rack.multiprocess"].should.be.true
response["rack.run_once"].should.be.true
end
specify "should have CGI headers on GET" do
should "have CGI headers on GET" do
GET("/test")
response["REQUEST_METHOD"].should.equal "GET"
response["SCRIPT_NAME"].should.equal "/test"
response["REQUEST_PATH"].should.equal "/"
response["PATH_INFO"].should.equal ""
response["PATH_INFO"].should.be.nil
response["QUERY_STRING"].should.equal ""
response["test.postdata"].should.equal ""
@ -59,7 +61,7 @@ context "Rack::Handler::CGI" do
response["QUERY_STRING"].should.equal "quux=1"
end
specify "should have CGI headers on POST" do
should "have CGI headers on POST" do
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
status.should.equal 200
response["REQUEST_METHOD"].should.equal "POST"
@ -70,20 +72,20 @@ context "Rack::Handler::CGI" do
response["test.postdata"].should.equal "rack-form-data=23"
end
specify "should support HTTP auth" do
should "support HTTP auth" do
GET("/test", {:user => "ruth", :passwd => "secret"})
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
end
specify "should set status" do
should "set status" do
GET("/test?secret")
status.should.equal 403
response["rack.url_scheme"].should.equal "http"
end
# Keep this last.
specify "shutdown" do
should "shutdown" do
Process.kill 15, $pid
Process.wait($pid).should.equal $pid
Process.wait($pid).should == $pid
end
end

View File

@ -1,15 +1,13 @@
require 'rack/mock'
require 'rack/chunked'
require 'rack/utils'
context "Rack::Chunked" do
require 'rack/mock'
describe Rack::Chunked do
before do
@env = Rack::MockRequest.
env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
end
specify 'chunks responses with no Content-Length' do
should 'chunk responses with no Content-Length' do
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
response.headers.should.not.include 'Content-Length'
@ -17,7 +15,7 @@ context "Rack::Chunked" do
response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
end
specify 'chunks empty bodies properly' do
should 'chunks empty bodies properly' do
app = lambda { |env| [200, {}, []] }
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
response.headers.should.not.include 'Content-Length'
@ -25,7 +23,7 @@ context "Rack::Chunked" do
response.body.should.equal "0\r\n\r\n"
end
specify 'does not modify response when Content-Length header present' do
should 'not modify response when Content-Length header present' do
app = lambda { |env| [200, {'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] }
status, headers, body = Rack::Chunked.new(app).call(@env)
status.should.equal 200
@ -34,7 +32,7 @@ context "Rack::Chunked" do
body.join.should.equal 'Hello World!'
end
specify 'does not modify response when client is HTTP/1.0' do
should 'not modify response when client is HTTP/1.0' do
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
@env['HTTP_VERSION'] = 'HTTP/1.0'
status, headers, body = Rack::Chunked.new(app).call(@env)
@ -43,7 +41,7 @@ context "Rack::Chunked" do
body.join.should.equal 'Hello World!'
end
specify 'does not modify response when Transfer-Encoding header already present' do
should 'not modify response when Transfer-Encoding header already present' do
app = lambda { |env| [200, {'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] }
status, headers, body = Rack::Chunked.new(app).call(@env)
status.should.equal 200
@ -52,7 +50,7 @@ context "Rack::Chunked" do
end
[100, 204, 304].each do |status_code|
specify "does not modify response when status code is #{status_code}" do
should "not modify response when status code is #{status_code}" do
app = lambda { |env| [status_code, {}, []] }
status, headers, body = Rack::Chunked.new(app).call(@env)
status.should.equal status_code

View File

@ -1,11 +1,10 @@
require 'test/spec'
require 'stringio'
require 'rack/commonlogger'
require 'rack/lobster'
require 'rack/mock'
context "Rack::CommonLogger" do
describe Rack::CommonLogger do
obj = 'foobar'
length = obj.size
app = lambda { |env|
[200,
{"Content-Type" => "text/html", "Content-Length" => length.to_s},
@ -19,42 +18,38 @@ context "Rack::CommonLogger" do
{"Content-Type" => "text/html", "Content-Length" => "0"},
[]]}
specify "should log to rack.errors by default" do
should "log to rack.errors by default" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
res.errors.should.not.be.empty
res.errors.should =~ /"GET \/ " 200 #{length} /
end
specify "should log to anything with +write+" do
should "log to anything with +write+" do
log = StringIO.new
res = Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
log.string.should =~ /"GET \/ " 200 #{length} /
end
specify "should log - content length if header is missing" do
should "log - content length if header is missing" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
res.errors.should.not.be.empty
res.errors.should =~ /"GET \/ " 200 - /
end
specify "should log - content length if header is zero" do
should "log - content length if header is zero" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
res.errors.should.not.be.empty
res.errors.should =~ /"GET \/ " 200 - /
end
def length
self.class.length
end
def self.length
123
end
def self.obj
"hello world"
end

View File

@ -1,11 +1,9 @@
require 'test/spec'
require 'time'
require 'rack/mock'
require 'rack/conditionalget'
require 'rack/mock'
context "Rack::ConditionalGet" do
specify "should set a 304 status and truncate body when If-Modified-Since hits" do
describe Rack::ConditionalGet do
should "set a 304 status and truncate body when If-Modified-Since hits" do
timestamp = Time.now.httpdate
app = Rack::ConditionalGet.new(lambda { |env|
[200, {'Last-Modified'=>timestamp}, ['TEST']] })
@ -17,7 +15,7 @@ context "Rack::ConditionalGet" do
response.body.should.be.empty
end
specify "should set a 304 status and truncate body when If-None-Match hits" do
should "set a 304 status and truncate body when If-None-Match hits" do
app = Rack::ConditionalGet.new(lambda { |env|
[200, {'Etag'=>'1234'}, ['TEST']] })
@ -28,7 +26,7 @@ context "Rack::ConditionalGet" do
response.body.should.be.empty
end
specify "should not affect non-GET/HEAD requests" do
should "not affect non-GET/HEAD requests" do
app = Rack::ConditionalGet.new(lambda { |env|
[200, {'Etag'=>'1234'}, ['TEST']] })

View File

@ -1,12 +1,11 @@
require 'test/spec'
require 'rack/mock'
require 'rack/builder'
require 'rack/content_length'
require 'rack/config'
require 'rack/content_length'
require 'rack/lint'
require 'rack/mock'
context "Rack::Config" do
specify "should accept a block that modifies the environment" do
describe Rack::Config do
should "accept a block that modifies the environment" do
app = Rack::Builder.new do
use Rack::Lint
use Rack::ContentLength
@ -17,8 +16,8 @@ context "Rack::Config" do
[200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']]
}
end
response = Rack::MockRequest.new(app).get('/')
response.body.should.equal('hello')
end
end

View File

@ -1,20 +1,19 @@
require 'rack/mock'
require 'rack/content_length'
context "Rack::ContentLength" do
specify "sets Content-Length on String bodies if none is set" do
describe Rack::ContentLength do
should "set Content-Length on String bodies if none is set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
response = Rack::ContentLength.new(app).call({})
response[1]['Content-Length'].should.equal '13'
end
specify "sets Content-Length on Array bodies if none is set" do
should "set Content-Length on Array bodies if none is set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
response = Rack::ContentLength.new(app).call({})
response[1]['Content-Length'].should.equal '13'
end
specify "does not set Content-Length on variable length bodies" do
should "not set Content-Length on variable length bodies" do
body = lambda { "Hello World!" }
def body.each ; yield call ; end
@ -23,19 +22,19 @@ context "Rack::ContentLength" do
response[1]['Content-Length'].should.be.nil
end
specify "does not change Content-Length if it is already set" do
should "not change Content-Length if it is already set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] }
response = Rack::ContentLength.new(app).call({})
response[1]['Content-Length'].should.equal '1'
end
specify "does not set Content-Length on 304 responses" do
should "not set Content-Length on 304 responses" do
app = lambda { |env| [304, {'Content-Type' => 'text/plain'}, []] }
response = Rack::ContentLength.new(app).call({})
response[1]['Content-Length'].should.equal nil
end
specify "does not set Content-Length when Transfer-Encoding is chunked" do
should "not set Content-Length when Transfer-Encoding is chunked" do
app = lambda { |env| [200, {'Transfer-Encoding' => 'chunked'}, []] }
response = Rack::ContentLength.new(app).call({})
response[1]['Content-Length'].should.equal nil

View File

@ -1,27 +1,26 @@
require 'rack/mock'
require 'rack/content_type'
context "Rack::ContentType" do
specify "sets Content-Type to default text/html if none is set" do
describe Rack::ContentType do
should "set Content-Type to default text/html if none is set" do
app = lambda { |env| [200, {}, "Hello, World!"] }
status, headers, body = Rack::ContentType.new(app).call({})
headers['Content-Type'].should.equal 'text/html'
end
specify "sets Content-Type to chosen default if none is set" do
should "set Content-Type to chosen default if none is set" do
app = lambda { |env| [200, {}, "Hello, World!"] }
status, headers, body =
Rack::ContentType.new(app, 'application/octet-stream').call({})
headers['Content-Type'].should.equal 'application/octet-stream'
end
specify "does not change Content-Type if it is already set" do
should "not change Content-Type if it is already set" do
app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
status, headers, body = Rack::ContentType.new(app).call({})
headers['Content-Type'].should.equal 'foo/bar'
end
specify "case insensitive detection of Content-Type" do
should "detect Content-Type case insensitive" do
app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
status, headers, body = Rack::ContentType.new(app).call({})
headers.to_a.select { |k,v| k.downcase == "content-type" }.

View File

@ -1,11 +1,9 @@
require 'test/spec'
require 'rack/mock'
require 'rack/deflater'
require 'stringio'
require 'time' # for Time#httpdate
require 'rack/deflater'
require 'rack/mock'
context "Rack::Deflater" do
describe Rack::Deflater do
def build_response(status, body, accept_encoding, headers = {})
body = [body] if body.respond_to? :to_str
app = lambda { |env| [status, {}, body] }
@ -15,7 +13,7 @@ context "Rack::Deflater" do
return response
end
specify "should be able to deflate bodies that respond to each" do
should "be able to deflate bodies that respond to each" do
body = Object.new
class << body; def each; yield("foo"); yield("bar"); end; end
@ -32,7 +30,7 @@ context "Rack::Deflater" do
end
# TODO: This is really just a special case of the above...
specify "should be able to deflate String bodies" do
should "be able to deflate String bodies" do
response = build_response(200, "Hello world!", "deflate")
response[0].should.equal(200)
@ -45,7 +43,7 @@ context "Rack::Deflater" do
buf.should.equal("\363H\315\311\311W(\317/\312IQ\004\000")
end
specify "should be able to gzip bodies that respond to each" do
should "be able to gzip bodies that respond to each" do
body = Object.new
class << body; def each; yield("foo"); yield("bar"); end; end
@ -65,7 +63,7 @@ context "Rack::Deflater" do
gz.close
end
specify "should be able to fallback to no deflation" do
should "be able to fallback to no deflation" do
response = build_response(200, "Hello world!", "superzip")
response[0].should.equal(200)
@ -73,7 +71,7 @@ context "Rack::Deflater" do
response[2].should.equal(["Hello world!"])
end
specify "should be able to skip when there is no response entity body" do
should "be able to skip when there is no response entity body" do
response = build_response(304, [], "gzip")
response[0].should.equal(304)
@ -81,7 +79,7 @@ context "Rack::Deflater" do
response[2].should.equal([])
end
specify "should handle the lack of an acceptable encoding" do
should "handle the lack of an acceptable encoding" do
response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
response1[0].should.equal(406)
response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
@ -93,7 +91,7 @@ context "Rack::Deflater" do
response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
end
specify "should handle gzip response with Last-Modified header" do
should "handle gzip response with Last-Modified header" do
last_modified = Time.now.httpdate
app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] }
@ -115,7 +113,7 @@ context "Rack::Deflater" do
gz.close
end
specify "should do nothing when no-transform Cache-Control directive present" do
should "do nothing when no-transform Cache-Control directive present" do
app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] }
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
response = Rack::Deflater.new(app).call(request)

View File

@ -1,16 +1,12 @@
require 'test/spec'
require 'rack/directory'
require 'rack/lint'
require 'rack/mock'
context "Rack::Directory" do
describe Rack::Directory do
DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
FILE_CATCH = proc{|env| [200, {'Content-Type'=>'text/plain', "Content-Length" => "7"}, ['passed!']] }
app = Rack::Directory.new DOCROOT, FILE_CATCH
specify "serves directory indices" do
should "serve directory indices" do
res = Rack::MockRequest.new(Rack::Lint.new(app)).
get("/cgi/")
@ -18,7 +14,7 @@ context "Rack::Directory" do
res.should =~ /<html><head>/
end
specify "passes to app if file found" do
should "pass to app if file found" do
res = Rack::MockRequest.new(Rack::Lint.new(app)).
get("/cgi/test")
@ -26,7 +22,7 @@ context "Rack::Directory" do
res.should =~ /passed!/
end
specify "serves uri with URL encoded filenames" do
should "serve uri with URL encoded filenames" do
res = Rack::MockRequest.new(Rack::Lint.new(app)).
get("/%63%67%69/") # "/cgi/test"
@ -40,7 +36,7 @@ context "Rack::Directory" do
res.should =~ /passed!/
end
specify "does not allow directory traversal" do
should "not allow directory traversal" do
res = Rack::MockRequest.new(Rack::Lint.new(app)).
get("/cgi/../test")
@ -52,7 +48,7 @@ context "Rack::Directory" do
res.should.be.forbidden
end
specify "404s if it can't find the file" do
should "404 if it can't find the file" do
res = Rack::MockRequest.new(Rack::Lint.new(app)).
get("/cgi/blubb")

View File

@ -1,15 +1,13 @@
require 'test/spec'
require 'rack/mock'
require 'rack/etag'
context "Rack::ETag" do
specify "sets ETag if none is set" do
describe Rack::ETag do
should "set ETag if none is set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
response = Rack::ETag.new(app).call({})
response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\""
end
specify "does not change ETag if it is already set" do
should "not change ETag if it is already set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] }
response = Rack::ETag.new(app).call({})
response[1]['ETag'].should.equal "\"abc\""

View File

@ -1,48 +1,55 @@
require 'test/spec'
require 'testrequest'
require File.expand_path('../testrequest', __FILE__)
require 'rack/handler/fastcgi'
context "Rack::Handler::FastCGI" do
include TestRequest::Helpers
describe Rack::Handler::FastCGI do
extend TestRequest::Helpers
setup do
@host = '0.0.0.0'
@port = 9203
end
@host = '0.0.0.0'
@port = 9203
# Keep this first.
specify "startup" do
$pid = fork {
Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
$pid = fork {
ENV['RACK_ENV'] = 'deployment'
ENV['RUBYLIB'] = [
File.expand_path('../../lib', __FILE__),
ENV['RUBYLIB'],
].compact.join(':')
Dir.chdir(File.expand_path("../cgi", __FILE__)) do
exec "lighttpd -D -f lighttpd.conf"
}
end
end
}
specify "should respond" do
should "respond" do
sleep 1
lambda {
GET("/test.fcgi")
}.should.not.raise
GET("/test")
response.should.not.be.nil
end
specify "should be a lighttpd" do
should "respond via rackup server" do
GET("/sample_rackup.ru")
status.should.equal 200
end
should "be a lighttpd" do
GET("/test.fcgi")
status.should.be 200
status.should.equal 200
response["SERVER_SOFTWARE"].should =~ /lighttpd/
response["HTTP_VERSION"].should.equal "HTTP/1.1"
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
response["SERVER_PORT"].should.equal @port.to_s
response["SERVER_NAME"].should =~ @host
response["SERVER_NAME"].should.equal @host
end
specify "should have rack headers" do
should "have rack headers" do
GET("/test.fcgi")
response["rack.version"].should.equal [1,1]
response["rack.multithread"].should.be false
response["rack.multiprocess"].should.be true
response["rack.run_once"].should.be false
response["rack.multithread"].should.be.false
response["rack.multiprocess"].should.be.true
response["rack.run_once"].should.be.false
end
specify "should have CGI headers on GET" do
should "have CGI headers on GET" do
GET("/test.fcgi")
response["REQUEST_METHOD"].should.equal "GET"
response["SCRIPT_NAME"].should.equal "/test.fcgi"
@ -59,7 +66,7 @@ context "Rack::Handler::FastCGI" do
response["QUERY_STRING"].should.equal "quux=1"
end
specify "should have CGI headers on POST" do
should "have CGI headers on POST" do
POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
status.should.equal 200
response["REQUEST_METHOD"].should.equal "POST"
@ -70,19 +77,19 @@ context "Rack::Handler::FastCGI" do
response["test.postdata"].should.equal "rack-form-data=23"
end
specify "should support HTTP auth" do
should "support HTTP auth" do
GET("/test.fcgi", {:user => "ruth", :passwd => "secret"})
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
end
specify "should set status" do
should "set status" do
GET("/test.fcgi?secret")
status.should.equal 403
response["rack.url_scheme"].should.equal "http"
end
# Keep this last.
specify "shutdown" do
should "shutdown" do
Process.kill 15, $pid
Process.wait($pid).should.equal $pid
end

View File

@ -1,14 +1,10 @@
require 'test/spec'
require 'rack/file'
require 'rack/lint'
require 'rack/mock'
context "Rack::File" do
describe Rack::File do
DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
specify "serves files" do
should "serve files" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/cgi/test")
@ -16,7 +12,7 @@ context "Rack::File" do
res.should =~ /ruby/
end
specify "sets Last-Modified header" do
should "set Last-Modified header" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/cgi/test")
@ -26,7 +22,7 @@ context "Rack::File" do
res["Last-Modified"].should.equal File.mtime(path).httpdate
end
specify "serves files with URL encoded filenames" do
should "serve files with URL encoded filenames" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/cgi/%74%65%73%74") # "/cgi/test"
@ -34,35 +30,35 @@ context "Rack::File" do
res.should =~ /ruby/
end
specify "does not allow directory traversal" do
should "not allow directory traversal" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/cgi/../test")
res.should.be.forbidden
end
specify "does not allow directory traversal with encoded periods" do
should "not allow directory traversal with encoded periods" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/%2E%2E/README")
res.should.be.forbidden
end
specify "404s if it can't find the file" do
should "404 if it can't find the file" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/cgi/blubb")
res.should.be.not_found
end
specify "detects SystemCallErrors" do
should "detect SystemCallErrors" do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/cgi")
res.should.be.not_found
end
specify "returns bodies that respond to #to_path" do
should "return bodies that respond to #to_path" do
env = Rack::MockRequest.env_for("/cgi/test")
status, headers, body = Rack::File.new(DOCROOT).call(env)

View File

@ -1,43 +1,41 @@
require 'test/spec'
require 'rack/handler'
class Rack::Handler::Lobster; end
class RockLobster; end
context "Rack::Handler" do
specify "has registered default handlers" do
describe Rack::Handler do
it "has registered default handlers" do
Rack::Handler.get('cgi').should.equal Rack::Handler::CGI
Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI
Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick
end
specify "handler that doesn't exist should raise a NameError" do
should "raise NameError if handler doesn't exist" do
lambda {
Rack::Handler.get('boom')
}.should.raise(NameError)
end
specify "should get unregistered, but already required, handler by name" do
should "get unregistered, but already required, handler by name" do
Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster
end
specify "should register custom handler" do
should "register custom handler" do
Rack::Handler.register('rock_lobster', 'RockLobster')
Rack::Handler.get('rock_lobster').should.equal RockLobster
end
specify "should not need registration for properly coded handlers even if not already required" do
should "not need registration for properly coded handlers even if not already required" do
begin
$:.push "test/unregistered_handler"
$LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__)
Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered
lambda {
Rack::Handler.get('UnRegistered')
}.should.raise(NameError)
Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
ensure
$:.delete "test/unregistered_handler"
$LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__)
end
end
end

View File

@ -1,7 +1,7 @@
require 'rack/head'
require 'rack/mock'
context "Rack::Head" do
describe Rack::Head do
def test_response(headers = {})
app = lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] }
request = Rack::MockRequest.env_for("/", headers)
@ -10,7 +10,7 @@ context "Rack::Head" do
return response
end
specify "passes GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
should "pass GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
%w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
resp = test_response("REQUEST_METHOD" => type)
@ -20,7 +20,7 @@ context "Rack::Head" do
end
end
specify "removes body from HEAD requests" do
should "remove body from HEAD requests" do
resp = test_response("REQUEST_METHOD" => "HEAD")
resp[0].should.equal(200)

View File

@ -1,15 +1,13 @@
require 'test/spec'
require 'stringio'
require 'rack/lint'
require 'rack/mock'
context "Rack::Lint" do
describe Rack::Lint do
def env(*args)
Rack::MockRequest.env_for("/", *args)
end
specify "passes valid request" do
should "pass valid request" do
lambda {
Rack::Lint.new(lambda { |env|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
@ -17,12 +15,12 @@ context "Rack::Lint" do
}.should.not.raise
end
specify "notices fatal errors" do
should "notice fatal errors" do
lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError).
message.should.match(/No env given/)
end
specify "notices environment errors" do
should "notice environment errors" do
lambda { Rack::Lint.new(nil).call 5 }.should.raise(Rack::Lint::LintError).
message.should.match(/not a Hash/)
@ -110,7 +108,7 @@ context "Rack::Lint" do
message.should.match(/cannot be .* make it ''/)
end
specify "notices input errors" do
should "notice input errors" do
lambda {
Rack::Lint.new(nil).call(env("rack.input" => ""))
}.should.raise(Rack::Lint::LintError).
@ -139,14 +137,14 @@ context "Rack::Lint" do
message.should.match(/does not have ASCII-8BIT as its external encoding/)
end
specify "notices error errors" do
should "notice error errors" do
lambda {
Rack::Lint.new(nil).call(env("rack.errors" => ""))
}.should.raise(Rack::Lint::LintError).
message.should.match(/does not respond to #puts/)
end
specify "notices status errors" do
should "notice status errors" do
lambda {
Rack::Lint.new(lambda { |env|
["cc", {}, ""]
@ -162,7 +160,7 @@ context "Rack::Lint" do
message.should.match(/must be >=100 seen as integer/)
end
specify "notices header errors" do
should "notice header errors" do
lambda {
Rack::Lint.new(lambda { |env|
[200, Object.new, []]
@ -235,7 +233,7 @@ context "Rack::Lint" do
}.should.not.raise(Rack::Lint::LintError)
end
specify "notices content-type errors" do
should "notice content-type errors" do
lambda {
Rack::Lint.new(lambda { |env|
[200, {"Content-length" => "0"}, []]
@ -253,7 +251,7 @@ context "Rack::Lint" do
end
end
specify "notices content-length errors" do
should "notice content-length errors" do
[100, 101, 204, 304].each do |status|
lambda {
Rack::Lint.new(lambda { |env|
@ -266,12 +264,12 @@ context "Rack::Lint" do
lambda {
Rack::Lint.new(lambda { |env|
[200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
}).call(env({}))
}).call(env({}))[2].each { }
}.should.raise(Rack::Lint::LintError).
message.should.match(/Content-Length header was 1, but should be 0/)
end
specify "notices body errors" do
should "notice body errors" do
lambda {
status, header, body = Rack::Lint.new(lambda { |env|
[200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
@ -281,7 +279,7 @@ context "Rack::Lint" do
message.should.match(/yielded non-string/)
end
specify "notices input handling errors" do
should "notice input handling errors" do
lambda {
Rack::Lint.new(lambda { |env|
env["rack.input"].gets("\r\n")
@ -425,7 +423,7 @@ context "Rack::Lint" do
message.should.match(/close must not be called/)
end
specify "notices error handling errors" do
should "notice error handling errors" do
lambda {
Rack::Lint.new(lambda { |env|
env["rack.errors"].write(42)
@ -443,7 +441,7 @@ context "Rack::Lint" do
message.should.match(/close must not be called/)
end
specify "notices HEAD errors" do
should "notice HEAD errors" do
lambda {
Rack::Lint.new(lambda { |env|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
@ -453,12 +451,12 @@ context "Rack::Lint" do
lambda {
Rack::Lint.new(lambda { |env|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
}).call(env({"REQUEST_METHOD" => "HEAD"}))
}).call(env({"REQUEST_METHOD" => "HEAD"}))[2].each { }
}.should.raise(Rack::Lint::LintError).
message.should.match(/body was given for HEAD/)
end
specify "passes valid read calls" do
should "pass valid read calls" do
hello_str = "hello world"
hello_str.force_encoding("ASCII-8BIT") if hello_str.respond_to? :force_encoding
lambda {
@ -505,19 +503,8 @@ context "Rack::Lint" do
end
end
context "Rack::Lint::InputWrapper" do
specify "delegates :size to underlying IO object" do
class IOMock
def size
101
end
end
wrapper = Rack::Lint::InputWrapper.new(IOMock.new)
wrapper.size.should == 101
end
specify "delegates :rewind to underlying IO object" do
describe "Rack::Lint::InputWrapper" do
should "delegate :rewind to underlying IO object" do
io = StringIO.new("123")
wrapper = Rack::Lint::InputWrapper.new(io)
wrapper.read.should.equal "123"

View File

@ -1,29 +1,27 @@
require 'test/spec'
require 'rack/lobster'
require 'rack/mock'
context "Rack::Lobster::LambdaLobster" do
specify "should be a single lambda" do
describe Rack::Lobster::LambdaLobster do
should "be a single lambda" do
Rack::Lobster::LambdaLobster.should.be.kind_of Proc
end
specify "should look like a lobster" do
should "look like a lobster" do
res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/")
res.should.be.ok
res.body.should.include "(,(,,(,,,("
res.body.should.include "?flip"
end
specify "should be flippable" do
should "be flippable" do
res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/?flip")
res.should.be.ok
res.body.should.include "(,,,(,,(,("
end
end
context "Rack::Lobster" do
specify "should look like a lobster" do
describe Rack::Lobster do
should "look like a lobster" do
res = Rack::MockRequest.new(Rack::Lobster.new).get("/")
res.should.be.ok
res.body.should.include "(,(,,(,,,("
@ -31,13 +29,13 @@ context "Rack::Lobster" do
res.body.should.include "crash"
end
specify "should be flippable" do
should "be flippable" do
res = Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=left")
res.should.be.ok
res.body.should.include "(,,,(,,(,("
end
specify "should provide crashing for testing purposes" do
should "provide crashing for testing purposes" do
lambda {
Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=crash")
}.should.raise

View File

@ -1,37 +1,35 @@
require 'test/spec'
require 'rack/mock'
require 'rack/lock'
require 'rack/mock'
context "Rack::Lock" do
class Lock
attr_reader :synchronized
class Lock
attr_reader :synchronized
def initialize
@synchronized = false
end
def synchronize
@synchronized = true
yield
end
def initialize
@synchronized = false
end
specify "should call synchronize on lock" do
def synchronize
@synchronized = true
yield
end
end
describe Rack::Lock do
should "call synchronize on lock" do
lock = Lock.new
env = Rack::MockRequest.env_for("/")
app = Rack::Lock.new(lambda { |env| }, lock)
app = Rack::Lock.new(lambda { |inner_env| }, lock)
lock.synchronized.should.equal false
app.call(env)
lock.synchronized.should.equal true
end
specify "should set multithread flag to false" do
should "set multithread flag to false" do
app = Rack::Lock.new(lambda { |env| env['rack.multithread'] })
app.call(Rack::MockRequest.env_for("/")).should.equal false
end
specify "should reset original multithread flag when exiting lock" do
should "reset original multithread flag when exiting lock" do
app = Rack::Lock.new(lambda { |env| env })
app.call(Rack::MockRequest.env_for("/"))['rack.multithread'].should.equal true
end

View File

@ -1,9 +1,8 @@
require 'rack/logger'
require 'rack/lint'
require 'stringio'
require 'rack/logger'
context "Rack::Logger" do
specify "logs to rack.errors" do
describe Rack::Logger do
should "log to rack.errors" do
app = lambda { |env|
log = env['rack.logger']
log.debug("Created logger")
@ -14,8 +13,8 @@ context "Rack::Logger" do
}
errors = StringIO.new
Rack::Logger.new(app).call({'rack.errors' => errors})
errors.string.should.match "INFO -- : Program started"
errors.string.should.match "WARN -- : Nothing to do"
Rack::Logger.new(app).call('rack.errors' => errors)
errors.string.should.match(/INFO -- : Program started/)
errors.string.should.match(/WARN -- : Nothing to do/)
end
end

View File

@ -1,58 +1,56 @@
require 'test/spec'
require 'rack/mock'
require 'rack/methodoverride'
require 'stringio'
require 'rack/methodoverride'
require 'rack/mock'
context "Rack::MethodOverride" do
specify "should not affect GET requests" do
describe Rack::MethodOverride do
should "not affect GET requests" do
env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
app = Rack::MethodOverride.new(lambda{|envx| Rack::Request.new(envx) })
req = app.call(env)
req.env["REQUEST_METHOD"].should.equal "GET"
end
specify "_method parameter should modify REQUEST_METHOD for POST requests" do
should "modify REQUEST_METHOD for POST requests when _method parameter is set" do
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
app = Rack::MethodOverride.new(lambda{|envx| Rack::Request.new(envx) })
req = app.call(env)
req.env["REQUEST_METHOD"].should.equal "PUT"
end
specify "X-HTTP-Method-Override header should modify REQUEST_METHOD for POST requests" do
should "modify REQUEST_METHOD for POST requests when X-HTTP-Method-Override is set" do
env = Rack::MockRequest.env_for("/",
:method => "POST",
"HTTP_X_HTTP_METHOD_OVERRIDE" => "PUT"
)
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
app = Rack::MethodOverride.new(lambda{|envx| Rack::Request.new(envx) })
req = app.call(env)
req.env["REQUEST_METHOD"].should.equal "PUT"
end
specify "should not modify REQUEST_METHOD if the method is unknown" do
should "not modify REQUEST_METHOD if the method is unknown" do
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
app = Rack::MethodOverride.new(lambda{|envx| Rack::Request.new(envx) })
req = app.call(env)
req.env["REQUEST_METHOD"].should.equal "POST"
end
specify "should not modify REQUEST_METHOD when _method is nil" do
should "not modify REQUEST_METHOD when _method is nil" do
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "foo=bar")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
app = Rack::MethodOverride.new(lambda{|envx| Rack::Request.new(envx) })
req = app.call(env)
req.env["REQUEST_METHOD"].should.equal "POST"
end
specify "should store the original REQUEST_METHOD prior to overriding" do
should "store the original REQUEST_METHOD prior to overriding" do
env = Rack::MockRequest.env_for("/",
:method => "POST",
:input => "_method=options")
app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) })
app = Rack::MethodOverride.new(lambda{|envx| Rack::Request.new(envx) })
req = app.call(env)
req.env["rack.methodoverride.original_method"].should.equal "POST"

View File

@ -1,35 +1,33 @@
require 'yaml'
require 'rack/mock'
require 'rack/request'
require 'rack/response'
app = lambda { |env|
req = Rack::Request.new(env)
env["mock.postdata"] = env["rack.input"].read
if req.GET["error"]
env["rack.errors"].puts req.GET["error"]
env["rack.errors"].flush
end
Rack::Response.new(env.to_yaml,
req.GET["status"] || 200,
"Content-Type" => "text/yaml").finish
}
context "Rack::MockRequest" do
specify "should return a MockResponse" do
describe Rack::MockRequest do
should "return a MockResponse" do
res = Rack::MockRequest.new(app).get("")
res.should.be.kind_of Rack::MockResponse
end
specify "should be able to only return the environment" do
should "be able to only return the environment" do
env = Rack::MockRequest.env_for("")
env.should.be.kind_of Hash
env.should.include "rack.version"
end
specify "should provide sensible defaults" do
should "provide sensible defaults" do
res = Rack::MockRequest.new(app).request
env = YAML.load(res.body)
@ -43,7 +41,7 @@ context "Rack::MockRequest" do
env["mock.postdata"].should.be.empty
end
specify "should allow GET/POST/PUT/DELETE" do
should "allow GET/POST/PUT/DELETE" do
res = Rack::MockRequest.new(app).get("", :input => "foo")
env = YAML.load(res.body)
env["REQUEST_METHOD"].should.equal "GET"
@ -64,12 +62,12 @@ context "Rack::MockRequest" do
should.equal "OPTIONS"
end
specify "should set content length" do
should "set content length" do
env = Rack::MockRequest.env_for("/", :input => "foo")
env["CONTENT_LENGTH"].should.equal "3"
end
specify "should allow posting" do
should "allow posting" do
res = Rack::MockRequest.new(app).get("", :input => "foo")
env = YAML.load(res.body)
env["mock.postdata"].should.equal "foo"
@ -79,7 +77,7 @@ context "Rack::MockRequest" do
env["mock.postdata"].should.equal "foo"
end
specify "should use all parts of an URL" do
should "use all parts of an URL" do
res = Rack::MockRequest.new(app).
get("https://bla.example.org:9292/meh/foo?bar")
res.should.be.kind_of Rack::MockResponse
@ -93,7 +91,7 @@ context "Rack::MockRequest" do
env["rack.url_scheme"].should.equal "https"
end
specify "should set SSL port and HTTP flag on when using https" do
should "set SSL port and HTTP flag on when using https" do
res = Rack::MockRequest.new(app).
get("https://example.org/foo")
res.should.be.kind_of Rack::MockResponse
@ -108,7 +106,7 @@ context "Rack::MockRequest" do
env["HTTPS"].should.equal "on"
end
specify "should prepend slash to uri path" do
should "prepend slash to uri path" do
res = Rack::MockRequest.new(app).
get("foo")
res.should.be.kind_of Rack::MockResponse
@ -122,33 +120,33 @@ context "Rack::MockRequest" do
env["rack.url_scheme"].should.equal "http"
end
specify "should properly convert method name to an uppercase string" do
should "properly convert method name to an uppercase string" do
res = Rack::MockRequest.new(app).request(:get)
env = YAML.load(res.body)
env["REQUEST_METHOD"].should.equal "GET"
end
specify "should accept params and build query string for GET requests" do
should "accept params and build query string for GET requests" do
res = Rack::MockRequest.new(app).get("/foo?baz=2", :params => {:foo => {:bar => "1"}})
env = YAML.load(res.body)
env["REQUEST_METHOD"].should.equal "GET"
env["QUERY_STRING"].should.match "baz=2"
env["QUERY_STRING"].should.match "foo[bar]=1"
env["QUERY_STRING"].should.include "baz=2"
env["QUERY_STRING"].should.include "foo[bar]=1"
env["PATH_INFO"].should.equal "/foo"
env["mock.postdata"].should.equal ""
end
specify "should accept raw input in params for GET requests" do
should "accept raw input in params for GET requests" do
res = Rack::MockRequest.new(app).get("/foo?baz=2", :params => "foo[bar]=1")
env = YAML.load(res.body)
env["REQUEST_METHOD"].should.equal "GET"
env["QUERY_STRING"].should.match "baz=2"
env["QUERY_STRING"].should.match "foo[bar]=1"
env["QUERY_STRING"].should.include "baz=2"
env["QUERY_STRING"].should.include "foo[bar]=1"
env["PATH_INFO"].should.equal "/foo"
env["mock.postdata"].should.equal ""
end
specify "should accept params and build url encoded params for POST requests" do
should "accept params and build url encoded params for POST requests" do
res = Rack::MockRequest.new(app).post("/foo", :params => {:foo => {:bar => "1"}})
env = YAML.load(res.body)
env["REQUEST_METHOD"].should.equal "POST"
@ -158,7 +156,7 @@ context "Rack::MockRequest" do
env["mock.postdata"].should.equal "foo[bar]=1"
end
specify "should accept raw input in params for POST requests" do
should "accept raw input in params for POST requests" do
res = Rack::MockRequest.new(app).post("/foo", :params => "foo[bar]=1")
env = YAML.load(res.body)
env["REQUEST_METHOD"].should.equal "POST"
@ -168,7 +166,7 @@ context "Rack::MockRequest" do
env["mock.postdata"].should.equal "foo[bar]=1"
end
specify "should accept params and build multipart encoded params for POST requests" do
should "accept params and build multipart encoded params for POST requests" do
files = Rack::Utils::Multipart::UploadedFile.new(File.join(File.dirname(__FILE__), "multipart", "file1.txt"))
res = Rack::MockRequest.new(app).post("/foo", :params => { "submit-name" => "Larry", "files" => files })
env = YAML.load(res.body)
@ -179,7 +177,7 @@ context "Rack::MockRequest" do
env["mock.postdata"].length.should.equal 206
end
specify "should behave valid according to the Rack spec" do
should "behave valid according to the Rack spec" do
lambda {
res = Rack::MockRequest.new(app).
get("https://bla.example.org:9292/meh/foo?bar", :lint => true)
@ -187,8 +185,8 @@ context "Rack::MockRequest" do
end
end
context "Rack::MockResponse" do
specify "should provide access to the HTTP status" do
describe Rack::MockResponse do
should "provide access to the HTTP status" do
res = Rack::MockRequest.new(app).get("")
res.should.be.successful
res.should.be.ok
@ -209,18 +207,18 @@ context "Rack::MockResponse" do
res.should.be.empty
end
specify "should provide access to the HTTP headers" do
should "provide access to the HTTP headers" do
res = Rack::MockRequest.new(app).get("")
res.should.include "Content-Type"
res.headers["Content-Type"].should.equal "text/yaml"
res.original_headers["Content-Type"].should.equal "text/yaml"
res["Content-Type"].should.equal "text/yaml"
res.content_type.should.equal "text/yaml"
res.content_length.should.be 414 # needs change often.
res.content_length.should.be > 0
res.location.should.be.nil
end
specify "should provide access to the HTTP body" do
should "provide access to the HTTP body" do
res = Rack::MockRequest.new(app).get("")
res.body.should =~ /rack/
res.should =~ /rack/
@ -228,14 +226,14 @@ context "Rack::MockResponse" do
res.should.satisfy { |r| r.match(/rack/) }
end
specify "should provide access to the Rack errors" do
should "provide access to the Rack errors" do
res = Rack::MockRequest.new(app).get("/?error=foo", :lint => true)
res.should.be.ok
res.errors.should.not.be.empty
res.errors.should.include "foo"
end
specify "should optionally make Rack errors fatal" do
should "optionally make Rack errors fatal" do
lambda {
Rack::MockRequest.new(app).get("/?error=foo", :fatal => true)
}.should.raise(Rack::MockRequest::FatalWarning)

View File

@ -1,37 +1,32 @@
require 'test/spec'
begin
require 'rack'
require 'rack/handler/mongrel'
require 'rack/urlmap'
require 'rack/lint'
require 'testrequest'
require File.expand_path('../testrequest', __FILE__)
require 'timeout'
Thread.abort_on_exception = true
$tcp_defer_accept_opts = nil
$tcp_cork_opts = nil
context "Rack::Handler::Mongrel" do
include TestRequest::Helpers
describe Rack::Handler::Mongrel do
extend TestRequest::Helpers
setup do
server = Mongrel::HttpServer.new(@host='0.0.0.0', @port=9201)
server.register('/test',
Rack::Handler::Mongrel.new(Rack::Lint.new(TestRequest.new)))
server.register('/stream',
Rack::Handler::Mongrel.new(Rack::Lint.new(StreamingRequest)))
@acc = server.run
end
@server = Mongrel::HttpServer.new(@host='0.0.0.0', @port=9201)
@server.register('/test',
Rack::Handler::Mongrel.new(Rack::Lint.new(TestRequest.new)))
@server.register('/stream',
Rack::Handler::Mongrel.new(Rack::Lint.new(StreamingRequest)))
@acc = @server.run
specify "should respond" do
should "respond" do
lambda {
GET("/test")
}.should.not.raise
end
specify "should be a Mongrel" do
should "be a Mongrel" do
GET("/test")
status.should.be 200
status.should.equal 200
response["SERVER_SOFTWARE"].should =~ /Mongrel/
response["HTTP_VERSION"].should.equal "HTTP/1.1"
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
@ -39,15 +34,15 @@ context "Rack::Handler::Mongrel" do
response["SERVER_NAME"].should.equal "0.0.0.0"
end
specify "should have rack headers" do
should "have rack headers" do
GET("/test")
response["rack.version"].should.equal [1,1]
response["rack.multithread"].should.be true
response["rack.multiprocess"].should.be false
response["rack.run_once"].should.be false
response["rack.multithread"].should.be.true
response["rack.multiprocess"].should.be.false
response["rack.run_once"].should.be.false
end
specify "should have CGI headers on GET" do
should "have CGI headers on GET" do
GET("/test")
response["REQUEST_METHOD"].should.equal "GET"
response["SCRIPT_NAME"].should.equal "/test"
@ -64,7 +59,7 @@ context "Rack::Handler::Mongrel" do
response["QUERY_STRING"].should.equal "quux=1"
end
specify "should have CGI headers on POST" do
should "have CGI headers on POST" do
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
status.should.equal 200
response["REQUEST_METHOD"].should.equal "POST"
@ -75,18 +70,18 @@ context "Rack::Handler::Mongrel" do
response["test.postdata"].should.equal "rack-form-data=23"
end
specify "should support HTTP auth" do
should "support HTTP auth" do
GET("/test", {:user => "ruth", :passwd => "secret"})
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
end
specify "should set status" do
should "set status" do
GET("/test?secret")
status.should.equal 403
response["rack.url_scheme"].should.equal "http"
end
specify "should provide a .run" do
should "provide a .run" do
block_ran = false
Thread.new {
Rack::Handler::Mongrel.run(lambda {}, {:Port => 9211}) { |server|
@ -95,16 +90,16 @@ context "Rack::Handler::Mongrel" do
}
}
sleep 1
block_ran.should.be true
block_ran.should.be.true
end
specify "should provide a .run that maps a hash" do
should "provide a .run that maps a hash" do
block_ran = false
Thread.new {
map = {'/'=>lambda{},'/foo'=>lambda{}}
Rack::Handler::Mongrel.run(map, :map => true, :Port => 9221) { |server|
server.should.be.kind_of Mongrel::HttpServer
server.classifier.uris.size.should.be 2
server.classifier.uris.size.should.equal 2
server.classifier.uris.should.not.include '/arf'
server.classifier.uris.should.include '/'
server.classifier.uris.should.include '/foo'
@ -112,16 +107,16 @@ context "Rack::Handler::Mongrel" do
}
}
sleep 1
block_ran.should.be true
block_ran.should.be.true
end
specify "should provide a .run that maps a urlmap" do
should "provide a .run that maps a urlmap" do
block_ran = false
Thread.new {
map = Rack::URLMap.new({'/'=>lambda{},'/bar'=>lambda{}})
Rack::Handler::Mongrel.run(map, {:map => true, :Port => 9231}) { |server|
server.should.be.kind_of Mongrel::HttpServer
server.classifier.uris.size.should.be 2
server.classifier.uris.size.should.equal 2
server.classifier.uris.should.not.include '/arf'
server.classifier.uris.should.include '/'
server.classifier.uris.should.include '/bar'
@ -129,10 +124,10 @@ context "Rack::Handler::Mongrel" do
}
}
sleep 1
block_ran.should.be true
block_ran.should.be.true
end
specify "should provide a .run that maps a urlmap restricting by host" do
should "provide a .run that maps a urlmap restricting by host" do
block_ran = false
Thread.new {
map = Rack::URLMap.new({
@ -148,22 +143,22 @@ context "Rack::Handler::Mongrel" do
Rack::Handler::Mongrel.run(map, opt) { |server|
server.should.be.kind_of Mongrel::HttpServer
server.classifier.uris.should.include '/'
server.classifier.handler_map['/'].size.should.be 2
server.classifier.handler_map['/'].size.should.equal 2
server.classifier.uris.should.include '/foo'
server.classifier.handler_map['/foo'].size.should.be 1
server.classifier.handler_map['/foo'].size.should.equal 1
server.classifier.uris.should.include '/bar'
server.classifier.handler_map['/bar'].size.should.be 2
server.classifier.handler_map['/bar'].size.should.equal 2
server.classifier.uris.should.not.include '/qux'
server.classifier.uris.should.not.include '/arf'
server.classifier.uris.size.should.be 3
server.classifier.uris.size.should.equal 3
block_ran = true
}
}
sleep 1
block_ran.should.be true
block_ran.should.be.true
end
specify "should stream #each part of the response" do
should "stream #each part of the response" do
body = ''
begin
Timeout.timeout(1) do
@ -179,11 +174,10 @@ context "Rack::Handler::Mongrel" do
body.should.not.be.empty
end
teardown do
@acc.raise Mongrel::StopServer
end
@acc.raise Mongrel::StopServer
end
rescue LoadError
$stderr.puts "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again."
rescue LoadError => ex
warn ex
warn "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again."
end

View File

@ -1,13 +1,12 @@
require 'rack/nulllogger'
require 'rack/lint'
require 'rack/mock'
context "Rack::NullLogger" do
specify "acks as a nop logger" do
describe Rack::NullLogger do
should "act as a noop logger" do
app = lambda { |env|
env['rack.logger'].warn "b00m"
[200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
}
Rack::NullLogger.new(app).call({})
logger = Rack::NullLogger.new(app)
lambda{ logger.call({}) }.should.not.raise
end
end

View File

@ -1,53 +1,45 @@
require 'test/spec'
require 'rack/recursive'
require 'rack/urlmap'
require 'rack/response'
require 'rack/mock'
context "Rack::Recursive" do
setup do
describe Rack::Recursive do
@app1 = lambda { |env|
res = Rack::Response.new
res["X-Path-Info"] = env["PATH_INFO"]
res["X-Query-String"] = env["QUERY_STRING"]
res.finish do |inner_res|
inner_res.write "App1"
end
}
@app1 = lambda { |env|
res = Rack::Response.new
res["X-Path-Info"] = env["PATH_INFO"]
res["X-Query-String"] = env["QUERY_STRING"]
res.finish do |res|
res.write "App1"
end
}
@app2 = lambda { |env|
Rack::Response.new.finish do |res|
res.write "App2"
_, _, body = env['rack.recursive.include'].call(env, "/app1")
body.each { |b|
res.write b
}
end
}
@app2 = lambda { |env|
Rack::Response.new.finish do |res|
res.write "App2"
_, _, body = env['rack.recursive.include'].call(env, "/app1")
body.each { |b|
res.write b
}
end
}
@app3 = lambda { |env|
raise Rack::ForwardRequest.new("/app1")
}
@app3 = lambda { |env|
raise Rack::ForwardRequest.new("/app1")
}
@app4 = lambda { |env|
raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh")
}
@app4 = lambda { |env|
raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh")
}
end
specify "should allow for subrequests" do
should "allow for subrequests" do
res = Rack::MockRequest.new(Rack::Recursive.new(
Rack::URLMap.new("/app1" => @app1,
"/app2" => @app2))).
get("/app2")
res.should.be.ok
res.body.should.equal "App2App1"
res.body.should.equal "App2App1"
end
specify "should raise error on requests not below the app" do
should "raise error on requests not below the app" do
app = Rack::URLMap.new("/app1" => @app1,
"/app" => Rack::Recursive.new(
Rack::URLMap.new("/1" => @app1,
@ -59,7 +51,7 @@ context "Rack::Recursive" do
message.should =~ /can only include below/
end
specify "should support forwarding" do
should "support forwarding" do
app = Rack::Recursive.new(Rack::URLMap.new("/app1" => @app1,
"/app3" => @app3,
"/app4" => @app4))

View File

@ -1,11 +1,10 @@
require 'test/spec'
require 'stringio'
require 'cgi'
require 'rack/request'
require 'rack/mock'
context "Rack::Request" do
specify "wraps the rack variables" do
describe Rack::Request do
should "wrap the rack variables" do
req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
req.body.should.respond_to? :gets
@ -29,7 +28,7 @@ context "Rack::Request" do
req.content_type.should.be.nil
end
specify "can figure out the correct host" do
should "figure out the correct host" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
req.host.should.equal "www2.example.org"
@ -53,7 +52,7 @@ context "Rack::Request" do
req.host.should.equal ""
end
specify "can parse the query string" do
should "parse the query string" do
req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
req.query_string.should.equal "foo=bar&quux=bla"
req.GET.should.equal "foo" => "bar", "quux" => "bla"
@ -61,12 +60,12 @@ context "Rack::Request" do
req.params.should.equal "foo" => "bar", "quux" => "bla"
end
specify "raises if rack.input is missing" do
should "raise if rack.input is missing" do
req = Rack::Request.new({})
lambda { req.POST }.should.raise(RuntimeError)
end
specify "can parse POST data when method is POST and no Content-Type given" do
should "parse POST data when method is POST and no Content-Type given" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/?foo=quux",
"REQUEST_METHOD" => 'POST',
@ -79,7 +78,7 @@ context "Rack::Request" do
req.params.should.equal "foo" => "bar", "quux" => "bla"
end
specify "can parse POST data with explicit content type regardless of method" do
should "parse POST data with explicit content type regardless of method" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/",
"CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
@ -91,7 +90,7 @@ context "Rack::Request" do
req.params.should.equal "foo" => "bar", "quux" => "bla"
end
specify "does not parse POST data when media type is not form-data" do
should "not parse POST data when media type is not form-data" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/?foo=quux",
"REQUEST_METHOD" => 'POST',
@ -105,7 +104,7 @@ context "Rack::Request" do
req.body.read.should.equal "foo=bar&quux=bla"
end
specify "can parse POST data on PUT when media type is form-data" do
should "parse POST data on PUT when media type is form-data" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/?foo=quux",
"REQUEST_METHOD" => 'PUT',
@ -115,7 +114,7 @@ context "Rack::Request" do
req.body.read.should.equal "foo=bar&quux=bla"
end
specify "rewinds input after parsing POST data" do
should "rewind input after parsing POST data" do
input = StringIO.new("foo=bar&quux=bla")
req = Rack::Request.new \
Rack::MockRequest.env_for("/",
@ -125,21 +124,21 @@ context "Rack::Request" do
input.read.should.equal "foo=bar&quux=bla"
end
specify "cleans up Safari's ajax POST body" do
should "clean up Safari's ajax POST body" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/",
'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
req.POST.should.equal "foo" => "bar", "quux" => "bla"
end
specify "can get value by key from params with #[]" do
should "get value by key from params with #[]" do
req = Rack::Request.new \
Rack::MockRequest.env_for("?foo=quux")
req['foo'].should.equal 'quux'
req[:foo].should.equal 'quux'
end
specify "can set value to key on params with #[]=" do
should "set value to key on params with #[]=" do
req = Rack::Request.new \
Rack::MockRequest.env_for("?foo=duh")
req['foo'].should.equal 'duh'
@ -157,7 +156,7 @@ context "Rack::Request" do
req[:foo].should.equal 'jaz'
end
specify "values_at answers values by keys in order given" do
should "return values for the keys in the order given from values_at" do
req = Rack::Request.new \
Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
req.values_at('foo').should.equal ['baz']
@ -165,7 +164,7 @@ context "Rack::Request" do
req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der']
end
specify "referrer should be extracted correct" do
should "extract referrer correctly" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
req.referer.should.equal "/some/path"
@ -175,7 +174,7 @@ context "Rack::Request" do
req.referer.should.equal "/"
end
specify "user agent should be extracted correct" do
should "extract user agent correctly" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
req.user_agent.should.equal "Mozilla/4.0 (compatible)"
@ -185,7 +184,7 @@ context "Rack::Request" do
req.user_agent.should.equal nil
end
specify "can cache, but invalidates the cache" do
should "cache, but invalidates the cache" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/?foo=quux",
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
@ -203,7 +202,7 @@ context "Rack::Request" do
req.POST.should.equal "foo" => "bla", "quux" => "bar"
end
specify "can figure out if called via XHR" do
should "figure out if called via XHR" do
req = Rack::Request.new(Rack::MockRequest.env_for(""))
req.should.not.be.xhr
@ -212,7 +211,7 @@ context "Rack::Request" do
req.should.be.xhr
end
specify "can parse cookies" do
should "parse cookies" do
req = Rack::Request.new \
Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
@ -221,13 +220,13 @@ context "Rack::Request" do
req.cookies.should.equal({})
end
specify "parses cookies according to RFC 2109" do
should "parse cookies according to RFC 2109" do
req = Rack::Request.new \
Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
req.cookies.should.equal 'foo' => 'bar'
end
specify "provides setters" do
should "provide setters" do
req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
req.script_name.should.equal ""
req.script_name = "/foo"
@ -240,12 +239,12 @@ context "Rack::Request" do
e["PATH_INFO"].should.equal "/foo"
end
specify "provides the original env" do
req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
req.env.should.be e
should "provide the original env" do
req = Rack::Request.new(e = Rack::MockRequest.env_for(""))
req.env.should == e
end
specify "can restore the URL" do
should "restore the URL" do
Rack::Request.new(Rack::MockRequest.env_for("")).url.
should.equal "http://example.org/"
Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
@ -263,7 +262,7 @@ context "Rack::Request" do
should.equal "https://example.com:8080/foo?foo"
end
specify "can restore the full path" do
should "restore the full path" do
Rack::Request.new(Rack::MockRequest.env_for("")).fullpath.
should.equal "/"
Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
@ -281,7 +280,7 @@ context "Rack::Request" do
should.equal "/foo?foo"
end
specify "can handle multiple media type parameters" do
should "handle multiple media type parameters" do
req = Rack::Request.new \
Rack::MockRequest.env_for("/",
"CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam')
@ -295,7 +294,7 @@ context "Rack::Request" do
req.media_type_params['bling'].should.equal 'bam'
end
specify "can parse multipart form data" do
should "parse multipart form data" do
# Adapted from RFC 1867.
input = <<EOF
--AaB03x\r
@ -334,7 +333,7 @@ EOF
f[:tempfile].size.should.equal 76
end
specify "can parse big multipart form data" do
should "parse big multipart form data" do
input = <<EOF
--AaB03x\r
content-disposition: form-data; name="huge"; filename="huge"\r
@ -356,7 +355,7 @@ EOF
req.POST["mean"][:tempfile].read.should.equal "--AaB03xha"
end
specify "can detect invalid multipart form data" do
should "detect invalid multipart form data" do
input = <<EOF
--AaB03x\r
content-disposition: form-data; name="huge"; filename="huge"\r
@ -395,12 +394,33 @@ EOF
lambda { req.POST }.should.raise(EOFError)
end
specify "shouldn't try to interpret binary as utf8" do
begin
original_kcode = $KCODE
$KCODE='UTF8'
should "not try to interpret binary as utf8" do
if /regexp/.respond_to?(:kcode) # < 1.9
begin
original_kcode = $KCODE
$KCODE='UTF8'
input = <<EOF
input = <<EOF
--AaB03x\r
content-disposition: form-data; name="fileupload"; filename="junk.a"\r
content-type: application/octet-stream\r
\r
#{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
--AaB03x--\r
EOF
req = Rack::Request.new Rack::MockRequest.env_for("/",
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
"CONTENT_LENGTH" => input.size,
:input => input)
lambda{req.POST}.should.not.raise(EOFError)
req.POST["fileupload"][:tempfile].size.should.equal 4
ensure
$KCODE = original_kcode
end
else # >= 1.9
input = <<EOF
--AaB03x\r
content-disposition: form-data; name="fileupload"; filename="junk.a"\r
content-type: application/octet-stream\r
@ -416,13 +436,10 @@ EOF
lambda{req.POST}.should.not.raise(EOFError)
req.POST["fileupload"][:tempfile].size.should.equal 4
ensure
$KCODE = original_kcode
end
end
specify "should work around buggy 1.8.* Tempfile equality" do
should "work around buggy 1.8.* Tempfile equality" do
input = <<EOF
--AaB03x\r
content-disposition: form-data; name="huge"; filename="huge"\r
@ -440,11 +457,11 @@ EOF
"CONTENT_LENGTH" => input.size,
:input => rack_input)
lambda {req.POST}.should.not.raise
lambda {req.POST}.should.blaming("input re-processed!").not.raise
lambda{ req.POST }.should.not.raise
lambda{ req.POST }.should.not.raise("input re-processed!")
end
specify "does conform to the Rack spec" do
should "conform to the Rack spec" do
app = lambda { |env|
content = Rack::Request.new(env).POST["file"].inspect
size = content.respond_to?(:bytesize) ? content.bytesize : content.size
@ -472,7 +489,7 @@ EOF
res.should.be.ok
end
specify "should parse Accept-Encoding correctly" do
should "parse Accept-Encoding correctly" do
parser = lambda do |x|
Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
end
@ -488,7 +505,7 @@ EOF
lambda { parser.call("gzip ; q=1.0") }.should.raise(RuntimeError)
end
specify 'should provide ip information' do
should 'provide ip information' do
app = lambda { |env|
request = Rack::Request.new(env)
response = Rack::Response.new
@ -510,7 +527,13 @@ EOF
'REMOTE_ADDR' => '123.123.123.123',
'HTTP_X_FORWARDED_FOR' => '234.234.234.234,212.212.212.212'
res.body.should.equal '212.212.212.212'
res.body.should.equal '234.234.234.234'
res = mock.get '/',
'REMOTE_ADDR' => '123.123.123.123',
'HTTP_X_FORWARDED_FOR' => 'unknown,234.234.234.234,212.212.212.212'
res.body.should.equal '234.234.234.234'
end
class MyRequest < Rack::Request
@ -519,7 +542,7 @@ EOF
end
end
specify "should allow subclass request to be instantiated after parent request" do
should "allow subclass request to be instantiated after parent request" do
env = Rack::MockRequest.env_for("/?foo=bar")
req1 = Rack::Request.new(env)
@ -531,7 +554,7 @@ EOF
req2.params.should.equal :foo => "bar"
end
specify "should allow parent request to be instantiated after subclass request" do
should "allow parent request to be instantiated after subclass request" do
env = Rack::MockRequest.env_for("/?foo=bar")
req1 = MyRequest.new(env)
@ -542,4 +565,16 @@ EOF
req2.GET.should.equal "foo" => "bar"
req2.params.should.equal "foo" => "bar"
end
(0x20...0x7E).collect { |a|
b = a.chr
c = CGI.escape(b)
should "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
url = "/?foo=#{c}bar#{c}"
env = Rack::MockRequest.env_for(url)
req2 = Rack::Request.new(env)
req2.GET.should.equal "foo" => "#{b}bar#{b}"
req2.params.should.equal "foo" => "#{b}bar#{b}"
end
}
end

View File

@ -1,10 +1,8 @@
require 'test/spec'
require 'set'
require 'rack/response'
context "Rack::Response" do
specify "has sensible default values" do
describe Rack::Response do
should "have sensible default values" do
response = Rack::Response.new
status, header, body = response.finish
status.should.equal 200
@ -22,7 +20,7 @@ context "Rack::Response" do
}
end
specify "can be written to" do
it "can be written to" do
response = Rack::Response.new
status, header, body = response.finish do
@ -30,61 +28,82 @@ context "Rack::Response" do
response.write "bar"
response.write "baz"
end
parts = []
body.each { |part| parts << part }
parts.should.equal ["foo", "bar", "baz"]
end
specify "can set and read headers" do
it "can set and read headers" do
response = Rack::Response.new
response["Content-Type"].should.equal "text/html"
response["Content-Type"] = "text/plain"
response["Content-Type"].should.equal "text/plain"
end
specify "can set cookies" do
it "can set cookies" do
response = Rack::Response.new
response.set_cookie "foo", "bar"
response["Set-Cookie"].should.equal "foo=bar"
response.set_cookie "foo2", "bar2"
response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2"]
response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2"].join("\n")
response.set_cookie "foo3", "bar3"
response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"]
response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"].join("\n")
end
specify "formats the Cookie expiration date accordingly to RFC 2109" do
it "can set cookies with the same name for multiple domains" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"}
response.set_cookie "foo", {:value => "bar", :domain => ".example.com"}
response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n")
end
it "formats the Cookie expiration date accordingly to RFC 2109" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :expires => Time.now+10}
response["Set-Cookie"].should.match(
/expires=..., \d\d-...-\d\d\d\d \d\d:\d\d:\d\d .../)
end
specify "can set secure cookies" do
it "can set secure cookies" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :secure => true}
response["Set-Cookie"].should.equal "foo=bar; secure"
end
specify "can set http only cookies" do
it "can set http only cookies" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :httponly => true}
response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
end
specify "can delete cookies" do
it "can delete cookies" do
response = Rack::Response.new
response.set_cookie "foo", "bar"
response.set_cookie "foo2", "bar2"
response.delete_cookie "foo"
response["Set-Cookie"].should.equal ["foo2=bar2",
"foo=; expires=Thu, 01-Jan-1970 00:00:00 GMT"]
response["Set-Cookie"].should.equal [
"foo2=bar2",
"foo=; expires=Thu, 01-Jan-1970 00:00:00 GMT"
].join("\n")
end
specify "can do redirects" do
it "can delete cookies with the same name from multiple domains" do
response = Rack::Response.new
response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"}
response.set_cookie "foo", {:value => "bar", :domain => ".example.com"}
response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n")
response.delete_cookie "foo", :domain => ".example.com"
response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=; domain=.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n")
response.delete_cookie "foo", :domain => "sample.example.com"
response["Set-Cookie"].should.equal ["foo=; domain=.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT",
"foo=; domain=sample.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n")
end
it "can do redirects" do
response = Rack::Response.new
response.redirect "/foo"
status, header, body = response.finish
@ -99,7 +118,7 @@ context "Rack::Response" do
status.should.equal 307
end
specify "has a useful constructor" do
it "has a useful constructor" do
r = Rack::Response.new("foo")
status, header, body = r.finish
str = ""; body.each { |part| str << part }
@ -123,7 +142,7 @@ context "Rack::Response" do
r.status.should.equal 200
end
specify "has a constructor that can take a block" do
it "has a constructor that can take a block" do
r = Rack::Response.new { |res|
res.status = 404
res.write "foo"
@ -133,8 +152,8 @@ context "Rack::Response" do
str.should.equal "foo"
status.should.equal 404
end
specify "doesn't return invalid responses" do
it "doesn't return invalid responses" do
r = Rack::Response.new(["foo", "bar"], 204)
status, header, body = r.finish
str = ""; body.each { |part| str << part }
@ -147,7 +166,7 @@ context "Rack::Response" do
message.should =~ /stringable or iterable required/
end
specify "knows if it's empty" do
it "knows if it's empty" do
r = Rack::Response.new
r.should.be.empty
r.write "foo"
@ -164,7 +183,7 @@ context "Rack::Response" do
r.should.not.be.empty
end
specify "should provide access to the HTTP status" do
should "provide access to the HTTP status" do
res = Rack::Response.new
res.status = 200
res.should.be.successful
@ -183,7 +202,7 @@ context "Rack::Response" do
res.should.be.redirect
end
specify "should provide access to the HTTP headers" do
should "provide access to the HTTP headers" do
res = Rack::Response.new
res["Content-Type"] = "text/yaml"
@ -195,7 +214,7 @@ context "Rack::Response" do
res.location.should.be.nil
end
specify "does not add or change Content-Length when #finish()ing" do
it "does not add or change Content-Length when #finish()ing" do
res = Rack::Response.new
res.status = 200
res.finish
@ -208,7 +227,7 @@ context "Rack::Response" do
res.headers["Content-Length"].should.equal "10"
end
specify "updates Content-Length when body appended to using #write" do
it "updates Content-Length when body appended to using #write" do
res = Rack::Response.new
res.status = 200
res.headers["Content-Length"].should.be.nil

View File

@ -0,0 +1,118 @@
require 'stringio'
require 'rack/rewindable_input'
shared "a rewindable IO object" do
before do
@rio = Rack::RewindableInput.new(@io)
end
should "be able to handle to read()" do
@rio.read.should.equal "hello world"
end
should "be able to handle to read(nil)" do
@rio.read(nil).should.equal "hello world"
end
should "be able to handle to read(length)" do
@rio.read(1).should.equal "h"
end
should "be able to handle to read(length, buffer)" do
buffer = ""
result = @rio.read(1, buffer)
result.should.equal "h"
result.object_id.should.equal buffer.object_id
end
should "be able to handle to read(nil, buffer)" do
buffer = ""
result = @rio.read(nil, buffer)
result.should.equal "hello world"
result.object_id.should.equal buffer.object_id
end
should "rewind to the beginning when #rewind is called" do
@rio.read(1)
@rio.rewind
@rio.read.should.equal "hello world"
end
should "be able to handle gets" do
@rio.gets.should == "hello world"
end
should "be able to handle each" do
array = []
@rio.each do |data|
array << data
end
array.should.equal(["hello world"])
end
should "not buffer into a Tempfile if no data has been read yet" do
@rio.instance_variable_get(:@rewindable_io).should.be.nil
end
should "buffer into a Tempfile when data has been consumed for the first time" do
@rio.read(1)
tempfile = @rio.instance_variable_get(:@rewindable_io)
tempfile.should.not.be.nil
@rio.read(1)
tempfile2 = @rio.instance_variable_get(:@rewindable_io)
tempfile2.path.should == tempfile.path
end
should "close the underlying tempfile upon calling #close" do
@rio.read(1)
tempfile = @rio.instance_variable_get(:@rewindable_io)
@rio.close
tempfile.should.be.closed
end
should "be possible to call #close when no data has been buffered yet" do
lambda{ @rio.close }.should.not.raise
end
should "be possible to call #close multiple times" do
lambda{
@rio.close
@rio.close
}.should.not.raise
end
@rio.close
@rio = nil
end
describe Rack::RewindableInput do
describe "given an IO object that is already rewindable" do
before do
@io = StringIO.new("hello world")
end
behaves_like "a rewindable IO object"
end
describe "given an IO object that is not rewindable" do
before do
@io = StringIO.new("hello world")
@io.instance_eval do
undef :rewind
end
end
behaves_like "a rewindable IO object"
end
describe "given an IO object whose rewind method raises Errno::ESPIPE" do
before do
@io = StringIO.new("hello world")
def @io.rewind
raise Errno::ESPIPE, "You can't rewind this!"
end
end
behaves_like "a rewindable IO object"
end
end

View File

@ -1,31 +1,35 @@
require 'test/spec'
require 'rack/mock'
require 'rack/runtime'
context "Rack::Runtime" do
specify "sets X-Runtime is none is set" do
describe Rack::Runtime do
it "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'].should =~ /[\d\.]+/
end
specify "does not set the X-Runtime if it is already set" do
it "doesn't set the X-Runtime if it is already set" do
app = lambda { |env| [200, {'Content-Type' => 'text/plain', "X-Runtime" => "foobar"}, "Hello, World!"] }
response = Rack::Runtime.new(app).call({})
response[1]['X-Runtime'].should == "foobar"
end
specify "should allow a suffix to be set" do
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'].should =~ /[\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({})
should "allow multiple timers to be set" do
app = lambda { |env| sleep 0.1; [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
runtime = Rack::Runtime.new(app, "App")
# wrap many times to guarantee a measurable difference
100.times do |i|
runtime = Rack::Runtime.new(runtime, i.to_s)
end
runtime = Rack::Runtime.new(runtime, "All")
response = runtime.call({})
response[1]['X-Runtime-App'].should =~ /[\d\.]+/
response[1]['X-Runtime-All'].should =~ /[\d\.]+/

View File

@ -1,14 +1,13 @@
require 'test/spec'
require 'rack/mock'
require 'rack/sendfile'
require 'rack/mock'
context "Rack::File" do
specify "should respond to #to_path" do
describe Rack::File do
should "respond to #to_path" do
Rack::File.new(Dir.pwd).should.respond_to :to_path
end
end
context "Rack::Sendfile" do
describe Rack::Sendfile do
def sendfile_body
res = ['Hello World']
def res.to_path ; "/tmp/hello.txt" ; end
@ -23,15 +22,13 @@ context "Rack::Sendfile" do
Rack::Sendfile.new(simple_app(body))
end
setup do
@request = Rack::MockRequest.new(sendfile_app)
end
@request = Rack::MockRequest.new(sendfile_app)
def request(headers={})
yield @request.get('/', headers)
end
specify "does nothing when no X-Sendfile-Type header present" do
it "does nothing when no X-Sendfile-Type header present" do
request do |response|
response.should.be.ok
response.body.should.equal 'Hello World'
@ -39,7 +36,7 @@ context "Rack::Sendfile" do
end
end
specify "sets X-Sendfile response header and discards body" do
it "sets X-Sendfile response header and discards body" do
request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
response.should.be.ok
response.body.should.be.empty
@ -47,7 +44,7 @@ context "Rack::Sendfile" do
end
end
specify "sets X-Lighttpd-Send-File response header and discards body" do
it "sets X-Lighttpd-Send-File response header and discards body" do
request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response|
response.should.be.ok
response.body.should.be.empty
@ -55,7 +52,7 @@ context "Rack::Sendfile" do
end
end
specify "sets X-Accel-Redirect response header and discards body" do
it "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/'
@ -67,7 +64,7 @@ context "Rack::Sendfile" do
end
end
specify 'writes to rack.error when no X-Accel-Mapping is specified' do
it 'writes to rack.error when no X-Accel-Mapping is specified' do
request 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect' do |response|
response.should.be.ok
response.body.should.equal 'Hello World'
@ -76,7 +73,7 @@ context "Rack::Sendfile" do
end
end
specify 'does nothing when body does not respond to #to_path' do
it 'does nothing when body does not respond to #to_path' do
@request = Rack::MockRequest.new(sendfile_app(['Not a file...']))
request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
response.body.should.equal 'Not a file...'

View File

@ -1,23 +1,20 @@
require 'test/spec'
require 'rack/session/cookie'
require 'rack/mock'
require 'rack/response'
context "Rack::Session::Cookie" do
incrementor = lambda { |env|
describe Rack::Session::Cookie do
incrementor = lambda do |env|
env["rack.session"]["counter"] ||= 0
env["rack.session"]["counter"] += 1
Rack::Response.new(env["rack.session"].inspect).to_a
}
end
specify "creates a new cookie" do
it "creates a new cookie" do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
res["Set-Cookie"].should.match("rack.session=")
res["Set-Cookie"].should.include("rack.session=")
res.body.should.equal '{"counter"=>1}'
end
specify "loads from a cookie" do
it "loads from a cookie" do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
cookie = res["Set-Cookie"]
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
@ -29,34 +26,25 @@ context "Rack::Session::Cookie" do
res.body.should.equal '{"counter"=>3}'
end
specify "survives broken cookies" do
it "survives broken cookies" do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
get("/", "HTTP_COOKIE" => "rack.session=blarghfasel")
res.body.should.equal '{"counter"=>1}'
end
bigcookie = lambda { |env|
bigcookie = lambda do |env|
env["rack.session"]["cookie"] = "big" * 3000
Rack::Response.new(env["rack.session"].inspect).to_a
}
end
specify "barks on too big cookies" do
lambda {
it "barks on too big cookies" do
lambda{
Rack::MockRequest.new(Rack::Session::Cookie.new(bigcookie)).
get("/", :fatal => true)
}.should.raise(Rack::MockRequest::FatalWarning)
end
specify "creates a new cookie with integrity hash" do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
if RUBY_VERSION < "1.9"
res["Set-Cookie"].should.match("rack.session=BAh7BiIMY291bnRlcmkG%0A--1439b4d37b9d4b04c603848382f712d6fcd31088")
else
res["Set-Cookie"].should.match("rack.session=BAh7BkkiDGNvdW50ZXIGOg1lbmNvZGluZyINVVMtQVNDSUlpBg%3D%3D%0A--d7a6637b94d2728194a96c18484e1f7ed9074a83")
end
end
specify "loads from a cookie wih integrity hash" do
it "loads from a cookie wih integrity hash" do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
cookie = res["Set-Cookie"]
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
@ -67,15 +55,15 @@ context "Rack::Session::Cookie" do
get("/", "HTTP_COOKIE" => cookie)
res.body.should.equal '{"counter"=>3}'
end
specify "ignores tampered with session cookies" do
it "ignores tampered with session cookies" do
app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
response1 = Rack::MockRequest.new(app).get("/")
_, digest = response1["Set-Cookie"].split("--")
tampered_with_cookie = "hackerman-was-here" + "--" + digest
response2 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" =>
tampered_with_cookie)
# The tampered-with cookie is ignored, so we get back an identical Set-Cookie
response2["Set-Cookie"].should.equal(response1["Set-Cookie"])
end

View File

@ -1,12 +1,9 @@
require 'test/spec'
begin
require 'rack/session/memcache'
require 'rack/mock'
require 'rack/response'
require 'thread'
context "Rack::Session::Memcache" do
describe Rack::Session::Memcache do
session_key = Rack::Session::Memcache::DEFAULT_OPTIONS[:key]
session_match = /#{session_key}=([0-9a-fA-F]+);/
incrementor = lambda do |env|
@ -27,30 +24,36 @@ begin
incrementor.call(env)
end
specify "faults on no connection" do
it "faults on no connection" do
if RUBY_VERSION < "1.9"
lambda do
Rack::Session::Memcache.new incrementor, :memcache_server => 'nosuchserver'
end.should.raise
lambda{
Rack::Session::Memcache.new(incrementor, :memcache_server => 'nosuchserver')
}.should.raise
else
lambda do
Rack::Session::Memcache.new incrementor, :memcache_server => 'nosuchserver'
end.should.raise ArgumentError
lambda{
Rack::Session::Memcache.new(incrementor, :memcache_server => 'nosuchserver')
}.should.raise ArgumentError
end
end
specify "connect to existing server" do
test_pool = MemCache.new incrementor, :namespace => 'test:rack:session'
it "connects to existing server" do
test_pool = MemCache.new(incrementor, :namespace => 'test:rack:session')
test_pool.namespace.should.equal 'test:rack:session'
end
specify "creates a new cookie" do
it "passes options to MemCache" do
pool = Rack::Session::Memcache.new(incrementor, :namespace => 'test:rack:session')
pool.pool.namespace.should.equal 'test:rack:session'
end
it "creates a new cookie" do
pool = Rack::Session::Memcache.new(incrementor)
res = Rack::MockRequest.new(pool).get("/")
res["Set-Cookie"].should.match("#{session_key}=")
res["Set-Cookie"].should.include("#{session_key}=")
res.body.should.equal '{"counter"=>1}'
end
specify "determines session from a cookie" do
it "determines session from a cookie" do
pool = Rack::Session::Memcache.new(incrementor)
req = Rack::MockRequest.new(pool)
res = req.get("/")
@ -61,7 +64,7 @@ begin
body.should.equal '{"counter"=>3}'
end
specify "survives nonexistant cookies" do
it "survives nonexistant cookies" do
bad_cookie = "rack.session=blarghfasel"
pool = Rack::Session::Memcache.new(incrementor)
res = Rack::MockRequest.new(pool).
@ -71,7 +74,7 @@ begin
cookie.should.not.match(/#{bad_cookie}/)
end
specify "maintains freshness" do
it "maintains freshness" do
pool = Rack::Session::Memcache.new(incrementor, :expire_after => 3)
res = Rack::MockRequest.new(pool).get('/')
res.body.should.include '"counter"=>1'
@ -86,7 +89,7 @@ begin
res.body.should.include '"counter"=>1'
end
specify "deletes cookies with :drop option" do
it "deletes cookies with :drop option" do
pool = Rack::Session::Memcache.new(incrementor)
req = Rack::MockRequest.new(pool)
drop = Rack::Utils::Context.new(pool, drop_session)
@ -109,7 +112,7 @@ begin
res3.body.should.equal '{"counter"=>1}'
end
specify "provides new session id with :renew option" do
it "provides new session id with :renew option" do
pool = Rack::Session::Memcache.new(incrementor)
req = Rack::MockRequest.new(pool)
renew = Rack::Utils::Context.new(pool, renew_session)
@ -134,7 +137,7 @@ begin
res3.body.should.equal '{"counter"=>4}'
end
specify "omits cookie with :defer option" do
it "omits cookie with :defer option" do
pool = Rack::Session::Memcache.new(incrementor)
req = Rack::MockRequest.new(pool)
defer = Rack::Utils::Context.new(pool, defer_session)
@ -157,7 +160,7 @@ begin
res3.body.should.equal '{"counter"=>4}'
end
specify "deep hashes are correctly updated" do
it "updates deep hashes correctly" do
store = nil
hash_check = proc do |env|
session = env['rack.session']
@ -183,8 +186,11 @@ begin
end
# anyone know how to do this better?
specify "multithread: should cleanly merge sessions" do
next unless $DEBUG
it "cleanly merges sessions when multithreaded" do
unless $DEBUG
1.should.equal 1 # fake assertion to appease the mighty bacon
next
end
warn 'Running multithread test for Session::Memcache'
pool = Rack::Session::Memcache.new(incrementor)
req = Rack::MockRequest.new(pool)
@ -215,8 +221,8 @@ begin
end
session = pool.pool.get(session_id)
session.size.should.be tnum+1 # counter
session['counter'].should.be 2 # meeeh
session.size.should.equal tnum+1 # counter
session['counter'].should.equal 2 # meeeh
tnum = rand(7).to_i+5
r = Array.new(tnum) do |i|

View File

@ -1,39 +1,40 @@
require 'test/spec'
require 'rack/session/pool'
require 'rack/mock'
require 'rack/response'
require 'thread'
require 'rack/mock'
require 'rack/session/pool'
context "Rack::Session::Pool" do
describe Rack::Session::Pool do
session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key]
session_match = /#{session_key}=[0-9a-fA-F]+;/
incrementor = lambda do |env|
env["rack.session"]["counter"] ||= 0
env["rack.session"]["counter"] += 1
Rack::Response.new(env["rack.session"].inspect).to_a
end
drop_session = proc do |env|
drop_session = lambda do |env|
env['rack.session.options'][:drop] = true
incrementor.call(env)
end
renew_session = proc do |env|
renew_session = lambda do |env|
env['rack.session.options'][:renew] = true
incrementor.call(env)
end
defer_session = proc do |env|
defer_session = lambda do |env|
env['rack.session.options'][:defer] = true
incrementor.call(env)
end
specify "creates a new cookie" do
it "creates a new cookie" do
pool = Rack::Session::Pool.new(incrementor)
res = Rack::MockRequest.new(pool).get("/")
res["Set-Cookie"].should.match session_match
res.body.should.equal '{"counter"=>1}'
end
specify "determines session from a cookie" do
it "determines session from a cookie" do
pool = Rack::Session::Pool.new(incrementor)
req = Rack::MockRequest.new(pool)
cookie = req.get("/")["Set-Cookie"]
@ -43,14 +44,14 @@ context "Rack::Session::Pool" do
body.should.equal '{"counter"=>3}'
end
specify "survives nonexistant cookies" do
it "survives nonexistant cookies" do
pool = Rack::Session::Pool.new(incrementor)
res = Rack::MockRequest.new(pool).
get("/", "HTTP_COOKIE" => "#{session_key}=blarghfasel")
res.body.should.equal '{"counter"=>1}'
end
specify "deletes cookies with :drop option" do
it "deletes cookies with :drop option" do
pool = Rack::Session::Pool.new(incrementor)
req = Rack::MockRequest.new(pool)
drop = Rack::Utils::Context.new(pool, drop_session)
@ -59,25 +60,25 @@ context "Rack::Session::Pool" do
res0 = req.get("/")
session = (cookie = res0["Set-Cookie"])[session_match]
res0.body.should.equal '{"counter"=>1}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res1 = req.get("/", "HTTP_COOKIE" => cookie)
res1["Set-Cookie"][session_match].should.equal session
res1.body.should.equal '{"counter"=>2}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
res2["Set-Cookie"].should.equal nil
res2.body.should.equal '{"counter"=>3}'
pool.pool.size.should.be 0
pool.pool.size.should.equal 0
res3 = req.get("/", "HTTP_COOKIE" => cookie)
res3["Set-Cookie"][session_match].should.not.equal session
res3.body.should.equal '{"counter"=>1}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
end
specify "provides new session id with :renew option" do
it "provides new session id with :renew option" do
pool = Rack::Session::Pool.new(incrementor)
req = Rack::MockRequest.new(pool)
renew = Rack::Utils::Context.new(pool, renew_session)
@ -86,27 +87,27 @@ context "Rack::Session::Pool" do
res0 = req.get("/")
session = (cookie = res0["Set-Cookie"])[session_match]
res0.body.should.equal '{"counter"=>1}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res1 = req.get("/", "HTTP_COOKIE" => cookie)
res1["Set-Cookie"][session_match].should.equal session
res1.body.should.equal '{"counter"=>2}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
new_cookie = res2["Set-Cookie"]
new_session = new_cookie[session_match]
new_session.should.not.equal session
res2.body.should.equal '{"counter"=>3}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
res3["Set-Cookie"][session_match].should.equal new_session
res3.body.should.equal '{"counter"=>4}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
end
specify "omits cookie with :defer option" do
it "omits cookie with :defer option" do
pool = Rack::Session::Pool.new(incrementor)
req = Rack::MockRequest.new(pool)
defer = Rack::Utils::Context.new(pool, defer_session)
@ -115,27 +116,31 @@ context "Rack::Session::Pool" do
res0 = req.get("/")
session = (cookie = res0["Set-Cookie"])[session_match]
res0.body.should.equal '{"counter"=>1}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res1 = req.get("/", "HTTP_COOKIE" => cookie)
res1["Set-Cookie"][session_match].should.equal session
res1.body.should.equal '{"counter"=>2}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
res2["Set-Cookie"].should.equal nil
res2.body.should.equal '{"counter"=>3}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
res3 = req.get("/", "HTTP_COOKIE" => cookie)
res3["Set-Cookie"][session_match].should.equal session
res3.body.should.equal '{"counter"=>4}'
pool.pool.size.should.be 1
pool.pool.size.should.equal 1
end
# anyone know how to do this better?
specify "multithread: should merge sessions" do
next unless $DEBUG
it "should merge sessions when multithreaded" do
unless $DEBUG
1.should.equal 1
next
end
warn 'Running multithread tests for Session::Pool'
pool = Rack::Session::Pool.new(incrementor)
req = Rack::MockRequest.new(pool)
@ -166,7 +171,7 @@ context "Rack::Session::Pool" do
end
session = pool.pool[sess_id]
session.size.should.be tnum+1 # counter
session['counter'].should.be 2 # meeeh
session.size.should.equal tnum+1 # counter
session['counter'].should.equal 2 # meeeh
end
end

View File

@ -0,0 +1,23 @@
require 'rack/showexceptions'
require 'rack/mock'
describe Rack::ShowExceptions do
it "catches exceptions" do
res = nil
req = Rack::MockRequest.new(
Rack::ShowExceptions.new(
lambda{|env| raise RuntimeError }
))
lambda{
res = req.get("/")
}.should.not.raise
res.should.be.a.server_error
res.status.should.equal 500
res.should =~ /RuntimeError/
res.should =~ /ShowExceptions/
end
end

View File

@ -0,0 +1,79 @@
require 'rack/showstatus'
require 'rack/mock'
describe Rack::ShowStatus do
should "provide a default status message" do
req = Rack::MockRequest.new(
Rack::ShowStatus.new(lambda{|env|
[404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []]
}))
res = req.get("/", :lint => true)
res.should.be.not_found
res.should.be.not.empty
res["Content-Type"].should.equal("text/html")
res.should =~ /404/
res.should =~ /Not Found/
end
should "let the app provide additional information" do
req = Rack::MockRequest.new(
Rack::ShowStatus.new(
lambda{|env|
env["rack.showstatus.detail"] = "gone too meta."
[404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []]
}))
res = req.get("/", :lint => true)
res.should.be.not_found
res.should.be.not.empty
res["Content-Type"].should.equal("text/html")
res.should =~ /404/
res.should =~ /Not Found/
res.should =~ /too meta/
end
should "not replace existing messages" do
req = Rack::MockRequest.new(
Rack::ShowStatus.new(
lambda{|env|
[404, {"Content-Type" => "text/plain", "Content-Length" => "4"}, ["foo!"]]
}))
res = req.get("/", :lint => true)
res.should.be.not_found
res.body.should == "foo!"
end
should "pass on original headers" do
headers = {"WWW-Authenticate" => "Basic blah"}
req = Rack::MockRequest.new(
Rack::ShowStatus.new(lambda{|env| [401, headers, []] }))
res = req.get("/", :lint => true)
res["WWW-Authenticate"].should.equal("Basic blah")
end
should "replace existing messages if there is detail" do
req = Rack::MockRequest.new(
Rack::ShowStatus.new(
lambda{|env|
env["rack.showstatus.detail"] = "gone too meta."
[404, {"Content-Type" => "text/plain", "Content-Length" => "4"}, ["foo!"]]
}))
res = req.get("/", :lint => true)
res.should.be.not_found
res.should.be.not.empty
res["Content-Type"].should.equal("text/html")
res["Content-Length"].should.not.equal("4")
res.should =~ /404/
res.should =~ /too meta/
res.body.should.not =~ /foo/
end
end

View File

@ -1,5 +1,3 @@
require 'test/spec'
require 'rack/static'
require 'rack/mock'
@ -9,26 +7,24 @@ class DummyApp
end
end
context "Rack::Static" do
describe Rack::Static do
root = File.expand_path(File.dirname(__FILE__))
OPTIONS = {:urls => ["/cgi"], :root => root}
setup do
@request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, OPTIONS))
end
@request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, OPTIONS))
specify "serves files" do
it "serves files" do
res = @request.get("/cgi/test")
res.should.be.ok
res.body.should =~ /ruby/
end
specify "404s if url root is known but it can't find the file" do
it "404s if url root is known but it can't find the file" do
res = @request.get("/cgi/foo")
res.should.be.not_found
end
specify "calls down the chain if url root is not known" do
it "calls down the chain if url root is not known" do
res = @request.get("/something/else")
res.should.be.ok
res.body.should == "Hello World"

View File

@ -1,34 +1,31 @@
require 'test/spec'
begin
require 'rack/handler/thin'
require 'testrequest'
require File.expand_path('../testrequest', __FILE__)
require 'timeout'
context "Rack::Handler::Thin" do
include TestRequest::Helpers
setup do
@app = Rack::Lint.new(TestRequest.new)
@server = nil
Thin::Logging.silent = true
@thread = Thread.new do
Rack::Handler::Thin.run(@app, :Host => @host='0.0.0.0', :Port => @port=9204) do |server|
@server = server
end
describe Rack::Handler::Thin do
extend TestRequest::Helpers
@app = Rack::Lint.new(TestRequest.new)
@server = nil
Thin::Logging.silent = true
@thread = Thread.new do
Rack::Handler::Thin.run(@app, :Host => @host='0.0.0.0', :Port => @port=9204) do |server|
@server = server
end
Thread.pass until @server && @server.running?
end
specify "should respond" do
lambda {
GET("/")
}.should.not.raise
end
Thread.pass until @server && @server.running?
specify "should be a Thin" do
should "respond" do
GET("/")
status.should.be 200
response.should.not.be.nil
end
should "be a Thin" do
GET("/")
status.should.equal 200
response["SERVER_SOFTWARE"].should =~ /thin/
response["HTTP_VERSION"].should.equal "HTTP/1.1"
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
@ -36,15 +33,15 @@ context "Rack::Handler::Thin" do
response["SERVER_NAME"].should.equal "0.0.0.0"
end
specify "should have rack headers" do
should "have rack headers" do
GET("/")
response["rack.version"].should.equal [0,3]
response["rack.multithread"].should.be false
response["rack.multiprocess"].should.be false
response["rack.run_once"].should.be false
response["rack.version"].should.equal [1,0]
response["rack.multithread"].should.equal false
response["rack.multiprocess"].should.equal false
response["rack.run_once"].should.equal false
end
specify "should have CGI headers on GET" do
should "have CGI headers on GET" do
GET("/")
response["REQUEST_METHOD"].should.equal "GET"
response["REQUEST_PATH"].should.equal "/"
@ -59,7 +56,7 @@ context "Rack::Handler::Thin" do
response["QUERY_STRING"].should.equal "quux=1"
end
specify "should have CGI headers on POST" do
should "have CGI headers on POST" do
POST("/", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
status.should.equal 200
response["REQUEST_METHOD"].should.equal "POST"
@ -69,21 +66,19 @@ context "Rack::Handler::Thin" do
response["test.postdata"].should.equal "rack-form-data=23"
end
specify "should support HTTP auth" do
should "support HTTP auth" do
GET("/test", {:user => "ruth", :passwd => "secret"})
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
end
specify "should set status" do
should "set status" do
GET("/test?secret")
status.should.equal 403
response["rack.url_scheme"].should.equal "http"
end
teardown do
@server.stop!
@thread.kill
end
@server.stop!
@thread.kill
end
rescue LoadError

View File

@ -1,10 +1,8 @@
require 'test/spec'
require 'rack/urlmap'
require 'rack/mock'
context "Rack::URLMap" do
specify "dispatches paths correctly" do
describe Rack::URLMap do
it "dispatches paths correctly" do
app = lambda { |env|
[200, {
'X-ScriptName' => env['SCRIPT_NAME'],
@ -67,7 +65,7 @@ context "Rack::URLMap" do
end
specify "dispatches hosts correctly" do
it "dispatches hosts correctly" do
map = Rack::URLMap.new("http://foo.org/" => lambda { |env|
[200,
{ "Content-Type" => "text/plain",
@ -125,7 +123,7 @@ context "Rack::URLMap" do
res["X-Position"].should.equal "default.org"
end
specify "should be nestable" do
should "be nestable" do
map = Rack::URLMap.new("/foo" =>
Rack::URLMap.new("/bar" =>
Rack::URLMap.new("/quux" => lambda { |env|
@ -147,7 +145,7 @@ context "Rack::URLMap" do
res["X-ScriptName"].should.equal "/foo/bar/quux"
end
specify "should route root apps correctly" do
should "route root apps correctly" do
map = Rack::URLMap.new("/" => lambda { |env|
[200,
{ "Content-Type" => "text/plain",
@ -189,7 +187,7 @@ context "Rack::URLMap" do
res["X-ScriptName"].should.equal ""
end
specify "should not squeeze slashes" do
should "not squeeze slashes" do
map = Rack::URLMap.new("/" => lambda { |env|
[200,
{ "Content-Type" => "text/plain",

View File

@ -1,18 +1,15 @@
require 'test/spec'
require 'rack/utils'
require 'rack/lint'
require 'rack/mock'
context "Rack::Utils" do
specify "should escape correctly" do
describe Rack::Utils do
should "escape correctly" do
Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
Rack::Utils.escape("a space").should.equal "a+space"
Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
end
specify "should escape correctly for multibyte characters" do
should "escape correctly for multibyte characters" do
matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
@ -21,7 +18,7 @@ context "Rack::Utils" do
Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
end
specify "should unescape correctly" do
should "unescape correctly" do
Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
Rack::Utils.unescape("a+space").should.equal "a space"
Rack::Utils.unescape("a%20space").should.equal "a space"
@ -29,11 +26,11 @@ context "Rack::Utils" do
should.equal "q1!2\"'w$5&7/z8)?\\"
end
specify "should parse query strings correctly" do
should "parse query strings correctly" do
Rack::Utils.parse_query("foo=bar").
should.equal "foo" => "bar"
Rack::Utils.parse_query("foo=\"bar\"").
should.equal "foo" => "bar"
should.equal "foo" => "\"bar\""
Rack::Utils.parse_query("foo=bar&foo=quux").
should.equal "foo" => ["bar", "quux"]
Rack::Utils.parse_query("foo=1&bar=2").
@ -43,7 +40,7 @@ context "Rack::Utils" do
Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
end
specify "should parse nested query strings correctly" do
should "parse nested query strings correctly" do
Rack::Utils.parse_nested_query("foo").
should.equal "foo" => nil
Rack::Utils.parse_nested_query("foo=").
@ -51,7 +48,7 @@ context "Rack::Utils" do
Rack::Utils.parse_nested_query("foo=bar").
should.equal "foo" => "bar"
Rack::Utils.parse_nested_query("foo=\"bar\"").
should.equal "foo" => "bar"
should.equal "foo" => "\"bar\""
Rack::Utils.parse_nested_query("foo=bar&foo=quux").
should.equal "foo" => "quux"
@ -121,7 +118,7 @@ context "Rack::Utils" do
message.should.equal "expected Array (got String) for param `y'"
end
specify "should build query strings correctly" do
should "build query strings correctly" do
Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar"
Rack::Utils.build_query("foo" => ["bar", "quux"]).
should.equal "foo=bar&foo=quux"
@ -131,7 +128,7 @@ context "Rack::Utils" do
should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
end
specify "should build nested query strings correctly" do
should "build nested query strings correctly" do
Rack::Utils.build_nested_query("foo" => nil).should.equal "foo"
Rack::Utils.build_nested_query("foo" => "").should.equal "foo="
Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
@ -178,7 +175,7 @@ context "Rack::Utils" do
message.should.equal "value must be a Hash"
end
specify "should figure out which encodings are acceptable" do
should "figure out which encodings are acceptable" do
helper = lambda do |a, b|
request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
Rack::Utils.select_best_encoding(a, b)
@ -201,43 +198,43 @@ context "Rack::Utils" do
helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity")
end
specify "should return the bytesize of String" do
should "return the bytesize of String" do
Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
end
specify "should return status code for integer" do
should "return status code for integer" do
Rack::Utils.status_code(200).should.equal 200
end
specify "should return status code for string" do
should "return status code for string" do
Rack::Utils.status_code("200").should.equal 200
end
specify "should return status code for symbol" do
should "return status code for symbol" do
Rack::Utils.status_code(:ok).should.equal 200
end
end
context "Rack::Utils::HeaderHash" do
specify "should retain header case" do
describe Rack::Utils::HeaderHash do
should "retain header case" do
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
h['ETag'] = 'Boo!'
h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
end
specify "should check existence of keys case insensitively" do
should "check existence of keys case insensitively" do
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
h.should.include 'content-md5'
h.should.not.include 'ETag'
end
specify "should merge case-insensitively" do
should "merge case-insensitively" do
h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
end
specify "should overwrite case insensitively and assume the new key's case" do
should "overwrite case insensitively and assume the new key's case" do
h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
h["foo-bar"] = "bizzle"
h["FOO-BAR"].should.equal "bizzle"
@ -245,55 +242,55 @@ context "Rack::Utils::HeaderHash" do
h.to_hash.should.equal "foo-bar" => "bizzle"
end
specify "should be converted to real Hash" do
should "be converted to real Hash" do
h = Rack::Utils::HeaderHash.new("foo" => "bar")
h.to_hash.should.be.instance_of Hash
end
specify "should convert Array values to Strings when converting to Hash" do
should "convert Array values to Strings when converting to Hash" do
h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
h.to_hash.should.equal({ "foo" => "bar\nbaz" })
end
specify "should replace hashes correctly" do
should "replace hashes correctly" do
h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
j = {"foo" => "bar"}
h.replace(j)
h["foo"].should.equal "bar"
end
specify "should be able to delete the given key case-sensitively" do
should "be able to delete the given key case-sensitively" do
h = Rack::Utils::HeaderHash.new("foo" => "bar")
h.delete("foo")
h["foo"].should.be.nil
h["FOO"].should.be.nil
end
specify "should be able to delete the given key case-insensitively" do
should "be able to delete the given key case-insensitively" do
h = Rack::Utils::HeaderHash.new("foo" => "bar")
h.delete("FOO")
h["foo"].should.be.nil
h["FOO"].should.be.nil
end
specify "should return the deleted value when #delete is called on an existing key" do
should "return the deleted value when #delete is called on an existing key" do
h = Rack::Utils::HeaderHash.new("foo" => "bar")
h.delete("Foo").should.equal("bar")
end
specify "should return nil when #delete is called on a non-existant key" do
should "return nil when #delete is called on a non-existant key" do
h = Rack::Utils::HeaderHash.new("foo" => "bar")
h.delete("Hello").should.be.nil
end
specify "should avoid unnecessary object creation if possible" do
should "avoid unnecessary object creation if possible" do
a = Rack::Utils::HeaderHash.new("foo" => "bar")
b = Rack::Utils::HeaderHash.new(a)
b.object_id.should.equal(a.object_id)
b.should.equal(a)
end
specify "should convert Array values to Strings when responding to #each" do
should "convert Array values to Strings when responding to #each" do
h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
h.each do |k,v|
k.should.equal("foo")
@ -303,7 +300,7 @@ context "Rack::Utils::HeaderHash" do
end
context "Rack::Utils::Context" do
describe Rack::Utils::Context do
class ContextTest
attr_reader :app
def initialize app; @app=app; end
@ -316,7 +313,7 @@ context "Rack::Utils::Context" do
test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
test_app = ContextTest.new test_target4
specify "should set context correctly" do
should "set context correctly" do
test_app.app.should.equal test_target4
c1 = Rack::Utils::Context.new(test_app, test_target1)
c1.for.should.equal test_app
@ -326,7 +323,7 @@ context "Rack::Utils::Context" do
c2.app.should.equal test_target2
end
specify "should alter app on recontexting" do
should "alter app on recontexting" do
c1 = Rack::Utils::Context.new(test_app, test_target1)
c2 = c1.recontext(test_target2)
c2.for.should.equal test_app
@ -336,7 +333,7 @@ context "Rack::Utils::Context" do
c3.app.should.equal test_target3
end
specify "should run different apps" do
should "run different apps" do
c1 = Rack::Utils::Context.new test_app, test_target1
c2 = c1.recontext test_target2
c3 = c2.recontext test_target3
@ -350,21 +347,37 @@ context "Rack::Utils::Context" do
r3 = c3.call(:misc_symbol)
r3.should.be.nil
r4 = Rack::MockRequest.new(a4).get('/')
r4.status.should.be 200
r4.status.should.equal 200
r5 = Rack::MockRequest.new(a5).get('/')
r5.status.should.be 200
r5.status.should.equal 200
r4.body.should.equal r5.body
end
end
context "Rack::Utils::Multipart" do
specify "should return nil if content type is not multipart" do
describe Rack::Utils::Multipart do
def multipart_fixture(name)
file = multipart_file(name)
data = File.open(file, 'rb') { |io| io.read }
type = "multipart/form-data; boundary=AaB03x"
length = data.respond_to?(:bytesize) ? data.bytesize : data.size
{ "CONTENT_TYPE" => type,
"CONTENT_LENGTH" => length.to_s,
:input => StringIO.new(data) }
end
def multipart_file(name)
File.join(File.dirname(__FILE__), "multipart", name.to_s)
end
should "return nil if content type is not multipart" do
env = Rack::MockRequest.env_for("/",
"CONTENT_TYPE" => 'application/x-www-form-urlencoded')
Rack::Utils::Multipart.parse_multipart(env).should.equal nil
end
specify "should parse multipart upload with text file" do
should "parse multipart upload with text file" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
params = Rack::Utils::Multipart.parse_multipart(env)
params["submit-name"].should.equal "Larry"
@ -377,7 +390,7 @@ context "Rack::Utils::Multipart" do
params["files"][:tempfile].read.should.equal "contents"
end
specify "should parse multipart upload with nested parameters" do
should "parse multipart upload with nested parameters" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
params = Rack::Utils::Multipart.parse_multipart(env)
params["foo"]["submit-name"].should.equal "Larry"
@ -390,7 +403,7 @@ context "Rack::Utils::Multipart" do
params["foo"]["files"][:tempfile].read.should.equal "contents"
end
specify "should parse multipart upload with binary file" do
should "parse multipart upload with binary file" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
params = Rack::Utils::Multipart.parse_multipart(env)
params["submit-name"].should.equal "Larry"
@ -403,7 +416,7 @@ context "Rack::Utils::Multipart" do
params["files"][:tempfile].read.length.should.equal 26473
end
specify "should parse multipart upload with empty file" do
should "parse multipart upload with empty file" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
params = Rack::Utils::Multipart.parse_multipart(env)
params["submit-name"].should.equal "Larry"
@ -416,7 +429,7 @@ context "Rack::Utils::Multipart" do
params["files"][:tempfile].read.should.equal ""
end
specify "should parse multipart upload with filename with semicolons" do
should "parse multipart upload with filename with semicolons" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "text/plain"
@ -428,7 +441,7 @@ context "Rack::Utils::Multipart" do
params["files"][:tempfile].read.should.equal "contents"
end
specify "should not include file params if no file was selected" do
should "not include file params if no file was selected" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
params = Rack::Utils::Multipart.parse_multipart(env)
params["submit-name"].should.equal "Larry"
@ -436,7 +449,7 @@ context "Rack::Utils::Multipart" do
params.keys.should.not.include "files"
end
specify "should parse IE multipart upload and clean up filename" do
should "parse IE multipart upload and clean up filename" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "text/plain"
@ -449,7 +462,76 @@ context "Rack::Utils::Multipart" do
params["files"][:tempfile].read.should.equal "contents"
end
specify "rewinds input after parsing upload" do
should "parse filename and modification param" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "image/jpeg"
params["files"][:filename].should.equal "genome.jpeg"
params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
"Content-Disposition: attachment; " +
"name=\"files\"; " +
"filename=genome.jpeg; " +
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
"Content-Description: a complete map of the human genome\r\n"
params["files"][:name].should.equal "files"
params["files"][:tempfile].read.should.equal "contents"
end
should "parse filename with escaped quotes" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "application/octet-stream"
params["files"][:filename].should.equal "escape \"quotes"
params["files"][:head].should.equal "Content-Disposition: form-data; " +
"name=\"files\"; " +
"filename=\"escape \\\"quotes\"\r\n" +
"Content-Type: application/octet-stream\r\n"
params["files"][:name].should.equal "files"
params["files"][:tempfile].read.should.equal "contents"
end
should "parse filename with percent escaped quotes" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "application/octet-stream"
params["files"][:filename].should.equal "escape \"quotes"
params["files"][:head].should.equal "Content-Disposition: form-data; " +
"name=\"files\"; " +
"filename=\"escape %22quotes\"\r\n" +
"Content-Type: application/octet-stream\r\n"
params["files"][:name].should.equal "files"
params["files"][:tempfile].read.should.equal "contents"
end
should "parse filename with unescaped quotes" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "application/octet-stream"
params["files"][:filename].should.equal "escape \"quotes"
params["files"][:head].should.equal "Content-Disposition: form-data; " +
"name=\"files\"; " +
"filename=\"escape \"quotes\"\r\n" +
"Content-Type: application/octet-stream\r\n"
params["files"][:name].should.equal "files"
params["files"][:tempfile].read.should.equal "contents"
end
should "parse filename with escaped quotes and modification param" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
params = Rack::Utils::Multipart.parse_multipart(env)
params["files"][:type].should.equal "image/jpeg"
params["files"][:filename].should.equal "\"human\" genome.jpeg"
params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
"Content-Disposition: attachment; " +
"name=\"files\"; " +
"filename=\"\"human\" genome.jpeg\"; " +
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
"Content-Description: a complete map of the human genome\r\n"
params["files"][:name].should.equal "files"
params["files"][:tempfile].read.should.equal "contents"
end
it "rewinds input after parsing upload" do
options = multipart_fixture(:text)
input = options[:input]
env = Rack::MockRequest.env_for("/", options)
@ -459,7 +541,7 @@ context "Rack::Utils::Multipart" do
input.read.length.should.equal 197
end
specify "builds multipart body" do
it "builds multipart body" do
files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
data = Rack::Utils::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
@ -475,7 +557,7 @@ context "Rack::Utils::Multipart" do
params["files"][:tempfile].read.should.equal "contents"
end
specify "builds nested multipart body" do
it "builds nested multipart body" do
files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
@ -491,7 +573,7 @@ context "Rack::Utils::Multipart" do
params["people"][0]["files"][:tempfile].read.should.equal "contents"
end
specify "can parse fields that end at the end of the buffer" do
it "can parse fields that end at the end of the buffer" do
input = File.read(multipart_file("bad_robots"))
req = Rack::Request.new Rack::MockRequest.env_for("/",
@ -503,7 +585,7 @@ context "Rack::Utils::Multipart" do
req.POST['addresses'].should.not.equal nil
end
specify "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
data = File.open(multipart_file("fail_16384_nofile")) { |f| f.read }.gsub(/\n/, "\r\n")
options = {
"CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
@ -522,31 +604,14 @@ context "Rack::Utils::Multipart" do
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
end
specify "should return nil if no UploadedFiles were used" do
should "return nil if no UploadedFiles were used" do
data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
data.should.equal nil
end
specify "should raise ArgumentError if params is not a Hash" do
should "raise ArgumentError if params is not a Hash" do
lambda { Rack::Utils::Multipart.build_multipart("foo=bar") }.
should.raise(ArgumentError).
message.should.equal "value must be a Hash"
end
private
def multipart_fixture(name)
file = multipart_file(name)
data = File.open(file, 'rb') { |io| io.read }
type = "multipart/form-data; boundary=AaB03x"
length = data.respond_to?(:bytesize) ? data.bytesize : data.size
{ "CONTENT_TYPE" => type,
"CONTENT_LENGTH" => length.to_s,
:input => StringIO.new(data) }
end
def multipart_file(name)
File.join(File.dirname(__FILE__), "multipart", name.to_s)
end
end

View File

@ -1,35 +1,29 @@
require 'test/spec'
require 'rack/handler/webrick'
require 'rack/lint'
require 'rack/response'
require 'testrequest'
require 'rack/mock'
require File.expand_path('../testrequest', __FILE__)
Thread.abort_on_exception = true
context "Rack::Handler::WEBrick" do
include TestRequest::Helpers
describe Rack::Handler::WEBrick do
extend TestRequest::Helpers
setup do
@server = WEBrick::HTTPServer.new(:Host => @host='0.0.0.0',
:Port => @port=9202,
:Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
:AccessLog => [])
@server.mount "/test", Rack::Handler::WEBrick,
Rack::Lint.new(TestRequest.new)
Thread.new { @server.start }
trap(:INT) { @server.shutdown }
end
@server = WEBrick::HTTPServer.new(:Host => @host='0.0.0.0',
:Port => @port=9202,
:Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
:AccessLog => [])
@server.mount "/test", Rack::Handler::WEBrick,
Rack::Lint.new(TestRequest.new)
Thread.new { @server.start }
trap(:INT) { @server.shutdown }
specify "should respond" do
should "respond" do
lambda {
GET("/test")
}.should.not.raise
end
specify "should be a WEBrick" do
should "be a WEBrick" do
GET("/test")
status.should.be 200
status.should.equal 200
response["SERVER_SOFTWARE"].should =~ /WEBrick/
response["HTTP_VERSION"].should.equal "HTTP/1.1"
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
@ -37,15 +31,15 @@ context "Rack::Handler::WEBrick" do
response["SERVER_NAME"].should.equal "0.0.0.0"
end
specify "should have rack headers" do
should "have rack headers" do
GET("/test")
response["rack.version"].should.equal [1,1]
response["rack.multithread"].should.be true
response["rack.multiprocess"].should.be false
response["rack.run_once"].should.be false
response["rack.multithread"].should.be.true
response["rack.multiprocess"].should.be.false
response["rack.run_once"].should.be.false
end
specify "should have CGI headers on GET" do
should "have CGI headers on GET" do
GET("/test")
response["REQUEST_METHOD"].should.equal "GET"
response["SCRIPT_NAME"].should.equal "/test"
@ -69,7 +63,7 @@ context "Rack::Handler::WEBrick" do
response["QUERY_STRING"].should.equal "quux=1"
end
specify "should have CGI headers on POST" do
should "have CGI headers on POST" do
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
status.should.equal 200
response["REQUEST_METHOD"].should.equal "POST"
@ -80,18 +74,18 @@ context "Rack::Handler::WEBrick" do
response["test.postdata"].should.equal "rack-form-data=23"
end
specify "should support HTTP auth" do
should "support HTTP auth" do
GET("/test", {:user => "ruth", :passwd => "secret"})
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
end
specify "should set status" do
should "set status" do
GET("/test?secret")
status.should.equal 403
response["rack.url_scheme"].should.equal "http"
end
specify "should correctly set cookies" do
should "correctly set cookies" do
@server.mount "/cookie-test", Rack::Handler::WEBrick,
Rack::Lint.new(lambda { |req|
res = Rack::Response.new
@ -107,7 +101,7 @@ context "Rack::Handler::WEBrick" do
}
end
specify "should provide a .run" do
should "provide a .run" do
block_ran = false
catch(:done) {
Rack::Handler::WEBrick.run(lambda {},
@ -120,11 +114,9 @@ context "Rack::Handler::WEBrick" do
throw :done
}
}
block_ran.should.be true
block_ran.should.be.true
@s.shutdown
end
teardown do
@server.shutdown
end
@server.shutdown
end

View File

@ -2,10 +2,15 @@ require 'yaml'
require 'net/http'
class TestRequest
NOSERIALIZE = [Method, Proc]
def call(env)
status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200
env["test.postdata"] = env["rack.input"].read
body = env.to_yaml
minienv = env.dup
# This may in the future want to replace with a dummy value instead.
minienv.delete_if { |k,v| NOSERIALIZE.any? { |c| v.kind_of?(c) } }
body = minienv.to_yaml
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
[status, {"Content-Type" => "text/yaml", "Content-Length" => size.to_s}, [body]]
end
@ -35,7 +40,7 @@ class TestRequest
@status = response.code.to_i
begin
@response = YAML.load(response.body)
rescue ArgumentError
rescue TypeError, ArgumentError
@response = nil
end
}

Some files were not shown because too many files have changed in this diff Show More