1 use crate::{
2 gatt::server::att_database::AttDatabase,
3 packets::{
4 AttAttributeDataBuilder, AttChild, AttErrorResponseBuilder, AttOpcode, AttReadRequestView,
5 AttReadResponseBuilder,
6 },
7 };
8
9 use super::helpers::truncate_att_data::truncate_att_data;
10
handle_read_request<T: AttDatabase>( request: AttReadRequestView<'_>, mtu: usize, db: &T, ) -> AttChild11 pub async fn handle_read_request<T: AttDatabase>(
12 request: AttReadRequestView<'_>,
13 mtu: usize,
14 db: &T,
15 ) -> AttChild {
16 let handle = request.get_attribute_handle().into();
17
18 match db.read_attribute(handle).await {
19 Ok(data) => AttReadResponseBuilder {
20 // as per 5.3 3F 3.4.4.4 ATT_READ_RSP, we truncate to MTU - 1
21 value: AttAttributeDataBuilder { _child_: truncate_att_data(data, mtu - 1) },
22 }
23 .into(),
24 Err(error_code) => AttErrorResponseBuilder {
25 opcode_in_error: AttOpcode::READ_REQUEST,
26 handle_in_error: handle.into(),
27 error_code,
28 }
29 .into(),
30 }
31 }
32
33 #[cfg(test)]
34 mod test {
35 use super::*;
36
37 use crate::{
38 core::uuid::Uuid,
39 gatt::{
40 ids::AttHandle,
41 server::{
42 att_database::{AttAttribute, AttPermissions},
43 test::test_att_db::TestAttDatabase,
44 },
45 },
46 packets::{AttAttributeDataChild, AttErrorCode, AttReadRequestBuilder, Serializable},
47 utils::packet::{build_att_data, build_view_or_crash},
48 };
49
make_db_with_handle_and_value(handle: u16, value: Vec<u8>) -> TestAttDatabase50 fn make_db_with_handle_and_value(handle: u16, value: Vec<u8>) -> TestAttDatabase {
51 TestAttDatabase::new(vec![(
52 AttAttribute {
53 handle: AttHandle(handle),
54 type_: Uuid::new(0x1234),
55 permissions: AttPermissions::READABLE,
56 },
57 value,
58 )])
59 }
60
do_read_request_with_handle_and_mtu( handle: u16, mtu: usize, db: &TestAttDatabase, ) -> AttChild61 fn do_read_request_with_handle_and_mtu(
62 handle: u16,
63 mtu: usize,
64 db: &TestAttDatabase,
65 ) -> AttChild {
66 let att_view = build_view_or_crash(AttReadRequestBuilder {
67 attribute_handle: AttHandle(handle).into(),
68 });
69 tokio_test::block_on(handle_read_request(att_view.view(), mtu, db))
70 }
71
72 #[test]
test_simple_read()73 fn test_simple_read() {
74 let db = make_db_with_handle_and_value(3, vec![4, 5]);
75
76 let response = do_read_request_with_handle_and_mtu(3, 31, &db);
77
78 response.to_vec().unwrap(); // check it serializes
79 assert_eq!(
80 response,
81 AttChild::AttReadResponse(AttReadResponseBuilder {
82 value: build_att_data(AttAttributeDataChild::RawData([4, 5].into()))
83 })
84 )
85 }
86
87 #[test]
test_truncated_read()88 fn test_truncated_read() {
89 let db = make_db_with_handle_and_value(3, vec![4, 5]);
90
91 // act
92 let response = do_read_request_with_handle_and_mtu(3, 2, &db);
93
94 // assert
95 assert_eq!(response.to_vec().unwrap(), vec![4]);
96 }
97
98 #[test]
test_missed_read()99 fn test_missed_read() {
100 let db = make_db_with_handle_and_value(3, vec![4, 5]);
101
102 // act
103 let response = do_read_request_with_handle_and_mtu(4, 31, &db);
104
105 // assert
106 assert_eq!(
107 response,
108 AttChild::AttErrorResponse(AttErrorResponseBuilder {
109 opcode_in_error: AttOpcode::READ_REQUEST,
110 handle_in_error: AttHandle(4).into(),
111 error_code: AttErrorCode::INVALID_HANDLE,
112 })
113 );
114 }
115
make_db_with_unreadable_handle(handle: u16) -> TestAttDatabase116 fn make_db_with_unreadable_handle(handle: u16) -> TestAttDatabase {
117 TestAttDatabase::new(vec![(
118 AttAttribute {
119 handle: AttHandle(handle),
120 type_: Uuid::new(0x1234),
121 permissions: AttPermissions::empty(),
122 },
123 vec![],
124 )])
125 }
126
127 #[test]
test_not_readable()128 fn test_not_readable() {
129 let db = make_db_with_unreadable_handle(3);
130
131 // act
132 let response = do_read_request_with_handle_and_mtu(3, 31, &db);
133
134 // assert
135 assert_eq!(
136 response,
137 AttChild::AttErrorResponse(AttErrorResponseBuilder {
138 opcode_in_error: AttOpcode::READ_REQUEST,
139 handle_in_error: AttHandle(3).into(),
140 error_code: AttErrorCode::READ_NOT_PERMITTED,
141 })
142 );
143 }
144 }
145