bt_obex/server/
put.rs
1use log::trace;
6
7use crate::error::Error;
8use crate::header::{Header, HeaderSet, SingleResponseMode};
9use crate::operation::{OpCode, RequestPacket, ResponseCode, ResponsePacket};
10use crate::server::handler::ObexOperationError;
11use crate::server::{ApplicationResponse, OperationRequest, ServerOperation};
12
13#[derive(Debug)]
15enum State {
16 Request { headers: HeaderSet, staged_data: Option<Vec<u8>> },
18 RequestPhaseComplete,
20 Complete,
22}
23
24pub struct PutOperation {
26 srm_supported: bool,
28 srm: Option<SingleResponseMode>,
32 state: State,
33}
34
35impl PutOperation {
36 pub fn new(srm_supported: bool) -> Self {
37 Self {
38 srm_supported,
39 srm: None,
40 state: State::Request { headers: HeaderSet::new(), staged_data: None },
41 }
42 }
43
44 #[cfg(test)]
45 fn new_at_state(state: State) -> Self {
46 Self { srm_supported: false, srm: None, state }
47 }
48}
49
50impl ServerOperation for PutOperation {
51 fn srm_status(&self) -> SingleResponseMode {
52 self.srm.unwrap_or(SingleResponseMode::Disable)
53 }
54
55 fn is_complete(&self) -> bool {
56 matches!(self.state, State::Complete)
57 }
58
59 fn handle_peer_request(&mut self, request: RequestPacket) -> Result<OperationRequest, Error> {
60 let code = *request.code();
61 let mut request_headers = HeaderSet::from(request);
62 match &mut self.state {
63 State::Request { ref mut headers, ref mut staged_data } if code == OpCode::Put => {
64 if let Ok(mut data) = request_headers.remove_body(false) {
67 let staged = staged_data.get_or_insert(Vec::new());
68 staged.append(&mut data);
69 }
70 headers.try_append(request_headers)?;
71
72 let response_headers = match self.srm {
78 Some(SingleResponseMode::Enable) => return Ok(OperationRequest::None),
79 Some(SingleResponseMode::Disable) => HeaderSet::new(),
80 None => {
81 self.srm = Self::check_headers_for_srm(self.srm_supported, &headers);
82 self.srm.as_ref().map_or_else(HeaderSet::new, |srm| {
84 HeaderSet::from_header(Header::SingleResponseMode(*srm))
85 })
86 }
87 };
88 let response =
89 ResponsePacket::new_no_data(ResponseCode::Continue, response_headers);
90 Ok(OperationRequest::SendPackets(vec![response]))
91 }
92 State::Request { ref mut headers, ref mut staged_data } if code == OpCode::PutFinal => {
93 if let Ok(mut data) = request_headers.remove_body(true) {
96 let staged = staged_data.get_or_insert(Vec::new());
97 staged.append(&mut data);
98 }
99 headers.try_append(request_headers)?;
100 let request_headers = std::mem::replace(headers, HeaderSet::new());
101 let request_data = std::mem::take(staged_data);
102 self.state = State::RequestPhaseComplete;
103 if let Some(data) = request_data {
107 Ok(OperationRequest::PutApplicationData(data, request_headers))
108 } else {
109 Ok(OperationRequest::DeleteApplicationData(request_headers))
110 }
111 }
112 _ => Err(Error::operation(OpCode::Put, "received invalid request")),
113 }
114 }
115
116 fn handle_application_response(
117 &mut self,
118 response: Result<ApplicationResponse, ObexOperationError>,
119 ) -> Result<Vec<ResponsePacket>, Error> {
120 if !matches!(self.state, State::RequestPhaseComplete) {
122 return Err(Error::operation(OpCode::Put, "invalid state"));
123 }
124
125 let response = match response {
126 Ok(ApplicationResponse::Put) => {
127 ResponsePacket::new_no_data(ResponseCode::Ok, HeaderSet::new())
128 }
129 Err((code, response_headers)) => {
130 trace!("Application rejected PUT request: {code:?}");
131 ResponsePacket::new_no_data(code, response_headers)
132 }
133 _ => {
134 return Err(Error::operation(
135 OpCode::Put,
136 "invalid application response to PUT request",
137 ));
138 }
139 };
140 self.state = State::Complete;
141 Ok(vec![response])
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 use assert_matches::assert_matches;
150
151 use crate::header::HeaderIdentifier;
152 use crate::server::test_utils::expect_single_packet;
153
154 #[fuchsia::test]
155 fn single_stage_put_success() {
156 let mut operation = PutOperation::new(false);
157 assert!(!operation.is_complete());
158
159 let body = (1..10).collect::<Vec<u8>>();
160 let eob = Header::EndOfBody(body.clone());
161 let name = Header::name("foo".into());
162 let type_ = Header::Type("text".into());
163 let headers = HeaderSet::from_headers(vec![eob, name, type_]).unwrap();
164 let request = RequestPacket::new_put_final(headers);
165 let response = operation.handle_peer_request(request).expect("valid request");
166 assert_matches!(response,
167 OperationRequest::PutApplicationData(data, headers)
168 if headers.contains_header(&HeaderIdentifier::Name)
169 && headers.contains_header(&HeaderIdentifier::Type)
170 && data == body
171 );
172 assert!(!operation.is_complete());
173
174 let responses = operation
176 .handle_application_response(ApplicationResponse::accept_put())
177 .expect("valid response");
178 assert_eq!(*responses[0].code(), ResponseCode::Ok);
179 assert!(operation.is_complete());
180 }
181
182 #[fuchsia::test]
183 fn multi_packet_put() {
184 let mut operation = PutOperation::new(false);
185 assert!(!operation.is_complete());
186
187 let headers1 = HeaderSet::from_header(Header::name("random file".into()));
190 let request1 = RequestPacket::new_put(headers1);
191 let response1 = operation.handle_peer_request(request1).expect("valid request");
192 let response_packet1 = expect_single_packet(response1);
193 assert_eq!(*response_packet1.code(), ResponseCode::Continue);
194 assert!(!operation.is_complete());
195
196 let body2 = (0..50).collect::<Vec<u8>>();
199 let headers2 = HeaderSet::from_header(Header::Body(body2));
200 let request2 = RequestPacket::new_put(headers2);
201 let response2 = operation.handle_peer_request(request2).expect("valid request");
202 let response_packet2 = expect_single_packet(response2);
203 assert_eq!(*response_packet2.code(), ResponseCode::Continue);
204 assert!(!operation.is_complete());
205
206 let body3 = (50..100).collect::<Vec<u8>>();
209 let headers3 = HeaderSet::from_header(Header::EndOfBody(body3));
210 let request3 = RequestPacket::new_put_final(headers3);
211 let response3 = operation.handle_peer_request(request3).expect("valid request");
212 let expected_payload = (0..100).collect::<Vec<u8>>();
213 assert_matches!(response3,
214 OperationRequest::PutApplicationData(data, headers)
215 if headers.contains_header(&HeaderIdentifier::Name)
216 && data == expected_payload
217 );
218 assert!(!operation.is_complete());
219
220 let responses4 = operation
222 .handle_application_response(ApplicationResponse::accept_put())
223 .expect("valid response");
224 assert_eq!(responses4.len(), 1);
225 assert_eq!(*responses4[0].code(), ResponseCode::Ok);
226 assert!(operation.is_complete());
227 }
228
229 #[fuchsia::test]
230 fn multi_packet_put_with_duplicate_headers_is_ok() {
231 let mut operation = PutOperation::new(false);
232
233 let type_header = Header::Type("foo".into());
235 let headers1 =
236 HeaderSet::from_headers(vec![Header::name("random file".into()), type_header.clone()])
237 .unwrap();
238 let request1 = RequestPacket::new_put(headers1);
239 let response1 = operation.handle_peer_request(request1).expect("valid request");
240 let response_packet1 = expect_single_packet(response1);
241 assert_eq!(*response_packet1.code(), ResponseCode::Continue);
242 assert!(!operation.is_complete());
243
244 let body = (0..10).collect::<Vec<u8>>();
246 let headers2 =
247 HeaderSet::from_headers(vec![type_header, Header::EndOfBody(body.clone())]).unwrap();
248 let request2 = RequestPacket::new_put_final(headers2);
249 let response2 = operation.handle_peer_request(request2).expect("valid request");
250 assert_matches!(response2,
251 OperationRequest::PutApplicationData(data, headers)
252 if headers.contains_header(&HeaderIdentifier::Name) && headers.contains_header(&HeaderIdentifier::Type)
253 && data == body
254 );
255 assert!(!operation.is_complete());
256
257 let responses3 = operation
259 .handle_application_response(ApplicationResponse::accept_put())
260 .expect("valid response");
261 assert_eq!(responses3.len(), 1);
262 assert_eq!(*responses3[0].code(), ResponseCode::Ok);
263 assert!(operation.is_complete());
264 }
265
266 #[fuchsia::test]
267 fn multi_packet_put_srm_enabled() {
268 let mut operation = PutOperation::new(true);
269 assert!(!operation.is_complete());
270 assert_eq!(operation.srm_status(), SingleResponseMode::Disable);
271
272 let headers1 = HeaderSet::from_headers(vec![
275 Header::name("random file".into()),
276 SingleResponseMode::Enable.into(),
277 ])
278 .unwrap();
279 let request1 = RequestPacket::new_put(headers1);
280 let response1 = operation.handle_peer_request(request1).expect("valid request");
281 let response_packet1 = expect_single_packet(response1);
282 assert_eq!(*response_packet1.code(), ResponseCode::Continue);
283 let received_srm = response_packet1
284 .headers()
285 .get(&HeaderIdentifier::SingleResponseMode)
286 .expect("contains SRM header");
287 assert_eq!(*received_srm, Header::SingleResponseMode(SingleResponseMode::Enable));
288 assert_eq!(operation.srm_status(), SingleResponseMode::Enable);
289 assert!(!operation.is_complete());
290
291 let body2 = (0..50).collect::<Vec<u8>>();
293 let request2 = RequestPacket::new_put(HeaderSet::from_header(Header::Body(body2)));
294 let response2 = operation.handle_peer_request(request2).expect("valid request");
295 assert_matches!(response2, OperationRequest::None);
296 assert!(!operation.is_complete());
297
298 let body3 = (50..100).collect::<Vec<u8>>();
301 let request3 =
302 RequestPacket::new_put_final(HeaderSet::from_header(Header::EndOfBody(body3)));
303 let response3 = operation.handle_peer_request(request3).expect("valid request");
304 let expected_payload = (0..100).collect::<Vec<u8>>();
305 assert_matches!(response3,
306 OperationRequest::PutApplicationData(data, headers)
307 if headers.contains_header(&HeaderIdentifier::Name)
308 && data == expected_payload
309 );
310 assert!(!operation.is_complete());
311
312 let responses4 = operation
314 .handle_application_response(ApplicationResponse::accept_put())
315 .expect("valid response");
316 assert_eq!(responses4.len(), 1);
317 assert_eq!(*responses4[0].code(), ResponseCode::Ok);
318 assert!(operation.is_complete());
319 }
320
321 #[fuchsia::test]
322 fn application_reject_is_ok() {
323 let mut operation = PutOperation::new_at_state(State::RequestPhaseComplete);
324
325 let headers = HeaderSet::from_header(Header::Description("not allowed".into()));
326 let reject = Err((ResponseCode::Forbidden, headers));
327 let response_packets =
328 operation.handle_application_response(reject).expect("valid response");
329 assert_eq!(*response_packets[0].code(), ResponseCode::Forbidden);
330 assert!(response_packets[0].headers().contains_header(&HeaderIdentifier::Description));
331 assert!(operation.is_complete());
332 }
333
334 #[fuchsia::test]
335 fn non_put_request_is_error() {
336 let mut operation = PutOperation::new(false);
337 let random_request1 = RequestPacket::new_get(HeaderSet::new());
338 assert_matches!(
339 operation.handle_peer_request(random_request1),
340 Err(Error::OperationError { .. })
341 );
342
343 let random_request2 = RequestPacket::new_disconnect(HeaderSet::new());
344 assert_matches!(
345 operation.handle_peer_request(random_request2),
346 Err(Error::OperationError { .. })
347 );
348 }
349
350 #[fuchsia::test]
351 fn invalid_application_response_is_error() {
352 let mut operation = PutOperation::new(false);
354 assert_matches!(
355 operation.handle_application_response(ApplicationResponse::accept_put()),
356 Err(Error::OperationError { .. })
357 );
358
359 let mut operation = PutOperation::new_at_state(State::RequestPhaseComplete);
361 let invalid = ApplicationResponse::accept_get(vec![], HeaderSet::new());
362 assert_matches!(
363 operation.handle_application_response(invalid),
364 Err(Error::OperationError { .. })
365 );
366 }
367
368 #[fuchsia::test]
369 fn delete_request_success() {
370 let mut operation = PutOperation::new(false);
371
372 let headers = HeaderSet::from_header(Header::name("randomfile.txt"));
373 let request = RequestPacket::new_put_final(headers);
374 let response = operation.handle_peer_request(request).expect("valid request");
375 assert_matches!(response, OperationRequest::DeleteApplicationData(headers) if headers.contains_header(&HeaderIdentifier::Name));
376 assert!(!operation.is_complete());
377
378 let final_responses = operation
380 .handle_application_response(ApplicationResponse::accept_put())
381 .expect("valid application response");
382 let final_response = final_responses.first().expect("one response");
383 assert_eq!(*final_response.code(), ResponseCode::Ok);
384 assert!(final_response.headers().is_empty());
385 assert!(operation.is_complete());
386 }
387}