[
https://issues.apache.org/jira/browse/THRIFT-6123?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dmytro Shteflyuk resolved THRIFT-6123.
--------------------------------------
Fix Version/s: 0.25.0
Resolution: Fixed
> Ruby Serializer retains JSON protocol state after write failures
> ----------------------------------------------------------------
>
> Key: THRIFT-6123
> URL: https://issues.apache.org/jira/browse/THRIFT-6123
> Project: Thrift
> Issue Type: Bug
> Components: Ruby - Library
> Reporter: Dmytro Shteflyuk
> Assignee: Dmytro Shteflyuk
> Priority: Major
> Fix For: 0.25.0
>
> Time Spent: 20m
> Remaining Estimate: 0h
>
> h3. Problem
> {{Thrift::Serializer}} reuses both a memory transport and a protocol object
> across calls. It clears the transport buffer before each serialization, but
> it does not reset protocol-owned state.
> {{JsonProtocol}} keeps a stack of container contexts and separator state. If
> an object's {{write}} method raises after beginning a message, struct, field,
> or container, those contexts are not unwound. Clearing only the output buffer
> leaves the protocol positioned inside the failed value.
> A later, otherwise unrelated serialization can therefore start with a stale
> comma or colon. Some failure positions allow the first later result to look
> correct while leaving stale state behind for another call, so recovery must
> remain correct across consecutive serializations.
> h3. Client impact
> Applications that retain a Ruby {{Serializer}} can receive the expected
> exception for one failed JSON write and then unknowingly emit malformed JSON
> for later objects. Replacing the serializer avoids the problem, but callers
> have no indication that the existing serializer has become unusable.
> h3. Reproduction
> This example uses the generated {{SpecNamespace::Hello}} fixture from the
> Ruby specs:
> {code:ruby}
> class BrokenValue
> def write(protocol)
> protocol.write_struct_begin("Broken")
> protocol.write_field_begin("value", Thrift::Types::STRING, 1)
> raise "write failed"
> end
> end
> factory = Thrift::JsonProtocolFactory.new
> serializer = Thrift::Serializer.new(factory)
> begin
> serializer.serialize(BrokenValue.new)
> rescue RuntimeError
> end
> value = SpecNamespace::Hello.new(greeting: "ok")
> reused = serializer.serialize(value)
> fresh = Thrift::Serializer.new(factory).serialize(value)
> puts reused.inspect
> # ":{\"1\":{\"str\":\"ok\"}}"
> puts fresh.inspect
> # "{\"1\":{\"str\":\"ok\"}}"
> {code}
> The reused serializer's payload begins with a colon and cannot be read as a
> Thrift JSON struct.
> h3. Expected behavior
> A failed serialization should preserve and re-raise the original exception
> without leaving the serializer poisoned. The next and subsequent calls should
> produce the same parseable bytes as a fresh serializer.
> Successful calls should continue reusing their existing protocol. A protocol
> that participated in a failed write should be discarded and recreated lazily
> before the next call.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)