devver-rack-contrib 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +18 -0
- data/README.rdoc +80 -0
- data/Rakefile +90 -0
- data/lib/rack/contrib.rb +40 -0
- data/lib/rack/contrib/accept_format.rb +46 -0
- data/lib/rack/contrib/access.rb +85 -0
- data/lib/rack/contrib/backstage.rb +20 -0
- data/lib/rack/contrib/bounce_favicon.rb +16 -0
- data/lib/rack/contrib/callbacks.rb +37 -0
- data/lib/rack/contrib/config.rb +16 -0
- data/lib/rack/contrib/cookies.rb +50 -0
- data/lib/rack/contrib/csshttprequest.rb +39 -0
- data/lib/rack/contrib/deflect.rb +137 -0
- data/lib/rack/contrib/evil.rb +12 -0
- data/lib/rack/contrib/garbagecollector.rb +14 -0
- data/lib/rack/contrib/jsonp.rb +41 -0
- data/lib/rack/contrib/lighttpd_script_name_fix.rb +16 -0
- data/lib/rack/contrib/locale.rb +31 -0
- data/lib/rack/contrib/mailexceptions.rb +120 -0
- data/lib/rack/contrib/nested_params.rb +143 -0
- data/lib/rack/contrib/not_found.rb +18 -0
- data/lib/rack/contrib/post_body_content_type_parser.rb +40 -0
- data/lib/rack/contrib/proctitle.rb +30 -0
- data/lib/rack/contrib/profiler.rb +108 -0
- data/lib/rack/contrib/relative_redirect.rb +44 -0
- data/lib/rack/contrib/response_cache.rb +59 -0
- data/lib/rack/contrib/route_exceptions.rb +49 -0
- data/lib/rack/contrib/sendfile.rb +142 -0
- data/lib/rack/contrib/signals.rb +63 -0
- data/lib/rack/contrib/time_zone.rb +25 -0
- data/rack-contrib.gemspec +88 -0
- data/test/404.html +1 -0
- data/test/Maintenance.html +1 -0
- data/test/mail_settings.rb +12 -0
- data/test/spec_rack_accept_format.rb +72 -0
- data/test/spec_rack_access.rb +154 -0
- data/test/spec_rack_backstage.rb +26 -0
- data/test/spec_rack_callbacks.rb +65 -0
- data/test/spec_rack_config.rb +22 -0
- data/test/spec_rack_contrib.rb +8 -0
- data/test/spec_rack_csshttprequest.rb +66 -0
- data/test/spec_rack_deflect.rb +107 -0
- data/test/spec_rack_evil.rb +19 -0
- data/test/spec_rack_garbagecollector.rb +13 -0
- data/test/spec_rack_jsonp.rb +34 -0
- data/test/spec_rack_lighttpd_script_name_fix.rb +16 -0
- data/test/spec_rack_mailexceptions.rb +97 -0
- data/test/spec_rack_nested_params.rb +46 -0
- data/test/spec_rack_not_found.rb +17 -0
- data/test/spec_rack_post_body_content_type_parser.rb +32 -0
- data/test/spec_rack_proctitle.rb +26 -0
- data/test/spec_rack_profiler.rb +41 -0
- data/test/spec_rack_relative_redirect.rb +78 -0
- data/test/spec_rack_response_cache.rb +137 -0
- data/test/spec_rack_sendfile.rb +86 -0
- metadata +174 -0
data/COPYING
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2008 The Committers
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
= Contributed Rack Middleware and Utilities
|
2
|
+
|
3
|
+
This package includes a variety of add-on components for Rack, a Ruby web server
|
4
|
+
interface:
|
5
|
+
|
6
|
+
* Rack::JSONP - Adds JSON-P support by stripping out the callback param
|
7
|
+
and padding the response with the appropriate callback format.
|
8
|
+
* Rack::LighttpdScriptNameFix - Fixes how lighttpd sets the SCRIPT_NAME
|
9
|
+
and PATH_INFO variables in certain configurations.
|
10
|
+
* Rack::Locale - Detects the client locale using the Accept-Language request
|
11
|
+
header and sets a rack.locale variable in the environment.
|
12
|
+
* Rack::MailExceptions - Rescues exceptions raised from the app and
|
13
|
+
sends a useful email with the exception, stacktrace, and contents of the
|
14
|
+
environment.
|
15
|
+
* Rack::NestedParams - parses form params with subscripts (e.g., * "post[title]=Hello")
|
16
|
+
into a nested/recursive Hash structure (based on Rails' implementation).
|
17
|
+
* Rack::PostBodyContentTypeParser - Adds support for JSON request bodies. The
|
18
|
+
Rack parameter hash is populated by deserializing the JSON data provided in
|
19
|
+
the request body when the Content-Type is application/json.
|
20
|
+
* Rack::ProcTitle - Displays request information in process title ($0) for
|
21
|
+
monitoring/inspection with ps(1).
|
22
|
+
* Rack::Profiler - Uses ruby-prof to measure request time.
|
23
|
+
* Rack::Sendfile - Enables X-Sendfile support for bodies that can be served
|
24
|
+
from file.
|
25
|
+
* Rack::Signals - Installs signal handlers that are safely processed after
|
26
|
+
a request
|
27
|
+
* Rack::StaticCache - Modifies the response headers to facilitiate client and proxy caching for
|
28
|
+
static files that minimizes http requests and improves overall load times for second time visitors.
|
29
|
+
* Rack::TimeZone - Detects the clients timezone using JavaScript and sets
|
30
|
+
a variable in Rack's environment with the offset from UTC.
|
31
|
+
* Rack::Evil - Lets the rack application return a response to the client from any place.
|
32
|
+
* Rack::Callbacks - Implements DLS for pure before/after filter like Middlewares.
|
33
|
+
* Rack::Config - Shared configuration for cooperative middleware.
|
34
|
+
* Rack::NotFound - A default 404 application.
|
35
|
+
* Rack::CSSHTTPRequest - Adds CSSHTTPRequest support by encoding responses as
|
36
|
+
CSS for cross-site AJAX-style data loading
|
37
|
+
* Rack::Deflect - Helps protect against DoS attacks.
|
38
|
+
* Rack::ResponseCache - Caches responses to requests without query strings
|
39
|
+
to Disk or a user provider Ruby object. Similar to Rails' page caching.
|
40
|
+
* Rack::RelativeRedirect - Transforms relative paths in redirects to
|
41
|
+
absolute URLs.
|
42
|
+
* Rack::Backstage - Returns content of specified file if it exists, which makes
|
43
|
+
it convenient for putting up maintenance pages.
|
44
|
+
* Rack::AcceptFormat - Adds a format extension at the end of the URI when there is none, corresponding to the mime-type given in the Accept HTTP header.
|
45
|
+
* Rack::HostMeta - Configures /host-meta using a block
|
46
|
+
* Rack::Cookies - Adds simple cookie jar hash to env
|
47
|
+
* Rack::Access - Limit access based on IP address
|
48
|
+
* Rack::ResponseHeaders - Manipulate response headers object at runtime
|
49
|
+
* Rack::SimpleEndpoint - Create simple endpoints with routing rules, similar to Sinatra actions
|
50
|
+
|
51
|
+
=== Use
|
52
|
+
|
53
|
+
Git is the quickest way to the rack-contrib sources:
|
54
|
+
|
55
|
+
git clone git://github.com/rack/rack-contrib.git
|
56
|
+
|
57
|
+
Gems are currently available from GitHub clones:
|
58
|
+
|
59
|
+
gem install rack-rack-contrib --source=http://gems.github.com/
|
60
|
+
|
61
|
+
Requiring 'rack/contrib' will add autoloads to the Rack modules for all of the
|
62
|
+
components included. The following example shows what a simple rackup
|
63
|
+
(+config.ru+) file might look like:
|
64
|
+
|
65
|
+
require 'rack'
|
66
|
+
require 'rack/contrib'
|
67
|
+
|
68
|
+
use Rack::Profiler if ENV['RACK_ENV'] == 'development'
|
69
|
+
|
70
|
+
use Rack::ETag
|
71
|
+
use Rack::MailExceptions
|
72
|
+
|
73
|
+
run theapp
|
74
|
+
|
75
|
+
=== Links
|
76
|
+
|
77
|
+
rack-contrib on GitHub:: <http://github.com/rack/rack-contrib>
|
78
|
+
Rack:: <http://rack.rubyforge.org/>
|
79
|
+
Rack On GitHub:: <http://github.org/rack/rack>
|
80
|
+
rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
|
data/Rakefile
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Rakefile for Rack::Contrib. -*-ruby-*-
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
desc "Run all the tests"
|
6
|
+
task :default => [:test]
|
7
|
+
|
8
|
+
desc "Generate RDox"
|
9
|
+
task "RDOX" do
|
10
|
+
sh "specrb -Ilib:test -a --rdox >RDOX"
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Run specs with test/unit style output"
|
14
|
+
task :test do
|
15
|
+
sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run specs with specdoc style output"
|
19
|
+
task :spec do
|
20
|
+
sh "specrb -Ilib:test -s -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Run all the tests"
|
24
|
+
task :fulltest do
|
25
|
+
sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Generate RDoc documentation"
|
29
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
30
|
+
rdoc.options << '--line-numbers' << '--inline-source' <<
|
31
|
+
'--main' << 'README' <<
|
32
|
+
'--title' << 'Rack Contrib Documentation' <<
|
33
|
+
'--charset' << 'utf-8'
|
34
|
+
rdoc.rdoc_dir = "doc"
|
35
|
+
rdoc.rdoc_files.include 'README.rdoc'
|
36
|
+
rdoc.rdoc_files.include 'RDOX'
|
37
|
+
rdoc.rdoc_files.include('lib/rack/*.rb')
|
38
|
+
rdoc.rdoc_files.include('lib/rack/*/*.rb')
|
39
|
+
end
|
40
|
+
task :rdoc => ["RDOX"]
|
41
|
+
|
42
|
+
|
43
|
+
# PACKAGING =================================================================
|
44
|
+
|
45
|
+
if defined?(Gem)
|
46
|
+
# load gemspec
|
47
|
+
$spec = eval(File.read('rack-contrib.gemspec'))
|
48
|
+
|
49
|
+
def package(ext='')
|
50
|
+
"pkg/rack-contrib-#{$spec.version}" + ext
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'Build packages'
|
54
|
+
task :package => %w[.gem .tar.gz].map {|e| package(e)}
|
55
|
+
|
56
|
+
desc 'Build and install as local gem'
|
57
|
+
task :install => package('.gem') do
|
58
|
+
sh "gem install #{package('.gem')}"
|
59
|
+
end
|
60
|
+
|
61
|
+
directory 'pkg/'
|
62
|
+
|
63
|
+
file package('.gem') => %w[pkg/ rack-contrib.gemspec] + $spec.files do |f|
|
64
|
+
sh "gem build rack-contrib.gemspec"
|
65
|
+
mv File.basename(f.name), f.name
|
66
|
+
end
|
67
|
+
|
68
|
+
file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
|
69
|
+
sh "git archive --format=tar HEAD | gzip > #{f.name}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# GEMSPEC ===================================================================
|
74
|
+
|
75
|
+
file 'rack-contrib.gemspec' => FileList['{lib,test}/**','Rakefile', 'README.rdoc'] do |f|
|
76
|
+
# read spec file and split out manifest section
|
77
|
+
spec = File.read(f.name)
|
78
|
+
parts = spec.split(" # = MANIFEST =\n")
|
79
|
+
fail 'bad spec' if parts.length != 3
|
80
|
+
# determine file list from git ls-files
|
81
|
+
files = `git ls-files`.
|
82
|
+
split("\n").sort.reject{ |file| file =~ /^\./ }.
|
83
|
+
map{ |file| " #{file}" }.join("\n")
|
84
|
+
# piece file back together and write...
|
85
|
+
parts[1] = " s.files = %w[\n#{files}\n ]\n"
|
86
|
+
spec = parts.join(" # = MANIFEST =\n")
|
87
|
+
spec.sub!(/s.date = '.*'/, "s.date = '#{Time.now.strftime("%Y-%m-%d")}'")
|
88
|
+
File.open(f.name, 'w') { |io| io.write(spec) }
|
89
|
+
puts "updated #{f.name}"
|
90
|
+
end
|
data/lib/rack/contrib.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Contrib
|
5
|
+
def self.release
|
6
|
+
"0.9.1"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
autoload :AcceptFormat, "rack/contrib/accept_format"
|
11
|
+
autoload :Access, "rack/contrib/access"
|
12
|
+
autoload :BounceFavicon, "rack/contrib/bounce_favicon"
|
13
|
+
autoload :Cookies, "rack/contrib/cookies"
|
14
|
+
autoload :CSSHTTPRequest, "rack/contrib/csshttprequest"
|
15
|
+
autoload :Deflect, "rack/contrib/deflect"
|
16
|
+
autoload :ETag, "rack/contrib/etag"
|
17
|
+
autoload :ExpectationCascade, "rack/contrib/expectation_cascade"
|
18
|
+
autoload :GarbageCollector, "rack/contrib/garbagecollector"
|
19
|
+
autoload :JSONP, "rack/contrib/jsonp"
|
20
|
+
autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix"
|
21
|
+
autoload :Locale, "rack/contrib/locale"
|
22
|
+
autoload :MailExceptions, "rack/contrib/mailexceptions"
|
23
|
+
autoload :PostBodyContentTypeParser, "rack/contrib/post_body_content_type_parser"
|
24
|
+
autoload :ProcTitle, "rack/contrib/proctitle"
|
25
|
+
autoload :Profiler, "rack/contrib/profiler"
|
26
|
+
autoload :ResponseHeaders, "rack/contrib/response_headers"
|
27
|
+
autoload :Runtime, "rack/contrib/runtime"
|
28
|
+
autoload :Sendfile, "rack/contrib/sendfile"
|
29
|
+
autoload :Signals, "rack/contrib/signals"
|
30
|
+
autoload :SimpleEndpoint, "rack/contrib/simple_endpoint"
|
31
|
+
autoload :TimeZone, "rack/contrib/time_zone"
|
32
|
+
autoload :Evil, "rack/contrib/evil"
|
33
|
+
autoload :Callbacks, "rack/contrib/callbacks"
|
34
|
+
autoload :NestedParams, "rack/contrib/nested_params"
|
35
|
+
autoload :Config, "rack/contrib/config"
|
36
|
+
autoload :NotFound, "rack/contrib/not_found"
|
37
|
+
autoload :ResponseCache, "rack/contrib/response_cache"
|
38
|
+
autoload :RelativeRedirect, "rack/contrib/relative_redirect"
|
39
|
+
autoload :StaticCache, "rack/contrib/static_cache"
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rack
|
2
|
+
#
|
3
|
+
# A Rack middleware for automatically adding a <tt>format</tt> token at the end of the request path
|
4
|
+
# when there is none. It can detect formats passed in the HTTP_ACCEPT header to populate this token.
|
5
|
+
#
|
6
|
+
# e.g.:
|
7
|
+
# GET /some/resource HTTP/1.1
|
8
|
+
# Accept: application/json
|
9
|
+
# ->
|
10
|
+
# GET /some/resource.json HTTP/1.1
|
11
|
+
# Accept: application/json
|
12
|
+
#
|
13
|
+
# You can add custom types with this kind of function (taken from sinatra):
|
14
|
+
# def mime(ext, type)
|
15
|
+
# ext = ".#{ext}" unless ext.to_s[0] == ?.
|
16
|
+
# Rack::Mime::MIME_TYPES[ext.to_s] = type
|
17
|
+
# end
|
18
|
+
# and then:
|
19
|
+
# mime :json, 'application/json'
|
20
|
+
#
|
21
|
+
# Note: it does not take into account multiple media types in the Accept header.
|
22
|
+
# The first media type takes precedence over all the others.
|
23
|
+
#
|
24
|
+
# MIT-License - Cyril Rohr
|
25
|
+
#
|
26
|
+
class AcceptFormat
|
27
|
+
|
28
|
+
def initialize(app, default_extention = '.html')
|
29
|
+
@ext = default_extention.to_s.strip
|
30
|
+
@ext = ".#{@ext}" unless @ext[0] == ?.
|
31
|
+
@app = app
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(env)
|
35
|
+
req = Rack::Request.new(env)
|
36
|
+
|
37
|
+
if ::File.extname(req.path_info).empty?
|
38
|
+
accept = env['HTTP_ACCEPT'].to_s.scan(/[^;,\s]*\/[^;,\s]*/)[0].to_s
|
39
|
+
extension = Rack::Mime::MIME_TYPES.invert[accept] || @ext
|
40
|
+
req.path_info = req.path_info+"#{extension}"
|
41
|
+
end
|
42
|
+
|
43
|
+
@app.call(env)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "ipaddr"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
|
5
|
+
##
|
6
|
+
# Rack middleware for limiting access based on IP address
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# === Options:
|
10
|
+
#
|
11
|
+
# path => ipmasks ipmasks: Array of remote addresses which are allowed to access
|
12
|
+
#
|
13
|
+
# === Examples:
|
14
|
+
#
|
15
|
+
# use Rack::Access, '/backend' => [ '127.0.0.1', '192.168.1.0/24' ]
|
16
|
+
#
|
17
|
+
#
|
18
|
+
|
19
|
+
class Access
|
20
|
+
|
21
|
+
attr_reader :options
|
22
|
+
|
23
|
+
def initialize(app, options = {})
|
24
|
+
@app = app
|
25
|
+
mapping = options.empty? ? {"/" => ["127.0.0.1"]} : options
|
26
|
+
@mapping = remap(mapping)
|
27
|
+
end
|
28
|
+
|
29
|
+
def remap(mapping)
|
30
|
+
mapping.map { |location, ipmasks|
|
31
|
+
if location =~ %r{\Ahttps?://(.*?)(/.*)}
|
32
|
+
host, location = $1, $2
|
33
|
+
else
|
34
|
+
host = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
unless location[0] == ?/
|
38
|
+
raise ArgumentError, "paths need to start with /"
|
39
|
+
end
|
40
|
+
location = location.chomp('/')
|
41
|
+
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
|
42
|
+
|
43
|
+
ipmasks.collect! do |ipmask|
|
44
|
+
ipmask.is_a?(IPAddr) ? ipmask : IPAddr.new(ipmask)
|
45
|
+
end
|
46
|
+
[host, location, match, ipmasks]
|
47
|
+
}.sort_by { |(h, l, m, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(env)
|
51
|
+
@original_request = Request.new(env)
|
52
|
+
ipmasks = ipmasks_for_path(env)
|
53
|
+
return forbidden! unless ip_authorized?(ipmasks)
|
54
|
+
status, headers, body = @app.call(env)
|
55
|
+
[status, headers, body]
|
56
|
+
end
|
57
|
+
|
58
|
+
def ipmasks_for_path(env)
|
59
|
+
path = env["PATH_INFO"].to_s
|
60
|
+
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
|
61
|
+
@mapping.each do |host, location, match, ipmasks|
|
62
|
+
next unless (hHost == host || sName == host \
|
63
|
+
|| (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
|
64
|
+
next unless path =~ match && rest = $1
|
65
|
+
next unless rest.empty? || rest[0] == ?/
|
66
|
+
|
67
|
+
return ipmasks
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def forbidden!
|
73
|
+
[403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, '']
|
74
|
+
end
|
75
|
+
|
76
|
+
def ip_authorized?(ipmasks)
|
77
|
+
return true if ipmasks.nil?
|
78
|
+
|
79
|
+
ipmasks.any? do |ip_mask|
|
80
|
+
ip_mask.include?(IPAddr.new(@original_request.ip))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rack
|
2
|
+
class Backstage
|
3
|
+
File = ::File
|
4
|
+
|
5
|
+
def initialize(app, path)
|
6
|
+
@app = app
|
7
|
+
@file = File.expand_path(path)
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
if File.exists?(@file)
|
12
|
+
content = File.read(@file)
|
13
|
+
length = "".respond_to?(:bytesize) ? content.bytesize.to_s : content.size.to_s
|
14
|
+
[503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]]
|
15
|
+
else
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rack
|
2
|
+
# Bounce those annoying favicon.ico requests
|
3
|
+
class BounceFavicon
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
if env["PATH_INFO"] == "/favicon.ico"
|
10
|
+
[404, {"Content-Type" => "text/html", "Content-Length" => "0"}, []]
|
11
|
+
else
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rack
|
2
|
+
class Callbacks
|
3
|
+
def initialize(&block)
|
4
|
+
@before = []
|
5
|
+
@after = []
|
6
|
+
instance_eval(&block) if block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def before(middleware, *args, &block)
|
10
|
+
if block_given?
|
11
|
+
@before << middleware.new(*args, &block)
|
12
|
+
else
|
13
|
+
@before << middleware.new(*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def after(middleware, *args, &block)
|
18
|
+
if block_given?
|
19
|
+
@after << middleware.new(*args, &block)
|
20
|
+
else
|
21
|
+
@after << middleware.new(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(app)
|
26
|
+
@app = app
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(env)
|
30
|
+
@before.each {|c| c.call(env) }
|
31
|
+
|
32
|
+
response = @app.call(env)
|
33
|
+
|
34
|
+
@after.inject(response) {|r, c| c.call(r) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rack
|
2
|
+
|
3
|
+
# Rack::Config modifies the environment using the block given during
|
4
|
+
# initialization.
|
5
|
+
class Config
|
6
|
+
def initialize(app, &block)
|
7
|
+
@app = app
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@block.call(env)
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|