From fdc1f376629272c26cfe21276247a3d88599b6d9 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 21 Jun 2020 00:55:50 -0700 Subject: [PATCH] Update documentation. (#73) * Update documentation. Synchronize goals section between README and rustdoc, and add two goals (blocking API; no unsafe) that were mentioned elsewhere in the README. Add error handling to examples for the module and for the Response object. And a section on synthetic errors to the top-level module documentation. * Add back missing close brace. * Add main function that returns Result. * Add links to send_bytes and send_form. * Document chunked encoding for send. * Use a larger vec of bytes for send. --- README.md | 35 +++++++++++++++++++---------------- src/lib.rs | 48 +++++++++++++++++++++++++++++++++++++----------- src/request.rs | 11 +++++++---- src/response.rs | 8 ++++++++ 4 files changed, 71 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index f66ca1c..9a55d30 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,21 @@ #[macro_use] extern crate ureq; -fn main() { +// sync post request of some json. +let resp = ureq::post("https://myapi.example.com/ingest") + .set("X-My-Header", "Secret") + .send_json(json!({ + "name": "martin", + "rust": true + })); - // sync post request of some json. - let resp = ureq::post("https://myapi.acme.com/ingest") - .set("X-My-Header", "Secret") - .send_json(json!({ - "name": "martin", - "rust": true - })); - - // .ok() tells if response is 200-299. - if resp.ok() { - // ... - } +// .ok() tells if response is 200-299. +if resp.ok() { + println!("success: {}", resp.into_string()?); +} else { + // This can include errors like failure to parse URL or connect timeout. + // They are treated as synthetic HTTP-level error statuses. + println!("error {}: {}", resp.status(), resp.into_string()?); } ``` @@ -77,7 +78,9 @@ You can control them when including `ureq` as a dependency. * Minimal dependency tree * Obvious API - * Convencience over correctness + * Blocking API + * Convenience over correctness + * No use of unsafe This library tries to provide a convenient request library with a minimal dependency tree and an obvious API. It is inspired by libraries like @@ -104,8 +107,8 @@ for convenience over correctness, so the decision is left to the user. This library uses blocking socket reads and writes. When it was created, there wasn't any async/await support in rust, and for my own purposes, blocking IO was fine. At this point, one good reason to keep -this library going is that it is blocking (the other is that it relies -on very little use of unsafe). +this library going is that it is blocking (the other is that it does not +use unsafe). ## TODO diff --git a/src/lib.rs b/src/lib.rs index b03228e..df3e1f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,23 +6,33 @@ //! //! * Minimal dependency tree //! * Obvious API +//! * Blocking API +//! * Convenience over correctness +//! * No use of unsafe //! //! ``` //! // requires feature: `ureq = { version = "*", features = ["json"] }` //! # #[cfg(feature = "json")] { //! use ureq::json; //! -//! // sync post request of some json. -//! let resp = ureq::post("https://myapi.acme.com/ingest") -//! .set("X-My-Header", "Secret") -//! .send_json(json!({ -//! "name": "martin", -//! "rust": true -//! })); +//! fn main() -> std::io::Result<()> { +//! // sync post request of some json. +//! let resp = ureq::post("https://myapi.example.com/ingest") +//! .set("X-My-Header", "Secret") +//! .send_json(json!({ +//! "name": "martin", +//! "rust": true +//! })); //! -//! // .ok() tells if response is 200-299. -//! if resp.ok() { -//! // .... +//! // .ok() tells if response is 200-299. +//! if resp.ok() { +//! println!("success: {}", resp.into_string()?); +//! } else { +//! // This can include errors like failure to parse URL or connect timeout. +//! // They are treated as synthetic HTTP-level error statuses. +//! println!("error {}: {}", resp.status(), resp.into_string()?); +//! } +//! Ok(()) //! } //! # } //! ``` @@ -37,8 +47,10 @@ //! which follows a build pattern. The builders are finished using: //! //! * [`.call()`](struct.Request.html#method.call) without a request body. -//! * [`.send()`](struct.Request.html#method.send) with a request body as `Read`. +//! * [`.send()`](struct.Request.html#method.send) with a request body as `Read` (chunked encoding). //! * [`.send_string()`](struct.Request.html#method.send_string) body as string. +//! * [`.send_bytes()`](struct.Request.html#method.send_bytes) body as bytes. +//! * [`.send_form()`](struct.Request.html#method.send_form) key-value pairs as application/x-www-form-urlencoded. //! //! # JSON //! @@ -91,6 +103,20 @@ //! we first check if the user has set a `; charset=` and attempt //! to encode the request body using that. //! +//! # Synthetic errors +//! +//! Rather than exposing a custom error type through results, this library has opted for +//! representing potential connection/TLS/etc errors as HTTP response codes. These invented codes +//! are called "[synthetic](struct.Response.html#method.synthetic)." +//! +//! The idea is that from a library user's point of view the distinction of whether a failure +//! originated in the remote server (500, 502) etc, or some transient network failure, the code +//! path of handling that would most often be the same. +//! +//! As a result, reading from a Response may yield an error message generated by the ureq library. +//! To handle these errors, use the +//! [`response.synthetic_error()`](struct.Response.html#method.synthetic_error) method. +//! mod agent; mod body; diff --git a/src/request.rs b/src/request.rs index 7290598..7429538 100644 --- a/src/request.rs +++ b/src/request.rs @@ -214,16 +214,19 @@ impl Request { /// Send data from a reader. /// - /// The `Content-Length` header is not set because we can't know the length of the reader. + /// This uses [chunked transfer encoding](https://tools.ietf.org/html/rfc7230#section-4.1). + /// The caller is responsible for setting the Transfer-Encoding: chunked header. + /// + /// The input from the reader is buffered into chunks of size 16,384, the max size of a TLS fragment. /// /// ``` /// use std::io::Cursor; /// - /// let text = "Hello there!\n"; - /// let read = Cursor::new(text.to_string().into_bytes()); + /// let read = Cursor::new(vec![0x20; 100_000]); /// - /// let resp = ureq::post("/somewhere") + /// let resp = ureq::post("http://localhost/example-upload") /// .set("Content-Type", "text/plain") + /// .set("Transfer-Encoding", "chunked") /// .send(read); /// ``` pub fn send(&mut self, reader: impl Read + 'static) -> Response { diff --git a/src/response.rs b/src/response.rs index aba2c00..3e52ead 100644 --- a/src/response.rs +++ b/src/response.rs @@ -29,8 +29,16 @@ pub const DEFAULT_CHARACTER_SET: &str = "utf-8"; /// [`into_json_deserialize()`](#method.into_json_deserialize) or /// [`into_string()`](#method.into_string) consumes the response. /// +/// All error handling, including URL parse errors and connection errors, is done by mapping onto +/// [synthetic errors](#method.synthetic). Callers must check response.synthetic_error(), +/// response.is_ok(), or response.error() before relying on the contents of the reader. +/// /// ``` /// let response = ureq::get("https://www.google.com").call(); +/// if let Some(error) = response.synthetic_error() { +/// eprintln!("{}", error); +/// return; +/// } /// /// // socket is still open and the response body has not been read. ///