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]