1 use std::ffi::c_void;
2 use std::pin::Pin;
3 use std::task::{Context, Poll};
4 
5 use libc::size_t;
6 use tokio::io::{AsyncRead, AsyncWrite};
7 
8 use super::task::hyper_context;
9 
10 /// Sentinel value to return from a read or write callback that the operation
11 /// is pending.
12 pub const HYPER_IO_PENDING: size_t = 0xFFFFFFFF;
13 /// Sentinel value to return from a read or write callback that the operation
14 /// has errored.
15 pub const HYPER_IO_ERROR: size_t = 0xFFFFFFFE;
16 
17 type hyper_io_read_callback =
18     extern "C" fn(*mut c_void, *mut hyper_context<'_>, *mut u8, size_t) -> size_t;
19 type hyper_io_write_callback =
20     extern "C" fn(*mut c_void, *mut hyper_context<'_>, *const u8, size_t) -> size_t;
21 
22 /// An IO object used to represent a socket or similar concept.
23 pub struct hyper_io {
24     read: hyper_io_read_callback,
25     write: hyper_io_write_callback,
26     userdata: *mut c_void,
27 }
28 
29 ffi_fn! {
30     /// Create a new IO type used to represent a transport.
31     ///
32     /// The read and write functions of this transport should be set with
33     /// `hyper_io_set_read` and `hyper_io_set_write`.
34     fn hyper_io_new() -> *mut hyper_io {
35         Box::into_raw(Box::new(hyper_io {
36             read: read_noop,
37             write: write_noop,
38             userdata: std::ptr::null_mut(),
39         }))
40     } ?= std::ptr::null_mut()
41 }
42 
43 ffi_fn! {
44     /// Free an unused `hyper_io *`.
45     ///
46     /// This is typically only useful if you aren't going to pass ownership
47     /// of the IO handle to hyper, such as with `hyper_clientconn_handshake()`.
48     fn hyper_io_free(io: *mut hyper_io) {
49         drop(non_null!(Box::from_raw(io) ?= ()));
50     }
51 }
52 
53 ffi_fn! {
54     /// Set the user data pointer for this IO to some value.
55     ///
56     /// This value is passed as an argument to the read and write callbacks.
57     fn hyper_io_set_userdata(io: *mut hyper_io, data: *mut c_void) {
58         non_null!(&mut *io ?= ()).userdata = data;
59     }
60 }
61 
62 ffi_fn! {
63     /// Set the read function for this IO transport.
64     ///
65     /// Data that is read from the transport should be put in the `buf` pointer,
66     /// up to `buf_len` bytes. The number of bytes read should be the return value.
67     ///
68     /// It is undefined behavior to try to access the bytes in the `buf` pointer,
69     /// unless you have already written them yourself. It is also undefined behavior
70     /// to return that more bytes have been written than actually set on the `buf`.
71     ///
72     /// If there is no data currently available, a waker should be claimed from
73     /// the `ctx` and registered with whatever polling mechanism is used to signal
74     /// when data is available later on. The return value should be
75     /// `HYPER_IO_PENDING`.
76     ///
77     /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
78     /// should be the return value.
79     fn hyper_io_set_read(io: *mut hyper_io, func: hyper_io_read_callback) {
80         non_null!(&mut *io ?= ()).read = func;
81     }
82 }
83 
84 ffi_fn! {
85     /// Set the write function for this IO transport.
86     ///
87     /// Data from the `buf` pointer should be written to the transport, up to
88     /// `buf_len` bytes. The number of bytes written should be the return value.
89     ///
90     /// If no data can currently be written, the `waker` should be cloned and
91     /// registered with whatever polling mechanism is used to signal when data
92     /// is available later on. The return value should be `HYPER_IO_PENDING`.
93     ///
94     /// Yeet.
95     ///
96     /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
97     /// should be the return value.
98     fn hyper_io_set_write(io: *mut hyper_io, func: hyper_io_write_callback) {
99         non_null!(&mut *io ?= ()).write = func;
100     }
101 }
102 
103 /// cbindgen:ignore
read_noop( _userdata: *mut c_void, _: *mut hyper_context<'_>, _buf: *mut u8, _buf_len: size_t, ) -> size_t104 extern "C" fn read_noop(
105     _userdata: *mut c_void,
106     _: *mut hyper_context<'_>,
107     _buf: *mut u8,
108     _buf_len: size_t,
109 ) -> size_t {
110     0
111 }
112 
113 /// cbindgen:ignore
write_noop( _userdata: *mut c_void, _: *mut hyper_context<'_>, _buf: *const u8, _buf_len: size_t, ) -> size_t114 extern "C" fn write_noop(
115     _userdata: *mut c_void,
116     _: *mut hyper_context<'_>,
117     _buf: *const u8,
118     _buf_len: size_t,
119 ) -> size_t {
120     0
121 }
122 
123 impl AsyncRead for hyper_io {
poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut tokio::io::ReadBuf<'_>, ) -> Poll<std::io::Result<()>>124     fn poll_read(
125         self: Pin<&mut Self>,
126         cx: &mut Context<'_>,
127         buf: &mut tokio::io::ReadBuf<'_>,
128     ) -> Poll<std::io::Result<()>> {
129         let buf_ptr = unsafe { buf.unfilled_mut() }.as_mut_ptr() as *mut u8;
130         let buf_len = buf.remaining();
131 
132         match (self.read)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) {
133             HYPER_IO_PENDING => Poll::Pending,
134             HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new(
135                 std::io::ErrorKind::Other,
136                 "io error",
137             ))),
138             ok => {
139                 // We have to trust that the user's read callback actually
140                 // filled in that many bytes... :(
141                 unsafe { buf.assume_init(ok) };
142                 buf.advance(ok);
143                 Poll::Ready(Ok(()))
144             }
145         }
146     }
147 }
148 
149 impl AsyncWrite for hyper_io {
poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<std::io::Result<usize>>150     fn poll_write(
151         self: Pin<&mut Self>,
152         cx: &mut Context<'_>,
153         buf: &[u8],
154     ) -> Poll<std::io::Result<usize>> {
155         let buf_ptr = buf.as_ptr();
156         let buf_len = buf.len();
157 
158         match (self.write)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) {
159             HYPER_IO_PENDING => Poll::Pending,
160             HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new(
161                 std::io::ErrorKind::Other,
162                 "io error",
163             ))),
164             ok => Poll::Ready(Ok(ok)),
165         }
166     }
167 
poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>>168     fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
169         Poll::Ready(Ok(()))
170     }
171 
poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>>172     fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> {
173         Poll::Ready(Ok(()))
174     }
175 }
176 
177 unsafe impl Send for hyper_io {}
178 unsafe impl Sync for hyper_io {}
179