ReadRequest: Basic stuff works across multi-hop reads

This commit is contained in:
Kedar Sovani 2023-02-26 15:02:56 +05:30
parent 487124c9dc
commit dd47aa9a69
6 changed files with 78 additions and 48 deletions

View file

@ -37,7 +37,7 @@ use crate::{
InteractionConsumer, Transaction, InteractionConsumer, Transaction,
}, },
secure_channel::pake::PaseMgr, secure_channel::pake::PaseMgr,
tlv::{TLVArray, TLVWriter, TagType, ToTLV}, tlv::{self, FromTLV, TLVArray, TLVWriter, TagType, ToTLV},
transport::{ transport::{
proto_demux::ResponseRequired, proto_demux::ResponseRequired,
session::{Session, SessionMode}, session::{Session, SessionMode},
@ -259,13 +259,17 @@ impl InteractionConsumer for DataModel {
fn consume_read_attr( fn consume_read_attr(
&self, &self,
req: &ReadReq, rx_buf: &[u8],
trans: &mut Transaction, trans: &mut Transaction,
tw: &mut TLVWriter, tw: &mut TLVWriter,
) -> Result<(), Error> { ) -> Result<(), Error> {
let resume = self.handle_read_attr_array(req, trans, tw)?; let mut resume_from = None;
if let Some(resume) = resume { let root = tlv::get_root_node(rx_buf)?;
let req = ReadReq::from_tlv(&root)?;
self.handle_read_attr_array(&req, trans, tw, &mut resume_from)?;
if resume_from.is_some() {
// This is a multi-hop read transaction, remember this read request // This is a multi-hop read transaction, remember this read request
let resume = read::ResumeReadReq::new(rx_buf, &resume_from)?;
if !trans.exch.is_data_none() { if !trans.exch.is_data_none() {
error!("Exchange data already set, and multi-hop read"); error!("Exchange data already set, and multi-hop read");
return Err(Error::InvalidState); return Err(Error::InvalidState);
@ -316,15 +320,16 @@ impl InteractionConsumer for DataModel {
trans: &mut Transaction, trans: &mut Transaction,
tw: &mut TLVWriter, tw: &mut TLVWriter,
) -> Result<(OpCode, ResponseRequired), Error> { ) -> Result<(OpCode, ResponseRequired), Error> {
if let Some(resume) = trans.exch.take_data_boxed::<ResumeReq>() { if let Some(mut resume) = trans.exch.take_data_boxed::<ResumeReq>() {
match *resume { let result = match *resume {
ResumeReq::Read(_) => Ok((OpCode::Reserved, ResponseRequired::No)), ResumeReq::Read(ref mut read) => self.handle_resume_read(read, trans, tw)?,
ResumeReq::Subscribe(mut ctx) => { ResumeReq::Subscribe(mut ctx) => {
let result = self.handle_subscription_confirm(trans, tw, &mut ctx)?; self.handle_subscription_confirm(trans, tw, &mut ctx)?
trans.exch.set_data_boxed(resume);
Ok(result)
} }
} };
trans.exch.set_data_boxed(resume);
Ok(result)
} else { } else {
// Nothing to do for now // Nothing to do for now
trans.complete(); trans.complete();

View file

@ -16,8 +16,10 @@
*/ */
use crate::data_model::{core::DataModel, objects::*}; use crate::data_model::{core::DataModel, objects::*};
use crate::interaction_model::core::OpCode;
use crate::tlv::FromTLV;
use crate::transport::packet::Packet; use crate::transport::packet::Packet;
use crate::utils::writebuf::WriteBuf; use crate::transport::proto_demux::ResponseRequired;
use crate::{ use crate::{
acl::{AccessReq, Accessor}, acl::{AccessReq, Accessor},
error::*, error::*,
@ -25,12 +27,12 @@ use crate::{
core::IMStatusCode, core::IMStatusCode,
messages::{ messages::{
ib::{self, DataVersionFilter}, ib::{self, DataVersionFilter},
msg::{self, ReadReq, ReportDataTag::MoreChunkedMsgs}, msg::{self, ReadReq, ReportDataTag::MoreChunkedMsgs, ReportDataTag::SupressResponse},
GenericPath, GenericPath,
}, },
Transaction, Transaction,
}, },
tlv::{TLVArray, TLVWriter, TagType, ToTLV}, tlv::{self, TLVArray, TLVWriter, TagType, ToTLV},
}; };
/// Encoder for generating a response to a read request /// Encoder for generating a response to a read request
@ -112,7 +114,21 @@ pub struct ResumeReadReq {
/// will start encoding from this attribute onwards. /// will start encoding from this attribute onwards.
/// Note that given wildcard reads, one PendingPath in the member above can generated /// Note that given wildcard reads, one PendingPath in the member above can generated
/// multiple encode paths. Hence this has to be maintained separately. /// multiple encode paths. Hence this has to be maintained separately.
resume_encode: Option<GenericPath>, resume_from: Option<GenericPath>,
}
impl ResumeReadReq {
pub fn new(rx_buf: &[u8], resume_from: &Option<GenericPath>) -> Result<Self, Error> {
let mut packet = Packet::new_rx()?;
let dst = packet.as_borrow_slice();
let src_len = rx_buf.len();
dst[..src_len].copy_from_slice(rx_buf);
packet.get_parsebuf()?.set_len(src_len);
Ok(ResumeReadReq {
pending_req: Some(packet),
resume_from: *resume_from,
})
}
} }
impl DataModel { impl DataModel {
@ -195,9 +211,8 @@ impl DataModel {
read_req: &ReadReq, read_req: &ReadReq,
trans: &mut Transaction, trans: &mut Transaction,
tw: &mut TLVWriter, tw: &mut TLVWriter,
) -> Result<Option<ResumeReadReq>, Error> { resume_from: &mut Option<GenericPath>,
let mut resume_read_req: ResumeReadReq = Default::default(); ) -> Result<(), Error> {
let mut attr_encoder = AttrReadEncoder::new(tw); let mut attr_encoder = AttrReadEncoder::new(tw);
if let Some(filters) = &read_req.dataver_filters { if let Some(filters) = &read_req.dataver_filters {
attr_encoder.set_data_ver_filters(filters); attr_encoder.set_data_ver_filters(filters);
@ -221,7 +236,7 @@ impl DataModel {
&accessor, &accessor,
&mut attr_encoder, &mut attr_encoder,
&mut attr_details, &mut attr_details,
&mut resume_read_req.resume_encode, resume_from,
); );
} }
tw.end_container()?; tw.end_container()?;
@ -229,25 +244,35 @@ impl DataModel {
// If there was an error, indicate chunking. The resume_read_req would have been // If there was an error, indicate chunking. The resume_read_req would have been
// already populated from in the loop above. // already populated from in the loop above.
tw.bool(TagType::Context(MoreChunkedMsgs as u8), true)?; tw.bool(TagType::Context(MoreChunkedMsgs as u8), true)?;
// Retain the entire request, because we need the data-filters, and subsequent attr-reads, if any return Ok(());
// when we resume this read in the next hop
resume_read_req.pending_req = Some(copy_read_req_to_packet(read_req)?);
return Ok(Some(resume_read_req));
} }
} }
Ok(None) // A None resume_from indicates no chunking
*resume_from = None;
Ok(())
}
pub fn handle_resume_read(
&self,
resume_read_req: &mut ResumeReadReq,
trans: &mut Transaction,
tw: &mut TLVWriter,
) -> Result<(OpCode, ResponseRequired), Error> {
if let Some(packet) = resume_read_req.pending_req.as_mut() {
let rx_buf = packet.get_parsebuf()?.as_borrow_slice();
let root = tlv::get_root_node(rx_buf)?;
let req = ReadReq::from_tlv(&root)?;
tw.start_struct(TagType::Anonymous)?;
self.handle_read_attr_array(&req, trans, tw, &mut resume_read_req.resume_from)?;
if resume_read_req.resume_from.is_none() {
tw.bool(TagType::Context(SupressResponse as u8), true)?;
// Mark transaction complete, if not chunked
trans.complete();
}
tw.end_container()?;
}
Ok((OpCode::ReportData, ResponseRequired::Yes))
} }
} }
fn copy_read_req_to_packet(read_req: &ReadReq) -> Result<Packet<'static>, Error> {
let mut packet = Packet::new_rx()?;
let backup = packet.as_borrow_slice();
let backup_len = backup.len();
let mut wb = WriteBuf::new(backup, backup_len);
let mut tw = TLVWriter::new(&mut wb);
// TODO: This is unnecessarily wasteful, could directly copy &[u8] if accessible
read_req.to_tlv(&mut tw, TagType::Anonymous)?;
let data_len = wb.as_borrow_slice().len();
packet.get_parsebuf()?.set_len(data_len);
Ok(packet)
}

View file

@ -50,7 +50,8 @@ impl DataModel {
TagType::Context(msg::ReportDataTag::SubscriptionId as u8), TagType::Context(msg::ReportDataTag::SubscriptionId as u8),
ctx.id, ctx.id,
)?; )?;
self.handle_read_attr_array(&read_req, trans, tw)?; let mut resume_from = None;
self.handle_read_attr_array(&read_req, trans, tw, &mut resume_from)?;
tw.end_container()?; tw.end_container()?;
Ok(ctx) Ok(ctx)

View file

@ -23,7 +23,7 @@ use crate::{
use self::{ use self::{
core::OpCode, core::OpCode,
messages::msg::{InvReq, ReadReq, StatusResp, SubscribeReq, WriteReq}, messages::msg::{InvReq, StatusResp, SubscribeReq, WriteReq},
}; };
#[derive(PartialEq)] #[derive(PartialEq)]
@ -47,7 +47,9 @@ pub trait InteractionConsumer {
fn consume_read_attr( fn consume_read_attr(
&self, &self,
req: &ReadReq, // TODO: This handling is different from the other APIs here, identify
// consistent options for this trait
req: &[u8],
trans: &mut Transaction, trans: &mut Transaction,
tw: &mut TLVWriter, tw: &mut TLVWriter,
) -> Result<(), Error>; ) -> Result<(), Error>;

View file

@ -18,13 +18,13 @@
use crate::{ use crate::{
error::Error, error::Error,
interaction_model::core::OpCode, interaction_model::core::OpCode,
tlv::{get_root_node_struct, FromTLV, TLVWriter, TagType}, tlv::{TLVWriter, TagType},
transport::{packet::Packet, proto_demux::ResponseRequired}, transport::{packet::Packet, proto_demux::ResponseRequired},
utils::writebuf::WriteBuf, utils::writebuf::WriteBuf,
wb_shrink, wb_unshrink, wb_shrink, wb_unshrink,
}; };
use super::{messages::msg::ReadReq, InteractionModel, Transaction}; use super::{InteractionModel, Transaction};
impl InteractionModel { impl InteractionModel {
pub fn handle_read_req( pub fn handle_read_req(
@ -40,13 +40,11 @@ impl InteractionModel {
let proto_tx_wb = proto_tx.get_writebuf()?; let proto_tx_wb = proto_tx.get_writebuf()?;
let mut child_wb = wb_shrink!(proto_tx_wb, RESERVE_SIZE); let mut child_wb = wb_shrink!(proto_tx_wb, RESERVE_SIZE);
let mut tw = TLVWriter::new(&mut child_wb); let mut tw = TLVWriter::new(&mut child_wb);
let root = get_root_node_struct(rx_buf)?;
let read_req = ReadReq::from_tlv(&root)?;
tw.start_struct(TagType::Anonymous)?; tw.start_struct(TagType::Anonymous)?;
self.consumer.consume_read_attr(&read_req, trans, &mut tw)?; self.consumer.consume_read_attr(rx_buf, trans, &mut tw)?;
// Now that we have everything, start using the proto_tx_wb, by unshrinking it //Now that we have everything, start using the proto_tx_wb, by unshrinking it
wb_unshrink!(proto_tx_wb, child_wb); wb_unshrink!(proto_tx_wb, child_wb);
let mut tw = TLVWriter::new(proto_tx_wb); let mut tw = TLVWriter::new(proto_tx_wb);
tw.end_container()?; tw.end_container()?;

View file

@ -19,7 +19,6 @@ use boxslab::Slab;
use matter::error::Error; use matter::error::Error;
use matter::interaction_model::core::OpCode; use matter::interaction_model::core::OpCode;
use matter::interaction_model::messages::msg::InvReq; use matter::interaction_model::messages::msg::InvReq;
use matter::interaction_model::messages::msg::ReadReq;
use matter::interaction_model::messages::msg::WriteReq; use matter::interaction_model::messages::msg::WriteReq;
use matter::interaction_model::InteractionConsumer; use matter::interaction_model::InteractionConsumer;
use matter::interaction_model::InteractionModel; use matter::interaction_model::InteractionModel;
@ -94,7 +93,7 @@ impl InteractionConsumer for DataModel {
fn consume_read_attr( fn consume_read_attr(
&self, &self,
_req: &ReadReq, _req: &[u8],
_trans: &mut Transaction, _trans: &mut Transaction,
_tlvwriter: &mut TLVWriter, _tlvwriter: &mut TLVWriter,
) -> Result<(), Error> { ) -> Result<(), Error> {