Struct Body

Source
pub struct Body { /* private fields */ }
Expand description

A response body returned as http::Response<Body>.

§Default size limit

Methods like read_to_string(), read_to_vec(), and read_json() have a default 10MB limit to prevent memory exhaustion. To download larger files, use with_config().limit(new_size):

// Download a 20MB file
let bytes = ureq::get("http://httpbin.org/bytes/200000000")
    .call()?
    .body_mut()
    .with_config()
    .limit(20 * 1024 * 1024) // 20MB
    .read_to_vec()?;

§Body lengths

HTTP/1.1 has two major modes of transfering body data. Either a Content-Length header defines exactly how many bytes to transfer, or Transfer-Encoding: chunked facilitates a streaming style when the size is not known up front.

To protect against a problem called request smuggling, ureq has heuristics for how to interpret a server sending both Transfer-Encoding and Content-Length headers.

  1. chunked takes precedence if there both headers are present (not for HTTP/1.0)
  2. content-length is used if there is no chunked
  3. If there are no headers, fall back on “close delimited” meaning the socket must close to end the body

When a Content-Length header is used, ureq will ensure the received body is EXACTLY as many bytes as declared (it cannot be less). This mechanic is in ureq-proto and is different to the BodyWithConfig::limit() below.

§Pool reuse

To return a connection (aka Transport) to the Agent’s pool, the body must be read to end. If BodyWithConfig::limit() is set shorter size than the actual response body, the connection will not be reused.

§Example

use std::io::Read;
let mut res = ureq::get("http://httpbin.org/bytes/100")
    .call()?;

assert!(res.headers().contains_key("Content-Length"));
let len: usize = res.headers().get("Content-Length")
    .unwrap().to_str().unwrap().parse().unwrap();

let mut bytes: Vec<u8> = Vec::with_capacity(len);
res.body_mut().as_reader()
    .read_to_end(&mut bytes)?;

assert_eq!(bytes.len(), len);

Implementations§

Source§

impl Body

Source

pub fn builder() -> BodyBuilder

Builder for creating a body

This is useful for testing, or for Middleware that returns another body than the requested one.

Source

pub fn mime_type(&self) -> Option<&str>

The mime-type of the content-type header.

For the below header, we would get Some("text/plain"):

    Content-Type: text/plain; charset=iso-8859-1

Caution: A bad server might set Content-Type to one thing and send something else. There is no way ureq can verify this.

§Example
let res = ureq::get("https://www.google.com/")
    .call()?;

assert_eq!(res.body().mime_type(), Some("text/html"));
Source

pub fn charset(&self) -> Option<&str>

The charset of the content-type header.

For the below header, we would get Some("iso-8859-1"):

    Content-Type: text/plain; charset=iso-8859-1

Caution: A bad server might set Content-Type to one thing and send something else. There is no way ureq can verify this.

§Example
let res = ureq::get("https://www.google.com/")
    .call()?;

assert_eq!(res.body().charset(), Some("ISO-8859-1"));
Source

pub fn content_length(&self) -> Option<u64>

The content length of the body.

This is the value of the Content-Length header, if there is one. For chunked responses (Transfer-Encoding: chunked) , this will be None. Similarly for HTTP/1.0 without a Content-Length header, the response is close delimited, which means the length is unknown.

A bad server might set Content-Length to one thing and send something else. ureq will double check this, see section on body length heuristics.

§Example
let res = ureq::get("https://httpbin.org/bytes/100")
    .call()?;

assert_eq!(res.body().content_length(), Some(100));
Source

pub fn as_reader(&mut self) -> BodyReader<'_>

Handle this body as a shared impl Read of the body.

This is the regular API which goes via http::Response::body_mut() to get a mut reference to the Body, and then use as_reader(). It is also possible to get a non-shared, owned reader via Body::into_reader().

  • Reader is not limited by default. That means a malicious server could exhaust all avaliable memory on your client machine. To set a limit use Body::into_with_config().
  • Reader will error if Content-Length is set, but the connection is closed before all bytes are received.
§Example
use std::io::Read;

let mut res = ureq::get("http://httpbin.org/bytes/100")
    .call()?;

let mut bytes: Vec<u8> = Vec::with_capacity(1000);
res.body_mut().as_reader()
    .read_to_end(&mut bytes)?;
Source

pub fn into_reader(self) -> BodyReader<'static>

Turn this response into an owned impl Read of the body.

