kissifer-rack-accept-only 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 kissifer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ = rack-accept-only
2
+
3
+ Description goes here.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 kissifer. See LICENSE for details.
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rack-accept-only"
8
+ gem.summary = %Q{Allows checking of reply content type with request accept types. Also allows contracting of downstream apps to supplying a single type.}
9
+ gem.email = "tierneydrchris@gmail.com"
10
+ gem.homepage = "http://github.com/kissifer/rack-accept-only"
11
+ gem.authors = ["kissifer"]
12
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
13
+ end
14
+
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ require 'spec/rake/spectask'
20
+ Spec::Rake::SpecTask.new(:spec) do |spec|
21
+ spec.libs << 'lib' << 'spec'
22
+ spec.spec_files = FileList['spec/**/*_spec.rb']
23
+ spec.spec_opts = ['--options spec/spec.opts']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+
33
+ task :default => :spec
34
+
35
+ require 'rake/rdoctask'
36
+ Rake::RDocTask.new do |rdoc|
37
+ if File.exist?('VERSION.yml')
38
+ config = YAML.load(File.read('VERSION.yml'))
39
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
40
+ else
41
+ version = ""
42
+ end
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "rack-accept-only #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
49
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,60 @@
1
+ module Rack
2
+ class AcceptOnly
3
+ def initialize(app, permitted_type = nil)
4
+ raise "Keep the permitted content-type simple please" if permitted_type && permitted_type.match(%r{[*; ]})
5
+ @app = app
6
+ @permitted_type = permitted_type
7
+ end
8
+
9
+ def call(env)
10
+ acceptable_types = get_acceptable_types(env)
11
+ return @app.call(env) unless !acceptable_types.empty? || @permitted_type
12
+
13
+ if !match_against_types?(acceptable_types, @permitted_type)
14
+ return unacceptable(@permitted_type)
15
+ end
16
+
17
+ reply = @app.call(env)
18
+ reply_type = reply[1]['Content-Type']
19
+
20
+ return server_error unless reply_type
21
+
22
+ if @permitted_type && reply_type != @permitted_type
23
+ reply = server_error
24
+ end
25
+
26
+ if !match_against_types?(acceptable_types, reply_type)
27
+ reply = unacceptable(reply_type)
28
+ end
29
+
30
+ reply
31
+ end
32
+
33
+ def get_acceptable_types(env)
34
+ return [] unless env['HTTP_ACCEPT']
35
+
36
+ acceptable_types = env['HTTP_ACCEPT'].split(",")
37
+ acceptable_types.collect do |type|
38
+ temp = type.strip.split(";")[0].gsub("*", "(.+)")
39
+ Regexp.new("^#{temp}$")
40
+ end
41
+ end
42
+
43
+ def match_against_types?(types, string)
44
+ return true unless !types.empty? && string
45
+
46
+ types.each do |type|
47
+ return true if string.match(type)
48
+ end
49
+ false
50
+ end
51
+
52
+ def unacceptable(supplyable_type)
53
+ [406, {'Content-Type' => supplyable_type, "Content-Length" => "0"}, [""]]
54
+ end
55
+
56
+ def server_error
57
+ [500, {"Content-Length" => "0"}, [""]]
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,150 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Rack::AcceptOnly" do
4
+ before(:each) do
5
+ @app = lambda{ |env| [111, {'foo' => 'bar', 'Content-Type' => @return_type}, ["Something"]] }
6
+ end
7
+
8
+ context "if not supplied a permitted content type" do
9
+ before(:each) do
10
+ @accept_only = Rack::AcceptOnly.new(@app)
11
+ end
12
+
13
+ context "and the Accept header is not present in the request" do
14
+ before(:each) do
15
+ @request = Rack::MockRequest.env_for("/")
16
+ end
17
+
18
+ it "should not modify the downstream response" do
19
+ @return_type = "foo"
20
+ @accept_only.call(@request).should == @app.call(@request)
21
+ end
22
+ end
23
+
24
+ context "and the Accept header is present in the request" do
25
+ before(:each) do
26
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "acceptable")
27
+ end
28
+
29
+ context "and the Content-Type of the downstream reply is not acceptable" do
30
+ it "should reject the request" do
31
+ @return_type = "not_acceptable"
32
+ @accept_only.call(@request)[0].should == 406
33
+ end
34
+
35
+ it "should include the downstream Content-Type in the reply" do
36
+ @return_type = "not_acceptable"
37
+ @accept_only.call(@request)[1]['Content-Type'].should == "not_acceptable"
38
+ end
39
+ end
40
+
41
+ context "and the Content-Type of the downstream reply is acceptable" do
42
+ it "should not modify the downstream response" do
43
+ @return_type = "acceptable"
44
+ @accept_only.call(@request).should == @app.call(@request)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ context "if supplied a (single) permitted content type" do
51
+ before(:each) do
52
+ @accept_only = Rack::AcceptOnly.new(@app, "permitted")
53
+ end
54
+
55
+ context "and the Accept header is not present in the request" do
56
+ before(:each) do
57
+ @request = Rack::MockRequest.env_for("/")
58
+ end
59
+
60
+ it "should signal server error if the Content-Type of the downstream reply is not permitted" do
61
+ @return_type = "not_permitted"
62
+ @accept_only.call(@request)[0].should == 500
63
+ end
64
+
65
+ it "should not modify the downstream response if the Content-Type of the downstream reply is permitted" do
66
+ @return_type = "permitted"
67
+ @accept_only.call(@request).should == @app.call(@request)
68
+ end
69
+ end
70
+
71
+ context "and the Accept header is present in the request" do
72
+ context "if the acceptable types do not include the permitted type" do
73
+ before(:each) do
74
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "not_permitted, also_not_permitted")
75
+ @return_type = "shouldnt_see_this"
76
+ end
77
+
78
+ it "should reject the request" do
79
+ @accept_only.call(@request)[0].should == 406
80
+ end
81
+
82
+ it "should include the permitted type in the reply" do
83
+ @accept_only.call(@request)[1]['Content-Type'].should == "permitted"
84
+ end
85
+
86
+ it "should not call downstream middleware" do
87
+ @app.should_not_receive(:call)
88
+ @accept_only.call(@request)
89
+ end
90
+ end
91
+
92
+ context "if the acceptable types do include the permitted type" do
93
+ before(:each) do
94
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "not_permitted, permitted")
95
+ end
96
+
97
+ it "should signal server error if the Content-Type of the downstream reply is not permitted" do
98
+ @return_type = "not_permitted"
99
+ @accept_only.call(@request)[0].should == 500
100
+ end
101
+
102
+ it "should not modify the downstream response if the Content-Type of the downstream reply is permitted" do
103
+ @return_type = "permitted"
104
+ @accept_only.call(@request).should == @app.call(@request)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ it "should reject non-simple permitted types" do
111
+ lambda{Rack::AcceptOnly.new(@app, "audio/*")}.should raise_error
112
+ lambda{Rack::AcceptOnly.new(@app, "*/*")}.should raise_error
113
+ lambda{Rack::AcceptOnly.new(@app, "image/gif; q=0.8")}.should raise_error
114
+ end
115
+
116
+ it "should handle more complex accept headers in the request when given a permitted type" do
117
+ @accept_only = Rack::AcceptOnly.new(@app, "image/jpeg")
118
+ @return_type = "image/jpeg"
119
+
120
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "image/gif, image/*; q=0.8")
121
+ @accept_only.call(@request).should == @app.call(@request)
122
+
123
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "*/*")
124
+ @accept_only.call(@request).should == @app.call(@request)
125
+ end
126
+
127
+ it "should handle more complex accept headers in the request when not given a permitted type" do
128
+ @accept_only = Rack::AcceptOnly.new(@app)
129
+ @return_type = "image/jpeg"
130
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "image/gif, image/*; q=0.8")
131
+ @accept_only.call(@request).should == @app.call(@request)
132
+
133
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "*/*")
134
+ @accept_only.call(@request).should == @app.call(@request)
135
+ end
136
+
137
+ it "should signal server error if the reply has no content-type when given a permitted type" do
138
+ @app = lambda{ |env| [111, {'foo' => 'bar'}, ["Something"]] }
139
+ @accept_only = Rack::AcceptOnly.new(@app, "image/jpeg")
140
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "image/jpeg")
141
+ @accept_only.call(@request)[0].should == 500
142
+ end
143
+
144
+ it "should signal server error if the reply has no content-type when not given a permitted type" do
145
+ @app = lambda{ |env| [111, {'foo' => 'bar'}, ["Something"]] }
146
+ @accept_only = Rack::AcceptOnly.new(@app)
147
+ @request = Rack::MockRequest.env_for("/", "HTTP_ACCEPT" => "image/jpeg")
148
+ @accept_only.call(@request)[0].should == 500
149
+ end
150
+ end
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format nested
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'rack/mock'
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ require 'rack/accept-only'
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kissifer-rack-accept-only
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kissifer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: tierneydrchris@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .document
27
+ - .gitignore
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - lib/rack/accept-only.rb
33
+ - spec/rack-accept-only_spec.rb
34
+ - spec/spec.opts
35
+ - spec/spec_helper.rb
36
+ has_rdoc: false
37
+ homepage: http://github.com/kissifer/rack-accept-only
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Allows checking of reply content type with request accept types. Also allows contracting of downstream apps to supplying a single type.
62
+ test_files:
63
+ - spec/rack-accept-only_spec.rb
64
+ - spec/spec_helper.rb