rack_respond_to_malformed_formats 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ *.gem
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem "nokogiri", "1.4.4"
6
+ gem "json"
7
+
8
+ group :development do
9
+ gem "rspec", "2.1"
10
+ gem "wirble"
11
+ gem "gemcutter"
12
+ gem "rack"
13
+ end
14
+
15
+ group :test do
16
+ gem "rspec", "2.1"
17
+ end
18
+
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Case Commons, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,39 @@
1
+ = Rack Middleware - respond_to_malformed_formats
2
+
3
+ * http://github.com/casecommons/rack_respond_to_malformed_formats/
4
+
5
+ == DESCRIPTION
6
+
7
+ Return graceful and well formatted errors in the same format of the malformed input for XML, JSON and YAML.
8
+
9
+ Rails will return HTML 500 errors when you send it badly formatted XML or JSON or YAML. It would be nicer if an XML or JSON client actually received the response back in the same format and also received an HTTP 400 error code which is reserved for malformed syntax errors.
10
+
11
+ == INSTALL
12
+
13
+ Add rack_respond_to_malformed_formats to your Gemfile
14
+
15
+ gem 'rack_respond_to_malformed_formats'
16
+
17
+ Then run:
18
+
19
+ $ bundle install
20
+
21
+ === Rails 3
22
+
23
+ Create an initializer file in config/initializers/respond_to_malformed_formats.rb
24
+
25
+ Rails.application.middleware.with_options :logger => Rails.logger do |m|
26
+ m.insert_before ActionDispatch::ParamsParser, Rack::RespondToMalformedFormats
27
+ end
28
+
29
+ === Rails 2
30
+
31
+ TODO
32
+
33
+ == REQUIREMENTS
34
+
35
+ * Rack
36
+
37
+ == LICENSE
38
+
39
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+ require 'rake'
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+
7
+ require 'bundler'
8
+ Bundler::GemHelper.install_tasks
9
+
10
+ task :default => :spec
11
+
12
+ desc "Run all specs in spec directory (excluding plugin specs)"
13
+ RSpec::Core::RakeTask.new(:spec)
14
+
@@ -0,0 +1,71 @@
1
+ require "rack/mime"
2
+ require "json"
3
+ require "nokogiri"
4
+ require "yaml"
5
+
6
+ module Rack
7
+ class RespondToMalformedFormats
8
+ VERSION = "0.0.1"
9
+
10
+ def initialize(app, options = {})
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ if malformed_response = respond_to_malformed_parameters(env)
16
+ input = env["rack.input"]
17
+ input.rewind
18
+ logger.error "Error occurred while parsing request.\nInput:\n\n#{input.read}" if logger
19
+ malformed_response
20
+ else
21
+ @app.call(env)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def respond_to_malformed_parameters(env)
28
+ request = Request.new(env)
29
+
30
+ case (env["HTTP_X_POST_DATA_FORMAT"] || request.content_type).to_s.downcase
31
+ when /xml/
32
+ parse_xml(request.body.read)
33
+ when /yaml/
34
+ parse_yaml(request.body.read)
35
+ when /json/
36
+ parse_json(request.body.read)
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ def parse_json(body)
43
+ JSON.parse(body)
44
+ false
45
+ rescue JSON::ParserError => e
46
+ [400, {"Content-Type" => "application/json"}, [{:error => e.to_s}.to_json]]
47
+ end
48
+
49
+ def parse_xml(body)
50
+ Nokogiri::XML(body) { |config| config.strict }
51
+ false
52
+ rescue Nokogiri::XML::SyntaxError => e
53
+ response = Nokogiri::XML::Builder.new do
54
+ error e.to_s
55
+ end
56
+
57
+ [400, {"Content-Type" => "application/xml"}, [response.to_xml]]
58
+ end
59
+
60
+ def parse_yaml(body)
61
+ YAML.load(body)
62
+ false
63
+ rescue ArgumentError => e
64
+ [400, {"Content-Type"=> "application/x-yaml"}, [e.to_s]]
65
+ end
66
+
67
+ def logger
68
+ defined?(Rails.logger) ? Rails.logger : nil
69
+ end
70
+ end
71
+ end
@@ -0,0 +1 @@
1
+ require "rack/respond_to_malformed_formats"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require File.join(File.dirname(__FILE__), "lib/rack/respond_to_malformed_formats")
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack_respond_to_malformed_formats"
7
+ s.version = Rack::RespondToMalformedFormats::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Case Commons, LLC"]
10
+ s.email = ["casecommons-dev@googlegroups.com"]
11
+ s.homepage = "https://github.com/Casecommons/rack_respond_to_malformed_formats"
12
+ s.license = "MIT"
13
+ s.add_dependency('activesupport', '>= 3.0.0')
14
+ s.summary = %q{Intercept malformed XML and JSON requests and return XML and JSON 400 responses not HTML 500}
15
+ s.description = %q{Rails currently returns HTML 500 responses when sent unparsable XML and JSON and gives no way to rescue and handle it}
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,93 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
+
3
+ describe Rack::RespondToMalformedFormats do
4
+ context "when the format is HTML" do
5
+ it "should do nothing" do
6
+ test_input = '<html></html>'
7
+ app = lambda { |env| [200, {"CONTEN_TYPE" => "text/html"}, test_input] }
8
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "test/html", :input => test_input)
9
+ body = Rack::RespondToMalformedFormats.new(app).call(request).last
10
+
11
+ body.should == test_input
12
+ end
13
+ end
14
+
15
+ context "when the format is JSON" do
16
+ context "and it is valid" do
17
+ it "should do nothing" do
18
+ test_input = '{"foo":"bar"}'
19
+ app = lambda { |env| [200, {"CONTENT_TYPE" => "application/json"}, test_input] }
20
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "application/json", :input => test_input)
21
+ body = Rack::RespondToMalformedFormats.new(app).call(request).last
22
+
23
+ body.should == test_input
24
+ end
25
+ end
26
+
27
+ context "and it is invalid" do
28
+ it "should return a 400 with a message" do
29
+ test_input = '{"foo":'
30
+ app = lambda { |env| [200, {"CONTENT_TYPE" => "application/json"}, test_input] }
31
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "application/json", :input => test_input)
32
+ response = Rack::RespondToMalformedFormats.new(app).call(request)
33
+
34
+ response[0].should == 400
35
+ response[1]["Content-Type"].should == "application/json"
36
+ response[2].first.should =~ /.*error.*unexpected token.*/
37
+ end
38
+ end
39
+ end
40
+
41
+ context "when the format is XML" do
42
+ context "and it is valid" do
43
+ it "should do nothing" do
44
+ test_input = '<?xml version="1.0"?><foo>bar</foo>'
45
+ app = lambda { |env| [200, {"CONTENT_TYPE" => "application/xml"}, test_input] }
46
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "application/xml", :input => test_input)
47
+ body = Rack::RespondToMalformedFormats.new(app).call(request).last
48
+
49
+ body.should == test_input
50
+ end
51
+ end
52
+
53
+ context "and it is invalid" do
54
+ it "should return a 400 with a message" do
55
+ test_input = '<ml><foo>bar'
56
+ app = lambda { |env| [200, {"CONTENT_TYPE" => "application/xml"}, test_input] }
57
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "application/xml", :input => test_input)
58
+ response = Rack::RespondToMalformedFormats.new(app).call(request)
59
+
60
+ response[0].should == 400
61
+ response[1]["Content-Type"].should == "application/xml"
62
+ response[2].first.should =~ /.*<error>Premature.*<\/error>.*/
63
+ end
64
+ end
65
+ end
66
+
67
+ context "when the format is YAML" do
68
+ context "and it is valid" do
69
+ it "should do nothing" do
70
+ test_input = "--- \nfoo: bar\n"
71
+ app = lambda { |env| [200, {"CONTENT_TYPE" => "application/x-yaml"}, test_input] }
72
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "application/x-yaml", :input => test_input)
73
+ body = Rack::RespondToMalformedFormats.new(app).call(request).last
74
+
75
+ body.should == test_input
76
+ end
77
+ end
78
+
79
+ context "and it is invalid" do
80
+ it "should return a 400 with a message" do
81
+ test_input = "--- what:\nawagasd"
82
+ app = lambda { |env| [200, {"CONTENT_TYPE" => "application/x-yaml"}, test_input] }
83
+ request = Rack::MockRequest.env_for("/", :params => "", "CONTENT_TYPE" => "application/x-yaml", :input => test_input)
84
+ response = Rack::RespondToMalformedFormats.new(app).call(request)
85
+
86
+ response[0].should == 400
87
+ response[1]["Content-Type"].should == "application/x-yaml"
88
+ response[2].first.should =~ /.*syntax error.*/
89
+ end
90
+ end
91
+ end
92
+ end
93
+
@@ -0,0 +1,10 @@
1
+ require "rubygems"
2
+ require "rspec"
3
+ require "rack"
4
+ require "rack/mock"
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib/respond_to_malformed_formats"))
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :rspec
10
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack_respond_to_malformed_formats
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
+ - Case Commons, LLC
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-17 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activesupport
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Rails currently returns HTML 500 responses when sent unparsable XML and JSON and gives no way to rescue and handle it
38
+ email:
39
+ - casecommons-dev@googlegroups.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - .gitignore
48
+ - Gemfile
49
+ - LICENSE
50
+ - README.rdoc
51
+ - Rakefile
52
+ - lib/rack/respond_to_malformed_formats.rb
53
+ - lib/respond_to_malformed_formats.rb
54
+ - rack_respond_to_malformed_formats.gemspec
55
+ - spec/respond_to_malformed_formats_spec.rb
56
+ - spec/spec_helper.rb
57
+ has_rdoc: true
58
+ homepage: https://github.com/Casecommons/rack_respond_to_malformed_formats
59
+ licenses:
60
+ - MIT
61
+ post_install_message:
62
+ rdoc_options: []
63
+
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 3
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.7
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Intercept malformed XML and JSON requests and return XML and JSON 400 responses not HTML 500
91
+ test_files:
92
+ - spec/respond_to_malformed_formats_spec.rb
93
+ - spec/spec_helper.rb