[go: up one dir, main page]

magenta/
socket.rs

1// Copyright 2016 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
5//! Type-safe bindings for Magenta sockets.
6
7use {AsHandleRef, HandleBased, Handle, HandleRef, Peered};
8use {sys, Status, into_result};
9
10use std::ptr;
11
12/// An object representing a Magenta
13/// [socket](https://fuchsia.googlesource.com/magenta/+/master/docs/concepts.md#Message-Passing_Sockets-and-Channels).
14///
15/// As essentially a subtype of `Handle`, it can be freely interconverted.
16#[derive(Debug, Eq, PartialEq)]
17pub struct Socket(Handle);
18impl_handle_based!(Socket);
19impl Peered for Socket {}
20
21/// Options for creating a socket pair.
22#[repr(u32)]
23#[derive(Debug, Copy, Clone, Eq, PartialEq)]
24pub enum SocketOpts {
25    /// Default options.
26    Default = 0,
27}
28
29impl Default for SocketOpts {
30    fn default() -> Self {
31        SocketOpts::Default
32    }
33}
34
35/// Options for writing into a socket.
36#[repr(u32)]
37#[derive(Debug, Copy, Clone, Eq, PartialEq)]
38pub enum SocketWriteOpts {
39    /// Default options.
40    Default = 0,
41}
42
43impl Default for SocketWriteOpts {
44    fn default() -> Self {
45        SocketWriteOpts::Default
46    }
47}
48
49/// Options for reading from a socket.
50#[repr(u32)]
51#[derive(Debug, Copy, Clone, Eq, PartialEq)]
52pub enum SocketReadOpts {
53    /// Default options.
54    Default = 0,
55}
56
57impl Default for SocketReadOpts {
58    fn default() -> Self {
59        SocketReadOpts::Default
60    }
61}
62
63
64impl Socket {
65    /// Create a socket, accessed through a pair of endpoints. Data written
66    /// into one may be read from the other.
67    ///
68    /// Wraps
69    /// [mx_socket_create](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/socket_create.md).
70    pub fn create(opts: SocketOpts) -> Result<(Socket, Socket), Status> {
71        unsafe {
72            let mut out0 = 0;
73            let mut out1 = 0;
74            let status = sys::mx_socket_create(opts as u32, &mut out0, &mut out1);
75            into_result(status, ||
76                (Self::from(Handle(out0)),
77                    Self::from(Handle(out1))))
78        }
79    }
80
81    /// Write the given bytes into the socket.
82    /// Return value (on success) is number of bytes actually written.
83    ///
84    /// Wraps
85    /// [mx_socket_write](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/socket_write.md).
86    pub fn write(&self, opts: SocketWriteOpts, bytes: &[u8]) -> Result<usize, Status> {
87        let mut actual = 0;
88        let status = unsafe {
89            sys::mx_socket_write(self.raw_handle(), opts as u32, bytes.as_ptr(), bytes.len(),
90                &mut actual)
91        };
92        into_result(status, || actual)
93    }
94
95    /// Read the given bytes from the socket.
96    /// Return value (on success) is number of bytes actually read.
97    ///
98    /// Wraps
99    /// [mx_socket_read](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/socket_read.md).
100    pub fn read(&self, opts: SocketReadOpts, bytes: &mut [u8]) -> Result<usize, Status> {
101        let mut actual = 0;
102        let status = unsafe {
103            sys::mx_socket_read(self.raw_handle(), opts as u32, bytes.as_mut_ptr(),
104                bytes.len(), &mut actual)
105        };
106        if status != sys::MX_OK {
107            // If an error is returned then actual is undefined, so to be safe we set it to 0 and
108            // ignore any data that is set in bytes.
109            actual = 0;
110        }
111        into_result(status, || actual)
112    }
113
114    /// Close half of the socket, so attempts by the other side to write will fail.
115    ///
116    /// Implements the `MX_SOCKET_HALF_CLOSE` option of
117    /// [mx_socket_write](https://fuchsia.googlesource.com/magenta/+/master/docs/syscalls/socket_write.md).
118    pub fn half_close(&self) -> Result<(), Status> {
119        let status = unsafe { sys::mx_socket_write(self.raw_handle(), sys::MX_SOCKET_HALF_CLOSE,
120            ptr::null(), 0, ptr::null_mut()) };
121        into_result(status, || ())
122    }
123
124    pub fn outstanding_read_bytes(&self) -> Result<usize, Status> {
125        let mut outstanding = 0;
126        let status = unsafe {
127            sys::mx_socket_read(self.raw_handle(), 0, ptr::null_mut(), 0, &mut outstanding)
128        };
129        into_result(status, || outstanding)
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn socket_basic() {
139        let (s1, s2) = Socket::create(SocketOpts::Default).unwrap();
140
141        // Write in one end and read it back out the other.
142        assert_eq!(s1.write(SocketWriteOpts::Default, b"hello").unwrap(), 5);
143
144        let mut read_vec = vec![0; 8];
145        assert_eq!(s2.read(SocketReadOpts::Default, &mut read_vec).unwrap(), 5);
146        assert_eq!(&read_vec[0..5], b"hello");
147
148        // Try reading when there is nothing to read.
149        assert_eq!(s2.read(SocketReadOpts::Default, &mut read_vec), Err(Status::ErrShouldWait));
150
151        // Close the socket from one end.
152        assert!(s1.half_close().is_ok());
153        assert_eq!(s2.read(SocketReadOpts::Default, &mut read_vec), Err(Status::ErrPeerClosed));
154        assert_eq!(s1.write(SocketWriteOpts::Default, b"fail"), Err(Status::ErrBadState));
155
156        // Writing in the other direction should still work.
157        assert_eq!(s1.read(SocketReadOpts::Default, &mut read_vec), Err(Status::ErrShouldWait));
158        assert_eq!(s2.write(SocketWriteOpts::Default, b"back").unwrap(), 4);
159        assert_eq!(s1.read(SocketReadOpts::Default, &mut read_vec).unwrap(), 4);
160        assert_eq!(&read_vec[0..4], b"back");
161    }
162}