1 use std::ptr; 2 use std::sync::Arc; 3 4 use libc::c_int; 5 6 use crate::client::conn; 7 use crate::rt::Executor as _; 8 9 use super::error::hyper_code; 10 use super::http_types::{hyper_request, hyper_response}; 11 use super::io::hyper_io; 12 use super::task::{hyper_executor, hyper_task, hyper_task_return_type, AsTaskType, WeakExec}; 13 14 /// An options builder to configure an HTTP client connection. 15 pub struct hyper_clientconn_options { 16 builder: conn::Builder, 17 /// Use a `Weak` to prevent cycles. 18 exec: WeakExec, 19 } 20 21 /// An HTTP client connection handle. 22 /// 23 /// These are used to send a request on a single connection. It's possible to 24 /// send multiple requests on a single connection, such as when HTTP/1 25 /// keep-alive or HTTP/2 is used. 26 pub struct hyper_clientconn { 27 tx: conn::SendRequest<crate::Body>, 28 } 29 30 // ===== impl hyper_clientconn ===== 31 32 ffi_fn! { 33 /// Starts an HTTP client connection handshake using the provided IO transport 34 /// and options. 35 /// 36 /// Both the `io` and the `options` are consumed in this function call. 37 /// 38 /// The returned `hyper_task *` must be polled with an executor until the 39 /// handshake completes, at which point the value can be taken. 40 fn hyper_clientconn_handshake(io: *mut hyper_io, options: *mut hyper_clientconn_options) -> *mut hyper_task { 41 let options = non_null! { Box::from_raw(options) ?= ptr::null_mut() }; 42 let io = non_null! { Box::from_raw(io) ?= ptr::null_mut() }; 43 44 Box::into_raw(hyper_task::boxed(async move { 45 options.builder.handshake::<_, crate::Body>(io) 46 .await 47 .map(|(tx, conn)| { 48 options.exec.execute(Box::pin(async move { 49 let _ = conn.await; 50 })); 51 hyper_clientconn { tx } 52 }) 53 })) 54 } ?= std::ptr::null_mut() 55 } 56 57 ffi_fn! { 58 /// Send a request on the client connection. 59 /// 60 /// Returns a task that needs to be polled until it is ready. When ready, the 61 /// task yields a `hyper_response *`. 62 fn hyper_clientconn_send(conn: *mut hyper_clientconn, req: *mut hyper_request) -> *mut hyper_task { 63 let mut req = non_null! { Box::from_raw(req) ?= ptr::null_mut() }; 64 65 // Update request with original-case map of headers 66 req.finalize_request(); 67 68 let fut = non_null! { &mut *conn ?= ptr::null_mut() }.tx.send_request(req.0); 69 70 let fut = async move { 71 fut.await.map(hyper_response::wrap) 72 }; 73 74 Box::into_raw(hyper_task::boxed(fut)) 75 } ?= std::ptr::null_mut() 76 } 77 78 ffi_fn! { 79 /// Free a `hyper_clientconn *`. 80 fn hyper_clientconn_free(conn: *mut hyper_clientconn) { 81 drop(non_null! { Box::from_raw(conn) ?= () }); 82 } 83 } 84 85 unsafe impl AsTaskType for hyper_clientconn { as_task_type(&self) -> hyper_task_return_type86 fn as_task_type(&self) -> hyper_task_return_type { 87 hyper_task_return_type::HYPER_TASK_CLIENTCONN 88 } 89 } 90 91 // ===== impl hyper_clientconn_options ===== 92 93 ffi_fn! { 94 /// Creates a new set of HTTP clientconn options to be used in a handshake. 95 fn hyper_clientconn_options_new() -> *mut hyper_clientconn_options { 96 #[allow(deprecated)] 97 let builder = conn::Builder::new(); 98 99 Box::into_raw(Box::new(hyper_clientconn_options { 100 builder, 101 exec: WeakExec::new(), 102 })) 103 } ?= std::ptr::null_mut() 104 } 105 106 ffi_fn! { 107 /// Set the whether or not header case is preserved. 108 /// 109 /// Pass `0` to allow lowercase normalization (default), `1` to retain original case. 110 fn hyper_clientconn_options_set_preserve_header_case(opts: *mut hyper_clientconn_options, enabled: c_int) { 111 let opts = non_null! { &mut *opts ?= () }; 112 opts.builder.http1_preserve_header_case(enabled != 0); 113 } 114 } 115 116 ffi_fn! { 117 /// Set the whether or not header order is preserved. 118 /// 119 /// Pass `0` to allow reordering (default), `1` to retain original ordering. 120 fn hyper_clientconn_options_set_preserve_header_order(opts: *mut hyper_clientconn_options, enabled: c_int) { 121 let opts = non_null! { &mut *opts ?= () }; 122 opts.builder.http1_preserve_header_order(enabled != 0); 123 } 124 } 125 126 ffi_fn! { 127 /// Free a `hyper_clientconn_options *`. 128 fn hyper_clientconn_options_free(opts: *mut hyper_clientconn_options) { 129 drop(non_null! { Box::from_raw(opts) ?= () }); 130 } 131 } 132 133 ffi_fn! { 134 /// Set the client background task executor. 135 /// 136 /// This does not consume the `options` or the `exec`. 137 fn hyper_clientconn_options_exec(opts: *mut hyper_clientconn_options, exec: *const hyper_executor) { 138 let opts = non_null! { &mut *opts ?= () }; 139 140 let exec = non_null! { Arc::from_raw(exec) ?= () }; 141 let weak_exec = hyper_executor::downgrade(&exec); 142 std::mem::forget(exec); 143 144 opts.builder.executor(weak_exec.clone()); 145 opts.exec = weak_exec; 146 } 147 } 148 149 ffi_fn! { 150 /// Set the whether to use HTTP2. 151 /// 152 /// Pass `0` to disable, `1` to enable. 153 fn hyper_clientconn_options_http2(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { 154 #[cfg(feature = "http2")] 155 { 156 let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; 157 opts.builder.http2_only(enabled != 0); 158 hyper_code::HYPERE_OK 159 } 160 161 #[cfg(not(feature = "http2"))] 162 { 163 drop(opts); 164 drop(enabled); 165 hyper_code::HYPERE_FEATURE_NOT_ENABLED 166 } 167 } 168 } 169 170 ffi_fn! { 171 /// Set the whether to include a copy of the raw headers in responses 172 /// received on this connection. 173 /// 174 /// Pass `0` to disable, `1` to enable. 175 /// 176 /// If enabled, see `hyper_response_headers_raw()` for usage. 177 fn hyper_clientconn_options_headers_raw(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { 178 let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; 179 opts.builder.http1_headers_raw(enabled != 0); 180 hyper_code::HYPERE_OK 181 } 182 } 183