summaryrefslogtreecommitdiffstats
path: root/third_party/rust/hyper/src/ffi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/hyper/src/ffi
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/hyper/src/ffi')
-rw-r--r--third_party/rust/hyper/src/ffi/body.rs229
-rw-r--r--third_party/rust/hyper/src/ffi/client.rs181
-rw-r--r--third_party/rust/hyper/src/ffi/error.rs85
-rw-r--r--third_party/rust/hyper/src/ffi/http_types.rs657
-rw-r--r--third_party/rust/hyper/src/ffi/io.rs178
-rw-r--r--third_party/rust/hyper/src/ffi/macros.rs53
-rw-r--r--third_party/rust/hyper/src/ffi/mod.rs94
-rw-r--r--third_party/rust/hyper/src/ffi/task.rs411
8 files changed, 1888 insertions, 0 deletions
diff --git a/third_party/rust/hyper/src/ffi/body.rs b/third_party/rust/hyper/src/ffi/body.rs
new file mode 100644
index 0000000000..39ba5beffb
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/body.rs
@@ -0,0 +1,229 @@
+use std::ffi::c_void;
+use std::mem::ManuallyDrop;
+use std::ptr;
+use std::task::{Context, Poll};
+
+use http::HeaderMap;
+use libc::{c_int, size_t};
+
+use super::task::{hyper_context, hyper_task, hyper_task_return_type, AsTaskType};
+use super::{UserDataPointer, HYPER_ITER_CONTINUE};
+use crate::body::{Body, Bytes, HttpBody as _};
+
+/// A streaming HTTP body.
+pub struct hyper_body(pub(super) Body);
+
+/// A buffer of bytes that is sent or received on a `hyper_body`.
+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! {
+ /// Create a new "empty" body.
+ ///
+ /// If not configured, this body acts as an empty payload.
+ fn hyper_body_new() -> *mut hyper_body {
+ Box::into_raw(Box::new(hyper_body(Body::empty())))
+ } ?= ptr::null_mut()
+}
+
+ffi_fn! {
+ /// Free a `hyper_body *`.
+ fn hyper_body_free(body: *mut hyper_body) {
+ drop(non_null!(Box::from_raw(body) ?= ()));
+ }
+}
+
+ffi_fn! {
+ /// Return a task that will poll the body for the next buffer of data.
+ ///
+ /// The task value 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.
+ ///
+ /// This does not consume the `hyper_body *`, so it may be used to again.
+ /// However, it MUST NOT be used or freed until the related task completes.
+ 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 {
+ body.0.data().await.map(|res| res.map(hyper_buf))
+ }))
+ } ?= ptr::null_mut()
+}
+
+ffi_fn! {
+ /// Return a task that will poll the body and execute the callback with each
+ /// body chunk that is received.
+ ///
+ /// The `hyper_buf` pointer is only a borrowed reference, it cannot live outside
+ /// the execution of the callback. You must make a copy to retain it.
+ ///
+ /// The callback should return `HYPER_ITER_CONTINUE` to continue iterating
+ /// chunks as they are received, or `HYPER_ITER_BREAK` to cancel.
+ ///
+ /// 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 {
+ while let Some(item) = body.0.data().await {
+ let chunk = item?;
+ 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 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<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(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
+ ))))),
+ }
+ }
+
+ pub(crate) fn poll_trailers(
+ &mut self,
+ _cx: &mut Context<'_>,
+ ) -> Poll<crate::Result<Option<HeaderMap>>> {
+ Poll::Ready(Ok(None))
+ }
+}
+
+/// 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 or changed afterwards.
+ ///
+ /// 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.
+ 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/third_party/rust/hyper/src/ffi/client.rs b/third_party/rust/hyper/src/ffi/client.rs
new file mode 100644
index 0000000000..4cdb257e30
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/client.rs
@@ -0,0 +1,181 @@
+use std::ptr;
+use std::sync::Arc;
+
+use libc::c_int;
+
+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.
+pub struct hyper_clientconn_options {
+ builder: conn::Builder,
+ /// Use a `Weak` to prevent cycles.
+ exec: WeakExec,
+}
+
+/// An HTTP client connection handle.
+///
+/// These are used to send a request 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.
+pub struct hyper_clientconn {
+ tx: conn::SendRequest<crate::Body>,
+}
+
+// ===== impl hyper_clientconn =====
+
+ffi_fn! {
+ /// Starts an HTTP client connection handshake using the provided IO transport
+ /// and options.
+ ///
+ /// Both the `io` and the `options` are consumed in this function call.
+ ///
+ /// The returned `hyper_task *` must be polled with an executor until the
+ /// handshake completes, at which point the value can be taken.
+ 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 {
+ options.builder.handshake::<_, crate::Body>(io)
+ .await
+ .map(|(tx, conn)| {
+ options.exec.execute(Box::pin(async move {
+ let _ = conn.await;
+ }));
+ hyper_clientconn { tx }
+ })
+ }))
+ } ?= std::ptr::null_mut()
+}
+
+ffi_fn! {
+ /// Send a request on the client connection.
+ ///
+ /// Returns a task that needs to be polled until it is ready. When ready, the
+ /// task yields a `hyper_response *`.
+ 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 = non_null! { &mut *conn ?= ptr::null_mut() }.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 *`.
+ 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.
+ fn hyper_clientconn_options_new() -> *mut hyper_clientconn_options {
+ let builder = conn::Builder::new();
+
+ Box::into_raw(Box::new(hyper_clientconn_options {
+ builder,
+ exec: WeakExec::new(),
+ }))
+ } ?= std::ptr::null_mut()
+}
+
+ffi_fn! {
+ /// Set the whether or not 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.builder.http1_preserve_header_case(enabled != 0);
+ }
+}
+
+ffi_fn! {
+ /// Set the whether or not 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.builder.http1_preserve_header_order(enabled != 0);
+ }
+}
+
+ffi_fn! {
+ /// Free a `hyper_clientconn_options *`.
+ 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.builder.executor(weak_exec.clone());
+ opts.exec = weak_exec;
+ }
+}
+
+ffi_fn! {
+ /// Set the 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.builder.http2_only(enabled != 0);
+ hyper_code::HYPERE_OK
+ }
+
+ #[cfg(not(feature = "http2"))]
+ {
+ drop(opts);
+ drop(enabled);
+ hyper_code::HYPERE_FEATURE_NOT_ENABLED
+ }
+ }
+}
+
+ffi_fn! {
+ /// Set the whether to include a copy of the raw headers in responses
+ /// received on this connection.
+ ///
+ /// Pass `0` to disable, `1` to enable.
+ ///
+ /// If enabled, see `hyper_response_headers_raw()` for usage.
+ fn hyper_clientconn_options_headers_raw(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code {
+ let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG };
+ opts.builder.http1_headers_raw(enabled != 0);
+ hyper_code::HYPERE_OK
+ }
+}
diff --git a/third_party/rust/hyper/src/ffi/error.rs b/third_party/rust/hyper/src/ffi/error.rs
new file mode 100644
index 0000000000..015e595aee
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/error.rs
@@ -0,0 +1,85 @@
+use libc::size_t;
+
+/// A more detailed error object returned by some hyper functions.
+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`.
+ 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/third_party/rust/hyper/src/ffi/http_types.rs b/third_party/rust/hyper/src/ffi/http_types.rs
new file mode 100644
index 0000000000..ea10f139cb
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/http_types.rs
@@ -0,0 +1,657 @@
+use bytes::Bytes;
+use libc::{c_int, size_t};
+use std::ffi::c_void;
+
+use super::body::{hyper_body, hyper_buf};
+use super::error::hyper_code;
+use super::task::{hyper_task_return_type, AsTaskType};
+use super::{UserDataPointer, HYPER_ITER_CONTINUE};
+use crate::ext::{HeaderCaseMap, OriginalHeaderOrder, ReasonPhrase};
+use crate::header::{HeaderName, HeaderValue};
+use crate::{Body, HeaderMap, Method, Request, Response, Uri};
+
+/// An HTTP request.
+pub struct hyper_request(pub(super) Request<Body>);
+
+/// An HTTP response.
+pub struct hyper_response(pub(super) Response<Body>);
+
+/// An HTTP header map.
+///
+/// These can be part of a request or response.
+pub struct hyper_headers {
+ pub(super) headers: HeaderMap,
+ orig_casing: HeaderCaseMap,
+ orig_order: OriginalHeaderOrder,
+}
+
+pub(crate) struct RawHeaders(pub(crate) hyper_buf);
+
+pub(crate) 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.
+ fn hyper_request_new() -> *mut hyper_request {
+ Box::into_raw(Box::new(hyper_request(Request::new(Body::empty()))))
+ } ?= std::ptr::null_mut()
+}
+
+ffi_fn! {
+ /// Free an HTTP request if not going to send it on a client.
+ 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 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.
+ ///
+ /// The default is an empty body.
+ ///
+ /// 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 {
+ let ext = OnInformational {
+ func: callback,
+ data: UserDataPointer(data),
+ };
+ let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
+ req.0.extensions_mut().insert(ext);
+ hyper_code::HYPERE_OK
+ }
+}
+
+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 after using it.
+ 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 a reference to the full raw headers of this response.
+ ///
+ /// You must have enabled `hyper_clientconn_options_headers_raw()`, or this
+ /// will return NULL.
+ ///
+ /// The returned `hyper_buf *` is just a reference, owned by the response.
+ /// You need to make a copy if you wish to use it after freeing the
+ /// response.
+ ///
+ /// The buffer is not null-terminated, see the `hyper_buf` functions for
+ /// getting the bytes and length.
+ fn hyper_response_headers_raw(resp: *const hyper_response) -> *const hyper_buf {
+ let resp = non_null!(&*resp ?= std::ptr::null());
+ match resp.0.extensions().get::<RawHeaders>() {
+ Some(raw) => &raw.0,
+ None => std::ptr::null(),
+ }
+ } ?= std::ptr::null()
+}
+
+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.
+ fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body {
+ let body = std::mem::take(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut());
+ Box::into_raw(Box::new(hyper_body(body)))
+ } ?= std::ptr::null_mut()
+}
+
+impl hyper_response {
+ pub(super) fn wrap(mut resp: Response<Body>) -> 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 =====
+
+impl OnInformational {
+ pub(crate) fn call(&mut self, resp: Response<Body>) {
+ let mut resp = hyper_response::wrap(resp);
+ (self.func)(self.data.0, &mut resp);
+ }
+}
+
+#[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/third_party/rust/hyper/src/ffi/io.rs b/third_party/rust/hyper/src/ffi/io.rs
new file mode 100644
index 0000000000..bff666dbcf
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/io.rs
@@ -0,0 +1,178 @@
+use std::ffi::c_void;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+use libc::size_t;
+use tokio::io::{AsyncRead, AsyncWrite};
+
+use super::task::hyper_context;
+
+/// 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;
+
+/// An IO object used to represent a socket or similar concept.
+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`.
+ 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 unused `hyper_io *`.
+ ///
+ /// This is typically only useful if you aren't going to pass ownership
+ /// of the IO handle to hyper, such as with `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, a waker should be claimed from
+ /// the `ctx` and registered with whatever polling mechanism is used to signal
+ /// when data is available later on. The return value should be
+ /// `HYPER_IO_PENDING`.
+ ///
+ /// 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 no data can currently be written, the `waker` should be cloned and
+ /// registered with whatever polling mechanism is used to signal when data
+ /// is available later on. The return value should be `HYPER_IO_PENDING`.
+ ///
+ /// Yeet.
+ ///
+ /// 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 AsyncRead for hyper_io {
+ fn poll_read(
+ self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut tokio::io::ReadBuf<'_>,
+ ) -> Poll<std::io::Result<()>> {
+ let buf_ptr = unsafe { buf.unfilled_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.assume_init(ok) };
+ buf.advance(ok);
+ Poll::Ready(Ok(()))
+ }
+ }
+ }
+}
+
+impl AsyncWrite 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/third_party/rust/hyper/src/ffi/macros.rs b/third_party/rust/hyper/src/ffi/macros.rs
new file mode 100644
index 0000000000..022711baaa
--- /dev/null
+++ b/third_party/rust/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/third_party/rust/hyper/src/ffi/mod.rs b/third_party/rust/hyper/src/ffi/mod.rs
new file mode 100644
index 0000000000..fd67a880a6
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/mod.rs
@@ -0,0 +1,94 @@
+// 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`, it can be compiled with the following command:
+//!
+//! ```notrust
+//! RUSTFLAGS="--cfg hyper_unstable_ffi" cargo build --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: libc::c_int = 0;
+/// Return in iter functions to stop iterating.
+#[allow(unused)]
+pub const HYPER_ITER_BREAK: libc::c_int = 1;
+
+/// An HTTP Version that is unspecified.
+pub const HYPER_HTTP_VERSION_NONE: libc::c_int = 0;
+/// The HTTP/1.0 version.
+pub const HYPER_HTTP_VERSION_1_0: libc::c_int = 10;
+/// The HTTP/1.1 version.
+pub const HYPER_HTTP_VERSION_1_1: libc::c_int = 11;
+/// The HTTP/2 version.
+pub const HYPER_HTTP_VERSION_2: libc::c_int = 20;
+
+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");
+
+ffi_fn! {
+ /// Returns a static ASCII (null terminated) string of the hyper version.
+ fn hyper_version() -> *const libc::c_char {
+ VERSION_CSTR.as_ptr() as _
+ } ?= std::ptr::null()
+}
diff --git a/third_party/rust/hyper/src/ffi/task.rs b/third_party/rust/hyper/src/ffi/task.rs
new file mode 100644
index 0000000000..ef54fe408f
--- /dev/null
+++ b/third_party/rust/hyper/src/ffi/task.rs
@@ -0,0 +1,411 @@
+use std::ffi::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 libc::c_int;
+
+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.
+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.
+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.
+pub struct hyper_context<'a>(Context<'a>);
+
+/// A waker that is saved and used to waken a pending task.
+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 {
+ match Pin::new(&mut *self.driver.lock().unwrap()).poll_next(&mut cx) {
+ Poll::Ready(val) => return val,
+ Poll::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;
+ }
+ }
+ }
+ }
+
+ 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 crate::rt::Executor<BoxFuture<()>> for WeakExec {
+ fn execute(&self, fut: BoxFuture<()>) {
+ if let Some(exec) = self.0.upgrade() {
+ exec.spawn(hyper_task::boxed(fut));
+ }
+ }
+}
+
+ffi_fn! {
+ /// Creates a new task executor.
+ 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.
+ 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, it should not be accessed
+ /// again unless returned back to the user with `hyper_executor_poll`.
+ 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 have notified
+ /// that they are ready again.
+ ///
+ /// If ready, returns a task from the executor that has completed.
+ ///
+ /// 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.
+ 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.
+ 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`.
+ 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! {
+ /// Copies a waker out of the task context.
+ 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 that hasn't been woken.
+ 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.
+ ///
+ /// 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();
+ }
+}