use packet_encoding::Encodable;
use std::collections::VecDeque;
use tracing::{trace, warn};
use crate::error::Error;
use crate::header::{Header, HeaderSet, SingleResponseMode};
use crate::operation::{OpCode, RequestPacket, ResponseCode, ResponsePacket};
use crate::server::handler::ObexOperationError;
use crate::server::{ApplicationResponse, OperationRequest, ServerOperation};
const BODY_HEADER_PREFIX_LENGTH_BYTES: usize = 3;
#[derive(Debug, PartialEq)]
struct StagedData {
first: Option<Option<Vec<u8>>>,
rest: VecDeque<Vec<u8>>,
}
impl StagedData {
fn new(first: Option<Vec<u8>>, rest: VecDeque<Vec<u8>>) -> Self {
Self { first: Some(first), rest }
}
fn empty() -> Self {
Self { first: None, rest: VecDeque::new() }
}
fn from_data(
mut data: Vec<u8>,
max_headers_size: u16,
headers_size: usize,
) -> Result<Self, Error> {
let max_headers_size = max_headers_size as usize;
if headers_size > max_headers_size {
warn!("Too many headers in GET");
return Err(Error::operation(OpCode::Get, "too many headers"));
}
if max_headers_size <= BODY_HEADER_PREFIX_LENGTH_BYTES {
return Err(Error::operation(OpCode::Get, "max_headers_size too small"));
}
let max_first_data_packet_size = max_headers_size - headers_size;
let data_encoded_len = data.len() + BODY_HEADER_PREFIX_LENGTH_BYTES;
if data_encoded_len <= max_first_data_packet_size {
return Ok(Self::new(Some(data), VecDeque::new()));
}
let first_chunk_size =
max_first_data_packet_size.checked_sub(BODY_HEADER_PREFIX_LENGTH_BYTES);
let (first, remaining) = if let Some(max) = first_chunk_size {
let remaining = data.split_off(max);
(Some(data), remaining)
} else {
(None, data)
};
let max_data_packet_size = max_headers_size - BODY_HEADER_PREFIX_LENGTH_BYTES;
let mut chunks = VecDeque::new();
for chunk in remaining.chunks(max_data_packet_size as usize) {
chunks.push_back(chunk.to_vec());
}
Ok(Self::new(first, chunks))
}
#[cfg(test)]
fn is_first_response(&self) -> bool {
self.first.is_some()
}
fn is_complete(&self) -> bool {
self.first.is_none() && self.rest.is_empty()
}
fn next_response(&mut self, mut headers: HeaderSet) -> Result<ResponsePacket, Error> {
if self.is_complete() {
return Err(Error::operation(OpCode::Get, "staged data is already complete"));
}
let chunk = if let Some(first_packet) = self.first.take() {
first_packet
} else {
Some(self.rest.pop_front().unwrap_or(vec![]))
};
let (code, h) = if self.rest.is_empty() {
(ResponseCode::Ok, chunk.map(|p| Header::EndOfBody(p)))
} else {
(ResponseCode::Continue, chunk.map(|p| Header::Body(p)))
};
if let Some(header) = h {
headers.add(header)?;
}
Ok(ResponsePacket::new_get(code, headers))
}
fn all_responses(
&mut self,
mut initial_headers: HeaderSet,
) -> Result<Vec<ResponsePacket>, Error> {
let mut responses = Vec::new();
while !self.is_complete() {
let headers = std::mem::replace(&mut initial_headers, HeaderSet::new());
let response = self.next_response(headers)?;
responses.push(response);
}
Ok(responses)
}
}
#[derive(Debug)]
enum State {
Request { headers: HeaderSet },
RequestPhaseComplete,
Response { staged_data: StagedData },
Complete,
}
enum SrmState {
NotNegotiated { srm_supported: bool },
Negotiating { negotiated_srm: SingleResponseMode },
Negotiated { srm: SingleResponseMode },
}
pub struct GetOperation {
max_headers_size: u16,
srm_state: SrmState,
state: State,
}
impl GetOperation {
pub fn new(max_packet_size: u16, srm_supported: bool) -> Self {
let max_headers_size = max_packet_size - ResponsePacket::MIN_PACKET_SIZE as u16;
Self {
max_headers_size,
srm_state: SrmState::NotNegotiated { srm_supported },
state: State::Request { headers: HeaderSet::new() },
}
}
#[cfg(test)]
fn new_at_state(max_packet_size: u16, state: State) -> Self {
let max_headers_size = max_packet_size - ResponsePacket::MIN_PACKET_SIZE as u16;
Self {
max_headers_size,
srm_state: SrmState::NotNegotiated { srm_supported: false },
state,
}
}
fn check_complete_and_update_state(&mut self) {
let State::Response { ref staged_data } = &self.state else { return };
if staged_data.is_complete() {
self.state = State::Complete;
}
}
fn maybe_add_srm_header(&mut self, headers: &mut HeaderSet) -> Result<bool, Error> {
if let SrmState::Negotiating { negotiated_srm } = self.srm_state {
headers.add(negotiated_srm.into())?;
self.srm_state = SrmState::Negotiated { srm: negotiated_srm };
return Ok(true);
}
Ok(false)
}
}
impl ServerOperation for GetOperation {
fn srm_status(&self) -> SingleResponseMode {
match self.srm_state {
SrmState::NotNegotiated { .. } | SrmState::Negotiating { .. } => {
SingleResponseMode::Disable
}
SrmState::Negotiated { srm } => srm,
}
}
fn is_complete(&self) -> bool {
matches!(self.state, State::Complete)
}
fn handle_peer_request(&mut self, request: RequestPacket) -> Result<OperationRequest, Error> {
let code = *request.code();
let current_srm_mode = self.srm_status();
match &mut self.state {
State::Request { ref mut headers } if code == OpCode::Get => {
let request_headers = HeaderSet::from(request);
match self.srm_state {
SrmState::Negotiated { srm: SingleResponseMode::Enable } => {
headers.try_append(request_headers)?;
return Ok(OperationRequest::None);
}
SrmState::Negotiated { srm: SingleResponseMode::Disable }
| SrmState::Negotiating { .. } => {}
SrmState::NotNegotiated { srm_supported } => {
if let Some(negotiated_srm) =
Self::check_headers_for_srm(srm_supported, &request_headers)
{
self.srm_state = SrmState::Negotiating { negotiated_srm };
}
}
};
Ok(OperationRequest::GetApplicationInfo(request_headers))
}
State::Request { ref mut headers } if code == OpCode::GetFinal => {
headers.try_append(HeaderSet::from(request))?;
if let SrmState::NotNegotiated { srm_supported } = self.srm_state {
if let Some(negotiated_srm) =
Self::check_headers_for_srm(srm_supported, &headers)
{
self.srm_state = SrmState::Negotiating { negotiated_srm };
}
}
let request_headers = std::mem::replace(headers, HeaderSet::new());
self.state = State::RequestPhaseComplete;
Ok(OperationRequest::GetApplicationData(request_headers))
}
State::Response { ref mut staged_data } if code == OpCode::GetFinal => {
let responses = if current_srm_mode == SingleResponseMode::Enable {
staged_data.all_responses(HeaderSet::new())?
} else {
vec![staged_data.next_response(HeaderSet::new())?]
};
self.check_complete_and_update_state();
Ok(OperationRequest::SendPackets(responses))
}
_ => Err(Error::operation(OpCode::Get, "received invalid request")),
}
}
fn handle_application_response(
&mut self,
response: Result<ApplicationResponse, ObexOperationError>,
) -> Result<Vec<ResponsePacket>, Error> {
let response = match response {
Ok(response) => response,
Err((code, response_headers)) => {
trace!("Application rejected GET request: {code:?}");
self.state = State::Response { staged_data: StagedData::empty() };
self.check_complete_and_update_state();
return Ok(vec![ResponsePacket::new_get(code, response_headers)]);
}
};
match response {
ApplicationResponse::GetInfo(mut response_headers) => {
if !matches!(self.state, State::Request { .. }) {
return Err(Error::operation(OpCode::Get, "GetInfo response in invalid state"));
}
let _ = self.maybe_add_srm_header(&mut response_headers)?;
Ok(vec![ResponsePacket::new_get(ResponseCode::Continue, response_headers)])
}
ApplicationResponse::GetData((data, response_headers)) => {
if !matches!(self.state, State::RequestPhaseComplete) {
return Err(Error::operation(
OpCode::Get,
"Get response before request phase complete",
));
}
let mut srm_headers = HeaderSet::new();
let srm_packet = if self.maybe_add_srm_header(&mut srm_headers)? {
Some(ResponsePacket::new_get(ResponseCode::Continue, srm_headers))
} else {
None
};
let mut staged_data = StagedData::from_data(
data,
self.max_headers_size,
response_headers.encoded_len(),
)?;
let responses = match (self.srm_status(), srm_packet) {
(SingleResponseMode::Enable, Some(packet)) => {
let mut packets = vec![packet];
packets.append(&mut staged_data.all_responses(response_headers)?);
packets
}
(SingleResponseMode::Disable, Some(packet)) => {
vec![packet]
}
(SingleResponseMode::Enable, None) => {
staged_data.all_responses(response_headers)?
}
(SingleResponseMode::Disable, None) => {
vec![staged_data.next_response(response_headers)?]
}
};
self.state = State::Response { staged_data };
self.check_complete_and_update_state();
Ok(responses)
}
ApplicationResponse::Put => {
Err(Error::operation(OpCode::Get, "invalid application response to GET request"))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use crate::header::header_set::{expect_body, expect_end_of_body};
use crate::header::HeaderIdentifier;
use crate::server::test_utils::expect_single_packet;
fn bytes(start_idx: usize, end_idx: usize) -> Vec<u8> {
let s = start_idx as u8;
let e = end_idx as u8;
(s..e).collect::<Vec<u8>>()
}
#[track_caller]
fn expect_packet_with_body(
operation_request: OperationRequest,
expected_code: ResponseCode,
expected_body: Vec<u8>,
) {
let packet = expect_single_packet(operation_request);
assert_eq!(*packet.code(), expected_code);
if expected_code == ResponseCode::Ok {
expect_end_of_body(packet.headers(), expected_body);
} else {
expect_body(packet.headers(), expected_body);
}
}
#[fuchsia::test]
fn single_packet_get_operation() {
let max_packet_size = 50;
let mut operation = GetOperation::new(max_packet_size, false);
assert!(!operation.is_complete());
let headers = HeaderSet::from_header(Header::name("default"));
let request = RequestPacket::new_get_final(headers);
let response1 = operation.handle_peer_request(request).expect("valid request");
assert_matches!(response1,
OperationRequest::GetApplicationData(headers)
if headers.contains_header(&HeaderIdentifier::Name)
);
let payload = bytes(0, 25);
let mut responses2 = operation
.handle_application_response(ApplicationResponse::accept_get(payload, HeaderSet::new()))
.expect("valid response");
let response2 = responses2.pop().expect("one response");
assert_eq!(*response2.code(), ResponseCode::Ok);
expect_end_of_body(response2.headers(), bytes(0, 25));
assert!(operation.is_complete());
}
#[fuchsia::test]
fn multi_packet_get_operation() {
let max_packet_size = 50;
let mut operation = GetOperation::new(max_packet_size, false);
assert!(!operation.is_complete());
let headers1 = HeaderSet::from_header(Header::name("foo".into()));
let request1 = RequestPacket::new_get(headers1);
let response1 = operation.handle_peer_request(request1).expect("valid request");
assert_matches!(response1, OperationRequest::GetApplicationInfo(headers) if headers.contains_header(&HeaderIdentifier::Name));
let info_headers = HeaderSet::from_header(Header::Description("ok".into()));
let response2 = operation
.handle_application_response(ApplicationResponse::accept_get_info(info_headers))
.expect("valid request");
assert_eq!(response2.len(), 1);
assert_eq!(*response2[0].code(), ResponseCode::Continue);
assert!(response2[0].headers().contains_header(&HeaderIdentifier::Description));
let headers3 = HeaderSet::from_header(Header::Type("text/x-vCard".into()));
let request3 = RequestPacket::new_get_final(headers3);
let response3 = operation.handle_peer_request(request3).expect("valid request");
assert_matches!(response3,
OperationRequest::GetApplicationData(headers)
if headers.contains_header(&HeaderIdentifier::Type)
);
let payload = bytes(0, 200);
let response_headers = HeaderSet::from_header(Header::Description("random payload".into()));
let response_packets4 = operation
.handle_application_response(ApplicationResponse::accept_get(payload, response_headers))
.expect("valid response");
assert_eq!(response_packets4.len(), 1);
assert_eq!(*response_packets4[0].code(), ResponseCode::Continue);
expect_body(response_packets4[0].headers(), bytes(0, 11));
let expected_bytes =
vec![bytes(11, 55), bytes(55, 99), bytes(99, 143), bytes(143, 187), bytes(187, 200)];
for (i, expected) in expected_bytes.into_iter().enumerate() {
let expected_code = if i == 4 { ResponseCode::Ok } else { ResponseCode::Continue };
let request = RequestPacket::new_get_final(HeaderSet::new());
let response = operation.handle_peer_request(request).expect("valid request");
expect_packet_with_body(response, expected_code, expected);
}
assert!(operation.is_complete());
}
#[fuchsia::test]
fn multi_packet_get_operation_srm_enabled() {
let max_packet_size = 50;
let mut operation = GetOperation::new(max_packet_size, true);
assert!(!operation.is_complete());
assert_eq!(operation.srm_status(), SingleResponseMode::Disable);
let headers1 = HeaderSet::from_headers(vec![
Header::name("foo".into()),
SingleResponseMode::Enable.into(),
])
.unwrap();
let request1 = RequestPacket::new_get(headers1);
let response1 = operation.handle_peer_request(request1).expect("valid request");
assert_matches!(response1, OperationRequest::GetApplicationInfo(headers) if headers.contains_header(&HeaderIdentifier::Name));
let info_headers = HeaderSet::from_header(Header::Description("ok".into()));
let response2 = operation
.handle_application_response(ApplicationResponse::accept_get_info(info_headers))
.expect("valid request");
assert_eq!(response2.len(), 1);
assert_eq!(*response2[0].code(), ResponseCode::Continue);
assert!(response2[0].headers().contains_header(&HeaderIdentifier::Description));
assert!(response2[0].headers().contains_header(&HeaderIdentifier::SingleResponseMode));
assert_eq!(operation.srm_status(), SingleResponseMode::Enable);
let headers3 = HeaderSet::from_header(Header::Description("random payload".into()));
let request3 = RequestPacket::new_get(headers3);
let response3 = operation.handle_peer_request(request3).expect("valid request");
assert_matches!(response3, OperationRequest::None);
let headers4 = HeaderSet::from_header(Header::Type("text/x-vCard".into()));
let request4 = RequestPacket::new_get_final(headers4);
let response4 = operation.handle_peer_request(request4).expect("valid request");
assert_matches!(response4,
OperationRequest::GetApplicationData(headers)
if
headers.contains_header(&HeaderIdentifier::Type)
&& headers.contains_header(&HeaderIdentifier::Description)
);
let payload = bytes(0, 100);
let response_headers = HeaderSet::from_header(Header::Description("random payload".into()));
let response_packets = operation
.handle_application_response(ApplicationResponse::accept_get(payload, response_headers))
.expect("valid response");
assert_eq!(response_packets.len(), 4);
assert_eq!(*response_packets[0].code(), ResponseCode::Continue);
expect_body(response_packets[0].headers(), bytes(0, 11));
let expected_bytes = [bytes(11, 55), bytes(55, 99), bytes(99, 100)];
for (i, expected) in expected_bytes.into_iter().enumerate() {
let idx = i + 1;
let expected_code = if idx == 3 { ResponseCode::Ok } else { ResponseCode::Continue };
assert_eq!(*response_packets[idx].code(), expected_code);
if expected_code == ResponseCode::Ok {
expect_end_of_body(response_packets[idx].headers(), expected);
} else {
expect_body(response_packets[idx].headers(), expected);
}
}
assert!(operation.is_complete());
}
#[fuchsia::test]
fn srm_enable_request_during_get_final_success() {
let max_packet_size = 50;
let mut operation = GetOperation::new(max_packet_size, true);
let headers1 = HeaderSet::from_header(SingleResponseMode::Enable.into());
let request1 = RequestPacket::new_get_final(headers1);
let response1 = operation.handle_peer_request(request1).expect("valid request");
assert_matches!(response1, OperationRequest::GetApplicationData(_));
assert!(!operation.is_complete());
let payload = bytes(0, 90);
let response_headers = HeaderSet::from_header(Header::Description("random payload".into()));
let response_packets = operation
.handle_application_response(ApplicationResponse::accept_get(payload, response_headers))
.expect("valid response");
assert_eq!(response_packets.len(), 4);
assert_eq!(*response_packets[0].code(), ResponseCode::Continue);
assert!(response_packets[0]
.headers()
.contains_header(&HeaderIdentifier::SingleResponseMode));
assert!(!response_packets[0].headers().contains_header(&HeaderIdentifier::Description));
assert!(!response_packets[0].headers().contains_header(&HeaderIdentifier::Body));
assert_eq!(operation.srm_status(), SingleResponseMode::Enable);
assert!(response_packets[1].headers().contains_header(&HeaderIdentifier::Description));
expect_body(response_packets[1].headers(), bytes(0, 11));
expect_body(response_packets[2].headers(), bytes(11, 55));
expect_end_of_body(response_packets[3].headers(), bytes(55, 90));
assert!(operation.is_complete());
}
#[fuchsia::test]
fn srm_disable_request_during_get_final_success() {
let max_packet_size = 50;
let mut operation = GetOperation::new(max_packet_size, false);
let headers1 = HeaderSet::from_header(SingleResponseMode::Enable.into());
let request1 = RequestPacket::new_get_final(headers1);
let response1 = operation.handle_peer_request(request1).expect("valid request");
assert_matches!(response1, OperationRequest::GetApplicationData(_));
assert!(!operation.is_complete());
let payload = bytes(0, 90);
let response_packets = operation
.handle_application_response(ApplicationResponse::accept_get(payload, HeaderSet::new()))
.expect("valid response");
assert_eq!(response_packets.len(), 1);
assert_eq!(*response_packets[0].code(), ResponseCode::Continue);
let received_srm = response_packets[0]
.headers()
.get(&HeaderIdentifier::SingleResponseMode)
.expect("contains SRM");
assert_eq!(*received_srm, Header::SingleResponseMode(SingleResponseMode::Disable));
assert!(!response_packets[0].headers().contains_header(&HeaderIdentifier::Body));
assert_eq!(operation.srm_status(), SingleResponseMode::Disable);
let expected_bytes = vec![bytes(0, 44), bytes(44, 88), bytes(88, 90)];
for (i, expected) in expected_bytes.into_iter().enumerate() {
let expected_code = if i == 2 { ResponseCode::Ok } else { ResponseCode::Continue };
let request2 = RequestPacket::new_get_final(HeaderSet::new());
let response2 = operation.handle_peer_request(request2).expect("valid request");
expect_packet_with_body(response2, expected_code, expected);
}
assert!(operation.is_complete());
}
#[fuchsia::test]
fn application_rejects_request_success() {
let mut operation = GetOperation::new_at_state(10, State::RequestPhaseComplete);
let headers = HeaderSet::from_header(Header::Description("not allowed today".into()));
let response_packets = operation
.handle_application_response(Err((ResponseCode::Forbidden, headers)))
.expect("rejection is ok");
assert_eq!(*response_packets[0].code(), ResponseCode::Forbidden);
assert!(response_packets[0].headers().contains_header(&HeaderIdentifier::Description));
assert!(operation.is_complete());
}
#[fuchsia::test]
fn handle_application_response_error() {
let max_packet_size = 15;
let mut operation = GetOperation::new(max_packet_size, false);
let data = vec![1, 2, 3];
assert_matches!(
operation.handle_application_response(ApplicationResponse::accept_get(
data,
HeaderSet::new()
)),
Err(Error::OperationError { .. })
);
let mut operation = GetOperation::new_at_state(10, State::RequestPhaseComplete);
assert_matches!(
operation.handle_application_response(ApplicationResponse::accept_get_info(
HeaderSet::new()
)),
Err(Error::OperationError { .. })
);
}
#[fuchsia::test]
fn non_get_request_is_error() {
let mut operation = GetOperation::new(50, false);
let random_request1 = RequestPacket::new_put(HeaderSet::new());
assert_matches!(
operation.handle_peer_request(random_request1),
Err(Error::OperationError { .. })
);
let random_request2 = RequestPacket::new_disconnect(HeaderSet::new());
assert_matches!(
operation.handle_peer_request(random_request2),
Err(Error::OperationError { .. })
);
}
#[fuchsia::test]
fn get_request_invalid_state_is_error() {
let random_headers = HeaderSet::from_header(Header::name("foo".into()));
let mut operation1 = GetOperation::new_at_state(10, State::RequestPhaseComplete);
let request1 = RequestPacket::new_get(random_headers.clone());
let response1 = operation1.handle_peer_request(request1);
assert_matches!(response1, Err(Error::OperationError { .. }));
let mut operation2 = GetOperation::new_at_state(10, State::Complete);
let request2 = RequestPacket::new_get(random_headers.clone());
let response2 = operation2.handle_peer_request(request2);
assert_matches!(response2, Err(Error::OperationError { .. }));
let staged_data = StagedData { first: None, rest: VecDeque::from(vec![vec![1, 2, 3]]) };
let mut operation3 = GetOperation::new_at_state(10, State::Response { staged_data });
let request3 = RequestPacket::new_get(random_headers);
let response3 = operation3.handle_peer_request(request3);
assert_matches!(response3, Err(Error::OperationError { .. }));
}
#[fuchsia::test]
fn build_staged_data_success() {
let empty_data = Vec::new();
let empty_headers = HeaderSet::new();
let result = StagedData::from_data(empty_data, 50, empty_headers.encoded_len())
.expect("can divide data");
let expected = StagedData { first: Some(Some(vec![])), rest: VecDeque::new() };
assert_eq!(result, expected);
let headers = HeaderSet::from_header(Header::name("foo".into()));
let data = vec![1, 2, 3];
let result =
StagedData::from_data(data, 50, headers.encoded_len()).expect("can divide data");
let expected = StagedData { first: Some(Some(vec![1, 2, 3])), rest: VecDeque::new() };
assert_eq!(result, expected);
let headers = HeaderSet::from_header(Header::Http(vec![5, 5, 5]));
let max = 10;
let large_data = (0..50).collect::<Vec<u8>>();
let result =
StagedData::from_data(large_data, max, headers.encoded_len()).expect("can divide data");
let first = Some(vec![0]);
let rest = VecDeque::from(vec![
bytes(1, 8),
bytes(8, 15),
bytes(15, 22),
bytes(22, 29),
bytes(29, 36),
bytes(36, 43),
bytes(43, 50),
]);
let expected = StagedData { first: Some(first), rest };
assert_eq!(result, expected);
}
#[fuchsia::test]
fn build_staged_data_error() {
let random_data = bytes(0, 50);
let too_small_max = 2;
assert_matches!(
StagedData::from_data(random_data.clone(), too_small_max, 0),
Err(Error::OperationError { .. })
);
assert_matches!(
StagedData::from_data(random_data.clone(), 0, 0),
Err(Error::OperationError { .. })
);
let small_max = 10;
let large_header_size = 20;
assert_matches!(
StagedData::from_data(random_data, small_max, large_header_size),
Err(Error::OperationError { .. })
);
}
#[fuchsia::test]
fn empty_staged_data_success() {
let empty = Vec::new();
let empty_headers = HeaderSet::new();
let mut staged = StagedData::from_data(empty.clone(), 50, empty_headers.encoded_len())
.expect("can construct");
assert!(staged.is_first_response());
assert!(!staged.is_complete());
let response = staged.next_response(empty_headers).expect("has first response");
assert_eq!(*response.code(), ResponseCode::Ok);
expect_end_of_body(response.headers(), vec![]);
assert!(!staged.is_first_response());
assert!(staged.is_complete());
}
#[fuchsia::test]
fn single_packet_staged_data_success() {
let single = vec![1, 2, 3];
let empty_headers = HeaderSet::new();
let mut staged = StagedData::from_data(single.clone(), 50, empty_headers.encoded_len())
.expect("can construct");
let response = staged.next_response(empty_headers).expect("has first response");
assert_eq!(*response.code(), ResponseCode::Ok);
expect_end_of_body(response.headers(), single);
assert!(staged.is_complete());
}
#[fuchsia::test]
fn multi_packet_staged_data_success() {
let max_packet_size = 10;
let large_data = (0..30).collect::<Vec<u8>>();
let headers = HeaderSet::from_header(Header::Who(vec![1, 2, 3, 4, 5]));
let mut staged = StagedData::from_data(large_data, max_packet_size, headers.encoded_len())
.expect("can construct");
let response1 = staged.next_response(headers).expect("has first response");
assert_eq!(*response1.code(), ResponseCode::Continue);
assert!(response1.headers().contains_header(&HeaderIdentifier::Who));
assert!(!response1.headers().contains_header(&HeaderIdentifier::Body));
assert!(!staged.is_complete());
let expected_bytes = vec![bytes(0, 7), bytes(7, 14), bytes(14, 21), bytes(21, 28)];
for expected in expected_bytes {
let r = staged.next_response(HeaderSet::new()).expect("has next response");
assert_eq!(*r.code(), ResponseCode::Continue);
expect_body(r.headers(), expected);
}
let final_response = staged.next_response(HeaderSet::new()).expect("has next response");
assert_eq!(*final_response.code(), ResponseCode::Ok);
let expected = bytes(28, 30);
expect_end_of_body(final_response.headers(), expected);
assert!(staged.is_complete());
}
#[fuchsia::test]
fn staged_data_response_error() {
let mut staged = StagedData::new(None, VecDeque::new());
let _ = staged.next_response(HeaderSet::new()).expect("has first response");
assert!(staged.is_complete());
assert_matches!(staged.next_response(HeaderSet::new()), Err(Error::OperationError { .. }));
}
}