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