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 {}