Skip to content

RecvStream::is_end_stream() is never true. RecvStream::data() never returns None #882

@EmilyShepherd

Description

@EmilyShepherd

When running a slightly modified version of the example (to get it to connect to an easily reproducable real world-endpoint), RecvStream's never stops returning when called and is_end_stream() never returns true.

Example Code

use std::error::Error as StdError;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4};
use std::sync::Arc;

use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, DnsName};
use tokio::net::TcpStream;
use tokio_rustls::{TlsConnector, rustls};

use h2::client;
use http::Request;

#[tokio::main]
async fn main() -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
    let mut root_cert_store = rustls::RootCertStore::empty();

    let (a, b) = root_cert_store.add_parsable_certificates(
        CertificateDer::pem_file_iter("/etc/ssl/cert.pem")
            .expect("Cannot open CA file")
            .map(|result| result.unwrap()),
    );

    let mut config = rustls::ClientConfig::builder()
        .with_root_certificates(root_cert_store)
        .with_no_client_auth();
    config.alpn_protocols = vec![b"h2".to_vec()];

    let domain: DnsName = "google.com".try_into().unwrap();
    let addr: SocketAddrV4 = "142.250.117.139:443".parse().unwrap(); // manual dns check for google.com

    let tcp_stream = TcpStream::connect(addr).await?;

    let tls_stream = TlsConnector::from(Arc::new(config))
        .connect(domain.into(), tcp_stream)
        .await?;

    let (mut client, h2) = client::handshake(tls_stream).await?;

    let request = Request::builder()
        .uri("https://google.com/")
        .body(())
        .unwrap();

    let (response, _) = client.send_request(request.clone(), false).unwrap();

    // Spawn a task to run the conn...
    tokio::spawn(async move {
        if let Err(e) = h2.await {
            println!("GOT ERR={:?}", e);
        }
    });

    let response = response.await?;

    // Get the body
    let mut body = response.into_body();

    let mut i = 0;

    while let Some(chunk) = body.data().await {
        match chunk {
            Ok(data) => println!("Yay data: {data:?}"),
            Err(err) => {
                println!("Boo! {err:?}. {:?}", body.is_end_stream());
            }
        }

        // Safety to prevent this going on forever.
        i += 1;
        if i > 10 {
            break;
        }
    }

    Ok(())
}

Output

Yay data: b"<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>301 Moved</TITLE></HEAD><BODY>\n<H1>301 Moved</H1>\nThe document has moved\n<A HREF=\"https://www.google.com/\">here</A>.\r\n</BODY></HTML>\r\n"
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false
Boo! Error { kind: Reset(StreamId(1), NO_ERROR, Remote) }. false

This is despite confirming via tcpdump while this code is running that the server does not send Reset frames over and over. Connecting to the endpoint via curl shows they set the eos flag on the first frame with html data in it. This never seems to be picked up by h2.

A frame with the end stream flag set should be enough to mark is_end_stream to true, surely, or failing that a real Reset frame with a reason of NO_ERROR should also.

This behaviour began with 0.4.10.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions