diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d4926afe58..07d1ea04ca 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -179,4 +179,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: rustdoc - args: --features full -- --cfg docsrs -D broken-intra-doc-links + args: --features full,ffi -- --cfg docsrs --cfg hyper_unstable_ffi -D broken-intra-doc-links diff --git a/Cargo.toml b/Cargo.toml index 6392b207cd..31ac0356f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,8 +118,8 @@ nightly = [] __internal_happy_eyeballs_tests = [] [package.metadata.docs.rs] -features = ["full"] -rustdoc-args = ["--cfg", "docsrs"] +features = ["ffi", "full"] +rustdoc-args = ["--cfg", "docsrs", "--cfg", "hyper_unstable_ffi"] [package.metadata.playground] features = ["full"] diff --git a/src/ffi/body.rs b/src/ffi/body.rs index e49d2dacc7..51e5c8eae9 100644 --- a/src/ffi/body.rs +++ b/src/ffi/body.rs @@ -10,8 +10,10 @@ 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(super) Bytes); pub(crate) struct UserBody { diff --git a/src/ffi/client.rs b/src/ffi/client.rs index 05d49015a0..a64842b6a3 100644 --- a/src/ffi/client.rs +++ b/src/ffi/client.rs @@ -10,12 +10,18 @@ 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, } diff --git a/src/ffi/error.rs b/src/ffi/error.rs index eb563a60d5..7b85407099 100644 --- a/src/ffi/error.rs +++ b/src/ffi/error.rs @@ -1,7 +1,9 @@ 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. diff --git a/src/ffi/http_types.rs b/src/ffi/http_types.rs index 6dba5a494c..edb43ed31c 100644 --- a/src/ffi/http_types.rs +++ b/src/ffi/http_types.rs @@ -9,10 +9,15 @@ use super::HYPER_ITER_CONTINUE; use crate::header::{HeaderName, HeaderValue}; use crate::{Body, HeaderMap, Method, Request, Response, Uri}; +/// An HTTP request. pub struct hyper_request(pub(super) Request); +/// An HTTP response. pub struct hyper_response(pub(super) Response); +/// An HTTP header map. +/// +/// These can be part of a request or response. #[derive(Default)] pub struct hyper_headers { pub(super) headers: HeaderMap, diff --git a/src/ffi/io.rs b/src/ffi/io.rs index 62db1ac49c..f37b6cb781 100644 --- a/src/ffi/io.rs +++ b/src/ffi/io.rs @@ -7,7 +7,11 @@ use tokio::io::{AsyncRead, AsyncWrite}; use super::task::hyper_context; +/// Sentinal value to return from a read or write callback that the operation +/// is pending. pub const HYPER_IO_PENDING: size_t = 0xFFFFFFFF; +/// Sentinal 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 = @@ -15,6 +19,7 @@ type hyper_io_read_callback = 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, diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index b593c89d77..39f3276bc8 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1,8 +1,33 @@ // 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`. @@ -29,16 +54,29 @@ 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::*; + pub(crate) use self::body::UserBody; pub(crate) use self::http_types::{HeaderCaseMap, ReasonPhrase}; +/// 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); diff --git a/src/ffi/task.rs b/src/ffi/task.rs index 86f870c2bd..b42be59808 100644 --- a/src/ffi/task.rs +++ b/src/ffi/task.rs @@ -17,10 +17,17 @@ use super::UserDataPointer; type BoxFuture = Pin + Send>>; type BoxAny = Box; +/// 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. /// @@ -47,6 +54,7 @@ pub(crate) struct WeakExec(Weak); struct ExecWaker(AtomicBool); +/// An async task. pub struct hyper_task { future: BoxFuture, output: Option, @@ -57,12 +65,15 @@ struct TaskFuture { task: Option>, } +/// 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). diff --git a/src/lib.rs b/src/lib.rs index d5fb494327..daddbdfb0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ pub mod service; pub mod upgrade; #[cfg(feature = "ffi")] -mod ffi; +pub mod ffi; cfg_proto! { mod headers;