rack-contrib 0.9.2 → 1.0.0
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.
Potentially problematic release.
This version of rack-contrib might be problematic. Click here for more details.
- data/AUTHORS +26 -0
- data/README.rdoc +7 -3
- data/Rakefile +2 -10
- data/lib/rack/contrib.rb +6 -0
- data/lib/rack/contrib/access.rb +85 -0
- data/lib/rack/contrib/cookies.rb +50 -0
- data/lib/rack/contrib/expectation_cascade.rb +32 -0
- data/lib/rack/contrib/host_meta.rb +47 -0
- data/lib/rack/contrib/jsonp.rb +1 -1
- data/lib/rack/contrib/mailexceptions.rb +1 -1
- data/lib/rack/contrib/profiler.rb +3 -1
- data/lib/rack/contrib/response_headers.rb +24 -0
- data/lib/rack/contrib/runtime.rb +31 -0
- data/lib/rack/contrib/simple_endpoint.rb +81 -0
- data/lib/rack/contrib/static_cache.rb +93 -0
- data/rack-contrib.gemspec +22 -5
- data/test/documents/test +1 -0
- data/test/spec_rack_access.rb +154 -0
- data/test/spec_rack_cookies.rb +56 -0
- data/test/spec_rack_expectation_cascade.rb +72 -0
- data/test/spec_rack_host_meta.rb +50 -0
- data/test/spec_rack_jsonp.rb +5 -5
- data/test/spec_rack_post_body_content_type_parser.rb +1 -1
- data/test/spec_rack_profiler.rb +1 -1
- data/test/spec_rack_response_headers.rb +35 -0
- data/test/spec_rack_runtime.rb +35 -0
- data/test/spec_rack_simple_endpoint.rb +95 -0
- data/test/spec_rack_static_cache.rb +91 -0
- data/test/statics/test +1 -0
- metadata +72 -25
- data/lib/rack/contrib/etag.rb +0 -20
- data/test/spec_rack_etag.rb +0 -23
@@ -0,0 +1,93 @@
|
|
1
|
+
module Rack
|
2
|
+
|
3
|
+
#
|
4
|
+
# The Rack::StaticCache middleware automatically adds, removes and modifies
|
5
|
+
# stuffs in response headers to facilitiate client and proxy caching for static files
|
6
|
+
# that minimizes http requests and improves overall load times for second time visitors.
|
7
|
+
#
|
8
|
+
# Once a static content is stored in a client/proxy the only way to enforce the browser
|
9
|
+
# to fetch the latest content and ignore the cache is to rename the static file.
|
10
|
+
#
|
11
|
+
# Alternatively, we can add a version number into the URL to the content to bypass
|
12
|
+
# the caches. Rack::StaticCache by default handles version numbers in the filename.
|
13
|
+
# As an example,
|
14
|
+
# http://yoursite.com/images/test-1.0.0.png and http://yoursite.com/images/test-2.0.0.png
|
15
|
+
# both reffers to the same image file http://yoursite.com/images/test.png
|
16
|
+
#
|
17
|
+
# Another way to bypass the cache is adding the version number in a field-value pair in the
|
18
|
+
# URL query string. As an example, http://yoursite.com/images/test.png?v=1.0.0
|
19
|
+
# In that case, set the option :versioning to false to avoid unneccessary regexp calculations.
|
20
|
+
#
|
21
|
+
# It's better to keep the current version number in some config file and use it in every static
|
22
|
+
# content's URL. So each time we modify our static contents, we just have to change the version
|
23
|
+
# number to enforce the browser to fetch the latest content.
|
24
|
+
#
|
25
|
+
# You can use Rack::Deflater along with Rack::StaticCache for further improvements in page loading time.
|
26
|
+
#
|
27
|
+
# Examples:
|
28
|
+
# use Rack::StaticCache, :urls => ["/images", "/css", "/js", "/documents*"], :root => "statics"
|
29
|
+
# will serve all requests beginning with /images, /csss or /js from the
|
30
|
+
# directory "statics/images", "statics/css", "statics/js".
|
31
|
+
# All the files from these directories will have modified headers to enable client/proxy caching,
|
32
|
+
# except the files from the directory "documents". Append a * (star) at the end of the pattern
|
33
|
+
# if you want to disable caching for any pattern . In that case, plain static contents will be served with
|
34
|
+
# default headers.
|
35
|
+
#
|
36
|
+
# use Rack::StaticCache, :urls => ["/images"], :duration => 2, :versioning => false
|
37
|
+
# will serve all requests begining with /images under the current directory (default for the option :root
|
38
|
+
# is current directory). All the contents served will have cache expiration duration set to 2 years in headers
|
39
|
+
# (default for :duration is 1 year), and StaticCache will not compute any versioning logics (default for
|
40
|
+
# :versioning is true)
|
41
|
+
#
|
42
|
+
|
43
|
+
|
44
|
+
class StaticCache
|
45
|
+
|
46
|
+
def initialize(app, options={})
|
47
|
+
@app = app
|
48
|
+
@urls = options[:urls]
|
49
|
+
@no_cache = {}
|
50
|
+
@urls.collect! do |url|
|
51
|
+
if url =~ /\*$/
|
52
|
+
url.sub!(/\*$/, '')
|
53
|
+
@no_cache[url] = 1
|
54
|
+
end
|
55
|
+
url
|
56
|
+
end
|
57
|
+
root = options[:root] || Dir.pwd
|
58
|
+
@file_server = Rack::File.new(root)
|
59
|
+
@cache_duration = options[:duration] || 1
|
60
|
+
@versioning_enabled = true
|
61
|
+
@versioning_enabled = options[:versioning] unless options[:versioning].nil?
|
62
|
+
@duration_in_seconds = self.duration_in_seconds
|
63
|
+
@duration_in_words = self.duration_in_words
|
64
|
+
end
|
65
|
+
|
66
|
+
def call(env)
|
67
|
+
path = env["PATH_INFO"]
|
68
|
+
url = @urls.detect{ |u| path.index(u) == 0 }
|
69
|
+
unless url.nil?
|
70
|
+
path.sub!(/-[\d.]+([.][a-zA-Z][\w]+)?$/, '\1') if @versioning_enabled
|
71
|
+
status, headers, body = @file_server.call(env)
|
72
|
+
if @no_cache[url].nil?
|
73
|
+
headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public"
|
74
|
+
headers['Expires'] = @duration_in_words
|
75
|
+
headers.delete 'Etag'
|
76
|
+
headers.delete 'Pragma'
|
77
|
+
headers.delete 'Last-Modified'
|
78
|
+
end
|
79
|
+
[status, headers, body]
|
80
|
+
else
|
81
|
+
@app.call(env)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def duration_in_words
|
86
|
+
(Time.now + self.duration_in_seconds).strftime '%a, %d %b %Y %H:%M:%S GMT'
|
87
|
+
end
|
88
|
+
|
89
|
+
def duration_in_seconds
|
90
|
+
60 * 60 * 24 * 365 * @cache_duration
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/rack-contrib.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'rack-contrib'
|
6
|
-
s.version = '0.
|
7
|
-
s.date = '
|
6
|
+
s.version = '1.0.0'
|
7
|
+
s.date = '2010-06-07'
|
8
8
|
|
9
9
|
s.description = "Contributed Rack Middleware and Utilities"
|
10
10
|
s.summary = "Contributed Rack Middleware and Utilities"
|
@@ -14,20 +14,24 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
# = MANIFEST =
|
16
16
|
s.files = %w[
|
17
|
+
AUTHORS
|
17
18
|
COPYING
|
18
19
|
README.rdoc
|
19
20
|
Rakefile
|
20
21
|
lib/rack/contrib.rb
|
21
22
|
lib/rack/contrib/accept_format.rb
|
23
|
+
lib/rack/contrib/access.rb
|
22
24
|
lib/rack/contrib/backstage.rb
|
23
25
|
lib/rack/contrib/bounce_favicon.rb
|
24
26
|
lib/rack/contrib/callbacks.rb
|
25
27
|
lib/rack/contrib/config.rb
|
28
|
+
lib/rack/contrib/cookies.rb
|
26
29
|
lib/rack/contrib/csshttprequest.rb
|
27
30
|
lib/rack/contrib/deflect.rb
|
28
|
-
lib/rack/contrib/etag.rb
|
29
31
|
lib/rack/contrib/evil.rb
|
32
|
+
lib/rack/contrib/expectation_cascade.rb
|
30
33
|
lib/rack/contrib/garbagecollector.rb
|
34
|
+
lib/rack/contrib/host_meta.rb
|
31
35
|
lib/rack/contrib/jsonp.rb
|
32
36
|
lib/rack/contrib/lighttpd_script_name_fix.rb
|
33
37
|
lib/rack/contrib/locale.rb
|
@@ -39,24 +43,32 @@ Gem::Specification.new do |s|
|
|
39
43
|
lib/rack/contrib/profiler.rb
|
40
44
|
lib/rack/contrib/relative_redirect.rb
|
41
45
|
lib/rack/contrib/response_cache.rb
|
46
|
+
lib/rack/contrib/response_headers.rb
|
42
47
|
lib/rack/contrib/route_exceptions.rb
|
48
|
+
lib/rack/contrib/runtime.rb
|
43
49
|
lib/rack/contrib/sendfile.rb
|
44
50
|
lib/rack/contrib/signals.rb
|
51
|
+
lib/rack/contrib/simple_endpoint.rb
|
52
|
+
lib/rack/contrib/static_cache.rb
|
45
53
|
lib/rack/contrib/time_zone.rb
|
46
54
|
rack-contrib.gemspec
|
47
55
|
test/404.html
|
48
56
|
test/Maintenance.html
|
57
|
+
test/documents/test
|
49
58
|
test/mail_settings.rb
|
50
59
|
test/spec_rack_accept_format.rb
|
60
|
+
test/spec_rack_access.rb
|
51
61
|
test/spec_rack_backstage.rb
|
52
62
|
test/spec_rack_callbacks.rb
|
53
63
|
test/spec_rack_config.rb
|
54
64
|
test/spec_rack_contrib.rb
|
65
|
+
test/spec_rack_cookies.rb
|
55
66
|
test/spec_rack_csshttprequest.rb
|
56
67
|
test/spec_rack_deflect.rb
|
57
|
-
test/spec_rack_etag.rb
|
58
68
|
test/spec_rack_evil.rb
|
69
|
+
test/spec_rack_expectation_cascade.rb
|
59
70
|
test/spec_rack_garbagecollector.rb
|
71
|
+
test/spec_rack_host_meta.rb
|
60
72
|
test/spec_rack_jsonp.rb
|
61
73
|
test/spec_rack_lighttpd_script_name_fix.rb
|
62
74
|
test/spec_rack_mailexceptions.rb
|
@@ -67,7 +79,12 @@ Gem::Specification.new do |s|
|
|
67
79
|
test/spec_rack_profiler.rb
|
68
80
|
test/spec_rack_relative_redirect.rb
|
69
81
|
test/spec_rack_response_cache.rb
|
82
|
+
test/spec_rack_response_headers.rb
|
83
|
+
test/spec_rack_runtime.rb
|
70
84
|
test/spec_rack_sendfile.rb
|
85
|
+
test/spec_rack_simple_endpoint.rb
|
86
|
+
test/spec_rack_static_cache.rb
|
87
|
+
test/statics/test
|
71
88
|
]
|
72
89
|
# = MANIFEST =
|
73
90
|
|
@@ -75,7 +92,7 @@ Gem::Specification.new do |s|
|
|
75
92
|
|
76
93
|
s.extra_rdoc_files = %w[README.rdoc COPYING]
|
77
94
|
s.add_dependency 'rack', '>= 0.9.1'
|
78
|
-
s.
|
95
|
+
s.add_development_dependency 'test-spec', '>= 0.9.0'
|
79
96
|
s.add_development_dependency 'tmail', '>= 1.2'
|
80
97
|
s.add_development_dependency 'json', '>= 1.1'
|
81
98
|
|
data/test/documents/test
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
nocache
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/access'
|
4
|
+
|
5
|
+
context "Rack::Access" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, 'hello'] }
|
9
|
+
@mock_addr_1 = '111.111.111.111'
|
10
|
+
@mock_addr_2 = '192.168.1.222'
|
11
|
+
@mock_addr_localhost = '127.0.0.1'
|
12
|
+
@mock_addr_range = '192.168.1.0/24'
|
13
|
+
end
|
14
|
+
|
15
|
+
def mock_env(remote_addr, path = '/')
|
16
|
+
Rack::MockRequest.env_for(path, { 'REMOTE_ADDR' => remote_addr })
|
17
|
+
end
|
18
|
+
|
19
|
+
def middleware(options = {})
|
20
|
+
Rack::Access.new(@app, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "default configuration should deny non-local requests" do
|
24
|
+
app = middleware
|
25
|
+
status, headers, body = app.call(mock_env(@mock_addr_1))
|
26
|
+
status.should.equal 403
|
27
|
+
body.should.equal ''
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "default configuration should allow requests from 127.0.0.1" do
|
31
|
+
app = middleware
|
32
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost))
|
33
|
+
status.should.equal 200
|
34
|
+
body.should.equal 'hello'
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "should allow remote addresses in allow_ipmasking" do
|
38
|
+
app = middleware('/' => [@mock_addr_1])
|
39
|
+
status, headers, body = app.call(mock_env(@mock_addr_1))
|
40
|
+
status.should.equal 200
|
41
|
+
body.should.equal 'hello'
|
42
|
+
end
|
43
|
+
|
44
|
+
specify "should deny remote addresses not in allow_ipmasks" do
|
45
|
+
app = middleware('/' => [@mock_addr_1])
|
46
|
+
status, headers, body = app.call(mock_env(@mock_addr_2))
|
47
|
+
status.should.equal 403
|
48
|
+
body.should.equal ''
|
49
|
+
end
|
50
|
+
|
51
|
+
specify "should allow remote addresses in allow_ipmasks range" do
|
52
|
+
app = middleware('/' => [@mock_addr_range])
|
53
|
+
status, headers, body = app.call(mock_env(@mock_addr_2))
|
54
|
+
status.should.equal 200
|
55
|
+
body.should.equal 'hello'
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "should deny remote addresses not in allow_ipmasks range" do
|
59
|
+
app = middleware('/' => [@mock_addr_range])
|
60
|
+
status, headers, body = app.call(mock_env(@mock_addr_1))
|
61
|
+
status.should.equal 403
|
62
|
+
body.should.equal ''
|
63
|
+
end
|
64
|
+
|
65
|
+
specify "should allow remote addresses in one of allow_ipmasking" do
|
66
|
+
app = middleware('/' => [@mock_addr_range, @mock_addr_localhost])
|
67
|
+
|
68
|
+
status, headers, body = app.call(mock_env(@mock_addr_2))
|
69
|
+
status.should.equal 200
|
70
|
+
body.should.equal 'hello'
|
71
|
+
|
72
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost))
|
73
|
+
status.should.equal 200
|
74
|
+
body.should.equal 'hello'
|
75
|
+
end
|
76
|
+
|
77
|
+
specify "should deny remote addresses not in one of allow_ipmasks" do
|
78
|
+
app = middleware('/' => [@mock_addr_range, @mock_addr_localhost])
|
79
|
+
status, headers, body = app.call(mock_env(@mock_addr_1))
|
80
|
+
status.should.equal 403
|
81
|
+
body.should.equal ''
|
82
|
+
end
|
83
|
+
|
84
|
+
specify "handles paths correctly" do
|
85
|
+
app = middleware({
|
86
|
+
'http://foo.org/bar' => [@mock_addr_localhost],
|
87
|
+
'/foo' => [@mock_addr_localhost],
|
88
|
+
'/foo/bar' => [@mock_addr_range, @mock_addr_localhost]
|
89
|
+
})
|
90
|
+
|
91
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/"))
|
92
|
+
status.should.equal 200
|
93
|
+
body.should.equal 'hello'
|
94
|
+
|
95
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/qux"))
|
96
|
+
status.should.equal 200
|
97
|
+
body.should.equal 'hello'
|
98
|
+
|
99
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/foo"))
|
100
|
+
status.should.equal 403
|
101
|
+
body.should.equal ''
|
102
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo"))
|
103
|
+
status.should.equal 200
|
104
|
+
body.should.equal 'hello'
|
105
|
+
|
106
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/"))
|
107
|
+
status.should.equal 403
|
108
|
+
body.should.equal ''
|
109
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/"))
|
110
|
+
status.should.equal 200
|
111
|
+
body.should.equal 'hello'
|
112
|
+
|
113
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/bar"))
|
114
|
+
status.should.equal 403
|
115
|
+
body.should.equal ''
|
116
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/bar"))
|
117
|
+
status.should.equal 200
|
118
|
+
body.should.equal 'hello'
|
119
|
+
status, headers, body = app.call(mock_env(@mock_addr_2, "/foo/bar"))
|
120
|
+
status.should.equal 200
|
121
|
+
body.should.equal 'hello'
|
122
|
+
|
123
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/bar/"))
|
124
|
+
status.should.equal 403
|
125
|
+
body.should.equal ''
|
126
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/bar/"))
|
127
|
+
status.should.equal 200
|
128
|
+
body.should.equal 'hello'
|
129
|
+
|
130
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/foo///bar//quux"))
|
131
|
+
status.should.equal 403
|
132
|
+
body.should.equal ''
|
133
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo///bar//quux"))
|
134
|
+
status.should.equal 200
|
135
|
+
body.should.equal 'hello'
|
136
|
+
|
137
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/quux"))
|
138
|
+
status.should.equal 403
|
139
|
+
body.should.equal ''
|
140
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/quux"))
|
141
|
+
status.should.equal 200
|
142
|
+
body.should.equal 'hello'
|
143
|
+
|
144
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/bar"))
|
145
|
+
status.should.equal 200
|
146
|
+
body.should.equal 'hello'
|
147
|
+
status, headers, body = app.call(mock_env(@mock_addr_1, "/bar").merge('HTTP_HOST' => 'foo.org'))
|
148
|
+
status.should.equal 403
|
149
|
+
body.should.equal ''
|
150
|
+
status, headers, body = app.call(mock_env(@mock_addr_localhost, "/bar").merge('HTTP_HOST' => 'foo.org'))
|
151
|
+
status.should.equal 200
|
152
|
+
body.should.equal 'hello'
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/cookies'
|
4
|
+
|
5
|
+
context "Rack::Cookies" do
|
6
|
+
specify "should be able to read received cookies" do
|
7
|
+
app = lambda { |env|
|
8
|
+
cookies = env['rack.cookies']
|
9
|
+
foo, quux = cookies[:foo], cookies['quux']
|
10
|
+
[200, {'Content-Type' => 'text/plain'}, ["foo: #{foo}, quux: #{quux}"]]
|
11
|
+
}
|
12
|
+
app = Rack::Cookies.new(app)
|
13
|
+
|
14
|
+
response = Rack::MockRequest.new(app).get('/', 'HTTP_COOKIE' => 'foo=bar;quux=h&m')
|
15
|
+
response.body.should.equal('foo: bar, quux: h&m')
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "should be able to set new cookies" do
|
19
|
+
app = lambda { |env|
|
20
|
+
cookies = env['rack.cookies']
|
21
|
+
cookies[:foo] = 'bar'
|
22
|
+
cookies['quux'] = 'h&m'
|
23
|
+
[200, {'Content-Type' => 'text/plain'}, []]
|
24
|
+
}
|
25
|
+
app = Rack::Cookies.new(app)
|
26
|
+
|
27
|
+
response = Rack::MockRequest.new(app).get('/')
|
28
|
+
response.headers['Set-Cookie'].should.equal("quux=h%26m; path=/\nfoo=bar; path=/")
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should be able to set cookie with options" do
|
32
|
+
app = lambda { |env|
|
33
|
+
cookies = env['rack.cookies']
|
34
|
+
cookies['foo'] = { :value => 'bar', :path => '/login', :secure => true }
|
35
|
+
[200, {'Content-Type' => 'text/plain'}, []]
|
36
|
+
}
|
37
|
+
app = Rack::Cookies.new(app)
|
38
|
+
|
39
|
+
response = Rack::MockRequest.new(app).get('/')
|
40
|
+
response.headers['Set-Cookie'].should.equal('foo=bar; path=/login; secure')
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "should be able to delete received cookies" do
|
44
|
+
app = lambda { |env|
|
45
|
+
cookies = env['rack.cookies']
|
46
|
+
cookies.delete(:foo)
|
47
|
+
foo, quux = cookies['foo'], cookies[:quux]
|
48
|
+
[200, {'Content-Type' => 'text/plain'}, ["foo: #{foo}, quux: #{quux}"]]
|
49
|
+
}
|
50
|
+
app = Rack::Cookies.new(app)
|
51
|
+
|
52
|
+
response = Rack::MockRequest.new(app).get('/', 'HTTP_COOKIE' => 'foo=bar;quux=h&m')
|
53
|
+
response.body.should.equal('foo: , quux: h&m')
|
54
|
+
response.headers['Set-Cookie'].should.equal('foo=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT')
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/expectation_cascade'
|
4
|
+
|
5
|
+
context "Rack::ExpectationCascade" do
|
6
|
+
specify "with no apps returns a 404 if no expectation header was set" do
|
7
|
+
app = Rack::ExpectationCascade.new
|
8
|
+
env = {}
|
9
|
+
response = app.call(env)
|
10
|
+
response[0].should.equal 404
|
11
|
+
env.should.equal({})
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "with no apps returns a 417 if expectation header was set" do
|
15
|
+
app = Rack::ExpectationCascade.new
|
16
|
+
env = {"Expect" => "100-continue"}
|
17
|
+
response = app.call(env)
|
18
|
+
response[0].should.equal 417
|
19
|
+
env.should.equal({"Expect" => "100-continue"})
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "returns first successful response" do
|
23
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
24
|
+
cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
25
|
+
cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] }
|
26
|
+
end
|
27
|
+
response = app.call({})
|
28
|
+
response[0].should.equal 200
|
29
|
+
response[2][0].should.equal "OK"
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "expectation is set if it has not been already" do
|
33
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
34
|
+
cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Expect: #{env["Expect"]}"]] }
|
35
|
+
end
|
36
|
+
response = app.call({})
|
37
|
+
response[0].should.equal 200
|
38
|
+
response[2][0].should.equal "Expect: 100-continue"
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "returns a 404 if no apps where matched and no expectation header was set" do
|
42
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
43
|
+
cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
44
|
+
end
|
45
|
+
response = app.call({})
|
46
|
+
response[0].should.equal 404
|
47
|
+
response[2][0].should.equal nil
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "returns a 417 if no apps where matched and a expectation header was set" do
|
51
|
+
app = Rack::ExpectationCascade.new do |cascade|
|
52
|
+
cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
53
|
+
end
|
54
|
+
response = app.call({"Expect" => "100-continue"})
|
55
|
+
response[0].should.equal 417
|
56
|
+
response[2][0].should.equal nil
|
57
|
+
end
|
58
|
+
|
59
|
+
specify "nests expectation cascades" do
|
60
|
+
app = Rack::ExpectationCascade.new do |c1|
|
61
|
+
c1 << Rack::ExpectationCascade.new do |c2|
|
62
|
+
c2 << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] }
|
63
|
+
end
|
64
|
+
c1 << Rack::ExpectationCascade.new do |c2|
|
65
|
+
c2 << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
response = app.call({})
|
69
|
+
response[0].should.equal 200
|
70
|
+
response[2][0].should.equal "OK"
|
71
|
+
end
|
72
|
+
end
|