rack-content_type_validator 0.0.1

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