bt_obex/server/
mod.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fuchsia_bluetooth::types::Channel;
6use futures::future::Future;
7use futures::stream::StreamExt;
8use log::{info, trace, warn};
9use packet_encoding::{Decodable, Encodable};
10
11use crate::error::{Error, PacketError};
12use crate::header::{
13    ConnectionIdentifier, Header, HeaderIdentifier, HeaderSet, SingleResponseMode,
14};
15use crate::operation::{OpCode, RequestPacket, ResponseCode, ResponsePacket, SetPathFlags};
16use crate::transport::max_packet_size_from_transport;
17pub use crate::transport::TransportType;
18
19/// Defines an interface for handling OBEX requests. All profiles & services should implement this
20/// interface.
21mod handler;
22pub use handler::{new_operation_error, ObexOperationError, ObexServerHandler};
23
24/// Implements the OBEX GET operation.
25mod get;
26use get::GetOperation;
27
28/// Implements the OBEX PUT operation.
29mod put;
30use put::PutOperation;
31
32/// Represents a request to be handled by the OBEX Server during a multi-step operation.
33#[derive(Debug)]
34pub enum OperationRequest {
35    /// Request to send response packets to the remote peer.
36    SendPackets(Vec<ResponsePacket>),
37    /// Request to get informational headers describing a payload from the upper layer application
38    /// -- occurs in a GET operation.
39    GetApplicationInfo(HeaderSet),
40    /// Request to get the payload from the upper layer application -- occurs in a GET operation.
41    GetApplicationData(HeaderSet),
42    /// Request to give the payload to the upper layer application -- occurs in a PUT operation.
43    PutApplicationData(Vec<u8>, HeaderSet),
44    /// Request to delete the payload in the upper layer application -- occurs in a PUT operation.
45    DeleteApplicationData(HeaderSet),
46    /// No action needed.
47    None,
48}
49
50/// Represents a response from the upper layer application during a multi-step operation.
51#[derive(Debug)]
52pub enum ApplicationResponse {
53    /// The application responded successfully to get GET information request by providing
54    /// informational headers.
55    GetInfo(HeaderSet),
56    /// The application responded successfully to the GET request by providing the data payload
57    /// and informational headers.
58    GetData((Vec<u8>, HeaderSet)),
59    /// The application responded successfully to the PUT request.
60    Put,
61}
62
63impl ApplicationResponse {
64    #[cfg(test)]
65    fn accept_get(data: Vec<u8>, headers: HeaderSet) -> Result<Self, ObexOperationError> {
66        Ok(ApplicationResponse::GetData((data, headers)))
67    }
68
69    #[cfg(test)]
70    fn accept_get_info(headers: HeaderSet) -> Result<Self, ObexOperationError> {
71        Ok(ApplicationResponse::GetInfo(headers))
72    }
73
74    #[cfg(test)]
75    fn accept_put() -> Result<Self, ObexOperationError> {
76        Ok(ApplicationResponse::Put)
77    }
78}
79
80/// An interface for implementing a multi-step OBEX operation. Currently, the only two such
81/// operations are GET and PUT.
82/// See OBEX 1.5 Sections 3.4.3 & 3.4.4.
83pub trait ServerOperation {
84    /// Returns the current SRM mode of the operation.
85    fn srm_status(&self) -> SingleResponseMode;
86
87    /// Checks the provided `headers` for the SRM flag and returns the negotiated SRM mode if
88    /// present, None otherwise.
89    fn check_headers_for_srm(
90        srm_supported_locally: bool,
91        headers: &HeaderSet,
92    ) -> Option<SingleResponseMode>
93    where
94        Self: Sized,
95    {
96        let Some(Header::SingleResponseMode(srm)) =
97            headers.get(&HeaderIdentifier::SingleResponseMode)
98        else {
99            trace!("No SRM header in request");
100            return None;
101        };
102
103        // If both parties support SRM, then it can be enabled.
104        if srm_supported_locally && *srm == SingleResponseMode::Enable {
105            Some(SingleResponseMode::Enable)
106        } else {
107            // Otherwise, either we don't support it locally, or the peer requested to disable it.
108            Some(SingleResponseMode::Disable)
109        }
110    }
111
112    /// Returns true if the operation is complete (e.g. all response packets have been sent).
113    fn is_complete(&self) -> bool;
114
115    /// Handle a `request` packet received from the OBEX client.
116    /// Returns an `OperationRequest` to be handled by the OBEX server on success, Error if the
117    /// request was invalid or couldn't be handled.
118    fn handle_peer_request(&mut self, request: RequestPacket) -> Result<OperationRequest, Error>;
119
120    /// Handle a response received from the upper layer application profile.
121    /// `response` is Ok<T> if the application accepted the GET or PUT request.
122    /// `response` is Err<E> if the application rejected the GET or PUT request.
123    /// Returns response packets to be sent to the remote peer if the application `response` was
124    /// successfully handled.
125    /// Returns Error if there was an internal operation error.
126    fn handle_application_response(
127        &mut self,
128        response: Result<ApplicationResponse, ObexOperationError>,
129    ) -> Result<Vec<ResponsePacket>, Error>;
130}
131
132#[derive(Clone, Copy, Debug, Default, PartialEq)]
133enum ConnectionStatus {
134    /// The transport is created but the CONNECT operation has not been completed.
135    #[default]
136    Initialized,
137    /// The transport is connected and the CONNECT operation has been completed.
138    /// `id` contains the optional identifier for this connection. It is typically set when the
139    /// OBEX client requests a directed OBEX connection to a specific Service by including the
140    /// `Target` header in the CONNECT request.
141    Connected { id: Option<ConnectionIdentifier> },
142    /// The transport is connected but a DISCONNECT request has been received. The `ObexServer`
143    /// will no longer process requests from the remote peer.
144    DisconnectReceived,
145}
146
147impl ConnectionStatus {
148    #[cfg(test)]
149    fn connected_no_id() -> Self {
150        Self::Connected { id: None }
151    }
152}
153
154/// Implements the Server role for the OBEX protocol.
155/// Provides an interface for receiving and responding to OBEX requests made by a remote OBEX client
156/// service. Supports the operations defined in OBEX 1.5.
157pub struct ObexServer {
158    /// The current connection status of the server.
159    connected: ConnectionStatus,
160    /// The maximum OBEX packet length for this OBEX session.
161    max_packet_size: u16,
162    /// The active OBEX operation. The only two supported multi-step operations are GET and PUT.
163    /// This is Some<T> when an operation is in progress, and None otherwise. There can only be one
164    /// active multi-step operation. An operation is considered complete when
165    /// `ServerOperation::is_complete` returns true.
166    /// The active operation is cleaned up lazily -- when a request to start a new operation is
167    /// received, the previously finished operation is removed.
168    active_operation: Option<Box<dyn ServerOperation>>,
169    /// The data channel that is used to read & write OBEX packets.
170    channel: Channel,
171    /// The type of transport used for the OBEX connection (RFCOMM or L2CAP).
172    type_: TransportType,
173    /// The handler provided by the application profile. This handler should implement the
174    /// operations defined in OBEX 1.5 and will be used to provide a response to an incoming
175    /// request made by the remote OBEX client.
176    handler: Box<dyn ObexServerHandler>,
177}
178
179impl ObexServer {
180    /// The default Connection Identifier used for directed OBEX connections.
181    /// Because a single `ObexServer` services a single transport (L2CAP or RFCOMM), this
182    /// identifier does not multiplex anything and is only sent in the CONNECT response.
183    /// This value is arbitrarily chosen and will be included in all subsequent requests made by
184    /// the remote OBEX Client.
185    const DIRECTED_CONNECTION_ID: ConnectionIdentifier = ConnectionIdentifier(1);
186
187    pub fn new(
188        channel: Channel,
189        type_: TransportType,
190        handler: Box<dyn ObexServerHandler>,
191    ) -> Self {
192        let max_packet_size = max_packet_size_from_transport(channel.max_tx_size());
193        Self {
194            connected: ConnectionStatus::default(),
195            max_packet_size,
196            active_operation: None,
197            channel,
198            type_,
199            handler,
200        }
201    }
202
203    /// Returns `true` if the OBEX connection is currently active (e.g. CONNECT operation done).
204    fn is_connected(&self) -> bool {
205        matches!(self.connected, ConnectionStatus::Connected { .. })
206    }
207
208    fn set_connection_status(&mut self, status: ConnectionStatus) {
209        self.connected = status;
210    }
211
212    fn set_max_packet_size(&mut self, peer_max_packet_size: u16) {
213        // Use the smaller of the peer max and local max for maximum compatibility.
214        let min_ = std::cmp::min(peer_max_packet_size, self.max_packet_size);
215        self.max_packet_size = min_;
216        trace!("Max packet size set to {}", self.max_packet_size);
217    }
218
219    /// Encodes and sends the OBEX `data` to the remote peer.
220    /// Returns Error if the send operation could not be completed.
221    fn send(&self, data: impl Encodable<Error = PacketError>) -> Result<(), Error> {
222        let mut buf = vec![0; data.encoded_len()];
223        data.encode(&mut buf[..])?;
224        let _ = self.channel.write(&buf)?;
225        Ok(())
226    }
227
228    async fn connect_request(&mut self, request: RequestPacket) -> Result<ResponsePacket, Error> {
229        // Parse the additional data first - the data length is already validated during decoding.
230        let data = request.data();
231        let version = data[0];
232        let flags = data[1];
233        let peer_max_packet_size = u16::from_be_bytes(data[2..4].try_into().unwrap());
234        trace!(version, flags, peer_max_packet_size; "Additional data in CONNECT request");
235        self.set_max_packet_size(peer_max_packet_size);
236
237        let headers = HeaderSet::from(request);
238
239        // The connection can optionally be considered "directed" if the Client provides a Target
240        // UUID identifying the service.
241        let id = if headers.contains_header(&HeaderIdentifier::Target) {
242            Some(Self::DIRECTED_CONNECTION_ID)
243        } else {
244            None
245        };
246        let (code, response_headers) = match self.handler.connect(headers).await {
247            Ok(mut headers) => {
248                trace!("Application accepted CONNECT request");
249                let _ = headers.try_add_connection_id(&id);
250                self.set_connection_status(ConnectionStatus::Connected { id });
251                (ResponseCode::Ok, headers)
252            }
253            Err(reject_parameters) => {
254                trace!("Application rejected CONNECT request");
255                reject_parameters
256            }
257        };
258        let response_packet =
259            ResponsePacket::new_connect(code, self.max_packet_size, response_headers);
260        Ok(response_packet)
261    }
262
263    /// Handles a Disconnect request made by the remote OBEX client.
264    /// Returns a response packet to be sent on success, Error if the request couldn't be handled.
265    async fn disconnect_request(
266        &mut self,
267        request: RequestPacket,
268    ) -> Result<ResponsePacket, Error> {
269        let headers = HeaderSet::from(request);
270        let response_headers = self.handler.disconnect(headers).await;
271        let response_packet = ResponsePacket::new_disconnect(response_headers);
272        self.set_connection_status(ConnectionStatus::DisconnectReceived);
273        Ok(response_packet)
274    }
275
276    /// Handles a SetPath request made by the remote OBEX client.
277    /// Returns a response packet to be sent on success, Error if the request couldn't be handled.
278    async fn setpath_request(&mut self, request: RequestPacket) -> Result<ResponsePacket, Error> {
279        if !self.is_connected() {
280            return Err(Error::operation(OpCode::SetPath, "CONNECT not completed"));
281        }
282        // Parse the additional data first - the data length is already validated during decoding.
283        // Only the `flags` field is used in OBEX 1.5. `constants` is RFA.
284        let data = request.data();
285        let flags = SetPathFlags::from_bits_truncate(data[0]);
286        let backup = flags.contains(SetPathFlags::BACKUP);
287        let create = !flags.contains(SetPathFlags::DONT_CREATE);
288
289        let headers = HeaderSet::from(request);
290        let (code, response_headers) = match self.handler.set_path(headers, backup, create).await {
291            Ok(headers) => {
292                trace!("Application accepted SETPATH request");
293                (ResponseCode::Ok, headers)
294            }
295            Err(reject_parameters) => {
296                trace!("Application rejected SETPATH request");
297                reject_parameters
298            }
299        };
300        let response_packet = ResponsePacket::new_setpath(code, response_headers);
301        Ok(response_packet)
302    }
303
304    /// Potentially initializes a new multi-step operation.
305    /// Returns true if a new operation was initialized, false otherwise.
306    fn maybe_start_new_operation(&mut self, code: &OpCode) -> bool {
307        if self.active_operation.as_ref().is_some_and(|o| !o.is_complete()) {
308            return false;
309        }
310
311        let op: Box<dyn ServerOperation> = match code {
312            OpCode::Get | OpCode::GetFinal => {
313                Box::new(GetOperation::new(self.max_packet_size, self.type_.srm_supported()))
314            }
315            OpCode::Put | OpCode::PutFinal => {
316                Box::new(PutOperation::new(self.type_.srm_supported()))
317            }
318            _ => unreachable!("only called from `Self::multistep_request`"),
319        };
320        trace!("Started new operation ({code:?})");
321        self.active_operation = Some(op);
322        return true;
323    }
324
325    /// Handles a request made by the remote OBEX client for a potentially multi-step
326    /// operation (PUT or GET).
327    /// Returns response packets to be sent to the peer on success, Error if the request can't
328    /// be handled or is invalid.
329    async fn multistep_request(
330        &mut self,
331        request: RequestPacket,
332    ) -> Result<Vec<ResponsePacket>, Error> {
333        let _ = self.maybe_start_new_operation(request.code());
334        let operation = self.active_operation.as_mut().expect("just initialized");
335
336        let application_response = match operation.handle_peer_request(request) {
337            Ok(OperationRequest::SendPackets(responses)) => return Ok(responses),
338            Ok(OperationRequest::GetApplicationInfo(info_headers)) => {
339                self.handler.get_info(info_headers).await.map(|x| ApplicationResponse::GetInfo(x))
340            }
341            Ok(OperationRequest::GetApplicationData(request_headers)) => self
342                .handler
343                .get_data(request_headers)
344                .await
345                .map(|x| ApplicationResponse::GetData(x)),
346            Ok(OperationRequest::PutApplicationData(data, request_headers)) => {
347                self.handler.put(data, request_headers).await.map(|_| ApplicationResponse::Put)
348            }
349            Ok(OperationRequest::DeleteApplicationData(request_headers)) => {
350                self.handler.delete(request_headers).await.map(|_| ApplicationResponse::Put)
351            }
352            Ok(OperationRequest::None) => return Ok(vec![]),
353            Err(e) => {
354                warn!("Internal error in operation: {e:?}");
355                return Ok(vec![ResponsePacket::new_no_data(
356                    ResponseCode::InternalServerError,
357                    HeaderSet::new(),
358                )]);
359            }
360        };
361
362        operation.handle_application_response(application_response)
363    }
364
365    /// Processes a raw data `packet` received from the remote peer acting as an OBEX client.
366    /// Returns a list of `ResponsePacket`s to be sent to the peer on success, Error if the request
367    /// was invalid or couldn't be handled.
368    async fn receive_packet(&mut self, packet: Vec<u8>) -> Result<Vec<ResponsePacket>, Error> {
369        let decoded = RequestPacket::decode(&packet[..])?;
370        trace!(packet:? = decoded; "Received request from OBEX client");
371        let response = match decoded.code() {
372            OpCode::Connect => self.connect_request(decoded).await?,
373            OpCode::Disconnect => self.disconnect_request(decoded).await?,
374            OpCode::SetPath => self.setpath_request(decoded).await?,
375            OpCode::Put | OpCode::PutFinal | OpCode::Get | OpCode::GetFinal => {
376                return self.multistep_request(decoded).await;
377            }
378            _code => todo!("Support other OBEX requests"),
379        };
380        Ok(vec![response])
381    }
382
383    pub fn run(mut self) -> impl Future<Output = Result<(), Error>> {
384        async move {
385            while let Some(packet) = self.channel.next().await {
386                match packet {
387                    Ok(bytes) => {
388                        let responses = self.receive_packet(bytes).await?;
389                        for response in responses {
390                            self.send(response)?;
391                        }
392
393                        // The OBEX Client requested to close the OBEX connection.
394                        if self.connected == ConnectionStatus::DisconnectReceived {
395                            trace!("Disconnect request - closing transport");
396                            return Ok(());
397                        }
398                    }
399                    Err(e) => warn!("Error reading data from transport: {e:?}"),
400                }
401            }
402            info!("Peer disconnected transport");
403            Ok(())
404        }
405    }
406}
407
408#[cfg(test)]
409pub(crate) mod test_utils {
410    use super::*;
411
412    #[track_caller]
413    pub fn expect_single_packet(request: OperationRequest) -> ResponsePacket {
414        let OperationRequest::SendPackets(mut packets) = request else {
415            panic!("Expected outgoing packet request, got: {request:?}");
416        };
417        assert_eq!(packets.len(), 1);
418        packets.pop().unwrap()
419    }
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425
426    use assert_matches::assert_matches;
427    use async_test_helpers::expect_stream_pending;
428    use async_utils::PollExt;
429    use fuchsia_async as fasync;
430    use std::pin::pin;
431
432    use crate::header::header_set::{expect_body, expect_end_of_body};
433    use crate::server::handler::test_utils::TestApplicationProfile;
434    use crate::transport::test_utils::{expect_response, send_packet};
435
436    /// Returns an ObexServer, a test only object representing an upper layer profile, & the remote
437    /// peer's side of the transport.
438    fn new_obex_server(srm: bool) -> (ObexServer, TestApplicationProfile, Channel) {
439        let (local, remote) = Channel::create();
440        let app = TestApplicationProfile::new();
441        let type_ = if srm { TransportType::L2cap } else { TransportType::Rfcomm };
442        let obex_server = ObexServer::new(local, type_, Box::new(app.clone()));
443        (obex_server, app, remote)
444    }
445
446    #[fuchsia::test]
447    fn obex_server_terminates_when_channel_closes() {
448        let mut exec = fasync::TestExecutor::new();
449        let (obex_server, _test_app, remote) = new_obex_server(/*srm=*/ false);
450
451        let server_fut = obex_server.run();
452        let mut server_fut = pin!(server_fut);
453        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server still active");
454
455        drop(remote);
456        let result = exec.run_until_stalled(&mut server_fut).expect("server finished");
457        assert_matches!(result, Ok(_));
458    }
459
460    #[fuchsia::test]
461    fn connect_accepted_by_app_success() {
462        let mut exec = fasync::TestExecutor::new();
463        let (obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
464        let server_fut = obex_server.run();
465        let mut server_fut = pin!(server_fut);
466        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
467
468        let connect_request = RequestPacket::new_connect(500, HeaderSet::new());
469        send_packet(&mut remote, connect_request);
470
471        // Expect the ObexServer to receive the request, parse it, ask the application, and reply.
472        // Simulate application accepting the request.
473        let headers = HeaderSet::from_header(Header::Description("foo".into()));
474        test_app.set_response(Ok(headers));
475        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
476
477        // Expect the remote peer to receive our CONNECT response. Our response shouldn't contain
478        // a `ConnectionIdentifier` since the request didn't contain a `Target` header.
479        let expectation = |response: ResponsePacket| {
480            assert_eq!(*response.code(), ResponseCode::Ok);
481            assert_eq!(response.data(), &[0x10, 0, 0x01, 0xf4]);
482            assert!(response.headers().contains_header(&HeaderIdentifier::Description));
483            assert!(!response.headers().contains_header(&HeaderIdentifier::ConnectionId));
484        };
485        expect_response(&mut exec, &mut remote, expectation, OpCode::Connect);
486    }
487
488    #[fuchsia::test]
489    fn directed_connect_accepted_by_app_success() {
490        let mut exec = fasync::TestExecutor::new();
491        let (obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
492        let server_fut = obex_server.run();
493        let mut server_fut = pin!(server_fut);
494        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
495
496        let request_headers = HeaderSet::from_header(Header::Target(vec![5, 6]));
497        let connect_request = RequestPacket::new_connect(500, request_headers);
498        send_packet(&mut remote, connect_request);
499
500        // Expect the ObexServer to receive the request, parse it, ask the application, and reply.
501        // Simulate application accepting the request.
502        let headers = HeaderSet::from_header(Header::name("foo"));
503        test_app.set_response(Ok(headers));
504        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
505
506        // Expect the remote peer to receive our CONNECT response. Our response should contain a
507        // connection identifier because the `request_headers` contains a `Target` header.
508        let expectation = |response: ResponsePacket| {
509            assert_eq!(*response.code(), ResponseCode::Ok);
510            assert_eq!(response.data(), &[0x10, 0, 0x01, 0xf4]);
511            assert!(response.headers().contains_header(&HeaderIdentifier::Name));
512            assert!(response.headers().contains_header(&HeaderIdentifier::ConnectionId));
513        };
514        expect_response(&mut exec, &mut remote, expectation, OpCode::Connect);
515    }
516
517    #[fuchsia::test]
518    fn connect_rejected_by_app_is_ok() {
519        let mut exec = fasync::TestExecutor::new();
520        let (obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
521        let server_fut = obex_server.run();
522        let mut server_fut = pin!(server_fut);
523        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
524
525        let connect_request = RequestPacket::new_connect(255, HeaderSet::new());
526        send_packet(&mut remote, connect_request);
527
528        // The ObexServer should receive the request and hand it to the profile - profile rejects.
529        test_app.set_response(Err((ResponseCode::Forbidden, HeaderSet::new())));
530        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
531
532        // Expect the remote peer to receive our negative CONNECT response.
533        let expectation = |response: ResponsePacket| {
534            assert_eq!(*response.code(), ResponseCode::Forbidden);
535            assert_eq!(response.data(), &[0x10, 0, 0x00, 0xff]);
536            let headers = HeaderSet::from(response);
537            assert!(headers.is_empty());
538        };
539        expect_response(&mut exec, &mut remote, expectation, OpCode::Connect);
540    }
541
542    #[fuchsia::test]
543    fn invalid_connect_request_is_error() {
544        let mut exec = fasync::TestExecutor::new();
545        let (obex_server, _test_app, remote) = new_obex_server(/*srm=*/ false);
546
547        let server_fut = obex_server.run();
548        let mut server_fut = pin!(server_fut);
549        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server still active");
550
551        // Invalid CONNECT request. Missing the 2 byte max packet size field.
552        let _ = remote.write(&[0x80, 0x00, 0x05, 0x00, 0x00]).expect("can send data");
553
554        let result = exec.run_until_stalled(&mut server_fut).expect("terminate due to error");
555        assert_matches!(result, Err(Error::Packet(_)));
556    }
557
558    #[fuchsia::test]
559    fn peer_disconnect_request_terminates_server() {
560        let mut exec = fasync::TestExecutor::new();
561        let (obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
562        let server_fut = obex_server.run();
563        let mut server_fut = pin!(server_fut);
564        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
565
566        let headers = HeaderSet::from_header(Header::Description("done".into()));
567        let disconnect_request = RequestPacket::new_disconnect(headers);
568        send_packet(&mut remote, disconnect_request);
569
570        // Expect the ObexServer to receive the request, parse it, get response headers from the
571        // application, and reply. Because this is a Disconnect request, the server run loop
572        // should finish.
573        let headers = HeaderSet::from_header(Header::Description("disconnecting".into()));
574        test_app.set_response(Ok(headers));
575        let result =
576            exec.run_until_stalled(&mut server_fut).expect("server terminated from disconnect");
577        assert_matches!(result, Ok(_));
578
579        // Expect the remote peer to receive our DISCONNECT response.
580        let expectation = |response: ResponsePacket| {
581            assert_eq!(*response.code(), ResponseCode::Ok);
582            let headers = HeaderSet::from(response);
583            assert!(headers.contains_header(&HeaderIdentifier::Description));
584        };
585        expect_response(&mut exec, &mut remote, expectation, OpCode::Disconnect);
586    }
587
588    #[fuchsia::test]
589    fn setpath_request_accepted_by_app_success() {
590        let mut exec = fasync::TestExecutor::new();
591        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
592        // Set to the Connected state to bypass CONNECT operation.
593        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
594        let server_fut = obex_server.run();
595        let mut server_fut = pin!(server_fut);
596        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
597
598        let headers = HeaderSet::from_header(Header::name("folder1"));
599        let setpath_request =
600            RequestPacket::new_set_path(SetPathFlags::all(), headers).expect("valid request");
601        send_packet(&mut remote, setpath_request);
602
603        // The ObexServer should receive the request and hand it to the profile - profile accepts.
604        test_app.set_response(Ok(HeaderSet::new()));
605        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
606
607        // Expect the remote peer to receive our SETPATH response.
608        let expectation = |response: ResponsePacket| {
609            assert_eq!(*response.code(), ResponseCode::Ok);
610            let headers = HeaderSet::from(response);
611            assert!(headers.is_empty());
612        };
613        expect_response(&mut exec, &mut remote, expectation, OpCode::SetPath);
614    }
615
616    #[fuchsia::test]
617    fn setpath_request_rejected_by_app_success() {
618        let mut exec = fasync::TestExecutor::new();
619        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
620        // Set to the Connected state to bypass CONNECT operation.
621        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
622        let server_fut = obex_server.run();
623        let mut server_fut = pin!(server_fut);
624        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
625
626        let setpath_request = RequestPacket::new_set_path(SetPathFlags::BACKUP, HeaderSet::new())
627            .expect("valid request");
628        send_packet(&mut remote, setpath_request);
629
630        // The ObexServer should receive the request and hand it to the profile - profile rejects.
631        test_app.set_response(Err((ResponseCode::Forbidden, HeaderSet::new())));
632        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
633
634        // Expect the remote peer to receive our negative SETPATH response.
635        let expectation = |response: ResponsePacket| {
636            assert_eq!(*response.code(), ResponseCode::Forbidden);
637            let headers = HeaderSet::from(response);
638            assert!(headers.is_empty());
639        };
640        expect_response(&mut exec, &mut remote, expectation, OpCode::SetPath);
641    }
642
643    #[fuchsia::test]
644    fn setpath_request_before_connect_is_error() {
645        let mut exec = fasync::TestExecutor::new();
646        let (obex_server, _test_app, mut remote) = new_obex_server(/*srm=*/ false);
647        let server_fut = obex_server.run();
648        let mut server_fut = pin!(server_fut);
649        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
650
651        let setpath_request = RequestPacket::new_set_path(SetPathFlags::BACKUP, HeaderSet::new())
652            .expect("valid request");
653        send_packet(&mut remote, setpath_request);
654        let result = exec
655            .run_until_stalled(&mut server_fut)
656            .expect("server terminated from invalid setpath");
657        assert_matches!(result, Err(Error::OperationError { operation: OpCode::SetPath, .. }));
658    }
659
660    #[fuchsia::test]
661    fn get_request_accepted_by_app_success() {
662        let mut exec = fasync::TestExecutor::new();
663        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
664        // Set to the Connected state to bypass CONNECT operation.
665        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
666        let server_fut = obex_server.run();
667        let mut server_fut = pin!(server_fut);
668        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
669
670        // Remote asks for information about the payload. The ObexServer should receive the request
671        // and ask the application for the response.
672        let get_request1 =
673            RequestPacket::new_get(HeaderSet::from_header(Header::name("random object")));
674        send_packet(&mut remote, get_request1);
675        // Simulate the application responding with the size of the object.
676        test_app.set_response(Ok(HeaderSet::from_header(Header::Length(0x10))));
677        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
678
679        // The remote peer should receive the info response.
680        let expectation = |response: ResponsePacket| {
681            assert_eq!(*response.code(), ResponseCode::Continue);
682            assert!(response.headers().contains_header(&HeaderIdentifier::Length));
683        };
684        expect_response(&mut exec, &mut remote, expectation, OpCode::Get);
685
686        // Remote sends a GET_FINAL request indicating that it is ready to receive the data payload.
687        let get_request2 = RequestPacket::new_get_final(HeaderSet::new());
688        send_packet(&mut remote, get_request2);
689
690        // The ObexServer should receive the request and hand it to the profile. Set the profile
691        // handler to return a static buffer.
692        let application_response_buf = vec![1, 2, 3, 4, 5, 6];
693        let response_headers = HeaderSet::from_header(Header::Description("foo".into()));
694        test_app.set_get_response((application_response_buf.clone(), response_headers));
695        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
696
697        let expectation = |response: ResponsePacket| {
698            assert_eq!(*response.code(), ResponseCode::Ok);
699            let mut headers = HeaderSet::from(response);
700            assert!(headers.contains_header(&HeaderIdentifier::Description));
701            let received_body = headers.remove_body(/*final_=*/ true).expect("contains body");
702            assert_eq!(received_body, application_response_buf);
703        };
704        expect_response(&mut exec, &mut remote, expectation, OpCode::GetFinal);
705    }
706
707    #[fuchsia::test]
708    fn get_request_rejected_by_app_success() {
709        let mut exec = fasync::TestExecutor::new();
710        let (mut obex_server, _test_app, mut remote) = new_obex_server(/*srm=*/ false);
711        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
712        let server_fut = obex_server.run();
713        let mut server_fut = pin!(server_fut);
714        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
715
716        // Send an example GET_FINAL request with a header describing the name of the object.
717        let headers = HeaderSet::from_header(Header::name("random object123"));
718        let get_request = RequestPacket::new_get_final(headers);
719        send_packet(&mut remote, get_request);
720
721        // The ObexServer receives request and hands to application. By default, it rejects the
722        // request.
723        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
724        // Expect the peer to received the rejection code.
725        let expectation = |response: ResponsePacket| {
726            assert_eq!(*response.code(), ResponseCode::NotImplemented);
727            assert!(response.headers().is_empty());
728        };
729        expect_response(&mut exec, &mut remote, expectation, OpCode::GetFinal);
730    }
731
732    #[fuchsia::test]
733    fn get_request_with_srm_enabled_success() {
734        let mut exec = fasync::TestExecutor::new();
735        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ true);
736        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
737        obex_server.set_max_packet_size(20); // Set max to something small.
738        let server_fut = obex_server.run();
739        let mut server_fut = pin!(server_fut);
740        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
741
742        // First request asks for information & SRM. Server should receive the request, ask the
743        // application, and negotiate SRM.
744        let headers1 = HeaderSet::from_headers(vec![
745            Header::name("random object"),
746            SingleResponseMode::Enable.into(),
747        ])
748        .unwrap();
749        let get_request1 = RequestPacket::new_get(headers1);
750        send_packet(&mut remote, get_request1);
751        test_app.set_response(Ok(HeaderSet::from_header(Header::Length(0x20))));
752        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
753        // Expect the response packet to the peer to negotiate SRM and contain the application
754        // response.
755        let expectation1 = |response: ResponsePacket| {
756            assert_eq!(*response.code(), ResponseCode::Continue);
757            let Header::SingleResponseMode(SingleResponseMode::Enable) =
758                response.headers().get(&HeaderIdentifier::SingleResponseMode).unwrap()
759            else {
760                panic!("Expected SRM enable in response");
761            };
762            assert!(response.headers().contains_header(&HeaderIdentifier::Length));
763        };
764        expect_response(&mut exec, &mut remote, expectation1, OpCode::Get);
765        // At this point, SRM is considered active for the operation.
766
767        // Second (final) request to get the payload.
768        let get_request2 = RequestPacket::new_get_final(HeaderSet::new());
769        send_packet(&mut remote, get_request2);
770        // The ObexServer should receive the request and hand it to the profile. Set the profile
771        // handler to return a static buffer that must be split across multiple payloads.
772        let application_response_buf = (0..50).collect::<Vec<u8>>();
773        test_app.set_get_response((application_response_buf, HeaderSet::new()));
774        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
775
776        // Since SRM is enabled, we expect consecutive packets containing the payload - no requests
777        // made by the remote in between.
778        let expected_bufs = vec![
779            (0..14).collect::<Vec<u8>>(),
780            (14..28).collect::<Vec<u8>>(),
781            (28..42).collect::<Vec<u8>>(),
782        ];
783        for expected_buf in expected_bufs {
784            let expectation = |response: ResponsePacket| {
785                assert_eq!(*response.code(), ResponseCode::Continue);
786                expect_body(response.headers(), expected_buf);
787            };
788            expect_response(&mut exec, &mut remote, expectation, OpCode::Get);
789        }
790
791        // Final packet has remaining bytes and operation is complete.
792        let final_expectation = |response: ResponsePacket| {
793            assert_eq!(*response.code(), ResponseCode::Ok);
794            expect_end_of_body(response.headers(), (42..50).collect::<Vec<u8>>());
795        };
796        expect_response(&mut exec, &mut remote, final_expectation, OpCode::GetFinal);
797    }
798
799    #[fuchsia::test]
800    fn put_request_accepted_by_app_success() {
801        let mut exec = fasync::TestExecutor::new();
802        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
803        // Set to the Connected state to bypass CONNECT operation.
804        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
805        let server_fut = obex_server.run();
806        let mut server_fut = pin!(server_fut);
807        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
808
809        let headers = HeaderSet::from_headers(vec![
810            Header::name("random object"),
811            Header::EndOfBody(vec![1, 2, 3, 4, 5]),
812        ])
813        .unwrap();
814        let put_request = RequestPacket::new_put_final(headers);
815        send_packet(&mut remote, put_request);
816
817        // The ObexServer should receive the request and hand it to the profile. Profile accepts.
818        test_app.set_put_response(Ok(()));
819        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
820        // Verify profile received correct data.
821        let (rec_data, rec_headers) = test_app.put_data();
822        assert_eq!(rec_data, vec![1, 2, 3, 4, 5]);
823        assert!(rec_headers.contains_header(&HeaderIdentifier::Name));
824
825        let expectation = |response: ResponsePacket| {
826            assert_eq!(*response.code(), ResponseCode::Ok);
827            assert!(response.headers().is_empty());
828        };
829        expect_response(&mut exec, &mut remote, expectation, OpCode::PutFinal);
830    }
831
832    #[fuchsia::test]
833    fn put_request_with_srm_enabled_success() {
834        let mut exec = fasync::TestExecutor::new();
835        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ true);
836        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
837        let server_fut = obex_server.run();
838        let mut server_fut = pin!(server_fut);
839        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
840
841        // First request asks to enable SRM and provides some info.
842        let headers1 = HeaderSet::from_headers(vec![
843            Header::name("my file"),
844            SingleResponseMode::Enable.into(),
845        ])
846        .unwrap();
847        let put_request1 = RequestPacket::new_put(headers1);
848        send_packet(&mut remote, put_request1);
849        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
850        // Expect the Obex Server to positively respond, and enable SRM. Subsequent requests won't
851        // receive a response.
852        let expectation1 = |response: ResponsePacket| {
853            assert_eq!(*response.code(), ResponseCode::Continue);
854            let Header::SingleResponseMode(SingleResponseMode::Enable) =
855                response.headers().get(&HeaderIdentifier::SingleResponseMode).unwrap()
856            else {
857                panic!("Expected SRM enable in response");
858            };
859        };
860        expect_response(&mut exec, &mut remote, expectation1, OpCode::Put);
861
862        // Next request sends over some data. Don't expect any response on the remote.
863        let headers2 = HeaderSet::from_header(Header::Body(vec![1, 2, 3, 4, 5]));
864        let put_request2 = RequestPacket::new_put(headers2);
865        send_packet(&mut remote, put_request2);
866        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
867        expect_stream_pending(&mut exec, &mut remote);
868
869        // Next (final) request sends over some data. Expect a response since this is the final
870        // packet.
871        let headers3 = HeaderSet::from_header(Header::EndOfBody(vec![6, 7, 8, 9, 10]));
872        let put_request3 = RequestPacket::new_put_final(headers3);
873        send_packet(&mut remote, put_request3);
874
875        // The entire request is complete and the Obex Server should hand it to the application.
876        // Verify profile received correct data.
877        test_app.set_put_response(Ok(()));
878        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
879        let (rec_data, rec_headers) = test_app.put_data();
880        assert_eq!(rec_data, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
881        assert!(rec_headers.contains_header(&HeaderIdentifier::Name));
882
883        let expectation = |response: ResponsePacket| {
884            assert_eq!(*response.code(), ResponseCode::Ok);
885            assert!(response.headers().is_empty());
886        };
887        expect_response(&mut exec, &mut remote, expectation, OpCode::PutFinal);
888    }
889
890    #[fuchsia::test]
891    fn delete_request_accepted_by_app_success() {
892        let mut exec = fasync::TestExecutor::new();
893        let (mut obex_server, test_app, mut remote) = new_obex_server(/*srm=*/ false);
894        obex_server.set_connection_status(ConnectionStatus::connected_no_id());
895        let server_fut = obex_server.run();
896        let mut server_fut = pin!(server_fut);
897        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
898
899        let headers = HeaderSet::from_header(Header::name("foo.txt"));
900        let put_request = RequestPacket::new_put_final(headers);
901        send_packet(&mut remote, put_request);
902
903        // The ObexServer should receive the request and hand it to the profile. Profile accepts.
904        test_app.set_put_response(Ok(()));
905        let _ = exec.run_until_stalled(&mut server_fut).expect_pending("server active");
906
907        let expectation = |response: ResponsePacket| {
908            assert_eq!(*response.code(), ResponseCode::Ok);
909            assert!(response.headers().is_empty());
910        };
911        expect_response(&mut exec, &mut remote, expectation, OpCode::PutFinal);
912    }
913}