rack-content_type_validator 0.0.1

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/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Rack Content-Type Validator middleware
2
+
3
+ Rack::ContentTypeValidator it's a **rack middleware** to validate the header **Content-Type** of requests.
4
+
5
+ ## What does it do?
6
+
7
+ When a ruby webserver receives a request, it validates the Content-Type. Whenever validation fails, the middleware sends a response to client with 415 (Unsupported Media Type) HTTP error.
8
+
9
+ ## Usage
10
+
11
+ ### Rails apps
12
+
13
+ In your Gemfile:
14
+
15
+ gem 'rack-content_type_validator'
16
+ In your environment.rb:
17
+
18
+ require 'rack/content_type_validator'
19
+ config.middleware.use Rack::ContentTypeValidator
20
+
21
+ ### Non-Rails apps
22
+
23
+ Just 'use Rack::ContentTypeValidator' as any other middleware.
24
+
25
+ ## Configuration Examples
26
+
27
+ #### POST requests at the path '/posts' should be 'application/json':
28
+ config.middleware.use Rack::ContentTypeValidator, :post, '/posts', {:mime_type => "application/json"}
29
+
30
+ #### POST and PUT requests at the path '/users' should be 'application/json':
31
+ config.middleware.use Rack::ContentTypeValidator, [:post, :put], '/users', {:mime_type => "application/json"}
32
+
33
+ #### POST requests at the path '/pages' should be 'application/xml' with charset UTF-8:
34
+ config.middleware.use Rack::ContentTypeValidator, :post, '/pages', {:mime_type => "application/xml", :charset => "UTF-8"}
35
+
36
+ #### PUT requests at the path '/pages' should have charset ISO-8859-1:
37
+ config.middleware.use Rack::ContentTypeValidator, :put, '/pages', {:charset => "ISO-8859-1"}
38
+
39
+ #### You can pass a Regexp on path:
40
+ config.middleware.use Rack::ContentTypeValidator, :put, /\/pages\/.+/, {:charset => "ISO-8859-1"}
41
+
42
+ #### You can configure more the one:
43
+ config.middleware.use Rack::ContentTypeValidator, :post, '/posts', {:mime_type => "application/json"}
44
+ config.middleware.use Rack::ContentTypeValidator, :put, /\/posts\/.+/, {:mime_type => "application/json"}
45
+
46
+ ## Report bugs and suggestions
47
+
48
+ * [Issue Tracker](http://github.com/abril/rack-content_type_validator/issues)
49
+
50
+ ## Authors
51
+
52
+ * [Lucas Fais](http://github.com/lucasfais)
53
+ * [Marcelo Manzan](http://github.com/kawamanza)
54
+
55
+ ## References
56
+
57
+ * [RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17)
58
+ * [Rails on Rack](http://guides.rubyonrails.org/rails_on_rack.html)
59
+
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ desc "Build the gem"
11
+ task :build do
12
+ opers = Dir.glob('*.gem')
13
+ opers = ["rm #{ opers.join(' ') }"] unless opers.empty?
14
+ opers << ["gem build rack-content_type_validator.gemspec"]
15
+ sh opers.join(" && ")
16
+ end
17
+
18
+ desc "Build and install the gem, removing old installation"
19
+ task :install => :build do
20
+ gem = Dir.glob('*.gem').first
21
+ if gem.nil?
22
+ puts "could not install the gem"
23
+ else
24
+ sh "gem uninstall rack-content_type_validator; gem install #{ gem }"
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Rack
2
+ class ContentTypeValidator
3
+ version = nil
4
+ version = $1 if ::File.expand_path('../../..', __FILE__) =~ /\/rack-content_type_validator-([\w\.\-]+)/
5
+ if version.nil? && ::File.exists?(::File.expand_path('../../../../.git', __FILE__))
6
+ require "step-up"
7
+ version = StepUp::Driver::Git.last_version
8
+ end
9
+ version = "0.0.0" if version.nil?
10
+ VERSION = version.gsub(/^v?([^\+]+)\+?\d*$/, '\1')
11
+ end
12
+ end
@@ -0,0 +1,66 @@
1
+ require 'json'
2
+
3
+ module Rack
4
+ class ContentTypeValidator
5
+ autoload :VERSION, 'rack/content_type_validator/version.rb'
6
+
7
+ def initialize(app, methods, path, expected_params)
8
+ @app = app
9
+ @methods = handle_methods(methods)
10
+ @path = path
11
+ @expected_params = expected_params
12
+ end
13
+
14
+ def call(env)
15
+ if match_path?(env['PATH_INFO']) && match_method?(env['REQUEST_METHOD'])
16
+ content_type = env['CONTENT_TYPE']
17
+
18
+ unless valid_content_type?(content_type) && valid_charset?(content_type)
19
+ response = Response.new
20
+ response.status = 415
21
+ response.write(error_message)
22
+ response.headers["Content-Type"] = 'text/plain'
23
+
24
+ return response.finish
25
+ end
26
+ end
27
+
28
+ @app.call(env)
29
+ end
30
+
31
+ private
32
+ def handle_methods(methods)
33
+ methods = methods.is_a?(Array) ? methods : [methods]
34
+ methods.collect {|m| m.to_s.upcase}
35
+ end
36
+
37
+ def match_path?(path)
38
+ @path.is_a?(Regexp) ? @path.match(path.to_s) : @path == path.to_s
39
+ end
40
+
41
+ def match_method?(method)
42
+ @methods.empty? || @methods.include?(method)
43
+ end
44
+
45
+ def valid_content_type?(content_type)
46
+ return true unless @expected_params[:mime_type]
47
+
48
+ type_and_subtype = content_type ? content_type[/^([\w\/]+)\b/, 1] : nil
49
+ type_and_subtype == @expected_params[:mime_type]
50
+ end
51
+
52
+ def valid_charset?(content_type)
53
+ return true unless @expected_params[:charset]
54
+
55
+ charset = content_type ? content_type[/\bcharset=([^;\s]*)/, 1] : nil
56
+ charset.gsub!('"', '') if charset
57
+ charset == @expected_params[:charset]
58
+ end
59
+
60
+ def error_message
61
+ charset = @expected_params[:charset] ? "; charset=#{@expected_params[:charset]}" : ""
62
+ message = "Unsupported Media Type. It should be like this: "
63
+ message += "\"#{@expected_params[:mime_type]}#{charset}\""
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ require 'rack/content_type_validator'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "rack-content_type_validator"
9
+ s.version = Rack::ContentTypeValidator::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Lucas Fais"]
12
+ s.email = ["lucasfais@gmail.com"]
13
+ s.homepage = "http://github.com/abril/rack-content_type_validator"
14
+ s.summary = %q{Makes easy to handle mutipart/related requests.}
15
+ s.description = %q{It's a rack middleware to parse multipart/related requests and rebuild a simple/merged parameters hash.}
16
+
17
+ s.required_rubygems_version = ">= 1.3.6"
18
+ s.rubyforge_project = "rack-content_type_validator"
19
+
20
+ s.add_dependency "rack", '>= 1.0'
21
+
22
+ s.add_development_dependency "step-up"
23
+
24
+ excepts = %w[
25
+ .gitignore
26
+ rack-multipart_related.gemspec
27
+ ]
28
+ tests = `git ls-files -- {test,spec,features}/*`.split("\n")
29
+ s.files = `git ls-files`.split("\n") - excepts - tests
30
+ s.test_files = tests
31
+ s.require_paths = ["lib"]
32
+ end
@@ -0,0 +1,108 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper.rb')
2
+
3
+ class ContentTypeValidatorTest < Test::Unit::TestCase
4
+
5
+ App = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env)] }
6
+
7
+ def build_env(method, path, content_type)
8
+ env = Rack::MockRequest.env_for(path, {:method => method})
9
+ env["CONTENT_TYPE"] = content_type
10
+ env
11
+ end
12
+
13
+ def build_middleware(methods, path, expected_params)
14
+ Rack::ContentTypeValidator.new(App, methods, path, expected_params)
15
+ end
16
+
17
+ def test_request_in_other_paths
18
+ expected_params = {:mime_type => "application/xml"}
19
+ middleware = build_middleware(:put, "/other/path", expected_params)
20
+
21
+ env = build_env("PUT", "/request_path", 'application/json')
22
+ status, headers, body = middleware.call(env)
23
+
24
+ assert_equal status, 200
25
+ end
26
+
27
+ def test_incorrect_content_type
28
+ path = "/users"
29
+
30
+ expected_params = {:mime_type => "application/xml"}
31
+ middleware = build_middleware([:put, :post], path, expected_params)
32
+
33
+ env = build_env("PUT", path, 'application/json')
34
+ status, headers, body = middleware.call(env)
35
+
36
+ assert_equal status, 415
37
+ end
38
+
39
+ def test_correct_content_type
40
+ path = "/posts"
41
+
42
+ expected_params = {:mime_type => "application/json"}
43
+ middleware = build_middleware(:post, path, expected_params)
44
+
45
+ env = build_env("POST", path, 'application/json')
46
+ status, headers, body = middleware.call(env)
47
+
48
+ assert_equal status, 200
49
+ end
50
+
51
+ def test_correct_content_type_and_incorrect_charset
52
+ path = "/comments"
53
+
54
+ expected_params = {:mime_type => "text/plain", :charset => "iso-8859-x"}
55
+ middleware = build_middleware(:post, path, expected_params)
56
+
57
+ env = build_env("POST", path, 'text/plain; charset=us-ascii')
58
+ status, headers, body = middleware.call(env)
59
+
60
+ assert_equal status, 415
61
+ end
62
+
63
+ def test_correct_content_type_and_charset
64
+ path = "/posts"
65
+
66
+ expected_params = {:mime_type => "application/json", :charset => "UTF-8"}
67
+ middleware = build_middleware("POST", path, expected_params)
68
+
69
+ env = build_env("POST", path, 'application/json; charset=UTF-8')
70
+ status, headers, body = middleware.call(env)
71
+
72
+ assert_equal status, 200
73
+ end
74
+
75
+ def test_correct_content_type_and_charset_not_given
76
+ path = "/posts"
77
+
78
+ expected_params = {:mime_type => "application/json"}
79
+ middleware = build_middleware([:post], path, expected_params)
80
+
81
+ env = build_env("POST", path, 'application/json; charset=UTF-8')
82
+ status, headers, body = middleware.call(env)
83
+
84
+ assert_equal status, 200
85
+ end
86
+
87
+ def test_regexp_on_path
88
+ expected_params = {:mime_type => "application/json", :charset => "UTF-8"}
89
+ middleware = build_middleware(:post, /comments/, expected_params)
90
+
91
+ env = build_env("POST", "/content/1/comments", 'application/json; charset=UTF-8')
92
+ status, headers, body = middleware.call(env)
93
+
94
+ assert_equal status, 200
95
+ end
96
+
97
+ def test_charset_with_quotes
98
+ expected_params = {:charset => "UTF-8"}
99
+ middleware = build_middleware(:post, /comments/, expected_params)
100
+
101
+ env = build_env("POST", "/content/1/comments", 'application/xml; charset="UTF-8"')
102
+ status, headers, body = middleware.call(env)
103
+
104
+ assert_equal status, 200
105
+
106
+ end
107
+
108
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'rack/mock'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'rack/content_type_validator'
8
+
9
+ begin
10
+ require "redgreen"
11
+ require "ruby-debug"
12
+ rescue LoadError
13
+ # Not required gems.
14
+ end
15
+
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-content_type_validator
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Lucas Fais
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-07 00:00:00 -02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rack
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 1
32
+ - 0
33
+ version: "1.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: step-up
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ description: It's a rack middleware to parse multipart/related requests and rebuild a simple/merged parameters hash.
51
+ email:
52
+ - lucasfais@gmail.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - README.md
61
+ - Rakefile
62
+ - lib/rack/content_type_validator.rb
63
+ - lib/rack/content_type_validator/version.rb
64
+ - rack-content_type_validator.gemspec
65
+ - test/rack/content_type_validator_test.rb
66
+ - test/test_helper.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/abril/rack-content_type_validator
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options: []
73
+
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 23
91
+ segments:
92
+ - 1
93
+ - 3
94
+ - 6
95
+ version: 1.3.6
96
+ requirements: []
97
+
98
+ rubyforge_project: rack-content_type_validator
99
+ rubygems_version: 1.3.7
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: Makes easy to handle mutipart/related requests.
103
+ test_files:
104
+ - test/rack/content_type_validator_test.rb
105
+ - test/test_helper.rb