diff options
Diffstat (limited to 'vendor/hyper/src/ffi')
| -rw-r--r-- | vendor/hyper/src/ffi/body.rs | 302 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/client.rs | 274 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/error.rs | 96 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/http_types.rs | 703 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/io.rs | 198 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/macros.rs | 53 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/mod.rs | 99 | ||||
| -rw-r--r-- | vendor/hyper/src/ffi/task.rs | 549 |
8 files changed, 2274 insertions, 0 deletions
diff --git a/vendor/hyper/src/ffi/body.rs b/vendor/hyper/src/ffi/body.rs new file mode 100644 index 00000000..e5a09e57 --- /dev/null +++ b/vendor/hyper/src/ffi/body.rs @@ -0,0 +1,302 @@ +use std::ffi::{c_int, c_void}; +use std::mem::ManuallyDrop; +use std::ptr; +use std::task::{Context, Poll}; + +use http_body_util::BodyExt as _; + +use super::task::{hyper_context, hyper_task, hyper_task_return_type, AsTaskType}; +use super::{UserDataPointer, HYPER_ITER_CONTINUE}; +use crate::body::{Bytes, Frame, Incoming as IncomingBody}; +use crate::ffi::size_t; + +/// A streaming HTTP body. +/// +/// This is used both for sending requests (with `hyper_request_set_body`) and +/// for receiving responses (with `hyper_response_body`). +/// +/// For outgoing request bodies, call `hyper_body_set_data_func` to provide the +/// data. +/// +/// For incoming response bodies, call `hyper_body_data` to get a task that will +/// yield a chunk of data each time it is polled. That task must be then be +/// added to the executor with `hyper_executor_push`. +/// +/// Methods: +/// +/// - hyper_body_new: Create a new “empty” body. +/// - hyper_body_set_userdata: Set userdata on this body, which will be passed to callback functions. +/// - hyper_body_set_data_func: Set the data callback for this body. +/// - hyper_body_data: Creates a task that will poll a response body for the next buffer of data. +/// - hyper_body_foreach: Creates a task to execute the callback with each body chunk received. +/// - hyper_body_free: Free a body. +pub struct hyper_body(pub(super) IncomingBody); + +/// A buffer of bytes that is sent or received on a `hyper_body`. +/// +/// Obtain one of these in the callback of `hyper_body_foreach` or by receiving +/// a task of type `HYPER_TASK_BUF` from `hyper_executor_poll` (after calling +/// `hyper_body_data` and pushing the resulting task). +/// +/// Methods: +/// +/// - hyper_buf_bytes: Get a pointer to the bytes in this buffer. +/// - hyper_buf_copy: Create a new hyper_buf * by copying the provided bytes. +/// - hyper_buf_free: Free this buffer. +/// - hyper_buf_len: Get the length of the bytes this buffer contains. +pub struct hyper_buf(pub(crate) Bytes); + +pub(crate) struct UserBody { + data_func: hyper_body_data_callback, + userdata: *mut c_void, +} + +// ===== Body ===== + +type hyper_body_foreach_callback = extern "C" fn(*mut c_void, *const hyper_buf) -> c_int; + +type hyper_body_data_callback = + extern "C" fn(*mut c_void, *mut hyper_context<'_>, *mut *mut hyper_buf) -> c_int; + +ffi_fn! { + /// Creates a new "empty" body. + /// + /// If not configured, this body acts as an empty payload. + /// + /// To avoid a memory leak, the body must eventually be consumed by + /// `hyper_body_free`, `hyper_body_foreach`, or `hyper_request_set_body`. + fn hyper_body_new() -> *mut hyper_body { + Box::into_raw(Box::new(hyper_body(IncomingBody::ffi()))) + } ?= ptr::null_mut() +} + +ffi_fn! { + /// Free a body. + /// + /// This should only be used if the request isn't consumed by + /// `hyper_body_foreach` or `hyper_request_set_body`. + fn hyper_body_free(body: *mut hyper_body) { + drop(non_null!(Box::from_raw(body) ?= ())); + } +} + +ffi_fn! { + /// Creates a task that will poll a response body for the next buffer of data. + /// + /// The task may have different types depending on the outcome: + /// + /// - `HYPER_TASK_BUF`: Success, and more data was received. + /// - `HYPER_TASK_ERROR`: An error retrieving the data. + /// - `HYPER_TASK_EMPTY`: The body has finished streaming data. + /// + /// When the application receives the task from `hyper_executor_poll`, + /// if the task type is `HYPER_TASK_BUF`, it should cast the task to + /// `hyper_buf *` and consume all the bytes in the buffer. Then + /// the application should call `hyper_body_data` again for the same + /// `hyper_body *`, to create a task for the next buffer of data. + /// Repeat until the polled task type is `HYPER_TASK_ERROR` or + /// `HYPER_TASK_EMPTY`. + /// + /// To avoid a memory leak, the task must eventually be consumed by + /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` + /// without subsequently being given back by `hyper_executor_poll`. + /// + /// This does not consume the `hyper_body *`, so it may be used again. + /// However, the `hyper_body *` MUST NOT be used or freed until the + /// related task is returned from `hyper_executor_poll`. + /// + /// For a more convenient method, see also `hyper_body_foreach`. + fn hyper_body_data(body: *mut hyper_body) -> *mut hyper_task { + // This doesn't take ownership of the Body, so don't allow destructor + let mut body = ManuallyDrop::new(non_null!(Box::from_raw(body) ?= ptr::null_mut())); + + Box::into_raw(hyper_task::boxed(async move { + loop { + match body.0.frame().await { + Some(Ok(frame)) => { + if let Ok(data) = frame.into_data() { + return Ok(Some(hyper_buf(data))); + } else { + continue; + } + }, + Some(Err(e)) => return Err(e), + None => return Ok(None), + } + } + })) + } ?= ptr::null_mut() +} + +ffi_fn! { + /// Creates a task to execute the callback with each body chunk received. + /// + /// To avoid a memory leak, the task must eventually be consumed by + /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` + /// without subsequently being given back by `hyper_executor_poll`. + /// + /// The `hyper_buf` pointer is only a borrowed reference. It cannot live outside + /// the execution of the callback. You must make a copy of the bytes to retain them. + /// + /// The callback should return `HYPER_ITER_CONTINUE` to continue iterating + /// chunks as they are received, or `HYPER_ITER_BREAK` to cancel. Each + /// invocation of the callback must consume all the bytes it is provided. + /// There is no mechanism to signal to Hyper that only a subset of bytes were + /// consumed. + /// + /// This will consume the `hyper_body *`, you shouldn't use it anymore or free it. + fn hyper_body_foreach(body: *mut hyper_body, func: hyper_body_foreach_callback, userdata: *mut c_void) -> *mut hyper_task { + let mut body = non_null!(Box::from_raw(body) ?= ptr::null_mut()); + let userdata = UserDataPointer(userdata); + + Box::into_raw(hyper_task::boxed(async move { + let _ = &userdata; + while let Some(item) = body.0.frame().await { + let frame = item?; + if let Ok(chunk) = frame.into_data() { + if HYPER_ITER_CONTINUE != func(userdata.0, &hyper_buf(chunk)) { + return Err(crate::Error::new_user_aborted_by_callback()); + } + } + } + Ok(()) + })) + } ?= ptr::null_mut() +} + +ffi_fn! { + /// Set userdata on this body, which will be passed to callback functions. + fn hyper_body_set_userdata(body: *mut hyper_body, userdata: *mut c_void) { + let b = non_null!(&mut *body ?= ()); + b.0.as_ffi_mut().userdata = userdata; + } +} + +ffi_fn! { + /// Set the outgoing data callback for this body. + /// + /// The callback is called each time hyper needs to send more data for the + /// body. It is passed the value from `hyper_body_set_userdata`. + /// + /// If there is data available, the `hyper_buf **` argument should be set + /// to a `hyper_buf *` containing the data, and `HYPER_POLL_READY` should + /// be returned. + /// + /// Returning `HYPER_POLL_READY` while the `hyper_buf **` argument points + /// to `NULL` will indicate the body has completed all data. + /// + /// If there is more data to send, but it isn't yet available, a + /// `hyper_waker` should be saved from the `hyper_context *` argument, and + /// `HYPER_POLL_PENDING` should be returned. You must wake the saved waker + /// to signal the task when data is available. + /// + /// If some error has occurred, you can return `HYPER_POLL_ERROR` to abort + /// the body. + fn hyper_body_set_data_func(body: *mut hyper_body, func: hyper_body_data_callback) { + let b = non_null!{ &mut *body ?= () }; + b.0.as_ffi_mut().data_func = func; + } +} + +// ===== impl UserBody ===== + +impl UserBody { + pub(crate) fn new() -> UserBody { + UserBody { + data_func: data_noop, + userdata: std::ptr::null_mut(), + } + } + + pub(crate) fn poll_data( + &mut self, + cx: &mut Context<'_>, + ) -> Poll<Option<crate::Result<Frame<Bytes>>>> { + let mut out = std::ptr::null_mut(); + match (self.data_func)(self.userdata, hyper_context::wrap(cx), &mut out) { + super::task::HYPER_POLL_READY => { + if out.is_null() { + Poll::Ready(None) + } else { + let buf = unsafe { Box::from_raw(out) }; + Poll::Ready(Some(Ok(Frame::data(buf.0)))) + } + } + super::task::HYPER_POLL_PENDING => Poll::Pending, + super::task::HYPER_POLL_ERROR => { + Poll::Ready(Some(Err(crate::Error::new_body_write_aborted()))) + } + unexpected => Poll::Ready(Some(Err(crate::Error::new_body_write(format!( + "unexpected hyper_body_data_func return code {}", + unexpected + ))))), + } + } +} + +/// cbindgen:ignore +extern "C" fn data_noop( + _userdata: *mut c_void, + _: *mut hyper_context<'_>, + _: *mut *mut hyper_buf, +) -> c_int { + super::task::HYPER_POLL_READY +} + +unsafe impl Send for UserBody {} +unsafe impl Sync for UserBody {} + +// ===== Bytes ===== + +ffi_fn! { + /// Create a new `hyper_buf *` by copying the provided bytes. + /// + /// This makes an owned copy of the bytes, so the `buf` argument can be + /// freed (with `hyper_buf_free`) or changed afterwards. + /// + /// To avoid a memory leak, the copy must eventually be consumed by + /// `hyper_buf_free`. + /// + /// This returns `NULL` if allocating a new buffer fails. + fn hyper_buf_copy(buf: *const u8, len: size_t) -> *mut hyper_buf { + let slice = unsafe { + std::slice::from_raw_parts(buf, len) + }; + Box::into_raw(Box::new(hyper_buf(Bytes::copy_from_slice(slice)))) + } ?= ptr::null_mut() +} + +ffi_fn! { + /// Get a pointer to the bytes in this buffer. + /// + /// This should be used in conjunction with `hyper_buf_len` to get the length + /// of the bytes data. + /// + /// This pointer is borrowed data, and not valid once the `hyper_buf` is + /// consumed/freed. + fn hyper_buf_bytes(buf: *const hyper_buf) -> *const u8 { + unsafe { (*buf).0.as_ptr() } + } ?= ptr::null() +} + +ffi_fn! { + /// Get the length of the bytes this buffer contains. + fn hyper_buf_len(buf: *const hyper_buf) -> size_t { + unsafe { (*buf).0.len() } + } +} + +ffi_fn! { + /// Free this buffer. + /// + /// This should be used for any buffer once it is no longer needed. + fn hyper_buf_free(buf: *mut hyper_buf) { + drop(unsafe { Box::from_raw(buf) }); + } +} + +unsafe impl AsTaskType for hyper_buf { + fn as_task_type(&self) -> hyper_task_return_type { + hyper_task_return_type::HYPER_TASK_BUF + } +} diff --git a/vendor/hyper/src/ffi/client.rs b/vendor/hyper/src/ffi/client.rs new file mode 100644 index 00000000..63b03d87 --- /dev/null +++ b/vendor/hyper/src/ffi/client.rs @@ -0,0 +1,274 @@ +use std::ffi::c_int; +use std::ptr; +use std::sync::Arc; + +use crate::client::conn; +use crate::rt::Executor as _; + +use super::error::hyper_code; +use super::http_types::{hyper_request, hyper_response}; +use super::io::hyper_io; +use super::task::{hyper_executor, hyper_task, hyper_task_return_type, AsTaskType, WeakExec}; + +/// An options builder to configure an HTTP client connection. +/// +/// Methods: +/// +/// - hyper_clientconn_options_new: Creates a new set of HTTP clientconn options to be used in a handshake. +/// - hyper_clientconn_options_exec: Set the client background task executor. +/// - hyper_clientconn_options_http2: Set whether to use HTTP2. +/// - hyper_clientconn_options_set_preserve_header_case: Set whether header case is preserved. +/// - hyper_clientconn_options_set_preserve_header_order: Set whether header order is preserved. +/// - hyper_clientconn_options_http1_allow_multiline_headers: Set whether HTTP/1 connections accept obsolete line folding for header values. +/// - hyper_clientconn_options_free: Free a set of HTTP clientconn options. +pub struct hyper_clientconn_options { + http1_allow_obsolete_multiline_headers_in_responses: bool, + http1_preserve_header_case: bool, + http1_preserve_header_order: bool, + http2: bool, + /// Use a `Weak` to prevent cycles. + exec: WeakExec, +} + +/// An HTTP client connection handle. +/// +/// These are used to send one or more requests on a single connection. +/// +/// It's possible to send multiple requests on a single connection, such +/// as when HTTP/1 keep-alive or HTTP/2 is used. +/// +/// To create a `hyper_clientconn`: +/// +/// 1. Create a `hyper_io` with `hyper_io_new`. +/// 2. Create a `hyper_clientconn_options` with `hyper_clientconn_options_new`. +/// 3. Call `hyper_clientconn_handshake` with the `hyper_io` and `hyper_clientconn_options`. +/// This creates a `hyper_task`. +/// 5. Call `hyper_task_set_userdata` to assign an application-specific pointer to the task. +/// This allows keeping track of multiple connections that may be handshaking +/// simultaneously. +/// 4. Add the `hyper_task` to an executor with `hyper_executor_push`. +/// 5. Poll that executor until it yields a task of type `HYPER_TASK_CLIENTCONN`. +/// 6. Extract the `hyper_clientconn` from the task with `hyper_task_value`. +/// This will require a cast from `void *` to `hyper_clientconn *`. +/// +/// This process results in a `hyper_clientconn` that permanently owns the +/// `hyper_io`. Because the `hyper_io` in turn owns a TCP or TLS connection, that means +/// the `hyper_clientconn` owns the connection for both the clientconn's lifetime +/// and the connection's lifetime. +/// +/// In other words, each connection (`hyper_io`) must have exactly one `hyper_clientconn` +/// associated with it. That's because `hyper_clientconn_handshake` sends the +/// [HTTP/2 Connection Preface] (for HTTP/2 connections). Since that preface can't +/// be sent twice, handshake can't be called twice. +/// +/// [HTTP/2 Connection Preface]: https://datatracker.ietf.org/doc/html/rfc9113#name-http-2-connection-preface +/// +/// Methods: +/// +/// - hyper_clientconn_handshake: Creates an HTTP client handshake task. +/// - hyper_clientconn_send: Creates a task to send a request on the client connection. +/// - hyper_clientconn_free: Free a hyper_clientconn *. +pub struct hyper_clientconn { + tx: Tx, +} + +enum Tx { + #[cfg(feature = "http1")] + Http1(conn::http1::SendRequest<crate::body::Incoming>), + #[cfg(feature = "http2")] + Http2(conn::http2::SendRequest<crate::body::Incoming>), +} + +// ===== impl hyper_clientconn ===== + +ffi_fn! { + /// Creates an HTTP client handshake task. + /// + /// Both the `io` and the `options` are consumed in this function call. + /// They should not be used or freed afterwards. + /// + /// The returned task must be polled with an executor until the handshake + /// completes, at which point the value can be taken. + /// + /// To avoid a memory leak, the task must eventually be consumed by + /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` + /// without subsequently being given back by `hyper_executor_poll`. + fn hyper_clientconn_handshake(io: *mut hyper_io, options: *mut hyper_clientconn_options) -> *mut hyper_task { + let options = non_null! { Box::from_raw(options) ?= ptr::null_mut() }; + let io = non_null! { Box::from_raw(io) ?= ptr::null_mut() }; + + Box::into_raw(hyper_task::boxed(async move { + #[cfg(feature = "http2")] + { + if options.http2 { + return conn::http2::Builder::new(options.exec.clone()) + .handshake::<_, crate::body::Incoming>(io) + .await + .map(|(tx, conn)| { + options.exec.execute(Box::pin(async move { + let _ = conn.await; + })); + hyper_clientconn { tx: Tx::Http2(tx) } + }); + } + } + + conn::http1::Builder::new() + .allow_obsolete_multiline_headers_in_responses(options.http1_allow_obsolete_multiline_headers_in_responses) + .preserve_header_case(options.http1_preserve_header_case) + .preserve_header_order(options.http1_preserve_header_order) + .handshake::<_, crate::body::Incoming>(io) + .await + .map(|(tx, conn)| { + options.exec.execute(Box::pin(async move { + let _ = conn.await; + })); + hyper_clientconn { tx: Tx::Http1(tx) } + }) + })) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Creates a task to send a request on the client connection. + /// + /// This consumes the request. You should not use or free the request + /// afterwards. + /// + /// Returns a task that needs to be polled until it is ready. When ready, the + /// task yields a `hyper_response *`. + /// + /// To avoid a memory leak, the task must eventually be consumed by + /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` + /// without subsequently being given back by `hyper_executor_poll`. + fn hyper_clientconn_send(conn: *mut hyper_clientconn, req: *mut hyper_request) -> *mut hyper_task { + let mut req = non_null! { Box::from_raw(req) ?= ptr::null_mut() }; + + // Update request with original-case map of headers + req.finalize_request(); + + let fut = match non_null! { &mut *conn ?= ptr::null_mut() }.tx { + Tx::Http1(ref mut tx) => futures_util::future::Either::Left(tx.send_request(req.0)), + Tx::Http2(ref mut tx) => futures_util::future::Either::Right(tx.send_request(req.0)), + }; + + let fut = async move { + fut.await.map(hyper_response::wrap) + }; + + Box::into_raw(hyper_task::boxed(fut)) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Free a `hyper_clientconn *`. + /// + /// This should be used for any connection once it is no longer needed. + fn hyper_clientconn_free(conn: *mut hyper_clientconn) { + drop(non_null! { Box::from_raw(conn) ?= () }); + } +} + +unsafe impl AsTaskType for hyper_clientconn { + fn as_task_type(&self) -> hyper_task_return_type { + hyper_task_return_type::HYPER_TASK_CLIENTCONN + } +} + +// ===== impl hyper_clientconn_options ===== + +ffi_fn! { + /// Creates a new set of HTTP clientconn options to be used in a handshake. + /// + /// To avoid a memory leak, the options must eventually be consumed by + /// `hyper_clientconn_options_free` or `hyper_clientconn_handshake`. + fn hyper_clientconn_options_new() -> *mut hyper_clientconn_options { + Box::into_raw(Box::new(hyper_clientconn_options { + http1_allow_obsolete_multiline_headers_in_responses: false, + http1_preserve_header_case: false, + http1_preserve_header_order: false, + http2: false, + exec: WeakExec::new(), + })) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Set whether header case is preserved. + /// + /// Pass `0` to allow lowercase normalization (default), `1` to retain original case. + fn hyper_clientconn_options_set_preserve_header_case(opts: *mut hyper_clientconn_options, enabled: c_int) { + let opts = non_null! { &mut *opts ?= () }; + opts.http1_preserve_header_case = enabled != 0; + } +} + +ffi_fn! { + /// Set whether header order is preserved. + /// + /// Pass `0` to allow reordering (default), `1` to retain original ordering. + fn hyper_clientconn_options_set_preserve_header_order(opts: *mut hyper_clientconn_options, enabled: c_int) { + let opts = non_null! { &mut *opts ?= () }; + opts.http1_preserve_header_order = enabled != 0; + } +} + +ffi_fn! { + /// Free a set of HTTP clientconn options. + /// + /// This should only be used if the options aren't consumed by + /// `hyper_clientconn_handshake`. + fn hyper_clientconn_options_free(opts: *mut hyper_clientconn_options) { + drop(non_null! { Box::from_raw(opts) ?= () }); + } +} + +ffi_fn! { + /// Set the client background task executor. + /// + /// This does not consume the `options` or the `exec`. + fn hyper_clientconn_options_exec(opts: *mut hyper_clientconn_options, exec: *const hyper_executor) { + let opts = non_null! { &mut *opts ?= () }; + + let exec = non_null! { Arc::from_raw(exec) ?= () }; + let weak_exec = hyper_executor::downgrade(&exec); + std::mem::forget(exec); + + opts.exec = weak_exec; + } +} + +ffi_fn! { + /// Set whether to use HTTP2. + /// + /// Pass `0` to disable, `1` to enable. + fn hyper_clientconn_options_http2(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { + #[cfg(feature = "http2")] + { + let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; + opts.http2 = enabled != 0; + hyper_code::HYPERE_OK + } + + #[cfg(not(feature = "http2"))] + { + drop(opts); + drop(enabled); + hyper_code::HYPERE_FEATURE_NOT_ENABLED + } + } +} + +ffi_fn! { + /// Set whether HTTP/1 connections accept obsolete line folding for header values. + /// + /// Newline codepoints (\r and \n) will be transformed to spaces when parsing. + /// + /// Pass `0` to disable, `1` to enable. + /// + fn hyper_clientconn_options_http1_allow_multiline_headers(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { + let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; + opts.http1_allow_obsolete_multiline_headers_in_responses = enabled != 0; + hyper_code::HYPERE_OK + } +} diff --git a/vendor/hyper/src/ffi/error.rs b/vendor/hyper/src/ffi/error.rs new file mode 100644 index 00000000..cc289ed7 --- /dev/null +++ b/vendor/hyper/src/ffi/error.rs @@ -0,0 +1,96 @@ +use crate::ffi::size_t; + +/// A more detailed error object returned by some hyper functions. +/// +/// Compare with `hyper_code`, which is a simpler error returned from +/// some hyper functions. +/// +/// Methods: +/// +/// - hyper_error_code: Get an equivalent hyper_code from this error. +/// - hyper_error_print: Print the details of this error to a buffer. +/// - hyper_error_free: Frees a hyper_error. +pub struct hyper_error(crate::Error); + +/// A return code for many of hyper's methods. +#[repr(C)] +pub enum hyper_code { + /// All is well. + HYPERE_OK, + /// General error, details in the `hyper_error *`. + HYPERE_ERROR, + /// A function argument was invalid. + HYPERE_INVALID_ARG, + /// The IO transport returned an EOF when one wasn't expected. + /// + /// This typically means an HTTP request or response was expected, but the + /// connection closed cleanly without sending (all of) it. + HYPERE_UNEXPECTED_EOF, + /// Aborted by a user supplied callback. + HYPERE_ABORTED_BY_CALLBACK, + /// An optional hyper feature was not enabled. + #[cfg_attr(feature = "http2", allow(unused))] + HYPERE_FEATURE_NOT_ENABLED, + /// The peer sent an HTTP message that could not be parsed. + HYPERE_INVALID_PEER_MESSAGE, +} + +// ===== impl hyper_error ===== + +impl hyper_error { + fn code(&self) -> hyper_code { + use crate::error::Kind as ErrorKind; + use crate::error::User; + + match self.0.kind() { + ErrorKind::Parse(_) => hyper_code::HYPERE_INVALID_PEER_MESSAGE, + ErrorKind::IncompleteMessage => hyper_code::HYPERE_UNEXPECTED_EOF, + ErrorKind::User(User::AbortedByCallback) => hyper_code::HYPERE_ABORTED_BY_CALLBACK, + // TODO: add more variants + _ => hyper_code::HYPERE_ERROR, + } + } + + fn print_to(&self, dst: &mut [u8]) -> usize { + use std::io::Write; + + let mut dst = std::io::Cursor::new(dst); + + // A write! error doesn't matter. As much as possible will have been + // written, and the Cursor position will know how far that is (even + // if that is zero). + let _ = write!(dst, "{}", &self.0); + dst.position() as usize + } +} + +ffi_fn! { + /// Frees a `hyper_error`. + /// + /// This should be used for any error once it is no longer needed. + fn hyper_error_free(err: *mut hyper_error) { + drop(non_null!(Box::from_raw(err) ?= ())); + } +} + +ffi_fn! { + /// Get an equivalent `hyper_code` from this error. + fn hyper_error_code(err: *const hyper_error) -> hyper_code { + non_null!(&*err ?= hyper_code::HYPERE_INVALID_ARG).code() + } +} + +ffi_fn! { + /// Print the details of this error to a buffer. + /// + /// The `dst_len` value must be the maximum length that the buffer can + /// store. + /// + /// The return value is number of bytes that were written to `dst`. + fn hyper_error_print(err: *const hyper_error, dst: *mut u8, dst_len: size_t) -> size_t { + let dst = unsafe { + std::slice::from_raw_parts_mut(dst, dst_len) + }; + non_null!(&*err ?= 0).print_to(dst) + } +} diff --git a/vendor/hyper/src/ffi/http_types.rs b/vendor/hyper/src/ffi/http_types.rs new file mode 100644 index 00000000..3dc4a254 --- /dev/null +++ b/vendor/hyper/src/ffi/http_types.rs @@ -0,0 +1,703 @@ +use std::ffi::{c_int, c_void}; + +use bytes::Bytes; + +use super::body::hyper_body; +use super::error::hyper_code; +use super::task::{hyper_task_return_type, AsTaskType}; +use super::{UserDataPointer, HYPER_ITER_CONTINUE}; +use crate::body::Incoming as IncomingBody; +use crate::ext::{HeaderCaseMap, OriginalHeaderOrder, ReasonPhrase}; +use crate::ffi::size_t; +use crate::header::{HeaderName, HeaderValue}; +use crate::{HeaderMap, Method, Request, Response, Uri}; + +/// An HTTP request. +/// +/// Once you've finished constructing a request, you can send it with +/// `hyper_clientconn_send`. +/// +/// Methods: +/// +/// - hyper_request_new: Construct a new HTTP request. +/// - hyper_request_headers: Gets a mutable reference to the HTTP headers of this request +/// - hyper_request_set_body: Set the body of the request. +/// - hyper_request_set_method: Set the HTTP Method of the request. +/// - hyper_request_set_uri: Set the URI of the request. +/// - hyper_request_set_uri_parts: Set the URI of the request with separate scheme, authority, and path/query strings. +/// - hyper_request_set_version: Set the preferred HTTP version of the request. +/// - hyper_request_on_informational: Set an informational (1xx) response callback. +/// - hyper_request_free: Free an HTTP request. +pub struct hyper_request(pub(super) Request<IncomingBody>); + +/// An HTTP response. +/// +/// Obtain one of these by making a request with `hyper_clientconn_send`, then +/// polling the executor unntil you get a `hyper_task` of type +/// `HYPER_TASK_RESPONSE`. To figure out which request this response +/// corresponds to, check the userdata of the task, which you should +/// previously have set to an application-specific identifier for the +/// request. +/// +/// Methods: +/// +/// - hyper_response_status: Get the HTTP-Status code of this response. +/// - hyper_response_version: Get the HTTP version used by this response. +/// - hyper_response_reason_phrase: Get a pointer to the reason-phrase of this response. +/// - hyper_response_reason_phrase_len: Get the length of the reason-phrase of this response. +/// - hyper_response_headers: Gets a reference to the HTTP headers of this response. +/// - hyper_response_body: Take ownership of the body of this response. +/// - hyper_response_free: Free an HTTP response. +pub struct hyper_response(pub(super) Response<IncomingBody>); + +/// An HTTP header map. +/// +/// These can be part of a request or response. +/// +/// Obtain a pointer to read or modify these from `hyper_request_headers` +/// or `hyper_response_headers`. +/// +/// Methods: +/// +/// - hyper_headers_add: Adds the provided value to the list of the provided name. +/// - hyper_headers_foreach: Iterates the headers passing each name and value pair to the callback. +/// - hyper_headers_set: Sets the header with the provided name to the provided value. +#[derive(Clone)] +pub struct hyper_headers { + pub(super) headers: HeaderMap, + orig_casing: HeaderCaseMap, + orig_order: OriginalHeaderOrder, +} + +#[derive(Clone)] +struct OnInformational { + func: hyper_request_on_informational_callback, + data: UserDataPointer, +} + +type hyper_request_on_informational_callback = extern "C" fn(*mut c_void, *mut hyper_response); + +// ===== impl hyper_request ===== + +ffi_fn! { + /// Construct a new HTTP request. + /// + /// The default request has an empty body. To send a body, call `hyper_request_set_body`. + /// + /// + /// To avoid a memory leak, the request must eventually be consumed by + /// `hyper_request_free` or `hyper_clientconn_send`. + fn hyper_request_new() -> *mut hyper_request { + Box::into_raw(Box::new(hyper_request(Request::new(IncomingBody::empty())))) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Free an HTTP request. + /// + /// This should only be used if the request isn't consumed by + /// `hyper_clientconn_send`. + fn hyper_request_free(req: *mut hyper_request) { + drop(non_null!(Box::from_raw(req) ?= ())); + } +} + +ffi_fn! { + /// Set the HTTP Method of the request. + fn hyper_request_set_method(req: *mut hyper_request, method: *const u8, method_len: size_t) -> hyper_code { + let bytes = unsafe { + std::slice::from_raw_parts(method, method_len as usize) + }; + let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); + match Method::from_bytes(bytes) { + Ok(m) => { + *req.0.method_mut() = m; + hyper_code::HYPERE_OK + }, + Err(_) => { + hyper_code::HYPERE_INVALID_ARG + } + } + } +} + +ffi_fn! { + /// Set the URI of the request. + /// + /// The request's URI is best described as the `request-target` from the RFCs. So in HTTP/1, + /// whatever is set will get sent as-is in the first line (GET $uri HTTP/1.1). It + /// supports the 4 defined variants, origin-form, absolute-form, authority-form, and + /// asterisk-form. + /// + /// The underlying type was built to efficiently support HTTP/2 where the request-target is + /// split over :scheme, :authority, and :path. As such, each part can be set explicitly, or the + /// type can parse a single contiguous string and if a scheme is found, that slot is "set". If + /// the string just starts with a path, only the path portion is set. All pseudo headers that + /// have been parsed/set are sent when the connection type is HTTP/2. + /// + /// To set each slot explicitly, use `hyper_request_set_uri_parts`. + fn hyper_request_set_uri(req: *mut hyper_request, uri: *const u8, uri_len: size_t) -> hyper_code { + let bytes = unsafe { + std::slice::from_raw_parts(uri, uri_len as usize) + }; + let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); + match Uri::from_maybe_shared(bytes) { + Ok(u) => { + *req.0.uri_mut() = u; + hyper_code::HYPERE_OK + }, + Err(_) => { + hyper_code::HYPERE_INVALID_ARG + } + } + } +} + +ffi_fn! { + /// Set the URI of the request with separate scheme, authority, and + /// path/query strings. + /// + /// Each of `scheme`, `authority`, and `path_and_query` should either be + /// null, to skip providing a component, or point to a UTF-8 encoded + /// string. If any string pointer argument is non-null, its corresponding + /// `len` parameter must be set to the string's length. + fn hyper_request_set_uri_parts( + req: *mut hyper_request, + scheme: *const u8, + scheme_len: size_t, + authority: *const u8, + authority_len: size_t, + path_and_query: *const u8, + path_and_query_len: size_t + ) -> hyper_code { + let mut builder = Uri::builder(); + if !scheme.is_null() { + let scheme_bytes = unsafe { + std::slice::from_raw_parts(scheme, scheme_len as usize) + }; + builder = builder.scheme(scheme_bytes); + } + if !authority.is_null() { + let authority_bytes = unsafe { + std::slice::from_raw_parts(authority, authority_len as usize) + }; + builder = builder.authority(authority_bytes); + } + if !path_and_query.is_null() { + let path_and_query_bytes = unsafe { + std::slice::from_raw_parts(path_and_query, path_and_query_len as usize) + }; + builder = builder.path_and_query(path_and_query_bytes); + } + match builder.build() { + Ok(u) => { + *unsafe { &mut *req }.0.uri_mut() = u; + hyper_code::HYPERE_OK + }, + Err(_) => { + hyper_code::HYPERE_INVALID_ARG + } + } + } +} + +ffi_fn! { + /// Set the preferred HTTP version of the request. + /// + /// The version value should be one of the `HYPER_HTTP_VERSION_` constants. + /// + /// Note that this won't change the major HTTP version of the connection, + /// since that is determined at the handshake step. + fn hyper_request_set_version(req: *mut hyper_request, version: c_int) -> hyper_code { + use http::Version; + + let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); + *req.0.version_mut() = match version { + super::HYPER_HTTP_VERSION_NONE => Version::HTTP_11, + super::HYPER_HTTP_VERSION_1_0 => Version::HTTP_10, + super::HYPER_HTTP_VERSION_1_1 => Version::HTTP_11, + super::HYPER_HTTP_VERSION_2 => Version::HTTP_2, + _ => { + // We don't know this version + return hyper_code::HYPERE_INVALID_ARG; + } + }; + hyper_code::HYPERE_OK + } +} + +ffi_fn! { + /// Gets a mutable reference to the HTTP headers of this request + /// + /// This is not an owned reference, so it should not be accessed after the + /// `hyper_request` has been consumed. + fn hyper_request_headers(req: *mut hyper_request) -> *mut hyper_headers { + hyper_headers::get_or_default(unsafe { &mut *req }.0.extensions_mut()) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Set the body of the request. + /// + /// You can get a `hyper_body` by calling `hyper_body_new`. + /// + /// This takes ownership of the `hyper_body *`, you must not use it or + /// free it after setting it on the request. + fn hyper_request_set_body(req: *mut hyper_request, body: *mut hyper_body) -> hyper_code { + let body = non_null!(Box::from_raw(body) ?= hyper_code::HYPERE_INVALID_ARG); + let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); + *req.0.body_mut() = body.0; + hyper_code::HYPERE_OK + } +} + +ffi_fn! { + /// Set an informational (1xx) response callback. + /// + /// The callback is called each time hyper receives an informational (1xx) + /// response for this request. + /// + /// The third argument is an opaque user data pointer, which is passed to + /// the callback each time. + /// + /// The callback is passed the `void *` data pointer, and a + /// `hyper_response *` which can be inspected as any other response. The + /// body of the response will always be empty. + /// + /// NOTE: The `hyper_response *` is just borrowed data, and will not + /// be valid after the callback finishes. You must copy any data you wish + /// to persist. + fn hyper_request_on_informational(req: *mut hyper_request, callback: hyper_request_on_informational_callback, data: *mut c_void) -> hyper_code { + #[cfg(feature = "client")] + { + let ext = OnInformational { + func: callback, + data: UserDataPointer(data), + }; + let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); + crate::ext::on_informational_raw(&mut req.0, ext); + hyper_code::HYPERE_OK + } + #[cfg(not(feature = "client"))] + { + drop((req, callback, data)); + hyper_code::HYPERE_FEATURE_NOT_ENABLED + } + } +} + +impl hyper_request { + pub(super) fn finalize_request(&mut self) { + if let Some(headers) = self.0.extensions_mut().remove::<hyper_headers>() { + *self.0.headers_mut() = headers.headers; + self.0.extensions_mut().insert(headers.orig_casing); + self.0.extensions_mut().insert(headers.orig_order); + } + } +} + +// ===== impl hyper_response ===== + +ffi_fn! { + /// Free an HTTP response. + /// + /// This should be used for any response once it is no longer needed. + fn hyper_response_free(resp: *mut hyper_response) { + drop(non_null!(Box::from_raw(resp) ?= ())); + } +} + +ffi_fn! { + /// Get the HTTP-Status code of this response. + /// + /// It will always be within the range of 100-599. + fn hyper_response_status(resp: *const hyper_response) -> u16 { + non_null!(&*resp ?= 0).0.status().as_u16() + } +} + +ffi_fn! { + /// Get a pointer to the reason-phrase of this response. + /// + /// This buffer is not null-terminated. + /// + /// This buffer is owned by the response, and should not be used after + /// the response has been freed. + /// + /// Use `hyper_response_reason_phrase_len()` to get the length of this + /// buffer. + fn hyper_response_reason_phrase(resp: *const hyper_response) -> *const u8 { + non_null!(&*resp ?= std::ptr::null()).reason_phrase().as_ptr() + } ?= std::ptr::null() +} + +ffi_fn! { + /// Get the length of the reason-phrase of this response. + /// + /// Use `hyper_response_reason_phrase()` to get the buffer pointer. + fn hyper_response_reason_phrase_len(resp: *const hyper_response) -> size_t { + non_null!(&*resp ?= 0).reason_phrase().len() + } +} + +ffi_fn! { + /// Get the HTTP version used by this response. + /// + /// The returned value could be: + /// + /// - `HYPER_HTTP_VERSION_1_0` + /// - `HYPER_HTTP_VERSION_1_1` + /// - `HYPER_HTTP_VERSION_2` + /// - `HYPER_HTTP_VERSION_NONE` if newer (or older). + fn hyper_response_version(resp: *const hyper_response) -> c_int { + use http::Version; + + match non_null!(&*resp ?= 0).0.version() { + Version::HTTP_10 => super::HYPER_HTTP_VERSION_1_0, + Version::HTTP_11 => super::HYPER_HTTP_VERSION_1_1, + Version::HTTP_2 => super::HYPER_HTTP_VERSION_2, + _ => super::HYPER_HTTP_VERSION_NONE, + } + } +} + +ffi_fn! { + /// Gets a reference to the HTTP headers of this response. + /// + /// This is not an owned reference, so it should not be accessed after the + /// `hyper_response` has been freed. + fn hyper_response_headers(resp: *mut hyper_response) -> *mut hyper_headers { + hyper_headers::get_or_default(unsafe { &mut *resp }.0.extensions_mut()) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Take ownership of the body of this response. + /// + /// It is safe to free the response even after taking ownership of its body. + /// + /// To avoid a memory leak, the body must eventually be consumed by + /// `hyper_body_free`, `hyper_body_foreach`, or `hyper_request_set_body`. + fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body { + let body = std::mem::replace(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut(), IncomingBody::empty()); + Box::into_raw(Box::new(hyper_body(body))) + } ?= std::ptr::null_mut() +} + +impl hyper_response { + pub(super) fn wrap(mut resp: Response<IncomingBody>) -> hyper_response { + let headers = std::mem::take(resp.headers_mut()); + let orig_casing = resp + .extensions_mut() + .remove::<HeaderCaseMap>() + .unwrap_or_else(HeaderCaseMap::default); + let orig_order = resp + .extensions_mut() + .remove::<OriginalHeaderOrder>() + .unwrap_or_else(OriginalHeaderOrder::default); + resp.extensions_mut().insert(hyper_headers { + headers, + orig_casing, + orig_order, + }); + + hyper_response(resp) + } + + fn reason_phrase(&self) -> &[u8] { + if let Some(reason) = self.0.extensions().get::<ReasonPhrase>() { + return reason.as_bytes(); + } + + if let Some(reason) = self.0.status().canonical_reason() { + return reason.as_bytes(); + } + + &[] + } +} + +unsafe impl AsTaskType for hyper_response { + fn as_task_type(&self) -> hyper_task_return_type { + hyper_task_return_type::HYPER_TASK_RESPONSE + } +} + +// ===== impl Headers ===== + +type hyper_headers_foreach_callback = + extern "C" fn(*mut c_void, *const u8, size_t, *const u8, size_t) -> c_int; + +impl hyper_headers { + pub(super) fn get_or_default(ext: &mut http::Extensions) -> &mut hyper_headers { + if let None = ext.get_mut::<hyper_headers>() { + ext.insert(hyper_headers::default()); + } + + ext.get_mut::<hyper_headers>().unwrap() + } +} + +ffi_fn! { + /// Iterates the headers passing each name and value pair to the callback. + /// + /// The `userdata` pointer is also passed to the callback. + /// + /// The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or + /// `HYPER_ITER_BREAK` to stop. + fn hyper_headers_foreach(headers: *const hyper_headers, func: hyper_headers_foreach_callback, userdata: *mut c_void) { + let headers = non_null!(&*headers ?= ()); + // For each header name/value pair, there may be a value in the casemap + // that corresponds to the HeaderValue. So, we iterator all the keys, + // and for each one, try to pair the originally cased name with the value. + // + // TODO: consider adding http::HeaderMap::entries() iterator + let mut ordered_iter = headers.orig_order.get_in_order().peekable(); + if ordered_iter.peek().is_some() { + for (name, idx) in ordered_iter { + let (name_ptr, name_len) = if let Some(orig_name) = headers.orig_casing.get_all(name).nth(*idx) { + (orig_name.as_ref().as_ptr(), orig_name.as_ref().len()) + } else { + ( + name.as_str().as_bytes().as_ptr(), + name.as_str().as_bytes().len(), + ) + }; + + let val_ptr; + let val_len; + if let Some(value) = headers.headers.get_all(name).iter().nth(*idx) { + val_ptr = value.as_bytes().as_ptr(); + val_len = value.as_bytes().len(); + } else { + // Stop iterating, something has gone wrong. + return; + } + + if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) { + return; + } + } + } else { + for name in headers.headers.keys() { + let mut names = headers.orig_casing.get_all(name); + + for value in headers.headers.get_all(name) { + let (name_ptr, name_len) = if let Some(orig_name) = names.next() { + (orig_name.as_ref().as_ptr(), orig_name.as_ref().len()) + } else { + ( + name.as_str().as_bytes().as_ptr(), + name.as_str().as_bytes().len(), + ) + }; + + let val_ptr = value.as_bytes().as_ptr(); + let val_len = value.as_bytes().len(); + + if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) { + return; + } + } + } + } + } +} + +ffi_fn! { + /// Sets the header with the provided name to the provided value. + /// + /// This overwrites any previous value set for the header. + fn hyper_headers_set(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code { + let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG); + match unsafe { raw_name_value(name, name_len, value, value_len) } { + Ok((name, value, orig_name)) => { + headers.headers.insert(&name, value); + headers.orig_casing.insert(name.clone(), orig_name.clone()); + headers.orig_order.insert(name); + hyper_code::HYPERE_OK + } + Err(code) => code, + } + } +} + +ffi_fn! { + /// Adds the provided value to the list of the provided name. + /// + /// If there were already existing values for the name, this will append the + /// new value to the internal list. + fn hyper_headers_add(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code { + let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG); + + match unsafe { raw_name_value(name, name_len, value, value_len) } { + Ok((name, value, orig_name)) => { + headers.headers.append(&name, value); + headers.orig_casing.append(&name, orig_name.clone()); + headers.orig_order.append(name); + hyper_code::HYPERE_OK + } + Err(code) => code, + } + } +} + +impl Default for hyper_headers { + fn default() -> Self { + Self { + headers: Default::default(), + orig_casing: HeaderCaseMap::default(), + orig_order: OriginalHeaderOrder::default(), + } + } +} + +unsafe fn raw_name_value( + name: *const u8, + name_len: size_t, + value: *const u8, + value_len: size_t, +) -> Result<(HeaderName, HeaderValue, Bytes), hyper_code> { + let name = std::slice::from_raw_parts(name, name_len); + let orig_name = Bytes::copy_from_slice(name); + let name = match HeaderName::from_bytes(name) { + Ok(name) => name, + Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG), + }; + let value = std::slice::from_raw_parts(value, value_len); + let value = match HeaderValue::from_bytes(value) { + Ok(val) => val, + Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG), + }; + + Ok((name, value, orig_name)) +} + +// ===== impl OnInformational ===== + +#[cfg(feature = "client")] +impl crate::ext::OnInformationalCallback for OnInformational { + fn on_informational(&self, res: http::Response<()>) { + let res = res.map(|()| IncomingBody::empty()); + let mut res = hyper_response::wrap(res); + (self.func)(self.data.0, &mut res); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_headers_foreach_cases_preserved() { + let mut headers = hyper_headers::default(); + + let name1 = b"Set-CookiE"; + let value1 = b"a=b"; + hyper_headers_add( + &mut headers, + name1.as_ptr(), + name1.len(), + value1.as_ptr(), + value1.len(), + ); + + let name2 = b"SET-COOKIE"; + let value2 = b"c=d"; + hyper_headers_add( + &mut headers, + name2.as_ptr(), + name2.len(), + value2.as_ptr(), + value2.len(), + ); + + let mut vec = Vec::<u8>::new(); + hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void); + + assert_eq!(vec, b"Set-CookiE: a=b\r\nSET-COOKIE: c=d\r\n"); + + extern "C" fn concat( + vec: *mut c_void, + name: *const u8, + name_len: usize, + value: *const u8, + value_len: usize, + ) -> c_int { + unsafe { + let vec = &mut *(vec as *mut Vec<u8>); + let name = std::slice::from_raw_parts(name, name_len); + let value = std::slice::from_raw_parts(value, value_len); + vec.extend(name); + vec.extend(b": "); + vec.extend(value); + vec.extend(b"\r\n"); + } + HYPER_ITER_CONTINUE + } + } + + #[cfg(all(feature = "http1", feature = "ffi"))] + #[test] + fn test_headers_foreach_order_preserved() { + let mut headers = hyper_headers::default(); + + let name1 = b"Set-CookiE"; + let value1 = b"a=b"; + hyper_headers_add( + &mut headers, + name1.as_ptr(), + name1.len(), + value1.as_ptr(), + value1.len(), + ); + + let name2 = b"Content-Encoding"; + let value2 = b"gzip"; + hyper_headers_add( + &mut headers, + name2.as_ptr(), + name2.len(), + value2.as_ptr(), + value2.len(), + ); + + let name3 = b"SET-COOKIE"; + let value3 = b"c=d"; + hyper_headers_add( + &mut headers, + name3.as_ptr(), + name3.len(), + value3.as_ptr(), + value3.len(), + ); + + let mut vec = Vec::<u8>::new(); + hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void); + + println!("{}", std::str::from_utf8(&vec).unwrap()); + assert_eq!( + vec, + b"Set-CookiE: a=b\r\nContent-Encoding: gzip\r\nSET-COOKIE: c=d\r\n" + ); + + extern "C" fn concat( + vec: *mut c_void, + name: *const u8, + name_len: usize, + value: *const u8, + value_len: usize, + ) -> c_int { + unsafe { + let vec = &mut *(vec as *mut Vec<u8>); + let name = std::slice::from_raw_parts(name, name_len); + let value = std::slice::from_raw_parts(value, value_len); + vec.extend(name); + vec.extend(b": "); + vec.extend(value); + vec.extend(b"\r\n"); + } + HYPER_ITER_CONTINUE + } + } +} diff --git a/vendor/hyper/src/ffi/io.rs b/vendor/hyper/src/ffi/io.rs new file mode 100644 index 00000000..89978b9e --- /dev/null +++ b/vendor/hyper/src/ffi/io.rs @@ -0,0 +1,198 @@ +use std::ffi::c_void; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use super::task::hyper_context; +use crate::ffi::size_t; +use crate::rt::{Read, Write}; + +/// Sentinel value to return from a read or write callback that the operation +/// is pending. +pub const HYPER_IO_PENDING: size_t = 0xFFFFFFFF; +/// Sentinel value to return from a read or write callback that the operation +/// has errored. +pub const HYPER_IO_ERROR: size_t = 0xFFFFFFFE; + +type hyper_io_read_callback = + extern "C" fn(*mut c_void, *mut hyper_context<'_>, *mut u8, size_t) -> size_t; +type hyper_io_write_callback = + extern "C" fn(*mut c_void, *mut hyper_context<'_>, *const u8, size_t) -> size_t; + +/// A read/write handle for a specific connection. +/// +/// This owns a specific TCP or TLS connection for the lifetime of +/// that connection. It contains a read and write callback, as well as a +/// void *userdata. Typically the userdata will point to a struct +/// containing a file descriptor and a TLS context. +/// +/// Methods: +/// +/// - hyper_io_new: Create a new IO type used to represent a transport. +/// - hyper_io_set_read: Set the read function for this IO transport. +/// - hyper_io_set_write: Set the write function for this IO transport. +/// - hyper_io_set_userdata: Set the user data pointer for this IO to some value. +/// - hyper_io_free: Free an IO handle. +pub struct hyper_io { + read: hyper_io_read_callback, + write: hyper_io_write_callback, + userdata: *mut c_void, +} + +ffi_fn! { + /// Create a new IO type used to represent a transport. + /// + /// The read and write functions of this transport should be set with + /// `hyper_io_set_read` and `hyper_io_set_write`. + /// + /// It is expected that the underlying transport is non-blocking. When + /// a read or write callback can't make progress because there is no + /// data available yet, it should use the `hyper_waker` mechanism to + /// arrange to be called again when data is available. + /// + /// To avoid a memory leak, the IO handle must eventually be consumed by + /// `hyper_io_free` or `hyper_clientconn_handshake`. + fn hyper_io_new() -> *mut hyper_io { + Box::into_raw(Box::new(hyper_io { + read: read_noop, + write: write_noop, + userdata: std::ptr::null_mut(), + })) + } ?= std::ptr::null_mut() +} + +ffi_fn! { + /// Free an IO handle. + /// + /// This should only be used if the request isn't consumed by + /// `hyper_clientconn_handshake`. + fn hyper_io_free(io: *mut hyper_io) { + drop(non_null!(Box::from_raw(io) ?= ())); + } +} + +ffi_fn! { + /// Set the user data pointer for this IO to some value. + /// + /// This value is passed as an argument to the read and write callbacks. + fn hyper_io_set_userdata(io: *mut hyper_io, data: *mut c_void) { + non_null!(&mut *io ?= ()).userdata = data; + } +} + +ffi_fn! { + /// Set the read function for this IO transport. + /// + /// Data that is read from the transport should be put in the `buf` pointer, + /// up to `buf_len` bytes. The number of bytes read should be the return value. + /// + /// It is undefined behavior to try to access the bytes in the `buf` pointer, + /// unless you have already written them yourself. It is also undefined behavior + /// to return that more bytes have been written than actually set on the `buf`. + /// + /// If there is no data currently available, the callback should create a + /// `hyper_waker` from its `hyper_context` argument and register the waker + /// with whatever polling mechanism is used to signal when data is available + /// later on. The return value should be `HYPER_IO_PENDING`. See the + /// documentation for `hyper_waker`. + /// + /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR` + /// should be the return value. + fn hyper_io_set_read(io: *mut hyper_io, func: hyper_io_read_callback) { + non_null!(&mut *io ?= ()).read = func; + } +} + +ffi_fn! { + /// Set the write function for this IO transport. + /// + /// Data from the `buf` pointer should be written to the transport, up to + /// `buf_len` bytes. The number of bytes written should be the return value. + /// + /// If there is no data currently available, the callback should create a + /// `hyper_waker` from its `hyper_context` argument and register the waker + /// with whatever polling mechanism is used to signal when data is available + /// later on. The return value should be `HYPER_IO_PENDING`. See the documentation + /// for `hyper_waker`. + /// + /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR` + /// should be the return value. + fn hyper_io_set_write(io: *mut hyper_io, func: hyper_io_write_callback) { + non_null!(&mut *io ?= ()).write = func; + } +} + +/// cbindgen:ignore +extern "C" fn read_noop( + _userdata: *mut c_void, + _: *mut hyper_context<'_>, + _buf: *mut u8, + _buf_len: size_t, +) -> size_t { + 0 +} + +/// cbindgen:ignore +extern "C" fn write_noop( + _userdata: *mut c_void, + _: *mut hyper_context<'_>, + _buf: *const u8, + _buf_len: size_t, +) -> size_t { + 0 +} + +impl Read for hyper_io { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + mut buf: crate::rt::ReadBufCursor<'_>, + ) -> Poll<std::io::Result<()>> { + let buf_ptr = unsafe { buf.as_mut() }.as_mut_ptr() as *mut u8; + let buf_len = buf.remaining(); + + match (self.read)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) { + HYPER_IO_PENDING => Poll::Pending, + HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::Other, + "io error", + ))), + ok => { + // We have to trust that the user's read callback actually + // filled in that many bytes... :( + unsafe { buf.advance(ok) }; + Poll::Ready(Ok(())) + } + } + } +} + +impl Write for hyper_io { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll<std::io::Result<usize>> { + let buf_ptr = buf.as_ptr(); + let buf_len = buf.len(); + + match (self.write)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) { + HYPER_IO_PENDING => Poll::Pending, + HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::Other, + "io error", + ))), + ok => Poll::Ready(Ok(ok)), + } + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<std::io::Result<()>> { + Poll::Ready(Ok(())) + } +} + +unsafe impl Send for hyper_io {} +unsafe impl Sync for hyper_io {} diff --git a/vendor/hyper/src/ffi/macros.rs b/vendor/hyper/src/ffi/macros.rs new file mode 100644 index 00000000..022711ba --- /dev/null +++ b/vendor/hyper/src/ffi/macros.rs @@ -0,0 +1,53 @@ +macro_rules! ffi_fn { + ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block ?= $default:expr) => { + $(#[$doc])* + #[no_mangle] + pub extern fn $name($($arg: $arg_ty),*) -> $ret { + use std::panic::{self, AssertUnwindSafe}; + + match panic::catch_unwind(AssertUnwindSafe(move || $body)) { + Ok(v) => v, + Err(_) => { + $default + } + } + } + }; + + ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block) => { + ffi_fn!($(#[$doc])* fn $name($($arg: $arg_ty),*) -> $ret $body ?= { + eprintln!("panic unwind caught, aborting"); + std::process::abort() + }); + }; + + ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block ?= $default:expr) => { + ffi_fn!($(#[$doc])* fn $name($($arg: $arg_ty),*) -> () $body ?= $default); + }; + + ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block) => { + ffi_fn!($(#[$doc])* fn $name($($arg: $arg_ty),*) -> () $body); + }; +} + +macro_rules! non_null { + ($ptr:ident, $eval:expr, $err:expr) => {{ + debug_assert!(!$ptr.is_null(), "{:?} must not be null", stringify!($ptr)); + if $ptr.is_null() { + return $err; + } + unsafe { $eval } + }}; + (&*$ptr:ident ?= $err:expr) => {{ + non_null!($ptr, &*$ptr, $err) + }}; + (&mut *$ptr:ident ?= $err:expr) => {{ + non_null!($ptr, &mut *$ptr, $err) + }}; + (Box::from_raw($ptr:ident) ?= $err:expr) => {{ + non_null!($ptr, Box::from_raw($ptr), $err) + }}; + (Arc::from_raw($ptr:ident) ?= $err:expr) => {{ + non_null!($ptr, Arc::from_raw($ptr), $err) + }}; +} diff --git a/vendor/hyper/src/ffi/mod.rs b/vendor/hyper/src/ffi/mod.rs new file mode 100644 index 00000000..cdcbc482 --- /dev/null +++ b/vendor/hyper/src/ffi/mod.rs @@ -0,0 +1,99 @@ +// We have a lot of c-types in here, stop warning about their names! +#![allow(non_camel_case_types)] +// fmt::Debug isn't helpful on FFI types +#![allow(missing_debug_implementations)] +// unreachable_pub warns `#[no_mangle] pub extern fn` in private mod. +#![allow(unreachable_pub)] + +//! # hyper C API +//! +//! This part of the documentation describes the C API for hyper. That is, how +//! to *use* the hyper library in C code. This is **not** a regular Rust +//! module, and thus it is not accessible in Rust. +//! +//! ## Unstable +//! +//! The C API of hyper is currently **unstable**, which means it's not part of +//! the semver contract as the rest of the Rust API is. Because of that, it's +//! only accessible if `--cfg hyper_unstable_ffi` is passed to `rustc` when +//! compiling. The easiest way to do that is setting the `RUSTFLAGS` +//! environment variable. +//! +//! ## Building +//! +//! The C API is part of the Rust library, but isn't compiled by default. Using +//! `cargo`, staring with `1.64.0`, it can be compiled with the following command: +//! +//! ```notrust +//! RUSTFLAGS="--cfg hyper_unstable_ffi" cargo rustc --crate-type cdylib --features client,http1,http2,ffi +//! ``` + +// We may eventually allow the FFI to be enabled without `client` or `http1`, +// that is why we don't auto enable them as `ffi = ["client", "http1"]` in +// the `Cargo.toml`. +// +// But for now, give a clear message that this compile error is expected. +#[cfg(not(all(feature = "client", feature = "http1")))] +compile_error!("The `ffi` feature currently requires the `client` and `http1` features."); + +#[cfg(not(hyper_unstable_ffi))] +compile_error!( + "\ + The `ffi` feature is unstable, and requires the \ + `RUSTFLAGS='--cfg hyper_unstable_ffi'` environment variable to be set.\ +" +); + +#[macro_use] +mod macros; + +mod body; +mod client; +mod error; +mod http_types; +mod io; +mod task; + +pub use self::body::*; +pub use self::client::*; +pub use self::error::*; +pub use self::http_types::*; +pub use self::io::*; +pub use self::task::*; + +/// Return in iter functions to continue iterating. +pub const HYPER_ITER_CONTINUE: std::ffi::c_int = 0; +/// Return in iter functions to stop iterating. +#[allow(unused)] +pub const HYPER_ITER_BREAK: std::ffi::c_int = 1; + +/// An HTTP Version that is unspecified. +pub const HYPER_HTTP_VERSION_NONE: std::ffi::c_int = 0; +/// The HTTP/1.0 version. +pub const HYPER_HTTP_VERSION_1_0: std::ffi::c_int = 10; +/// The HTTP/1.1 version. +pub const HYPER_HTTP_VERSION_1_1: std::ffi::c_int = 11; +/// The HTTP/2 version. +pub const HYPER_HTTP_VERSION_2: std::ffi::c_int = 20; + +#[derive(Clone)] +struct UserDataPointer(*mut std::ffi::c_void); + +// We don't actually know anything about this pointer, it's up to the user +// to do the right thing. +unsafe impl Send for UserDataPointer {} +unsafe impl Sync for UserDataPointer {} + +/// cbindgen:ignore +static VERSION_CSTR: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); + +// `core::ffi::c_size_t` is a nightly-only experimental API. +// https://github.com/rust-lang/rust/issues/88345 +type size_t = usize; + +ffi_fn! { + /// Returns a static ASCII (null terminated) string of the hyper version. + fn hyper_version() -> *const std::ffi::c_char { + VERSION_CSTR.as_ptr() as _ + } ?= std::ptr::null() +} diff --git a/vendor/hyper/src/ffi/task.rs b/vendor/hyper/src/ffi/task.rs new file mode 100644 index 00000000..5b33d42b --- /dev/null +++ b/vendor/hyper/src/ffi/task.rs @@ -0,0 +1,549 @@ +use std::ffi::{c_int, c_void}; +use std::future::Future; +use std::pin::Pin; +use std::ptr; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, Weak, +}; +use std::task::{Context, Poll}; + +use futures_util::stream::{FuturesUnordered, Stream}; + +use super::error::hyper_code; +use super::UserDataPointer; + +type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>; +type BoxAny = Box<dyn AsTaskType + Send + Sync>; + +/// Return in a poll function to indicate it was ready. +pub const HYPER_POLL_READY: c_int = 0; +/// Return in a poll function to indicate it is still pending. +/// +/// The passed in `hyper_waker` should be registered to wake up the task at +/// some later point. +pub const HYPER_POLL_PENDING: c_int = 1; +/// Return in a poll function indicate an error. +pub const HYPER_POLL_ERROR: c_int = 3; + +/// A task executor for `hyper_task`s. +/// +/// A task is a unit of work that may be blocked on IO, and can be polled to +/// make progress on that work. +/// +/// An executor can hold many tasks, included from unrelated HTTP connections. +/// An executor is single threaded. Typically you might have one executor per +/// thread. Or, for simplicity, you may choose one executor per connection. +/// +/// Progress on tasks happens only when `hyper_executor_poll` is called, and only +/// on tasks whose corresponding `hyper_waker` has been called to indicate they +/// are ready to make progress (for instance, because the OS has indicated there +/// is more data to read or more buffer space available to write). +/// +/// Deadlock potential: `hyper_executor_poll` must not be called from within a task's +/// callback. Doing so will result in a deadlock. +/// +/// Methods: +/// +/// - hyper_executor_new: Creates a new task executor. +/// - hyper_executor_push: Push a task onto the executor. +/// - hyper_executor_poll: Polls the executor, trying to make progress on any tasks that have notified that they are ready again. +/// - hyper_executor_free: Frees an executor and any incomplete tasks still part of it. +pub struct hyper_executor { + /// The executor of all task futures. + /// + /// There should never be contention on the mutex, as it is only locked + /// to drive the futures. However, we cannot guarantee proper usage from + /// `hyper_executor_poll()`, which in C could potentially be called inside + /// one of the stored futures. The mutex isn't re-entrant, so doing so + /// would result in a deadlock, but that's better than data corruption. + driver: Mutex<FuturesUnordered<TaskFuture>>, + + /// The queue of futures that need to be pushed into the `driver`. + /// + /// This is has a separate mutex since `spawn` could be called from inside + /// a future, which would mean the driver's mutex is already locked. + spawn_queue: Mutex<Vec<TaskFuture>>, + + /// This is used to track when a future calls `wake` while we are within + /// `hyper_executor::poll_next`. + is_woken: Arc<ExecWaker>, +} + +#[derive(Clone)] +pub(crate) struct WeakExec(Weak<hyper_executor>); + +struct ExecWaker(AtomicBool); + +/// An async task. +/// +/// A task represents a chunk of work that will eventually yield exactly one +/// `hyper_task_value`. Tasks are pushed onto an executor, and that executor is +/// responsible for calling the necessary private functions on the task to make +/// progress. In most cases those private functions will eventually cause read +/// or write callbacks on a `hyper_io` object to be called. +/// +/// Tasks are created by various functions: +/// +/// - hyper_clientconn_handshake: Creates an HTTP client handshake task. +/// - hyper_clientconn_send: Creates a task to send a request on the client connection. +/// - hyper_body_data: Creates a task that will poll a response body for the next buffer of data. +/// - hyper_body_foreach: Creates a task to execute the callback with each body chunk received. +/// +/// Tasks then have a userdata associated with them using `hyper_task_set_userdata``. This +/// is important, for instance, to associate a request id with a given request. When multiple +/// tasks are running on the same executor, this allows distinguishing tasks for different +/// requests. +/// +/// Tasks are then pushed onto an executor, and eventually yielded from hyper_executor_poll: +/// +/// - hyper_executor_push: Push a task onto the executor. +/// - hyper_executor_poll: Polls the executor, trying to make progress on any tasks that have notified that they are ready again. +/// +/// Once a task is yielded from poll, retrieve its userdata, check its type, +/// and extract its value. This will require a case from void* to the appropriate type. +/// +/// Methods on hyper_task: +/// +/// - hyper_task_type: Query the return type of this task. +/// - hyper_task_value: Takes the output value of this task. +/// - hyper_task_set_userdata: Set a user data pointer to be associated with this task. +/// - hyper_task_userdata: Retrieve the userdata that has been set via hyper_task_set_userdata. +/// - hyper_task_free: Free a task. +pub struct hyper_task { + future: BoxFuture<BoxAny>, + output: Option<BoxAny>, + userdata: UserDataPointer, +} + +struct TaskFuture { + task: Option<Box<hyper_task>>, +} + +/// An async context for a task that contains the related waker. +/// +/// This is provided to `hyper_io`'s read and write callbacks. Currently +/// its only purpose is to provide access to the waker. See `hyper_waker`. +/// +/// Corresponding Rust type: <https://doc.rust-lang.org/std/task/struct.Context.html> +pub struct hyper_context<'a>(Context<'a>); + +/// A waker that is saved and used to waken a pending task. +/// +/// This is provided to `hyper_io`'s read and write callbacks via `hyper_context` +/// and `hyper_context_waker`. +/// +/// When nonblocking I/O in one of those callbacks can't make progress (returns +/// `EAGAIN` or `EWOULDBLOCK`), the callback has to return to avoid blocking the +/// executor. But it also has to arrange to get called in the future when more +/// data is available. That's the role of the async context and the waker. The +/// waker can be used to tell the executor "this task is ready to make progress." +/// +/// The read or write callback, upon finding it can't make progress, must get a +/// waker from the context (`hyper_context_waker`), arrange for that waker to be +/// called in the future, and then return `HYPER_POLL_PENDING`. +/// +/// The arrangements for the waker to be called in the future are up to the +/// application, but usually it will involve one big `select(2)` loop that checks which +/// FDs are ready, and a correspondence between FDs and waker objects. For each +/// FD that is ready, the corresponding waker must be called. Then `hyper_executor_poll` +/// must be called. That will cause the executor to attempt to make progress on each +/// woken task. +/// +/// Corresponding Rust type: <https://doc.rust-lang.org/std/task/struct.Waker.html> +pub struct hyper_waker { + waker: std::task::Waker, +} + +/// A descriptor for what type a `hyper_task` value is. +#[repr(C)] +pub enum hyper_task_return_type { + /// The value of this task is null (does not imply an error). + HYPER_TASK_EMPTY, + /// The value of this task is `hyper_error *`. + HYPER_TASK_ERROR, + /// The value of this task is `hyper_clientconn *`. + HYPER_TASK_CLIENTCONN, + /// The value of this task is `hyper_response *`. + HYPER_TASK_RESPONSE, + /// The value of this task is `hyper_buf *`. + HYPER_TASK_BUF, +} + +pub(crate) unsafe trait AsTaskType { + fn as_task_type(&self) -> hyper_task_return_type; +} + +pub(crate) trait IntoDynTaskType { + fn into_dyn_task_type(self) -> BoxAny; +} + +// ===== impl hyper_executor ===== + +impl hyper_executor { + fn new() -> Arc<hyper_executor> { + Arc::new(hyper_executor { + driver: Mutex::new(FuturesUnordered::new()), + spawn_queue: Mutex::new(Vec::new()), + is_woken: Arc::new(ExecWaker(AtomicBool::new(false))), + }) + } + + pub(crate) fn downgrade(exec: &Arc<hyper_executor>) -> WeakExec { + WeakExec(Arc::downgrade(exec)) + } + + fn spawn(&self, task: Box<hyper_task>) { + self.spawn_queue + .lock() + .unwrap() + .push(TaskFuture { task: Some(task) }); + } + + fn poll_next(&self) -> Option<Box<hyper_task>> { + // Drain the queue first. + self.drain_queue(); + + let waker = futures_util::task::waker_ref(&self.is_woken); + let mut cx = Context::from_waker(&waker); + + loop { + { + // Scope the lock on the driver to ensure it is dropped before + // calling drain_queue below. + let mut driver = self.driver.lock().unwrap(); + match Pin::new(&mut *driver).poll_next(&mut cx) { + Poll::Ready(val) => return val, + Poll::Pending => {} + }; + } + + // poll_next returned Pending. + // Check if any of the pending tasks tried to spawn + // some new tasks. If so, drain into the driver and loop. + if self.drain_queue() { + continue; + } + + // If the driver called `wake` while we were polling, + // we should poll again immediately! + if self.is_woken.0.swap(false, Ordering::SeqCst) { + continue; + } + + return None; + } + } + + /// drain_queue locks both self.spawn_queue and self.driver, so it requires + /// that neither of them be locked already. + fn drain_queue(&self) -> bool { + let mut queue = self.spawn_queue.lock().unwrap(); + if queue.is_empty() { + return false; + } + + let driver = self.driver.lock().unwrap(); + + for task in queue.drain(..) { + driver.push(task); + } + + true + } +} + +impl futures_util::task::ArcWake for ExecWaker { + fn wake_by_ref(me: &Arc<ExecWaker>) { + me.0.store(true, Ordering::SeqCst); + } +} + +// ===== impl WeakExec ===== + +impl WeakExec { + pub(crate) fn new() -> Self { + WeakExec(Weak::new()) + } +} + +impl<F> crate::rt::Executor<F> for WeakExec +where + F: Future + Send + 'static, + F::Output: Send + Sync + AsTaskType, +{ + fn execute(&self, fut: F) { + if let Some(exec) = self.0.upgrade() { + exec.spawn(hyper_task::boxed(fut)); + } + } +} + +ffi_fn! { + /// Creates a new task executor. + /// + /// To avoid a memory leak, the executor must eventually be consumed by + /// `hyper_executor_free`. + fn hyper_executor_new() -> *const hyper_executor { + Arc::into_raw(hyper_executor::new()) + } ?= ptr::null() +} + +ffi_fn! { + /// Frees an executor and any incomplete tasks still part of it. + /// + /// This should be used for any executor once it is no longer needed. + fn hyper_executor_free(exec: *const hyper_executor) { + drop(non_null!(Arc::from_raw(exec) ?= ())); + } +} + +ffi_fn! { + /// Push a task onto the executor. + /// + /// The executor takes ownership of the task, which must not be accessed + /// again. + /// + /// Ownership of the task will eventually be returned to the user from + /// `hyper_executor_poll`. + /// + /// To distinguish multiple tasks running on the same executor, use + /// hyper_task_set_userdata. + fn hyper_executor_push(exec: *const hyper_executor, task: *mut hyper_task) -> hyper_code { + let exec = non_null!(&*exec ?= hyper_code::HYPERE_INVALID_ARG); + let task = non_null!(Box::from_raw(task) ?= hyper_code::HYPERE_INVALID_ARG); + exec.spawn(task); + hyper_code::HYPERE_OK + } +} + +ffi_fn! { + /// Polls the executor, trying to make progress on any tasks that can do so. + /// + /// If any task from the executor is ready, returns one of them. The way + /// tasks signal being finished is internal to Hyper. The order in which tasks + /// are returned is not guaranteed. Use userdata to distinguish between tasks. + /// + /// To avoid a memory leak, the task must eventually be consumed by + /// `hyper_task_free`. + /// + /// If there are no ready tasks, this returns `NULL`. + fn hyper_executor_poll(exec: *const hyper_executor) -> *mut hyper_task { + let exec = non_null!(&*exec ?= ptr::null_mut()); + match exec.poll_next() { + Some(task) => Box::into_raw(task), + None => ptr::null_mut(), + } + } ?= ptr::null_mut() +} + +// ===== impl hyper_task ===== + +impl hyper_task { + pub(crate) fn boxed<F>(fut: F) -> Box<hyper_task> + where + F: Future + Send + 'static, + F::Output: IntoDynTaskType + Send + Sync + 'static, + { + Box::new(hyper_task { + future: Box::pin(async move { fut.await.into_dyn_task_type() }), + output: None, + userdata: UserDataPointer(ptr::null_mut()), + }) + } + + fn output_type(&self) -> hyper_task_return_type { + match self.output { + None => hyper_task_return_type::HYPER_TASK_EMPTY, + Some(ref val) => val.as_task_type(), + } + } +} + +impl Future for TaskFuture { + type Output = Box<hyper_task>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + match Pin::new(&mut self.task.as_mut().unwrap().future).poll(cx) { + Poll::Ready(val) => { + let mut task = self.task.take().unwrap(); + task.output = Some(val); + Poll::Ready(task) + } + Poll::Pending => Poll::Pending, + } + } +} + +ffi_fn! { + /// Free a task. + /// + /// This should only be used if the task isn't consumed by + /// `hyper_clientconn_handshake` or taken ownership of by + /// `hyper_executor_push`. + fn hyper_task_free(task: *mut hyper_task) { + drop(non_null!(Box::from_raw(task) ?= ())); + } +} + +ffi_fn! { + /// Takes the output value of this task. + /// + /// This must only be called once polling the task on an executor has finished + /// this task. + /// + /// Use `hyper_task_type` to determine the type of the `void *` return value. + /// + /// To avoid a memory leak, a non-empty return value must eventually be + /// consumed by a function appropriate for its type, one of + /// `hyper_error_free`, `hyper_clientconn_free`, `hyper_response_free`, or + /// `hyper_buf_free`. + fn hyper_task_value(task: *mut hyper_task) -> *mut c_void { + let task = non_null!(&mut *task ?= ptr::null_mut()); + + if let Some(val) = task.output.take() { + let p = Box::into_raw(val) as *mut c_void; + // protect from returning fake pointers to empty types + if p == std::ptr::NonNull::<c_void>::dangling().as_ptr() { + ptr::null_mut() + } else { + p + } + } else { + ptr::null_mut() + } + } ?= ptr::null_mut() +} + +ffi_fn! { + /// Query the return type of this task. + fn hyper_task_type(task: *mut hyper_task) -> hyper_task_return_type { + // instead of blowing up spectacularly, just say this null task + // doesn't have a value to retrieve. + non_null!(&*task ?= hyper_task_return_type::HYPER_TASK_EMPTY).output_type() + } +} + +ffi_fn! { + /// Set a user data pointer to be associated with this task. + /// + /// This value will be passed to task callbacks, and can be checked later + /// with `hyper_task_userdata`. + /// + /// This is useful for telling apart tasks for different requests that are + /// running on the same executor. + fn hyper_task_set_userdata(task: *mut hyper_task, userdata: *mut c_void) { + if task.is_null() { + return; + } + + unsafe { (*task).userdata = UserDataPointer(userdata) }; + } +} + +ffi_fn! { + /// Retrieve the userdata that has been set via `hyper_task_set_userdata`. + fn hyper_task_userdata(task: *mut hyper_task) -> *mut c_void { + non_null!(&*task ?= ptr::null_mut()).userdata.0 + } ?= ptr::null_mut() +} + +// ===== impl AsTaskType ===== + +unsafe impl AsTaskType for () { + fn as_task_type(&self) -> hyper_task_return_type { + hyper_task_return_type::HYPER_TASK_EMPTY + } +} + +unsafe impl AsTaskType for crate::Error { + fn as_task_type(&self) -> hyper_task_return_type { + hyper_task_return_type::HYPER_TASK_ERROR + } +} + +impl<T> IntoDynTaskType for T +where + T: AsTaskType + Send + Sync + 'static, +{ + fn into_dyn_task_type(self) -> BoxAny { + Box::new(self) + } +} + +impl<T> IntoDynTaskType for crate::Result<T> +where + T: IntoDynTaskType + Send + Sync + 'static, +{ + fn into_dyn_task_type(self) -> BoxAny { + match self { + Ok(val) => val.into_dyn_task_type(), + Err(err) => Box::new(err), + } + } +} + +impl<T> IntoDynTaskType for Option<T> +where + T: IntoDynTaskType + Send + Sync + 'static, +{ + fn into_dyn_task_type(self) -> BoxAny { + match self { + Some(val) => val.into_dyn_task_type(), + None => ().into_dyn_task_type(), + } + } +} + +// ===== impl hyper_context ===== + +impl hyper_context<'_> { + pub(crate) fn wrap<'a, 'b>(cx: &'a mut Context<'b>) -> &'a mut hyper_context<'b> { + // A struct with only one field has the same layout as that field. + unsafe { std::mem::transmute::<&mut Context<'_>, &mut hyper_context<'_>>(cx) } + } +} + +ffi_fn! { + /// Creates a waker associated with the task context. + /// + /// The waker can be used to inform the task's executor that the task is + /// ready to make progress (using `hyper_waker_wake``). + /// + /// Typically this only needs to be called once, but it can be called + /// multiple times, returning a new waker each time. + /// + /// To avoid a memory leak, the waker must eventually be consumed by + /// `hyper_waker_free` or `hyper_waker_wake`. + fn hyper_context_waker(cx: *mut hyper_context<'_>) -> *mut hyper_waker { + let waker = non_null!(&mut *cx ?= ptr::null_mut()).0.waker().clone(); + Box::into_raw(Box::new(hyper_waker { waker })) + } ?= ptr::null_mut() +} + +// ===== impl hyper_waker ===== + +ffi_fn! { + /// Free a waker. + /// + /// This should only be used if the request isn't consumed by + /// `hyper_waker_wake`. + fn hyper_waker_free(waker: *mut hyper_waker) { + drop(non_null!(Box::from_raw(waker) ?= ())); + } +} + +ffi_fn! { + /// Wake up the task associated with a waker. + /// + /// This does not do work towards associated task. Instead, it signals + /// to the task's executor that the task is ready to make progress. The + /// application is responsible for calling hyper_executor_poll, which + /// will in turn do work on all tasks that are ready to make progress. + /// + /// NOTE: This consumes the waker. You should not use or free the waker afterwards. + fn hyper_waker_wake(waker: *mut hyper_waker) { + let waker = non_null!(Box::from_raw(waker) ?= ()); + waker.waker.wake(); + } +} |
