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 +59 -0
- data/Rakefile +26 -0
- data/lib/rack/content_type_validator/version.rb +12 -0
- data/lib/rack/content_type_validator.rb +66 -0
- data/rack-content_type_validator.gemspec +32 -0
- data/test/rack/content_type_validator_test.rb +108 -0
- data/test/test_helper.rb +15 -0
- metadata +105 -0
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
|
data/test/test_helper.rb
ADDED
@@ -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
|