justinmclean opened a new issue, #4262:
URL: https://github.com/apache/iggy/issues/4262

   ### Bug description
   
   A TCP client's connection blocks forever, with no error and no timeout, when 
the server accepts the connection but never answers the login.
   
   I hit this with a version-mismatched pair (an older client against a newer 
server), but the underlying fault is simpler than a version problem: the client 
waits on the login reply with no read timeout, so any unanswered login wedges 
it. Neither side logs a warning or error at default levels. The program just 
hangs, and it is indistinguishable from "the server isn't running".
   
   Expected: the client applies a read timeout to the login handshake and 
returns an error when the server does not answer, instead of hanging forever.
   
   Actual: the login call never returns. The process has to be killed.
   
   Confirmed in the Rust SDK (0.8.0 and 0.9.0) and the Python SDK (0.8.0), so 
this is live in current published clients, not an old-only defect.
   
   ### Affected area / component
   
   Rust SDK
   
   ### Deployment
   
   None
   
   ### Versions
   
   In both 0.8.0 and 0.9.0 Rust clients so it's live in the current release, 
not old-only
   
   ### Hardware / environment
   
   local MacOS runing Iggy 0.9.0 in docker
   
   ### Sample code
   
   ```
   // Rust, iggy = "=0.8.0" (and "=0.9.0")
   use iggy::prelude::{Client, IggyClient};
   
   #[tokio::main]
   async fn main() {
       let client = IggyClient::from_connection_string(
           "iggy://iggy:[email protected]:8090",
       ).unwrap();
       eprintln!("before connect");
       match client.connect().await {
           Ok(()) => eprintln!("connected"),
           Err(e) => eprintln!("connect error: {e:?}"),
       }
   }
   ```
   
   ```
   # Python, apache-iggy==0.8.0
   import asyncio
   from apache_iggy import IggyClient
   
   async def main():
       c = IggyClient("127.0.0.1:8090")
       await c.connect()            # TCP connects fine
       print("before login")
       await c.login_user("iggy", "iggy")   # hangs here forever
       print("logged in")
   
   asyncio.run(main())
   ```
   
   ### Logs
   
   Rust client, RUST_LOG=iggy=trace (0.8.0 against 0.9.0 server):
   INFO  Iggy client has connected to server: 127.0.0.1:8090
   INFO  Iggy client is signing in...
   TRACE Sending a TCP request of size 27 with code: 38
   TRACE Sent a TCP request with code: 38, waiting for a response...
   (then blocks forever; exit 124)
   
   Code 38 is LOGIN_USER. The client sends the login and does a blocking read 
for the reply. No reply arrives, the read has no timeout, so it waits forever. 
The 0.9.0 Rust client is identical. The Python 0.8.0 client connects at TCP 
then hangs on login_user() the same way.
   
   Server, default info level, same run:
   server::boot::listeners: TCP client delegated client_id=7
   shard::router: installing delegated client fd shard=0 client_id=7 raw_fd=15
   message_bus::installer::tcp: consensus client disconnected client=7
   
   The server accepts the connection, then logs nothing until the client is 
killed, at which point it records the disconnect. It sends no reply to the 
login, logs no rejection, and holds the socket open. The connection ends only 
because the client process dies. The image does emit warn lines (startup 
root-password and JWT warnings are present), so a login-rejection warning would 
show if one were emitted. None is.
   
   ### Iggy server config
   
   _No response_
   
   ### Reproduction
   
   1. Run the published server apache/iggy:0.9.0, reachable on 127.0.0.1:8090.
   2. Rust: build a binary with iggy = "=0.8.0" (repeat with "=0.9.0") and the 
sample code. Run with a timeout. It prints "before connect" then hangs to exit 
124, no "connected", no error.
   3. Python: pip install apache-iggy==0.8.0, run the sample. It connects at 
TCP, prints "before login", then hangs on login_user() to exit 124.
   
   Isolation (shows it is an unanswered read, not a version handshake): point 
either Rust client at a plain TCP listener that accepts the connection, reads 
the request bytes, and sends nothing back. Both clients hang the same way (code 
38 sent, waiting for a response, exit 124). So the client fault is not about 
protocol versions; it is that the login read has no timeout, so a silent server 
hangs the client indefinitely.
   
   Root cause, two sides:
   1. Client: the login read has no timeout, so silence becomes an infinite 
hang instead of an error.
   2. Server: on a login it will not honour, the 0.9.0 server sends nothing and 
leaves the socket open, giving the client nothing to unblock on.
   
   A client read timeout is the direct fix but only helps clients built with 
it. A server that closes or replies on a refused login is the half that reaches 
clients already released, because their existing blocking read returns as soon 
as the socket closes or any bytes arrive. Both are worth doing.
   
   ### Contribution
   
   - [ ] I'm willing to submit a pull request to fix this bug
   
   ### Good first issue
   
   - [ ] I think this could be a good first issue for a new contributor


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to