From 830c57876377b496c607004416e59851cb889fe9 Mon Sep 17 00:00:00 2001 From: Juan Manuel Cuello Date: Sun, 18 Jun 2023 13:21:31 -0300 Subject: [PATCH] Rack::JSONBodyParser: rescue all parser exceptions Rescue all exceptions raised by the parser as long as it is StandardError subclass (which should be all the exceptions raised by different libraries). This is specially needed when using a parser other than JSON. Otherwise we need to rescue the corresponding exception and raise JSON:ParseError inside the block. --- lib/rack/contrib/json_body_parser.rb | 13 +++++++++++-- test/spec_rack_json_body_parser_spec.rb | 11 +++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/rack/contrib/json_body_parser.rb b/lib/rack/contrib/json_body_parser.rb index 77023a2..39a7425 100644 --- a/lib/rack/contrib/json_body_parser.rb +++ b/lib/rack/contrib/json_body_parser.rb @@ -61,7 +61,7 @@ def call(env) update_form_hash_with_json_body(env) end - rescue JSON::ParserError + rescue ParserError body = { error: 'Failed to parse body as JSON' }.to_json header = { 'Content-Type' => 'application/json' } return Rack::Response.new(body, 400, header).finish @@ -71,13 +71,22 @@ def call(env) private + class ParserError < StandardError; end + def update_form_hash_with_json_body(env) body = env[Rack::RACK_INPUT] return unless (body_content = body.read) && !body_content.empty? body.rewind # somebody might try to read this stream + + begin + parsed_body = @parser.call(body_content) + rescue StandardError + raise ParserError + end + env.update( - Rack::RACK_REQUEST_FORM_HASH => @parser.call(body_content), + Rack::RACK_REQUEST_FORM_HASH => parsed_body, Rack::RACK_REQUEST_FORM_INPUT => body ) end diff --git a/test/spec_rack_json_body_parser_spec.rb b/test/spec_rack_json_body_parser_spec.rb index 665e7e0..784c9fd 100644 --- a/test/spec_rack_json_body_parser_spec.rb +++ b/test/spec_rack_json_body_parser_spec.rb @@ -72,6 +72,17 @@ def create_parser(app, **args, &block) _( -> { create_parser(app).call(env) }).must_raise JSON::ParserError end + specify "should rescue StandardError subclasses raised by the parser" do + class CustomParserError < StandardError; end + + parser = create_parser(app) do |_body| + raise CustomParserError.new + end + + res = parser.call(mock_env) + _(res[0]).must_equal 400 + end + describe "contradiction between body and type" do specify "should return bad request with a JSON-encoded error message" do env = mock_env(input: 'This is not JSON')