Skip to main content

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