diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fbc4fd9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +## License + +Copyright (c) 2019 Martin Algesten + +Licensed under either of + + * Apache License, Version 2.0 + ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license + ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed under the Apache License, Version 2.0, and the MIT license, without +any additional terms or conditions. See LICENSE-APACHE and LICENSE-MIT for +details. + diff --git a/Cargo.toml b/Cargo.toml index a088b79..ca7fd68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,11 @@ name = "ureq" version = "1.5.1" authors = ["Martin Algesten ", "Jacob Hoffman-Andrews "] -description = "Minimal HTTP request library" +description = "Simple, safe HTTP client" license = "MIT/Apache-2.0" repository = "https://github.com/algesten/ureq" readme = "README.md" -keywords = ["web", "request", "http", "rest", "client"] +keywords = ["web", "request", "rest", "https", "http", "client"] categories = ["web-programming::http-client"] edition = "2018" diff --git a/README.md b/README.md index 396acac..8c5d3a4 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,195 @@ +[comment]: # (README.md is autogenerated from src/lib.rs by `cargo readme > README.md`) + # ureq -![](https://github.com/algesten/ureq/workflows/CI/badge.svg) -[![CratesIO](https://img.shields.io/crates/v/ureq.svg)](https://crates.io/crates/ureq) -[![Documentation](https://docs.rs/ureq/badge.svg)](https://docs.rs/ureq) +A simple, safe HTTP client. -> Minimal request library in rust. +Ureq's first priority is being easy for you to use. It's great for +anyone who wants a low-overhead HTTP client that just gets the job done. Works +very well with HTTP APIs. Its features include cookies, JSON, HTTP proxies, +HTTPS, and charset decoding. -## Usage +Ureq is in pure Rust for safety and ease of understanding. It avoids using +`unsafe` directly. It [uses blocking I/O][blocking] instead of async I/O, because that keeps +the API simple and and keeps dependencies to a minimum. For TLS, ureq uses +[rustls]. + +[blocking]: #blocking-i-o-for-simplicity + +### Usage + +In its simplest form, ureq looks like this: ```rust -// sync post request of some json. -// requires feature: -// `ureq = { version = "*", features = ["json"] }` -let resp = ureq::post("https://myapi.example.com/ingest") - .set("X-My-Header", "Secret") - .send_json(serde_json::json!({ - "name": "martin", - "rust": true - }))?; - -// .ok() tells if response is 200-299. -if resp.ok() { - println!("success: {}", resp.into_string()?); -} else { - println!("error {}: {}", resp.status(), resp.into_string()?); -} +let body: String = ureq::get("http://example.com") + .set("Accept", "text/html") + .call()? + .into_string()?; ``` -## About 1.0.0 +For more involved tasks, you'll want to create an [Agent]. An Agent +holds a connection pool for reuse, and a cookie store if you use the +"cookies" feature. An Agent can be cheaply cloned due to an internal +[Arc](std::sync::Arc) and all clones of an Agent share state among each other. Creating +an Agent also allows setting options like the TLS configuration. -This crate is now 1.x.x. It signifies there will be no more breaking -API changes (for better or worse). I personally use this code in -production system reading data from AWS. Whether the quality is good -enough for other use cases is a "YMMV". +```rust + use ureq::{Agent, AgentBuilder}; + use std::time::Duration; -## ureq's future + let agent: Agent = ureq::AgentBuilder::new() + .timeout_read(Duration::from_secs(5)) + .timeout_write(Duration::from_secs(5)) + .build(); + let body: String = agent.get("http://example.com/page") + .call()? + .into_string()?; -I asked for feedback on [ureq's future -direction](https://www.reddit.com/r/rust/comments/eu6qg8/future_of_ureq_http_client_library/) -and came to the conclusion that there's enough interest in a simple -blocking http client to keep it going. Another motivation is that I -use it extensively for my own work, to talk to S3. + // Reuses the connection from previous request. + let response: String = agent.put("http://example.com/upload") + .set("Authorization", "example-token") + .call()? + .into_string()?; +``` -I'll keep maintaining ureq. I will try to keep dependencies somewhat -fresh and try to address bad bugs. I will however not personally -implement new features in ureq, but I do welcome PR with open arms. +Ureq supports sending and receiving json, if you enable the "json" feature: -The code base is extremely simple, one might even call naive. It's a -good project to hack on as first learning experience in Rust. I will -uphold some base line of code hygiene, but won't block a PR due to -something being a bit inelegant. +```rust + // Requires the `json` feature enabled. + let resp: String = ureq::post("http://myapi.example.com/ingest") + .set("X-My-Header", "Secret") + .send_json(ureq::json!({ + "name": "martin", + "rust": true + }))? + .into_string()?; +``` -## Features +### Features To enable a minimal dependency tree, some features are off by default. -You can control them when including `ureq` as a dependency. +You can control them when including ureq as a dependency. -``` - ureq = { version = "*", features = ["json", "charset"] } -``` +`ureq = { version = "*", features = ["json", "charset"] }` * `tls` enables https. This is enabled by default. -* `cookies` enables handling cookies between requests in an agent. -* `json` enables `response.into_json()` and `request.send_json()` via serde_json. -* `charset` enables interpreting the charset part of - `Content-Type: text/plain; charset=iso-8859-1`. Without this, the library - defaults to rust's built in `utf-8`. +* `cookies` enables cookies. +* `json` enables [Response::into_json()] and [Request::send_json()] via serde_json. +* `charset` enables interpreting the charset part of the Content-Type header + (e.g. `Content-Type: text/plain; charset=iso-8859-1`). Without this, the + library defaults to Rust's built in `utf-8`. -## Motivation +## Plain requests - * Minimal dependency tree - * Obvious API - * Blocking API - * Convenience over correctness - * No use of unsafe +Most standard methods (GET, POST, PUT etc), are supported as functions from the +top of the library ([get()], [post()], [put()], etc). -This library tries to provide a convenient request library with a minimal dependency -tree and an obvious API. It is inspired by libraries like +These top level http method functions create a [Request] instance +which follows a build pattern. The builders are finished using: + +* [`.call()`][Request::call()] without a request body. +* [`.send()`][Request::send()] with a request body as [Read][std::io::Read] (chunked encoding support for non-known sized readers). +* [`.send_string()`][Request::send_string()] body as string. +* [`.send_bytes()`][Request::send_bytes()] body as bytes. +* [`.send_form()`][Request::send_form()] key-value pairs as application/x-www-form-urlencoded. + +## JSON + +By enabling the `ureq = { version = "*", features = ["json"] }` feature, +the library supports serde json. + +* [`request.send_json()`][Request::send_json()] send body as serde json. +* [`response.into_json()`][Response::into_json()] transform response to json. + +## Content-Length and Transfer-Encoding + +The library will send a Content-Length header on requests with bodies of +known size, in other words, those sent with +[`.send_string()`][Request::send_string()], +[`.send_bytes()`][Request::send_bytes()], +[`.send_form()`][Request::send_form()], or +[`.send_json()`][Request::send_json()]. If you send a +request body with [`.send()`][Request::send()], +which takes a [Read][std::io::Read] of unknown size, ureq will send Transfer-Encoding: +chunked, and encode the body accordingly. Bodyless requests +(GETs and HEADs) are sent with [`.call()`][Request::call()] +and ureq adds neither a Content-Length nor a Transfer-Encoding header. + +If you set your own Content-Length or Transfer-Encoding header before +sending the body, ureq will respect that header by not overriding it, +and by encoding the body or not, as indicated by the headers you set. + +```rust +let resp = ureq::post("http://my-server.com/ingest") + .set("Transfer-Encoding", "chunked") + .send_string("Hello world"); +``` + +## Character encoding + +By enabling the `ureq = { version = "*", features = ["charset"] }` feature, +the library supports sending/receiving other character sets than `utf-8`. + +For [`response.into_string()`][Response::into_string()] we read the +header `Content-Type: text/plain; charset=iso-8859-1` and if it contains a charset +specification, we try to decode the body using that encoding. In the absence of, or failing +to interpret the charset, we fall back on `utf-8`. + +Similarly when using [`request.send_string()`][Request::send_string()], +we first check if the user has set a `; charset=` and attempt +to encode the request body using that. + +## Blocking I/O for simplicity + +Rust supports [asynchronous (async) I/O][async], but ureq does not use it. Async I/O +allows serving many concurrent requests without high costs in memory and OS threads. But +it comes at a cost in complexity. Async programs need to pull in a runtime (usually +[async-std] or [tokio]). They also need async variants of any method that might block, and of +[any method that might call another method that might block][what-color]. That means async +programs usually have a lot of dependencies - which adds to compile times, and increases +risk. + +The costs of async are worth paying, if you're writing an HTTP server that must serve +many many clients with minimal overhead. However, for HTTP _clients_, we believe that the +cost is usually not worth paying. The low-cost alternative to async I/O is blocking I/O, +which has a different price: it requires an OS thread per concurrent request. However, +that price is usually not high: most HTTP clients make requests sequentially, or with +low concurrency. + +That's why ureq uses blocking I/O and plans to stay that way. Other HTTP clients offer both +an async API and a blocking API, but we want to offer a blocking API without pulling in all +the dependencies required by an async API. + +[async]: https://rust-lang.github.io/async-book/01_getting_started/02_why_async.html +[async-std]: https://github.com/async-rs/async-std#async-std +[tokio]: https://github.com/tokio-rs/tokio#tokio +[what-color]: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ + +------------------------------------------------------------------------------ + +Ureq is inspired by other great HTTP clients like [superagent](http://visionmedia.github.io/superagent/) and -[fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). +[the fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). -### Sync forever +If ureq is not what you're looking for, check out these other Rust HTTP clients: +[surf](https://crates.io/crates/surf), [reqwest](https://crates.io/crates/reqwest), +[isahc](https://crates.io/crates/isahc), [attohttpc](https://crates.io/crates/attohttpc), +[actix-web](https://crates.io/crates/actix-web), and [hyper](https://crates.io/crates/hyper). -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 does not -use unsafe). -## TODO - -- [ ] Forms with application/x-www-form-urlencoded -- [ ] multipart/form-data -- [ ] Expect 100-continue -- [x] Use `rustls` when [ring with versioned asm symbols](https://github.com/briansmith/ring/pull/619) is released. (PR is not resolved, but most implementations have settled on 0.13) - -## License - -Copyright (c) 2019 Martin Algesten - -Licensed under either of - - * Apache License, Version 2.0 - ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license - ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -## Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +[rustls]: https://docs.rs/rustls/ +[std::sync::Arc]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html +[std::io::Read]: https://doc.rust-lang.org/stable/std/io/trait.Read.html +[Agent]: https://docs.rs/ureq/latest/ureq/struct.Agent.html +[get()]: https://docs.rs/ureq/latest/ureq/fn.get.html +[post()]: https://docs.rs/ureq/latest/ureq/fn.post.html +[put()]: https://docs.rs/ureq/latest/ureq/fn.put.html +[Request]: https://docs.rs/ureq/latest/ureq/struct.Request.html +[Request::call()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.call +[Request::send()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send +[Request::send_bytes()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_bytes +[Request::send_string()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_string +[Request::send_json()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_json +[Request::send_form()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_form +[Response::into_json()]: https://docs.rs/ureq/latest/ureq/struct.Response.html#method.into_json +[Response::into_string()]: https://docs.rs/ureq/latest/ureq/struct.Response.html#method.into_string diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..92a944d --- /dev/null +++ b/README.tpl @@ -0,0 +1,22 @@ +[comment]: # (README.md is autogenerated from src/lib.rs by `cargo readme > README.md`) + +# {{crate}} + +{{readme}} + +[rustls]: https://docs.rs/rustls/ +[std::sync::Arc]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html +[std::io::Read]: https://doc.rust-lang.org/stable/std/io/trait.Read.html +[Agent]: https://docs.rs/ureq/latest/ureq/struct.Agent.html +[get()]: https://docs.rs/ureq/latest/ureq/fn.get.html +[post()]: https://docs.rs/ureq/latest/ureq/fn.post.html +[put()]: https://docs.rs/ureq/latest/ureq/fn.put.html +[Request]: https://docs.rs/ureq/latest/ureq/struct.Request.html +[Request::call()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.call +[Request::send()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send +[Request::send_bytes()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_bytes +[Request::send_string()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_string +[Request::send_json()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_json +[Request::send_form()]: https://docs.rs/ureq/latest/ureq/struct.Request.html#method.send_form +[Response::into_json()]: https://docs.rs/ureq/latest/ureq/struct.Response.html#method.into_json +[Response::into_string()]: https://docs.rs/ureq/latest/ureq/struct.Response.html#method.into_string diff --git a/src/lib.rs b/src/lib.rs index 3d3ae1e..e18a7b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,84 +1,136 @@ #![forbid(unsafe_code)] #![warn(clippy::all)] -//! ureq is a minimal request library. +//! A simple, safe HTTP client. //! -//! The goals of this library are: +//! Ureq's first priority is being easy for you to use. It's great for +//! anyone who wants a low-overhead HTTP client that just gets the job done. Works +//! very well with HTTP APIs. Its features include cookies, JSON, HTTP proxies, +//! HTTPS, and charset decoding. //! -//! * Minimal dependency tree -//! * Obvious API -//! * Blocking API -//! * No use of unsafe +//! Ureq is in pure Rust for safety and ease of understanding. It avoids using +//! `unsafe` directly. It [uses blocking I/O][blocking] instead of async I/O, because that keeps +//! the API simple and and keeps dependencies to a minimum. For TLS, ureq uses +//! [rustls]. //! -//! ``` -//! // requires feature: `ureq = { version = "*", features = ["json"] }` -//! # #[cfg(feature = "json")] { -//! use ureq::json; +//! [blocking]: #blocking-i-o-for-simplicity //! -//! 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 -//! })); +//! ## Usage //! -//! if let Ok(resp) = resp { -//! println!("success: {}", resp.into_string()?); -//! } else { -//! // This can include errors like failure to parse URL or connect timeout. -//! println!("error {}", resp.err().unwrap()); -//! } -//! Ok(()) -//! } +//! In its simplest form, ureq looks like this: +//! +//! ```rust +//! # fn main() -> Result<(), ureq::Error> { +//! # ureq::is_test(true); +//! let body: String = ureq::get("http://example.com") +//! .set("Accept", "text/html") +//! .call()? +//! .into_string()?; +//! # Ok(()) //! # } //! ``` //! +//! For more involved tasks, you'll want to create an [Agent]. An Agent +//! holds a connection pool for reuse, and a cookie store if you use the +//! "cookies" feature. An Agent can be cheaply cloned due to an internal +//! [Arc](std::sync::Arc) and all clones of an Agent share state among each other. Creating +//! an Agent also allows setting options like the TLS configuration. +//! +//! ```no_run +//! # fn main() -> std::result::Result<(), ureq::Error> { +//! # ureq::is_test(true); +//! use ureq::{Agent, AgentBuilder}; +//! use std::time::Duration; +//! +//! let agent: Agent = ureq::AgentBuilder::new() +//! .timeout_read(Duration::from_secs(5)) +//! .timeout_write(Duration::from_secs(5)) +//! .build(); +//! let body: String = agent.get("http://example.com/page") +//! .call()? +//! .into_string()?; +//! +//! // Reuses the connection from previous request. +//! let response: String = agent.put("http://example.com/upload") +//! .set("Authorization", "example-token") +//! .call()? +//! .into_string()?; +//! # Ok(()) +//! # } +//! ``` +//! +//! Ureq supports sending and receiving json, if you enable the "json" feature: +//! +//! ```rust +//! # #[cfg(feature = "json")] +//! # fn main() -> std::result::Result<(), ureq::Error> { +//! # ureq::is_test(true); +//! // Requires the `json` feature enabled. +//! let resp: String = ureq::post("http://myapi.example.com/ingest") +//! .set("X-My-Header", "Secret") +//! .send_json(ureq::json!({ +//! "name": "martin", +//! "rust": true +//! }))? +//! .into_string()?; +//! # Ok(()) +//! # } +//! # #[cfg(not(feature = "json"))] +//! # fn main() {} +//! ``` +//! +//! ## Features +//! +//! To enable a minimal dependency tree, some features are off by default. +//! You can control them when including ureq as a dependency. +//! +//! `ureq = { version = "*", features = ["json", "charset"] }` +//! +//! * `tls` enables https. This is enabled by default. +//! * `cookies` enables cookies. +//! * `json` enables [Response::into_json()] and [Request::send_json()] via serde_json. +//! * `charset` enables interpreting the charset part of the Content-Type header +//! (e.g. `Content-Type: text/plain; charset=iso-8859-1`). Without this, the +//! library defaults to Rust's built in `utf-8`. +//! //! # Plain requests //! //! Most standard methods (GET, POST, PUT etc), are supported as functions from the -//! top of the library ([`ureq::get`](fn.get.html), [`ureq::post`](fn.post.html), -//! [`ureq::put`](fn.put.html), etc). +//! top of the library ([get()], [post()], [put()], etc). //! -//! These top level http method functions create a [Request](struct.Request.html) instance +//! These top level http method functions create a [Request] instance //! 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` (chunked encoding support for non-known sized readers). -//! * [`.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. +//! * [`.call()`][Request::call()] without a request body. +//! * [`.send()`][Request::send()] with a request body as [Read][std::io::Read] (chunked encoding support for non-known sized readers). +//! * [`.send_string()`][Request::send_string()] body as string. +//! * [`.send_bytes()`][Request::send_bytes()] body as bytes. +//! * [`.send_form()`][Request::send_form()] key-value pairs as application/x-www-form-urlencoded. //! //! # JSON //! //! By enabling the `ureq = { version = "*", features = ["json"] }` feature, //! the library supports serde json. //! -//! * [`request.send_json()`](struct.Request.html#method.send_json) send body as serde json. -//! * [`response.into_json()`](struct.Response.html#method.into_json) transform response to json. +//! * [`request.send_json()`][Request::send_json()] send body as serde json. +//! * [`response.into_json()`][Response::into_json()] transform response to json. //! -//! # Agents +//! # Content-Length and Transfer-Encoding //! -//! To maintain a state, cookies, between requests, you use an [agent](struct.Agent.html). -//! Agents also follow the build pattern. Agents are created with -//! [`ureq::agent()`](struct.Agent.html). +//! The library will send a Content-Length header on requests with bodies of +//! known size, in other words, those sent with +//! [`.send_string()`][Request::send_string()], +//! [`.send_bytes()`][Request::send_bytes()], +//! [`.send_form()`][Request::send_form()], or +//! [`.send_json()`][Request::send_json()]. If you send a +//! request body with [`.send()`][Request::send()], +//! which takes a [Read][std::io::Read] of unknown size, ureq will send Transfer-Encoding: +//! chunked, and encode the body accordingly. Bodyless requests +//! (GETs and HEADs) are sent with [`.call()`][Request::call()] +//! and ureq adds neither a Content-Length nor a Transfer-Encoding header. //! -//! # Content-Length -//! -//! The library will set the content length on the request when using -//! [`.send_string()`](struct.Request.html#method.send_string) or -//! [`.send_json()`](struct.Request.html#method.send_json). In other cases the user -//! can optionally `request.set("Content-Length", 1234)`. -//! -//! For responses, if the `Content-Length` header is present, the methods that reads the -//! body (as string, json or read trait) are all limited to the length specified in the header. -//! -//! # Transfer-Encoding: chunked -//! -//! Dechunking is a response body is done automatically if the response headers contains -//! a `Transfer-Encoding` header. -//! -//! Sending a chunked request body is done by setting the header prior to sending a body. +//! If you set your own Content-Length or Transfer-Encoding header before +//! sending the body, ureq will respect that header by not overriding it, +//! and by encoding the body or not, as indicated by the headers you set. //! //! ``` //! let resp = ureq::post("http://my-server.com/ingest") @@ -91,15 +143,52 @@ //! By enabling the `ureq = { version = "*", features = ["charset"] }` feature, //! the library supports sending/receiving other character sets than `utf-8`. //! -//! For [`response.into_string()`](struct.Response.html#method.into_string) we read the +//! For [`response.into_string()`][Response::into_string()] we read the //! header `Content-Type: text/plain; charset=iso-8859-1` and if it contains a charset //! specification, we try to decode the body using that encoding. In the absence of, or failing //! to interpret the charset, we fall back on `utf-8`. //! -//! Similarly when using [`request.send_string()`](struct.Request.html#method.send_string), +//! Similarly when using [`request.send_string()`][Request::send_string()], //! we first check if the user has set a `; charset=` and attempt //! to encode the request body using that. //! +//! # Blocking I/O for simplicity +//! +//! Rust supports [asynchronous (async) I/O][async], but ureq does not use it. Async I/O +//! allows serving many concurrent requests without high costs in memory and OS threads. But +//! it comes at a cost in complexity. Async programs need to pull in a runtime (usually +//! [async-std] or [tokio]). They also need async variants of any method that might block, and of +//! [any method that might call another method that might block][what-color]. That means async +//! programs usually have a lot of dependencies - which adds to compile times, and increases +//! risk. +//! +//! The costs of async are worth paying, if you're writing an HTTP server that must serve +//! many many clients with minimal overhead. However, for HTTP _clients_, we believe that the +//! cost is usually not worth paying. The low-cost alternative to async I/O is blocking I/O, +//! which has a different price: it requires an OS thread per concurrent request. However, +//! that price is usually not high: most HTTP clients make requests sequentially, or with +//! low concurrency. +//! +//! That's why ureq uses blocking I/O and plans to stay that way. Other HTTP clients offer both +//! an async API and a blocking API, but we want to offer a blocking API without pulling in all +//! the dependencies required by an async API. +//! +//! [async]: https://rust-lang.github.io/async-book/01_getting_started/02_why_async.html +//! [async-std]: https://github.com/async-rs/async-std#async-std +//! [tokio]: https://github.com/tokio-rs/tokio#tokio +//! [what-color]: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ +//! +//! ------------------------------------------------------------------------------ +//! +//! Ureq is inspired by other great HTTP clients like +//! [superagent](http://visionmedia.github.io/superagent/) and +//! [the fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). +//! +//! If ureq is not what you're looking for, check out these other Rust HTTP clients: +//! [surf](https://crates.io/crates/surf), [reqwest](https://crates.io/crates/reqwest), +//! [isahc](https://crates.io/crates/isahc), [attohttpc](https://crates.io/crates/attohttpc), +//! [actix-web](https://crates.io/crates/actix-web), and [hyper](https://crates.io/crates/hyper). +//! mod agent; mod body; diff --git a/src/request.rs b/src/request.rs index 67e2db5..d0fe8e6 100644 --- a/src/request.rs +++ b/src/request.rs @@ -58,7 +58,10 @@ impl Request { } } - /// Executes the request and blocks the caller until done. + /// Sends the request with no body and blocks the caller until done. + /// + /// Use this with GET, HEAD, or TRACE. It sends neither Content-Length + /// nor Transfer-Encoding. /// /// ``` /// # fn main() -> Result<(), ureq::Error> {