195 lines
6.4 KiB
Rust
195 lines
6.4 KiB
Rust
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
|
|
}
|
|
}
|
|
|
|
ffi_fn! {
|
|
/// Set whether HTTP/1 connections will 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.builder.http1_allow_obsolete_multiline_headers_in_responses(enabled != 0);
|
|
hyper_code::HYPERE_OK
|
|
}
|
|
}
|