hyper/ffi/
io.rs

1use std::ffi::c_void;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use super::task::hyper_context;
6use crate::ffi::size_t;
7use crate::rt::{Read, Write};
8
9/// Sentinel value to return from a read or write callback that the operation
10/// is pending.
11pub const HYPER_IO_PENDING: size_t = 0xFFFFFFFF;
12/// Sentinel value to return from a read or write callback that the operation
13/// has errored.
14pub const HYPER_IO_ERROR: size_t = 0xFFFFFFFE;
15
16type hyper_io_read_callback =
17    extern "C" fn(*mut c_void, *mut hyper_context<'_>, *mut u8, size_t) -> size_t;
18type hyper_io_write_callback =
19    extern "C" fn(*mut c_void, *mut hyper_context<'_>, *const u8, size_t) -> size_t;
20
21/// A read/write handle for a specific connection.
22///
23/// This owns a specific TCP or TLS connection for the lifetime of
24/// that connection. It contains a read and write callback, as well as a
25/// void *userdata. Typically the userdata will point to a struct
26/// containing a file descriptor and a TLS context.
27///
28/// Methods:
29///
30/// - hyper_io_new:          Create a new IO type used to represent a transport.
31/// - hyper_io_set_read:     Set the read function for this IO transport.
32/// - hyper_io_set_write:    Set the write function for this IO transport.
33/// - hyper_io_set_userdata: Set the user data pointer for this IO to some value.
34/// - hyper_io_free:         Free an IO handle.
35pub struct hyper_io {
36    read: hyper_io_read_callback,
37    write: hyper_io_write_callback,
38    userdata: *mut c_void,
39}
40
41ffi_fn! {
42    /// Create a new IO type used to represent a transport.
43    ///
44    /// The read and write functions of this transport should be set with
45    /// `hyper_io_set_read` and `hyper_io_set_write`.
46    ///
47    /// It is expected that the underlying transport is non-blocking. When
48    /// a read or write callback can't make progress because there is no
49    /// data available yet, it should use the `hyper_waker` mechanism to
50    /// arrange to be called again when data is available.
51    ///
52    /// To avoid a memory leak, the IO handle must eventually be consumed by
53    /// `hyper_io_free` or `hyper_clientconn_handshake`.
54    fn hyper_io_new() -> *mut hyper_io {
55        Box::into_raw(Box::new(hyper_io {
56            read: read_noop,
57            write: write_noop,
58            userdata: std::ptr::null_mut(),
59        }))
60    } ?= std::ptr::null_mut()
61}
62
63ffi_fn! {
64    /// Free an IO handle.
65    ///
66    /// This should only be used if the request isn't consumed by
67    /// `hyper_clientconn_handshake`.
68    fn hyper_io_free(io: *mut hyper_io) {
69        drop(non_null!(Box::from_raw(io) ?= ()));
70    }
71}
72
73ffi_fn! {
74    /// Set the user data pointer for this IO to some value.
75    ///
76    /// This value is passed as an argument to the read and write callbacks.
77    fn hyper_io_set_userdata(io: *mut hyper_io, data: *mut c_void) {
78        non_null!(&mut *io ?= ()).userdata = data;
79    }
80}
81
82ffi_fn! {
83    /// Set the read function for this IO transport.
84    ///
85    /// Data that is read from the transport should be put in the `buf` pointer,
86    /// up to `buf_len` bytes. The number of bytes read should be the return value.
87    ///
88    /// It is undefined behavior to try to access the bytes in the `buf` pointer,
89    /// unless you have already written them yourself. It is also undefined behavior
90    /// to return that more bytes have been written than actually set on the `buf`.
91    ///
92    /// If there is no data currently available, the callback should create a
93    /// `hyper_waker` from its `hyper_context` argument and register the waker
94    /// with whatever polling mechanism is used to signal when data is available
95    /// later on. The return value should be `HYPER_IO_PENDING`. See the
96    /// documentation for `hyper_waker`.
97    ///
98    /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
99    /// should be the return value.
100    fn hyper_io_set_read(io: *mut hyper_io, func: hyper_io_read_callback) {
101        non_null!(&mut *io ?= ()).read = func;
102    }
103}
104
105ffi_fn! {
106    /// Set the write function for this IO transport.
107    ///
108    /// Data from the `buf` pointer should be written to the transport, up to
109    /// `buf_len` bytes. The number of bytes written should be the return value.
110    ///
111    /// If there is no data currently available, the callback should create a
112    /// `hyper_waker` from its `hyper_context` argument and register the waker
113    /// with whatever polling mechanism is used to signal when data is available
114    /// later on. The return value should be `HYPER_IO_PENDING`. See the documentation
115    /// for `hyper_waker`.
116    ///
117    /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
118    /// should be the return value.
119    fn hyper_io_set_write(io: *mut hyper_io, func: hyper_io_write_callback) {
120        non_null!(&mut *io ?= ()).write = func;
121    }
122}
123
124/// cbindgen:ignore
125extern "C" fn read_noop(
126    _userdata: *mut c_void,
127    _: *mut hyper_context<'_>,
128    _buf: *mut u8,
129    _buf_len: size_t,
130) -> size_t {
131    0
132}
133
134/// cbindgen:ignore
135extern "C" fn write_noop(
136    _userdata: *mut c_void,
137    _: *mut hyper_context<'_>,
138    _buf: *const u8,
139    _buf_len: size_t,
140) -> size_t {
141    0
142}
143
144impl Read for hyper_io {
145    fn poll_read(
146        self: Pin<&mut Self>,
147        cx: &mut Context<'_>,
148        mut buf: crate::rt::ReadBufCursor<'_>,
149    ) -> Poll<std::io::Result<()>> {
150        let buf_ptr = unsafe { buf.as_mut() }.as_mut_ptr() as *mut u8;
151        let buf_len = buf.remaining();
152
153        match (self.read)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) {
154            HYPER_IO_PENDING => Poll::Pending,
155            HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new(
156                std::io::ErrorKind::Other,
157                "io error",
158            ))),
159            ok => {
160                // We have to trust that the user's read callback actually
161                // filled in that many bytes... :(
162                unsafe { buf.advance(ok) };
163                Poll::Ready(Ok(()))
164            }
165        }
166    }
167}
168
169impl Write for hyper_io {
170    fn poll_write(
171        self: Pin<&mut Self>,
172        cx: &mut Context<'_>,
173        buf: &[u8],
174    ) -> Poll<std::io::Result<usize>> {
175        let buf_ptr = buf.as_ptr();
176        let buf_len = buf.len();
177
178        match (self.write)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) {
179            HYPER_IO_PENDING => Poll::Pending,
180            HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new(
181                std::io::ErrorKind::Other,
182                "io error",
183            ))),
184            ok => Poll::Ready(Ok(ok)),
185        }
186    }
187
188    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
189        Poll::Ready(Ok(()))
190    }
191
192    fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
193        Poll::Ready(Ok(()))
194    }
195}
196
197unsafe impl Send for hyper_io {}
198unsafe impl Sync for hyper_io {}