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