mio_httpc/api/
builder.rs

1use crate::tls_api;
2use crate::tls_api::TlsConnector;
3use crate::types::{CallBuilderImpl, Method};
4use crate::SimpleCall;
5use crate::{Call, CallRef, Result};
6use mio::{event::Event, Registry};
7
8/// Used to start a call and get a Call for it.
9#[derive(Debug, Default)]
10pub struct CallBuilder {
11    cb: Option<CallBuilderImpl>,
12}
13
14#[cfg(feature = "rustls")]
15type CONNECTOR = tls_api::rustls::TlsConnector;
16#[cfg(feature = "native")]
17type CONNECTOR = tls_api::native::TlsConnector;
18#[cfg(feature = "openssl")]
19type CONNECTOR = tls_api::openssl::TlsConnector;
20#[cfg(not(any(feature = "rustls", feature = "native", feature = "openssl")))]
21type CONNECTOR = tls_api::dummy::TlsConnector;
22
23/// CallBuilder constructs a call. It is finished after calling: exec, simple_call, call or websocket.
24///
25/// Some headers are set automatically unless set explicitly:
26/// user-agent, connection, host, auth, content-length
27///
28/// If you're only executing a one-off call you should set connection header to close as default
29/// is keep-alive.
30///
31/// If you do not set body, but do set content-length,
32/// it will wait for send body to be provided through Httpc::call_send.
33/// You must use a streaming interface in this case and can not use SimpleCall.
34impl CallBuilder {
35    pub fn new() -> CallBuilder {
36        CallBuilder {
37            // builder: Builder::new(),
38            cb: Some(CallBuilderImpl::new()),
39            ..Default::default()
40        }
41    }
42
43    /// Start a GET request.
44    pub fn get() -> CallBuilder {
45        let mut b = CallBuilder::new();
46        b.cb.as_mut().unwrap().method = Method::GET;
47        b
48    }
49
50    /// Start a POST request.
51    pub fn post(body: Vec<u8>) -> CallBuilder {
52        let mut b = CallBuilder::new();
53        b.cb.as_mut().unwrap().body = body;
54        b.cb.as_mut().unwrap().method = Method::POST;
55        b
56    }
57
58    /// Start a PUT request.
59    pub fn put(body: Vec<u8>) -> CallBuilder {
60        let mut b = CallBuilder::new();
61        b.cb.as_mut().unwrap().body = body;
62        b.cb.as_mut().unwrap().method = Method::PUT;
63        b
64    }
65
66    /// Start a DELETE request.
67    pub fn delete() -> CallBuilder {
68        let mut b = CallBuilder::new();
69        b.cb.as_mut().unwrap().method = Method::DELETE;
70        b
71    }
72
73    /// Start an OPTIONS request.
74    pub fn options() -> CallBuilder {
75        let mut b = CallBuilder::new();
76        b.cb.as_mut().unwrap().method = Method::OPTIONS;
77        b
78    }
79
80    /// Start a HEAD request.
81    pub fn head() -> CallBuilder {
82        let mut b = CallBuilder::new();
83        b.cb.as_mut().unwrap().method = Method::HEAD;
84        b
85    }
86
87    /// Set method: "GET", "POST", "PUT", "OPTIONS", "DELETE" or "HEAD"
88    pub fn method(&mut self, m: &str) -> &mut Self {
89        self.cb.as_mut().unwrap().method(m);
90        self
91    }
92
93    /// Default: http
94    ///
95    /// Use https for call.
96    /// Shorthand for set_https(true)
97    pub fn https(&mut self) -> &mut Self {
98        self.cb.as_mut().unwrap().https();
99        self
100    }
101
102    /// Default: false
103    ///
104    /// Use https for call.
105    pub fn set_https(&mut self, v: bool) -> &mut Self {
106        self.cb.as_mut().unwrap().set_https(v);
107        self
108    }
109
110    /// Set host where to connect to. It can be a domain or IP address.
111    pub fn host(&mut self, s: &str) -> &mut Self {
112        self.cb.as_mut().unwrap().host(s);
113        self
114    }
115
116    /// Set connection port.
117    pub fn port(&mut self, p: u16) -> &mut Self {
118        self.cb.as_mut().unwrap().port = p;
119        self
120    }
121
122    /// Specifically set mio token IDs for call. Must be outside of token range specified im Httpc::new
123    /// Two tokens are required becase when initiating connection both ipv4 and ipv6 connections
124    /// are attempted. First one to connect wins and the other one is closed.
125    pub fn event_token(&mut self, p: [usize; 2]) -> &mut Self {
126        self.cb.as_mut().unwrap().evids = p;
127        self
128    }
129
130    /// Use http authentication with username and password.
131    pub fn auth(&mut self, us: &str, pw: &str) -> &mut Self {
132        self.cb.as_mut().unwrap().auth(us, pw);
133        self
134    }
135
136    /// Set full path. No percent encoding is done. Will fail later if it contains invalid characters.
137    pub fn path(&mut self, inpath: &str) -> &mut Self {
138        self.cb.as_mut().unwrap().path(inpath);
139        self
140    }
141
142    /// Add a single segment of path. Parts are delimited by / which are added automatically.
143    /// Any path unsafe characters are percent encoded.
144    /// If part contains /, it will be percent encoded!
145    pub fn path_segm(&mut self, segm: &str) -> &mut Self {
146        self.cb.as_mut().unwrap().path_segm(segm);
147        self
148    }
149
150    /// Add multiple segments in one go.
151    pub fn path_segms(&mut self, parts: &[&str]) -> &mut Self {
152        for p in parts {
153            self.path_segm(p);
154        }
155        self
156    }
157
158    /// Add a key-value pair to query. Any url unsafe characters are percent encoded.
159    pub fn query(&mut self, k: &str, v: &str) -> &mut Self {
160        self.cb.as_mut().unwrap().query(k, v);
161        self
162    }
163
164    /// Add multiple key-value pars in one go.
165    pub fn query_list(&mut self, kvl: &[(&str, &str)]) -> &mut Self
166// where
167    //     I: Deref<Target = str>,
168    {
169        for &(ref k, ref v) in kvl {
170            self.query(k, v);
171        }
172        self
173    }
174
175    /// Set full URL. If not valid it will return error. Be mindful of characters
176    /// that need to be percent encoded. Using https, path_segm, query and auth functions
177    /// to construct URL is much safer as those encode data automatically.
178    pub fn url(&mut self, url: &str) -> crate::Result<&mut Self> {
179        self.cb.as_mut().unwrap().url(url)?;
180        Ok(self)
181    }
182
183    /// Set body.
184    pub fn body(&mut self, body: Vec<u8>) -> &mut Self {
185        self.cb.as_mut().unwrap().body = body;
186        self
187    }
188
189    /// Set HTTP header.
190    pub fn header(&mut self, key: &str, value: &str) -> &mut CallBuilder {
191        self.cb.as_mut().unwrap().header(key, value);
192        self
193    }
194
195    /// Execute directly. This will block until completion!
196    pub fn exec(&mut self) -> crate::Result<(crate::Response, Vec<u8>)> {
197        let mut poll = mio::Poll::new()?;
198        let mut htp = Httpc::new(0, None);
199        let mut events = mio::Events::with_capacity(2);
200        let mut call = self.simple_call(&mut htp, poll.registry())?;
201        loop {
202            poll.poll(&mut events, Some(std::time::Duration::from_millis(100)))?;
203            for cref in htp.timeout().into_iter() {
204                if call.is_ref(cref) {
205                    return Err(crate::Error::TimeOut);
206                }
207            }
208
209            for ev in events.iter() {
210                let cref = htp.event(&ev);
211
212                if call.is_call(&cref) {
213                    if call.perform(&mut htp, poll.registry())? {
214                        if let Some((resp, v)) = call.finish() {
215                            return Ok((resp, v));
216                        }
217                        return Ok((crate::Response::new(), Vec::new()));
218                    }
219                }
220            }
221        }
222    }
223
224    /// Consume and execute HTTP call. Returns SimpleCall interface.
225    /// CallBuilder is invalid after this call and will panic if used again.
226    pub fn simple_call(&mut self, httpc: &mut Httpc, poll: &Registry) -> Result<SimpleCall> {
227        // self.finish()?;
228        let cb = self.cb.take().unwrap();
229        Ok(httpc.call::<CONNECTOR>(cb, poll)?.simple())
230    }
231
232    /// Consume and execute HTTP call. Return low level streaming call interface.
233    /// CallBuilder is invalid after this call and will panic if used again.
234    pub fn call(&mut self, httpc: &mut Httpc, poll: &Registry) -> Result<Call> {
235        // self.finish()?;
236        let mut cb = self.cb.take().unwrap();
237        // cant stream response with gzip on
238        cb.gzip(false);
239        httpc.call::<CONNECTOR>(cb, poll)
240    }
241
242    /// Consume and start a WebSocket
243    /// CallBuilder is invalid after this call and will panic if used again.
244    pub fn websocket(&mut self, httpc: &mut Httpc, poll: &Registry) -> Result<crate::WebSocket> {
245        // self.finish()?;
246        let mut cb = self.cb.take().unwrap();
247        cb.websocket();
248        let cid = httpc.call::<CONNECTOR>(cb, poll)?;
249        Ok(crate::WebSocket::new(cid, httpc.h.get_buf()))
250    }
251
252    /// Default 10MB.
253    ///
254    /// This will limit how big the internal Vec<u8> can grow.
255    /// HTTP response headers are always stored in internal buffer.
256    /// HTTP response body is stored in internal buffer if no external
257    /// buffer is provided.
258    ///
259    /// For WebSockets this will also be a received fragment size limit!
260    pub fn max_response(&mut self, m: usize) -> &mut Self {
261        self.cb.as_mut().unwrap().max_response(m);
262        self
263    }
264
265    /// Default: 100ms
266    ///
267    /// Starting point of dns packet resends if nothing received.
268    /// Every next resend timeout is 2x the previous one but stops at 1s.
269    /// Make sure to call Httpc::timeout!
270    /// So for 100ms: 100ms, 200ms, 400ms, 800ms, 1000ms, 1000ms...
271    pub fn dns_retry_ms(&mut self, n: u64) -> &mut Self {
272        self.cb.as_mut().unwrap().dns_retry_ms(n);
273        self
274    }
275
276    /// Default true.
277    ///
278    /// Configurable because it entails copying the data stream.
279    pub fn chunked_parse(&mut self, b: bool) -> &mut Self {
280        self.cb.as_mut().unwrap().chunked_parse(b);
281        self
282    }
283
284    /// Default 128K
285    ///
286    /// Max size of chunk in a chunked transfer.
287    pub fn chunked_max_chunk(&mut self, v: usize) -> &mut Self {
288        self.cb.as_mut().unwrap().chunked_max_chunk(v);
289        self
290    }
291
292    /// Default 60s
293    ///
294    /// Maximum amount of time a call should last.
295    /// Make sure to call Httpc::timeout!
296    pub fn timeout_ms(&mut self, d: u64) -> &mut Self {
297        self.cb.as_mut().unwrap().timeout_ms(d);
298        self
299    }
300
301    /// Default 4.
302    ///
303    /// How many redirects to follow. 0 to disable following redirects.
304    pub fn max_redirects(&mut self, v: u8) -> &mut Self {
305        self.cb.as_mut().unwrap().max_redirects(v);
306        self
307    }
308
309    /// Default true.
310    ///
311    /// Sets Accept-Encoding: gzip, deflate
312    /// Will decompress response.
313    pub fn gzip(&mut self, b: bool) -> &mut Self {
314        self.cb.as_mut().unwrap().gzip(b);
315        self
316    }
317
318    /// Default secure.
319    ///
320    /// Turn off domain verification over ssl. This should only be used when testing as you are throwing away
321    /// a big part of ssl security.
322    pub fn insecure_do_not_verify_domain(&mut self) -> &mut Self {
323        self.cb.as_mut().unwrap().insecure();
324        self
325    }
326
327    /// Use digest authentication. If you know server is using digest auth you REALLY should set it to true.
328    /// If server is using basic authentication and you set digest_auth to true, mio_httpc will retry with basic.
329    /// If not set, basic auth is assumed which is very insecure.
330    pub fn digest_auth(&mut self, v: bool) -> &mut Self {
331        self.cb.as_mut().unwrap().digest_auth(v);
332        self
333    }
334
335    /// Return constructed URL.
336    pub fn get_url(&mut self) -> String {
337        self.cb.as_mut().unwrap().get_url()
338    }
339}
340
341pub struct Httpc {
342    h: crate::httpc::HttpcImpl,
343}
344
345impl Httpc {
346    /// Httpc will create connections with mio token in range [con_offset..con_offset+0xFFFF]
347    pub fn new(con_offset: usize, cfg: Option<crate::HttpcCfg>) -> Httpc {
348        Httpc {
349            h: crate::httpc::HttpcImpl::new(con_offset, cfg),
350        }
351    }
352    pub(crate) fn call<C: TlsConnector>(
353        &mut self,
354        b: CallBuilderImpl,
355        poll: &Registry,
356    ) -> Result<Call> {
357        self.h.call::<C>(b, poll)
358    }
359    pub(crate) fn peek_body(&mut self, id: &crate::Call, off: &mut usize) -> &[u8] {
360        self.h.peek_body(id, off)
361    }
362    pub(crate) fn try_truncate(&mut self, id: &crate::Call, off: &mut usize) {
363        self.h.try_truncate(id, off);
364    }
365    /// Reconfigure httpc.
366    pub fn cfg_mut(&mut self) -> &mut crate::HttpcCfg {
367        self.h.cfg_mut()
368    }
369    /// Number of currently open connections (in active and idle keep-alive state)
370    pub fn open_connections(&self) -> usize {
371        self.h.open_connections()
372    }
373    /// Reuse a response buffer for subsequent calls.
374    pub fn reuse(&mut self, buf: Vec<u8>) {
375        self.h.reuse(buf);
376    }
377    /// Prematurely finish call.
378    pub fn call_close(&mut self, id: Call) {
379        self.h.call_close(id, false);
380    }
381    /// Call periodically to check for call timeouts and DNS retries.
382    /// Returns list of calls that have timed out.
383    /// You must execute call_close yourself (or SimpleCall::abort) and timeout will return them
384    /// every time until you do.
385    /// (every 100ms for example)
386    pub fn timeout(&mut self) -> Vec<CallRef> {
387        self.h.timeout()
388    }
389    /// Same as timeout except that timed out calls get appended.
390    /// This way you can reuse old allocations (if you truncated to 0).
391    pub fn timeout_extend<C: TlsConnector>(&mut self, out: &mut Vec<CallRef>) {
392        self.h.timeout_extend(out)
393    }
394    /// Get CallRef for ev if token in configured range for Httpc.
395    /// Compare CallRef with external Call to find out which call this
396    /// event belongs to if any.
397    ///
398    /// This call is mandatory as it sets up internal data structures for when something
399    /// is signalled! Even if you know which call is being signalled, you must call event
400    /// to let mio_httpc know what state socket is in.
401    ///
402    /// For streaming API first you must call call_send until you get a SendState::Receiving
403    /// after that call is in receive state and you must call call_recv.
404    pub fn event(&mut self, ev: &Event) -> Option<CallRef> {
405        self.h.event::<CONNECTOR>(ev)
406    }
407    /// If request has body it will be either taken from buf, from Request provided to CallBuilder
408    /// or will return SendState::WaitReqBody.
409    ///
410    /// buf slice is assumed to have taken previous SendState::SentBody(usize) into account
411    /// and starts from part of buffer that has not been sent yet.
412    pub fn call_send(
413        &mut self,
414        poll: &Registry,
415        id: &mut Call,
416        buf: Option<&[u8]>,
417    ) -> crate::SendState {
418        self.h.call_send::<CONNECTOR>(poll, id, buf)
419    }
420
421    /// If no buf provided, response body (if any) is stored in an internal buffer.
422    /// If buf provided after some body has been received, it will be copied to it.
423    ///
424    /// Buf will be expanded if required. Bytes are always appended. If you want to receive
425    /// response entirely in buf, you should reserve capacity for entire body before calling call_recv.
426    ///
427    /// If body is only stored in internal buffer it will be limited to CallBuilder::max_response.
428    pub fn call_recv(
429        &mut self,
430        poll: &Registry,
431        id: &mut Call,
432        buf: Option<&mut Vec<u8>>,
433    ) -> crate::RecvState {
434        self.h.call_recv::<CONNECTOR>(poll, id, buf)
435    }
436}