332 lines
11 KiB
Rust
332 lines
11 KiB
Rust
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
#![cfg(test)]
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
use neqo_common::event::Provider as _;
|
|
use neqo_crypto::AuthenticationStatus;
|
|
use neqo_http3::{
|
|
Error, Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Server,
|
|
Http3ServerEvent, Priority,
|
|
};
|
|
use test_fixture::*;
|
|
|
|
const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63];
|
|
|
|
fn response_header_no_data() -> &'static Vec<Header> {
|
|
static HEADERS: OnceLock<Vec<Header>> = OnceLock::new();
|
|
HEADERS.get_or_init(|| vec![Header::new(":status", "200"), Header::new("something", "3")])
|
|
}
|
|
|
|
fn response_header_103() -> &'static Vec<Header> {
|
|
static HEADERS: OnceLock<Vec<Header>> = OnceLock::new();
|
|
HEADERS.get_or_init(|| vec![Header::new(":status", "103"), Header::new("link", "...")])
|
|
}
|
|
|
|
fn exchange_packets(client: &mut Http3Client, server: &mut Http3Server) {
|
|
let mut out = None;
|
|
loop {
|
|
out = client.process(out, now()).dgram();
|
|
out = server.process(out, now()).dgram();
|
|
if out.is_none() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn receive_request(server: &Http3Server) -> Option<Http3OrWebTransportStream> {
|
|
while let Some(event) = server.next_event() {
|
|
if let Http3ServerEvent::Headers {
|
|
stream,
|
|
headers,
|
|
fin,
|
|
} = event
|
|
{
|
|
assert_eq!(
|
|
&headers,
|
|
&[
|
|
Header::new(":method", "GET"),
|
|
Header::new(":scheme", "https"),
|
|
Header::new(":authority", "something.com"),
|
|
Header::new(":path", "/")
|
|
]
|
|
);
|
|
assert!(fin);
|
|
return Some(stream);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn send_trailers(request: &Http3OrWebTransportStream) -> Result<(), Error> {
|
|
request.send_headers(&[
|
|
Header::new("something1", "something"),
|
|
Header::new("something2", "3"),
|
|
])
|
|
}
|
|
|
|
fn send_informational_headers(request: &Http3OrWebTransportStream) -> Result<(), Error> {
|
|
request.send_headers(response_header_103())
|
|
}
|
|
|
|
fn send_headers(request: &Http3OrWebTransportStream) -> Result<(), Error> {
|
|
request.send_headers(&[
|
|
Header::new(":status", "200"),
|
|
Header::new("content-length", "3"),
|
|
])
|
|
}
|
|
|
|
fn process_client_events(conn: &mut Http3Client) {
|
|
let mut response_header_found = false;
|
|
let mut response_data_found = false;
|
|
while let Some(event) = conn.next_event() {
|
|
match event {
|
|
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
|
|
assert!(
|
|
(headers.as_ref()
|
|
== [
|
|
Header::new(":status", "200"),
|
|
Header::new("content-length", "3"),
|
|
])
|
|
|| (headers.as_ref() == *response_header_103())
|
|
);
|
|
assert!(!fin);
|
|
response_header_found = true;
|
|
}
|
|
Http3ClientEvent::DataReadable { stream_id } => {
|
|
let mut buf = [0u8; 100];
|
|
let (amount, fin) = conn.read_data(now(), stream_id, &mut buf).unwrap();
|
|
assert!(fin);
|
|
assert_eq!(amount, RESPONSE_DATA.len());
|
|
assert_eq!(&buf[..RESPONSE_DATA.len()], RESPONSE_DATA);
|
|
response_data_found = true;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
assert!(response_header_found);
|
|
assert!(response_data_found);
|
|
}
|
|
|
|
fn process_client_events_no_data(conn: &mut Http3Client) {
|
|
let mut response_header_found = false;
|
|
let mut fin_received = false;
|
|
while let Some(event) = conn.next_event() {
|
|
match event {
|
|
Http3ClientEvent::HeaderReady { headers, fin, .. } => {
|
|
assert_eq!(headers.as_ref(), *response_header_no_data());
|
|
fin_received = fin;
|
|
response_header_found = true;
|
|
}
|
|
Http3ClientEvent::DataReadable { stream_id } => {
|
|
let mut buf = [0u8; 100];
|
|
let (amount, fin) = conn.read_data(now(), stream_id, &mut buf).unwrap();
|
|
assert!(fin);
|
|
fin_received = true;
|
|
assert_eq!(amount, 0);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
assert!(response_header_found);
|
|
assert!(fin_received);
|
|
}
|
|
|
|
fn connect_send_and_receive_request() -> (Http3Client, Http3Server, Http3OrWebTransportStream) {
|
|
let mut hconn_c = default_http3_client();
|
|
let mut hconn_s = default_http3_server();
|
|
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
|
|
assert!(hconn_c.events().any(authentication_needed));
|
|
hconn_c.authenticated(AuthenticationStatus::Ok, now());
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
|
|
let req = hconn_c
|
|
.fetch(
|
|
now(),
|
|
"GET",
|
|
&("https", "something.com", "/"),
|
|
&[],
|
|
Priority::default(),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(req, 0);
|
|
hconn_c.stream_close_send(req).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
|
|
let request = receive_request(&hconn_s).unwrap();
|
|
|
|
(hconn_c, hconn_s, request)
|
|
}
|
|
|
|
#[test]
|
|
fn response_trailers1() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
send_trailers(&request).unwrap();
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn response_trailers2() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
send_trailers(&request).unwrap();
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn response_trailers3() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
send_trailers(&request).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn response_trailers_no_data() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
request.send_headers(response_header_no_data()).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
send_trailers(&request).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events_no_data(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_response_trailers() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
send_trailers(&request).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
|
|
assert_eq!(send_trailers(&request), Err(Error::InvalidInput));
|
|
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn data_after_trailer() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
send_trailers(&request).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
|
|
assert_eq!(request.send_data(RESPONSE_DATA), Err(Error::InvalidInput));
|
|
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn trailers_after_close() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
request.stream_close_send().unwrap();
|
|
|
|
assert_eq!(send_trailers(&request), Err(Error::InvalidStreamId));
|
|
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_response_headers() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
request.send_headers(response_header_no_data()).unwrap();
|
|
|
|
assert_eq!(
|
|
request.send_headers(response_header_no_data()),
|
|
Err(Error::InvalidHeader)
|
|
);
|
|
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events_no_data(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn informational_after_response_headers() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
request.send_headers(response_header_no_data()).unwrap();
|
|
|
|
assert_eq!(
|
|
send_informational_headers(&request),
|
|
Err(Error::InvalidHeader)
|
|
);
|
|
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events_no_data(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn data_after_informational() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_informational_headers(&request).unwrap();
|
|
|
|
assert_eq!(request.send_data(RESPONSE_DATA), Err(Error::InvalidInput));
|
|
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn non_trailers_headers_after_data() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
|
|
assert_eq!(
|
|
request.send_headers(response_header_no_data()),
|
|
Err(Error::InvalidHeader)
|
|
);
|
|
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|
|
|
|
#[test]
|
|
fn data_before_headers() {
|
|
let (mut hconn_c, mut hconn_s, request) = connect_send_and_receive_request();
|
|
assert_eq!(request.send_data(RESPONSE_DATA), Err(Error::InvalidInput));
|
|
|
|
send_headers(&request).unwrap();
|
|
request.send_data(RESPONSE_DATA).unwrap();
|
|
request.stream_close_send().unwrap();
|
|
exchange_packets(&mut hconn_c, &mut hconn_s);
|
|
process_client_events(&mut hconn_c);
|
|
}
|