kissifer-rack-accept-only 0.1.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.
@@ -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