[ 
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)

Reply via email to