devver-rack-contrib 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|