[ 
https://issues.apache.org/jira/browse/THRIFT-6127?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Dmytro Shteflyuk resolved THRIFT-6127.
--------------------------------------
    Fix Version/s: 0.25.0
       Resolution: Fixed

> Ruby sockets remain reusable after I/O timeouts
> -----------------------------------------------
>
>                 Key: THRIFT-6127
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6127
>             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
> When a timed Ruby socket read or write raises 
> {{Thrift::TransportException::TIMED_OUT}}, the transport leaves its 
> underlying socket open.
> This is unsafe once the operation has made partial progress. A read may 
> already have removed bytes from the stream before a later read times out, 
> while a write timeout may occur after some request bytes have been delivered. 
> The connection's message boundary or delivery state is then uncertain, but 
> {{open?}} still reports true and later operations are allowed to reuse it.
> The behavior is shared by {{Thrift::Socket}} and its TLS and UNIX socket 
> subclasses. It also applies to sockets returned by the Ruby server transports.
> h3. Client impact
> Applications that catch a timeout may continue using a connection whose 
> stream is no longer aligned with the Thrift message the application expected. 
> A later request or response can consequently fail with an unrelated decoding 
> or transport error, and a timed-out write cannot be retried safely on the 
> same connection because its partial-delivery state is unknown.
> Callers should receive the original timeout and reconnect before performing 
> more I/O.
> h3. Reproduction
> From {{lib/rb}}, run:
> {code:ruby}
> require "thrift"
> require "socket"
> local, peer = ::Socket.pair(:UNIX, :STREAM, 0)
> transport = Thrift::Socket.new
> transport.handle = local
> transport.timeout = 0.05
> peer.write("A")
> begin
>   transport.read_all(2)
> rescue Thrift::TransportException => error
>   puts "#{error.class}: #{error.message}"
>   puts "open=#{transport.open?}"
> ensure
>   transport.close
>   peer.close
> end
> {code}
> The peer supplies one byte, so {{read_all(2)}} makes partial progress before 
> waiting for the missing byte. Before this change, the operation raises a 
> timeout but prints:
> {noformat}
> Thrift::TransportException: Socket: Timed out reading 1 bytes from 
> localhost:9090
> open=true
> {noformat}
> The equivalent write path also remains open after partially writing a request 
> and then timing out.
> h3. Expected behavior
> A timed read or write must preserve the original {{TIMED_OUT}} exception and 
> close the affected transport. {{open?}} should then return false, subsequent 
> I/O should fail with {{NOT_OPEN}}, and the peer should observe the closed 
> connection.
> Successful reads and writes, including partial operations that complete 
> before the deadline, should remain unchanged.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to