ngrok / ngrok-rust

Embed ngrok secure ingress into your Rust apps with a single line of code.
Apache License 2.0
315 stars 19 forks source link

Muxado Heartbeat example with regular Stream #154

Open pielgrzym opened 1 week ago

pielgrzym commented 1 week ago

Hi,

I'm struggling to implement heartbeat with muxado. The example provided forces to use TypedSession which does not implement AsyncRead and AsyncWrite traits. Is there any way to get around that?

pielgrzym commented 1 week ago

Perhaps I misuderstood something and muxado crate already supports sending heartbeats on protocol level instead of relying on user to implement them?

pielgrzym commented 6 days ago

So I figured out how to get actual stream, but still I'm not sure how to implement heartbeat. I guess this needs to be set up on both server/client since typed sessions are prefixed with type. So I implemented roughtly something like this (don't mind broken indentation):

Client:

        let typed = Typed::new(sess);
        let (mut heartbeat_sess, hbctl) = Heartbeat::start(
            typed,
            HeartbeatConfig {
                handler: Some(Arc::new(|lat| async move {
                    info!("got heartbeat {:?}", lat);
                    Result::<(), Box<dyn std::error::Error>>::Ok(())
                })),
                ..Default::default()
            },
        )
        .await?;

// later in stream handling loop:
let mut stream = heartbeat_sess.open_typed(StreamType::default()).await?;
do_stuff_with_stream(&mut *stream);

Server:

        let typed = Typed::new(sess);
        let (mut heartbeat_sess, hbctl) = Heartbeat::start(
            typed,
            HeartbeatConfig {
                handler: Some(Arc::new(|lat| async move {
                    info!("got heartbeat {:?}", lat);
                    Result::<(), Box<dyn std::error::Error>>::Ok(())
                })),
                ..Default::default()
            },
        )
        .await?;

// later in stream accepting loop
let mut stream = heartbeat_sess.accept_typed().await?;
do_something_with_the_stream(&mut *stream);

Now at first this seemed to work - server was printing 'got heartbeat' but without latency info (got None there). I checked network activity with tcpdump and dicovered there is no activity so no heartbeat is sent accross network.

I managed to kinda get it to work manually by creating a task that runs hbctl.beat().await? and sleeps. This indeed generates some traffic every time it beats, but I guess this functionality is used for manual checking of underlying network quality.

Help much appreciated.

pielgrzym commented 6 days ago

Ok, managed to get this to work, but I don't know why it failed. The code initializing heartbeat was calling a tokio::spawn that would .open_typed(...) on this heartbeat. This for some reason caused the internal heartbeat requester to fail to write to stream and die. When I moved stream init into same spawn where .open_typed(...) happens it started working. whew :D