hyperium / h2

HTTP 2.0 client & server implementation for Rust.
MIT License
1.35k stars 270 forks source link

Server Push loop within the same http/2 connection #551

Closed rvgulinski closed 3 years ago

rvgulinski commented 3 years ago

Here is the scenario:

use std::error::Error;
use std::thread;
use std::time;

use bytes::Bytes;
use h2::RecvStream;
use h2::server::{self, SendResponse};
use http::Request;
use http::uri;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};
use uuid::Uuid;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>>{
    let _ = env_logger::try_init();
    let listener = TcpListener::bind("127.0.0.1:8081").await?;
    println!("Listening on {:?}", listener.local_addr());

    loop {
        if let Ok((socket, _peer_addr)) = listener.accept().await {
            tokio::spawn(async move {
                if let Err(e) = serve(socket).await {
                    println!(" -> err={:?}", e);
                }
            });
        }
    }
}

async fn serve(socket: TcpStream) -> Result<(), Box<dyn Error + Send + Sync>> {
    let mut connection = server::handshake(socket).await?;
    println!("H2 connection bound");

    while let Some(result) = connection.accept().await {
        let (request, mut respond) = result?;
        tokio::spawn(async move {
            handle_request(request, respond).await;  // <-------------------- how can I "trigger" push data prepared in handle_request to be actually flushed on the socket?
        });
    }
    println!("H2 connection close");
    Ok(())
}

async fn handle_request(
    mut request: Request<RecvStream>,
    mut respond: SendResponse<Bytes>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
... // sending few server push requests here
}

My question is related to the commented part with the handle_request call. Is there a way to implement above scenario? I found that pushed messages are actually sent when handle_request(request, respond).await; code block finished.

rvgulinski commented 3 years ago

Ok I was finally able to figure it out. I had to spawn another tokio task in the loop inside of the handle_request.

async fn handle_request(
    mut request: Request<RecvStream>,
    mut respond: SendResponse<Bytes>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
    ...
    loop {
       ...
        let mut send_pushed = respond
            .push_request(pushed_req)
            .unwrap()
            .send_response(pushed_rsp, false)
            .unwrap();

        tokio::spawn(async move {
            send_pushed.send_data(Bytes::from_static(b"Pushed data!"), true);
        });
       ...
    }