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 +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
|