use http::{StatusCode, Version, header};
use crate::client::test::scenario::Scenario;
use crate::ext::HeaderIterExt;
const RESPONSE: &[u8] = b"\
HTTP/1.1 200 OK\r\n\
Content-Length: 123\r\n\
Content-Type: text/plain\r\n\
\r\n";
#[test]
fn receive_incomplete_response() {
for i in 14..RESPONSE.len() - 1 {
let scenario = Scenario::builder().get("https://q.test").build();
let mut call = scenario.to_recv_response();
let (input_used, maybe_response) = call.try_response(&RESPONSE[..i], true).unwrap();
assert_eq!(input_used, 0);
assert!(maybe_response.is_none());
assert!(!call.can_proceed());
}
}
#[test]
fn receive_complete_response() {
let scenario = Scenario::builder().get("https://q.test").build();
let mut call = scenario.to_recv_response();
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
let response = maybe_response.unwrap();
assert_eq!(response.version(), Version::HTTP_11);
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.headers().get(header::CONTENT_LENGTH).unwrap(),
"123"
);
assert!(
response
.headers()
.iter()
.has(header::CONTENT_TYPE, "text/plain")
);
assert!(call.can_proceed());
}
#[test]
fn prepended_100_continue() {
let scenario = Scenario::builder()
.post("https://q.test")
.header("expect", "100-continue")
.build();
let mut call = scenario.to_recv_response();
let (input_used, maybe_response) = call
.try_response(b"HTTP/1.1 100 Continue\r\n", true)
.unwrap();
assert_eq!(input_used, 0);
assert!(maybe_response.is_none());
assert!(!call.can_proceed());
let (input_used, maybe_response) = call
.try_response(b"HTTP/1.1 100 Continue\r\n\r\n", true)
.unwrap();
assert_eq!(input_used, 25);
assert!(maybe_response.is_none());
assert!(!call.can_proceed());
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
assert!(call.can_proceed());
}
#[test]
fn expect_100_without_100_continue() {
let scenario = Scenario::builder()
.post("https://q.test")
.header("expect", "100-continue")
.build();
let mut call = scenario.to_recv_response();
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
assert!(call.can_proceed());
}
#[test]
fn unsolicited_100_continue_on_get() {
let scenario = Scenario::builder().get("https://q.test").build();
let mut call = scenario.to_recv_response();
let (input_used, maybe_response) = call
.try_response(b"HTTP/1.1 100 Continue\r\n\r\n", true)
.unwrap();
assert_eq!(input_used, 25);
assert!(
maybe_response.is_none(),
"100-continue should be consumed, not returned"
);
assert!(!call.can_proceed());
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
assert!(call.can_proceed());
let response = maybe_response.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn unsolicited_102_processing() {
let scenario = Scenario::builder().get("https://q.test").build();
let mut call = scenario.to_recv_response();
let processing = b"HTTP/1.1 102 Processing\r\n\r\n";
let (input_used, maybe_response) = call.try_response(processing, true).unwrap();
assert_eq!(input_used, processing.len());
assert!(
maybe_response.is_none(),
"102 Processing should be consumed, not returned"
);
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
assert!(call.can_proceed());
let response = maybe_response.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn unsolicited_103_early_hints() {
let scenario = Scenario::builder().get("https://q.test").build();
let mut call = scenario.to_recv_response();
let early_hints = b"HTTP/1.1 103 Early Hints\r\nLink: </style.css>; rel=preload\r\n\r\n";
let (input_used, maybe_response) = call.try_response(early_hints, true).unwrap();
assert_eq!(input_used, early_hints.len());
assert!(
maybe_response.is_none(),
"103 Early Hints should be consumed, not returned"
);
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
assert!(call.can_proceed());
let response = maybe_response.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn multiple_1xx_responses_in_sequence() {
let scenario = Scenario::builder()
.post("https://q.test")
.header("expect", "100-continue")
.build();
let mut call = scenario.to_recv_response();
let early_hints = b"HTTP/1.1 103 Early Hints\r\nLink: </style.css>; rel=preload\r\n\r\n";
let (input_used, maybe_response) = call.try_response(early_hints, true).unwrap();
assert_eq!(input_used, early_hints.len());
assert!(maybe_response.is_none());
let continue_resp = b"HTTP/1.1 100 Continue\r\n\r\n";
let (input_used, maybe_response) = call.try_response(continue_resp, true).unwrap();
assert_eq!(input_used, continue_resp.len());
assert!(maybe_response.is_none());
let processing = b"HTTP/1.1 102 Processing\r\n\r\n";
let (input_used, maybe_response) = call.try_response(processing, true).unwrap();
assert_eq!(input_used, processing.len());
assert!(maybe_response.is_none());
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
assert!(call.can_proceed());
let response = maybe_response.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn switching_protocols_101_returned() {
let scenario = Scenario::builder()
.get("https://q.test")
.header("upgrade", "websocket")
.build();
let mut call = scenario.to_recv_response();
let switching =
b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n\r\n";
let (input_used, maybe_response) = call.try_response(switching, true).unwrap();
assert_eq!(input_used, switching.len());
assert!(
maybe_response.is_some(),
"101 Switching Protocols should be returned, not consumed"
);
assert!(call.can_proceed());
let response = maybe_response.unwrap();
assert_eq!(response.status(), StatusCode::SWITCHING_PROTOCOLS);
assert_eq!(response.headers().get("upgrade").unwrap(), "websocket");
let result = call.proceed().unwrap();
let has_protocol_switch = match &result {
crate::client::RecvResponseResult::RecvBody(call) => call
.inner
.close_reason
.iter()
.any(|r| *r == crate::CloseReason::ProtocolSwitch),
crate::client::RecvResponseResult::Redirect(call) => call
.inner
.close_reason
.iter()
.any(|r| *r == crate::CloseReason::ProtocolSwitch),
crate::client::RecvResponseResult::Cleanup(call) => call
.inner
.close_reason
.iter()
.any(|r| *r == crate::CloseReason::ProtocolSwitch),
};
assert!(
has_protocol_switch,
"Connection should be marked with ProtocolSwitch"
);
}
#[test]
fn multiple_103_before_final_response() {
let scenario = Scenario::builder().get("https://q.test").build();
let mut call = scenario.to_recv_response();
let early_hints1 = b"HTTP/1.1 103 Early Hints\r\nLink: </style.css>; rel=preload\r\n\r\n";
let (input_used, maybe_response) = call.try_response(early_hints1, true).unwrap();
assert_eq!(input_used, early_hints1.len());
assert!(maybe_response.is_none());
let early_hints2 = b"HTTP/1.1 103 Early Hints\r\nLink: </script.js>; rel=preload\r\n\r\n";
let (input_used, maybe_response) = call.try_response(early_hints2, true).unwrap();
assert_eq!(input_used, early_hints2.len());
assert!(maybe_response.is_none());
let (input_used, maybe_response) = call.try_response(RESPONSE, true).unwrap();
assert_eq!(input_used, 66);
assert!(maybe_response.is_some());
let response = maybe_response.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}