leyline-http 0.1.1

HTTP client that mimics browsers on the wire
Documentation
use super::*;
use crate::h2::H2Error;
use crate::h2::frame::{Frame, FrameHeader, SettingsFrame};
use bytes::BytesMut;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::test]
async fn write_and_read_settings() {
    let (client, server) = tokio::io::duplex(4096);

    let frame = SettingsFrame {
        ack: false,
        params: vec![(0x1, 65536), (0x2, 0), (0x4, 6291456)],
    };

    let mut writer = FrameWriter::new(client);
    writer.write_settings(&frame).await.unwrap();
    drop(writer);

    let mut reader = FrameReader::new(server);
    let read_frame = reader.next().await.unwrap().unwrap();

    match read_frame {
        Frame::Settings(s) => {
            assert!(!s.ack);
            assert_eq!(s.params, vec![(0x1, 65536), (0x2, 0), (0x4, 6291456)]);
        }
        _ => panic!("expected Settings frame"),
    }

    assert!(reader.next().await.unwrap().is_none());
}

#[tokio::test]
async fn write_and_read_preface_then_settings() {
    let (client, server) = tokio::io::duplex(4096);

    let mut writer = FrameWriter::new(client);
    writer.write_preface().await.unwrap();
    writer
        .write_settings(&SettingsFrame {
            ack: false,
            params: vec![(0x4, 6291456)],
        })
        .await
        .unwrap();
    drop(writer);

    let mut reader_raw = server;
    let mut preface = [0u8; 24];
    reader_raw.read_exact(&mut preface).await.unwrap();
    assert_eq!(&preface, b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");

    let mut reader = FrameReader::new(reader_raw);
    let frame = reader.next().await.unwrap().unwrap();
    assert!(matches!(frame, Frame::Settings(_)));
}

#[tokio::test]
async fn next_resumes_after_cancellation() {
    let (client, mut server) = tokio::io::duplex(1024);

    let frame = SettingsFrame {
        ack: false,
        params: vec![(0x1, 65536), (0x4, 6291456)],
    };
    let mut encoded = BytesMut::new();
    frame.encode(&mut encoded);
    let encoded = encoded.freeze();

    let server_task = tokio::spawn(async move {
        for byte in encoded.iter() {
            server.write_all(&[*byte]).await.unwrap();
            tokio::task::yield_now().await;
        }
        drop(server);
    });

    let mut reader = FrameReader::new(client);
    let read_frame = loop {
        tokio::select! {
            biased;
            res = reader.next() => {
                break res.unwrap().unwrap();
            }
            _ = tokio::task::yield_now() => {
                continue;
            }
        }
    };

    server_task.await.unwrap();
    match read_frame {
        Frame::Settings(s) => {
            assert_eq!(s.params, vec![(0x1, 65536), (0x4, 6291456)]);
        }
        _ => panic!("expected Settings frame"),
    }
}

#[tokio::test]
async fn rejects_oversized_frame() {
    let (client, server) = tokio::io::duplex(4096);

    let mut writer = client;
    let header = FrameHeader {
        length: 32768,
        frame_type: 0x0,
        flags: 0,
        stream_id: 1,
    };
    let mut buf = BytesMut::with_capacity(9);
    header.encode(&mut buf);
    writer.write_all(&buf).await.unwrap();
    drop(writer);

    let mut reader = FrameReader::new(server);
    let result = reader.next().await;
    assert!(matches!(result, Err(H2Error::FrameTooLarge { .. })));
}