Class: Rack::PostBodyToParams
- Inherits:
-
Object
- Object
- Rack::PostBodyToParams
- Defined in:
- lib/rack/post-body-to-params.rb
Overview
A Rack middleware for parsing POST/PUT body data when Content-Type is application/json
or application/xml
.
Uses ActiveSupport::JSON.decode for json and ActiveSupports enhanced Hash #from_xml for xml. Be shure to have ActiveSupport required beforehand.
Configure parsers for ActiveSupport (you should do this perhaps anyway):
ActiveSupport::JSON.backend = 'Yajl'
ActiveSupport::XmlMini.backend = 'Nokogiri'
concerning Yajl: rails.lighthouseapp.com/projects/8994/tickets/4897-yajl-backend-discovery-fails-in-activesupportjson
Note that all parsing errors will be rescued and returned back to the client.
Most parts blantly stolen from github.com/rack/rack-contrib.
Defined Under Namespace
Classes: RCETEST, YamlNotSafe
Constant Summary collapse
- CONTENT_TYPE =
Constants
'CONTENT_TYPE'.freeze
- POST_BODY =
'rack.input'.freeze
- FORM_INPUT =
'rack.request.form_input'.freeze
- FORM_HASH =
'rack.request.form_hash'.freeze
- APPLICATION_JSON =
Supported Content-Types
'application/json'.freeze
- APPLICATION_XML =
'application/xml'.freeze
Instance Attribute Summary collapse
-
#error_responses ⇒ Object
readonly
Returns the value of attribute error_responses.
-
#parsers ⇒ Object
readonly
Returns the value of attribute parsers.
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, config = {}) ⇒ PostBodyToParams
constructor
Override the parsers and the error responses as needed:.
- #json_error_response(error) ⇒ Object
- #parse_as_json(json_data) ⇒ Object
- #parse_as_xml(xml_data) ⇒ Object
- #xml_error_response(error) ⇒ Object
Constructor Details
#initialize(app, config = {}) ⇒ PostBodyToParams
Override the parsers and the error responses as needed:
use Rack::PostBodyContentTypeParser,
:content_types => ['application/xml'],
:parsers => {
'application/xml' => Proc.new{|a| my_own_xml_parser a },
'application/foo' => Proc.new{|a| my_foo_parser a }
}
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/rack/post-body-to-params.rb', line 57 def initialize(app, config={}) @content_types = config.delete(:content_types) || [APPLICATION_JSON, APPLICATION_XML] @parsers = { APPLICATION_JSON => Proc.new{ |post_body| parse_as_json post_body }, APPLICATION_XML => Proc.new{ |post_body| parse_as_xml post_body } } @parsers.update(config[:parsers]) if config[:parsers] @error_responses = { APPLICATION_JSON => Proc.new{ |error| json_error_response error }, APPLICATION_XML => Proc.new{ |error| xml_error_response error } } @error_responses.update(config[:error_responses]) if config[:error_responses] # Check wether we're vulnerable via YAML: begin parsers[APPLICATION_XML].call %Q{<?xml version="1.0" encoding="UTF-8"?><bang type="yaml">--- !ruby/hash:Rack::PostBodyToParams::RCETEST\n foo: bar</bang>} # We shouldn't get here, the safe thing is to throw an exception (which ActiveSupport 3.1.x+ and safe_yaml ) raise YamlNotSafe, 'Please educate about the ActiveSupport YAML remote code execution vulnerability and take measures. Either install and require safe_yaml or upgrade ActiveSupport' rescue YamlNotSafe => yns raise yns rescue Exception => e # Do nothing, we expect this to happen when we have safe parsing end @app = app end |
Instance Attribute Details
#error_responses ⇒ Object (readonly)
Returns the value of attribute error_responses.
46 47 48 |
# File 'lib/rack/post-body-to-params.rb', line 46 def error_responses @error_responses end |
#parsers ⇒ Object (readonly)
Returns the value of attribute parsers.
46 47 48 |
# File 'lib/rack/post-body-to-params.rb', line 46 def parsers @parsers end |
Instance Method Details
#call(env) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/rack/post-body-to-params.rb', line 100 def call(env) content_type = env[CONTENT_TYPE] && env[CONTENT_TYPE].split(';').first if content_type && @content_types.include?(content_type) post_body = env[POST_BODY].read unless post_body.blank? begin new_form_hash = parsers[content_type].call post_body rescue StandardError => error logger.warn "#{self.class} #{content_type} parsing error: #{error.to_s}" if respond_to? :logger return error_responses[content_type].call error end env.update(FORM_HASH => new_form_hash, FORM_INPUT => env[POST_BODY]) end end @app.call(env) end |
#json_error_response(error) ⇒ Object
93 94 95 |
# File 'lib/rack/post-body-to-params.rb', line 93 def json_error_response(error) [ 400, {'Content-Type' => APPLICATION_JSON}, [ {"json-syntax-error" => error.to_s}.to_json ] ] end |
#parse_as_json(json_data) ⇒ Object
89 90 91 |
# File 'lib/rack/post-body-to-params.rb', line 89 def parse_as_json(json_data) ActiveSupport::JSON.decode json_data end |
#parse_as_xml(xml_data) ⇒ Object
86 87 88 |
# File 'lib/rack/post-body-to-params.rb', line 86 def parse_as_xml(xml_data) Hash.from_xml xml_data end |
#xml_error_response(error) ⇒ Object
96 97 98 |
# File 'lib/rack/post-body-to-params.rb', line 96 def xml_error_response(error) [ 400, {'Content-Type' => APPLICATION_XML}, [ {"xml-syntax-error" => error.to_s}.to_xml(:root => :errors) ] ] end |