Sometimes it might be useful to disconnect the body reader from the body. The reader returned by Body::as_reader() borrows the body, while this variant consumes the body and turns it into a reader with lifetime 'static. The reader can for instance be sent to another thread.

  • Reader is not limited by default. That means a malicious server could exhaust all avaliable memory on your client machine. To set a limit use Body::into_with_config().
  • Reader will error if Content-Length is set, but the connection is closed before all bytes are received.
use std::io::Read;

let res = ureq::get("http://httpbin.org/bytes/100")
    .call()?;

let (_, body) = res.into_parts();

let mut bytes: Vec<u8> = Vec::with_capacity(1000);
body.into_reader()
    .read_to_end(&mut bytes)?;
Source

pub fn read_to_string(&mut self) -> Result<String, Error>

Read the response as a string.

  • Response is limited to 10MB
  • Replaces incorrect utf-8 chars to ?

To change these defaults use Body::with_config().

let mut res = ureq::get("http://httpbin.org/robots.txt")
    .call()?;

let s = res.body_mut().read_to_string()?;
assert_eq!(s, "User-agent: *\nDisallow: /deny\n");

For larger text files, you must explicitly increase the limit:

// Read a large text file (25MB)
let text = ureq::get("http://httpbin.org/get")
    .call()?
    .body_mut()
    .with_config()
    .limit(25 * 1024 * 1024) // 25MB
    .read_to_string()?;
Source

pub fn read_to_vec(&mut self) -> Result<Vec<u8>, Error>

Read the response to a vec.

  • Response is limited to 10MB.

To change this default use Body::with_config().

let mut res = ureq::get("http://httpbin.org/bytes/100")
    .call()?;

let bytes = res.body_mut().read_to_vec()?;
assert_eq!(bytes.len(), 100);

For larger files, you must explicitly increase the limit:

// Download a larger file (50MB)
let bytes = ureq::get("http://httpbin.org/bytes/200000000")
    .call()?
    .body_mut()
    .with_config()
    .limit(50 * 1024 * 1024) // 50MB
    .read_to_vec()?;
Source

pub fn read_json<T: DeserializeOwned>(&mut self) -> Result<T, Error>

Read the response from JSON.

  • Response is limited to 10MB.

To change this default use Body::with_config().

The returned value is something that derives Deserialize. You might need to be explicit with which type you want. See example below.

use serde::Deserialize;

#[derive(Deserialize)]
struct BodyType {
  slideshow: BodyTypeInner,
}

#[derive(Deserialize)]
struct BodyTypeInner {
  author: String,
}

let body = ureq::get("https://httpbin.org/json")
    .call()?
    .body_mut()
    .read_json::<BodyType>()?;

assert_eq!(body.slideshow.author, "Yours Truly");

For larger JSON files, you must explicitly increase the limit:

use serde_json::Value;

// Parse a large JSON file (30MB)
let json: Value = ureq::get("https://httpbin.org/json")
    .call()?
    .body_mut()
    .with_config()
    .limit(30 * 1024 * 1024) // 30MB
    .read_json()?;
Source

pub fn with_config(&mut self) -> BodyWithConfig<'_>

Read the body data with configuration.

This borrows the body which gives easier use with http::Response::body_mut(). To get a non-borrowed reader use Body::into_with_config().

§Example
let reader = ureq::get("http://httpbin.org/bytes/100")
    .call()?
    .body_mut()
    .with_config()
    // Reader will only read 50 bytes
    .limit(50)
    .reader();
Source

pub fn into_with_config(self) -> BodyWithConfig<'static>

Consume self and read the body with configuration.

This consumes self and returns a reader with 'static lifetime.

§Example
// Get the body out of http::Response
let (_, body) = ureq::get("http://httpbin.org/bytes/100")
    .call()?
    .into_parts();

let reader = body
    .into_with_config()
    // Reader will only read 50 bytes
    .limit(50)
    .reader();

This limit behavior can be used to prevent a malicious server from exhausting memory on the client machine. For example, if the machine running ureq has 1GB of RAM, you could protect the machine by setting a smaller limit such as 128MB. The exact number will vary by your client’s download needs, available system resources, and system utilization.

Trait Implementations§

Source§

impl Debug for Body

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl AsSendBody for Body

Auto Trait Implementations§

§

impl Freeze for Body

§

impl !RefUnwindSafe for Body

§

impl Send for Body

§

impl Sync for Body

§

impl Unpin for Body

§

impl !UnwindSafe for Body

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> ErasedDestructor for T
where T: 'static,