|
5 | 5 | //! Types and functions related to HTTP request. |
6 | 6 |
|
7 | 7 | use http::{header::HeaderName, Method}; |
| 8 | +pub use http::{HeaderMap, StatusCode}; |
8 | 9 | use serde::{Deserialize, Serialize}; |
9 | 10 | use serde_json::Value; |
10 | 11 | use serde_repr::{Deserialize_repr, Serialize_repr}; |
@@ -352,17 +353,85 @@ pub struct Response(ResponseType, reqwest::Response); |
352 | 353 | #[derive(Debug)] |
353 | 354 | pub struct Response(ResponseType, attohttpc::Response, Url); |
354 | 355 |
|
| 356 | +#[cfg(not(feature = "reqwest-client"))] |
| 357 | +struct AttohttpcByteReader(attohttpc::ResponseReader); |
| 358 | + |
| 359 | +#[cfg(not(feature = "reqwest-client"))] |
| 360 | +impl futures::Stream for AttohttpcByteReader { |
| 361 | + type Item = crate::api::Result<bytes::Bytes>; |
| 362 | + |
| 363 | + fn poll_next( |
| 364 | + mut self: std::pin::Pin<&mut Self>, |
| 365 | + _cx: &mut futures::task::Context<'_>, |
| 366 | + ) -> futures::task::Poll<Option<Self::Item>> { |
| 367 | + use std::io::Read; |
| 368 | + let mut buf = [0; 256]; |
| 369 | + match self.0.read(&mut buf) { |
| 370 | + Ok(b) => { |
| 371 | + if b == 0 { |
| 372 | + futures::task::Poll::Ready(None) |
| 373 | + } else { |
| 374 | + futures::task::Poll::Ready(Some(Ok(buf[0..b].to_vec().into()))) |
| 375 | + } |
| 376 | + } |
| 377 | + Err(_) => futures::task::Poll::Ready(None), |
| 378 | + } |
| 379 | + } |
| 380 | +} |
| 381 | + |
355 | 382 | impl Response { |
| 383 | + /// Get the [`StatusCode`] of this Response. |
| 384 | + pub fn status(&self) -> StatusCode { |
| 385 | + self.1.status() |
| 386 | + } |
| 387 | + |
| 388 | + /// Get the headers of this Response. |
| 389 | + pub fn headers(&self) -> &HeaderMap { |
| 390 | + self.1.headers() |
| 391 | + } |
| 392 | + |
356 | 393 | /// Reads the response as raw bytes. |
357 | 394 | pub async fn bytes(self) -> crate::api::Result<RawResponse> { |
358 | | - let status = self.1.status().as_u16(); |
| 395 | + let status = self.status().as_u16(); |
359 | 396 | #[cfg(feature = "reqwest-client")] |
360 | 397 | let data = self.1.bytes().await?.to_vec(); |
361 | 398 | #[cfg(not(feature = "reqwest-client"))] |
362 | 399 | let data = self.1.bytes()?; |
363 | 400 | Ok(RawResponse { status, data }) |
364 | 401 | } |
365 | 402 |
|
| 403 | + /// Convert the response into a Stream of [`bytes::Bytes`] from the body. |
| 404 | + /// |
| 405 | + /// # Examples |
| 406 | + /// |
| 407 | + /// ```no_run |
| 408 | + /// use futures::StreamExt; |
| 409 | + /// |
| 410 | + /// # async fn run() -> Result<(), Box<dyn std::error::Error>> { |
| 411 | + /// let client = tauri::api::http::ClientBuilder::new().build()?; |
| 412 | + /// let mut stream = client.send(tauri::api::http::HttpRequestBuilder::new("GET", "http://httpbin.org/ip")?) |
| 413 | + /// .await? |
| 414 | + /// .bytes_stream(); |
| 415 | + /// |
| 416 | + /// while let Some(item) = stream.next().await { |
| 417 | + /// println!("Chunk: {:?}", item?); |
| 418 | + /// } |
| 419 | + /// # Ok(()) |
| 420 | + /// # } |
| 421 | + /// ``` |
| 422 | + pub fn bytes_stream(self) -> impl futures::Stream<Item = crate::api::Result<bytes::Bytes>> { |
| 423 | + #[cfg(not(feature = "reqwest-client"))] |
| 424 | + { |
| 425 | + let (_, _, reader) = self.1.split(); |
| 426 | + AttohttpcByteReader(reader) |
| 427 | + } |
| 428 | + #[cfg(feature = "reqwest-client")] |
| 429 | + { |
| 430 | + use futures::StreamExt; |
| 431 | + self.1.bytes_stream().map(|res| res.map_err(Into::into)) |
| 432 | + } |
| 433 | + } |
| 434 | + |
366 | 435 | /// Reads the response. |
367 | 436 | /// |
368 | 437 | /// Note that the body is serialized to a [`Value`]. |
|
0 commit comments