pact-mock_service icon indicating copy to clipboard operation
pact-mock_service copied to clipboard

Internal server error when request payload cannot be encoded to UTF-8

Open lextiz opened this issue 6 years ago • 9 comments

I am trying to stub a response for a PUT request that has a zip file as a payload. As far as I understand from the documentation this is a valid use case. However the stub server responds with 500 code and has the following stack trace in logs:

INFO  WEBrick 1.3.1
INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
INFO  WEBrick::HTTPServer#start: pid=1 port=80
I, [2019-03-22T10:24:24.911391 #1]  INFO -- : Received request PUT /data/478ef4f8-fe03-4b91-9b3a-41164ce23fde/dataset.zip
E, [2019-03-22T10:24:24.923967 #1] ERROR -- : Error ocurred in mock service: Encoding::UndefinedConversionError - "\xEB" from ASCII-8BIT to UTF-8
E, [2019-03-22T10:24:24.924102 #1] ERROR -- : /pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/request_handlers/interaction_replay.rb:16:in `encode'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/request_handlers/interaction_replay.rb:16:in `to_json'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/request_handlers/interaction_replay.rb:16:in `pretty_generate'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/request_handlers/interaction_replay.rb:49:in `find_response'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/request_handlers/interaction_replay.rb:41:in `respond'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/request_handlers/base_request_handler.rb:17:in `call'
/pact/lib/vendor/ruby/2.2.0/gems/rack-2.0.6/lib/rack/cascade.rb:33:in `block in call'
/pact/lib/vendor/ruby/2.2.0/gems/rack-2.0.6/lib/rack/cascade.rb:24:in `each'
/pact/lib/vendor/ruby/2.2.0/gems/rack-2.0.6/lib/rack/cascade.rb:24:in `call'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/consumer/mock_service/cors_origin_header_middleware.rb:11:in `call'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/consumer/mock_service/error_handler.rb:13:in `call'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/mock_service/app.rb:33:in `call'
/pact/lib/vendor/ruby/2.2.0/gems/pact-mock_service-2.12.0/lib/pact/consumer/mock_service/set_location.rb:14:in `call'
/pact/lib/vendor/ruby/2.2.0/gems/rack-2.0.6/lib/rack/handler/webrick.rb:86:in `service'
/pact/lib/vendor/ruby/2.2.0/gems/webrick-1.3.1/lib/webrick/httpserver.rb:138:in `service'
/pact/lib/vendor/ruby/2.2.0/gems/webrick-1.3.1/lib/webrick/httpserver.rb:94:in `run'
/pact/lib/vendor/ruby/2.2.0/gems/webrick-1.3.1/lib/webrick/server.rb:191:in `block in start_thread'

Am I missing something or the stub server expects JSON payload for all interactions?

The assumption is wrong: the JSON object is Pact internal, but includes the actual payload.

The root cause of this specific failure is that encode method fails to encode some payload as UTF-8, is that is not meant to be interpreted as such. In my case no validations for body are needed, is that possible to bypass the failing code by having a different contract?

No, it is used everywhere.

lextiz avatar Mar 22 '19 10:03 lextiz

Fix proposal: wrap JSON parsing in a try/catch block, when parsing would fail than the object that is printed for debugging purposes would not be pretty printed, but the transaction would not fail. Would such a fix be accepted as PR?

lextiz avatar Mar 22 '19 15:03 lextiz

Hm... I'm really not sure how a zip file would work. How would it get written into the JSON file?

bethesque avatar Mar 24 '19 21:03 bethesque

What I am trying to do is to use pact stub server to return a response to a POST request, that has binary body. The attempt to encode a go object (that contains also the binary payload) to JSON and back comes from pretty_generate function, that is used only to beautify the debugging printouts. So the proposal is to wrap this logic in a try/catch block, so the transaction would not fail when pretty printing of the debug logs fails.

lextiz avatar Mar 25 '19 13:03 lextiz

I still don't think this is going to work. How do you expect the binary data to be encoded in the UTF-8 encoded json pact file?

bethesque avatar Apr 01 '19 01:04 bethesque

That works for me fine, because this data is not written to a JSON file. I am not running any assertions on the binary data (except that it exists), I just want pact-mock_service not to crash when it receives a request with non-UTF-8-encodable data. I do not think that running assertions on the binary data, that would require writing it to a JSON file is a good idea in general.

lextiz avatar Apr 03 '19 15:04 lextiz

I've just come across this issue when creating a pact-test that involves uploading a pact file with a binary upload.

I found it also erroring at the same point as you, although I have had to use rescue Encoding::UndefinedConversionError

			begin
				JSON.pretty_generate(JSON.parse(object.to_json))
			  rescue JSON::ParserError
				object
			  rescue Encoding::UndefinedConversionError
				object
			  end

I am not sure if pact is going to work for my current use case (mutlipart/form-data) but spiking it today

YOU54F avatar Apr 29 '19 12:04 YOU54F

Released in 3.1.1

bethesque avatar May 30 '19 03:05 bethesque

@lextiz does this solve your issue now? I need to try it out at work tomorrow

YOU54F avatar Jun 09 '19 23:06 YOU54F

@YOU54F I have tested the fix "in-place" only by replacing the ruby code in live environment and checking the API behavior, without releasing the whole library. The 3.1.1 version was not tested by me yet.

lextiz avatar Jun 11 '19 12:06 lextiz