Farcaller
Farcaller

Reputation: 3070

BufReader::lines() over TcpStream stops iteration

I have a reasonably simple code using TcpStream and SslStream around it, reading the socket line by line with BufReader. Sometime the iterator just stops returning any data with Ok(0):

let mut stream = TcpStream::connect((self.config.host.as_ref(), self.config.port)).unwrap();
if self.config.ssl {
    let context = ssl::SslContext::new(ssl::SslMethod::Tlsv1_2).unwrap();
    let mut stream = ssl::SslStream::connect(&context, stream).unwrap();
    self.stream = Some(ssl::MaybeSslStream::Ssl(stream));
} else {
    self.stream = Some(ssl::MaybeSslStream::Normal(stream));
}

...

let read_stream = clone_stream(&self.stream);

let line_reader = BufReader::new(read_stream);

for line in line_reader.lines() {
    match line {
        Ok(line) => {
            ...
        }
        Err(e) => panic!("line read failed: {}", e),
    }
}
println!("lines out, {:?}", self.stream);

The loop just stops randomly, as far as I can see and there's no reason to believe socket was closed server-side. Calling self.stream.as_mut().unwrap().read_to_end(&mut buf) after loop ended returns Ok(0).

Any advice on how this is expected to be handled? I don't get any Err so I can assume socket is still alive but then I can't read anything from it. What is the current state of the socket and how should I proceed?

PS: I'm providing the implementation of clone_stream as the reference, as advied by commenter.

fn clone_stream(stream: &Option<ssl::MaybeSslStream<TcpStream>>) -> ssl::MaybeSslStream<TcpStream> {
    if let &Some(ref s) = stream {
        match s {
            &ssl::MaybeSslStream::Ssl(ref s) => ssl::MaybeSslStream::Ssl(s.try_clone().unwrap()),
            &ssl::MaybeSslStream::Normal(ref s) => ssl::MaybeSslStream::Normal(s.try_clone().unwrap()),
        }
    } else {
        panic!();
    }
}

Upvotes: 4

Views: 617

Answers (1)

Farcaller
Farcaller

Reputation: 3070

Surprisingly, this was a "default timeout" on client I guess (socket was moving into CLOSE_WAIT state).

I fixed it by first adding:

stream.set_read_timeout(Some(Duration::new(60*5, 0)));
stream.set_write_timeout(Some(Duration::new(60*5, 0)));

that made the iterator fail with ErrorKind::WouldBlock on timeout, at which point I added a code to send a ping packet over the wire, the next iteration worked exactly as expected.

Upvotes: 1

Related Questions