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}