How To Make HTTP/HTTPS Web Requests in Rust
Introduction
This tutorial will show you how to send web requests in Rust over HTTP/HTTPS using the GET and POST HTTP methods through the Reqwest and Hyper crates. Building web requests forms the backbone of interacting with many different services such as Application Programming Interfaces (API’s), Artificial Intelligence (AI), websites, microservices, connected devices such as Internet-of-Things (IoT) devices, as well as a plethora of cloud native services on popular public clouds such as Amazon Web Services (AWS), Microsoft Azure, and Google Cloud.
The Hyper and reqwest crates are powerful and popular Rust programming language libraries that empower developers with streamlined and efficient tools for making HTTP requests and building network applications. Hyper, a versatile HTTP library, equips developers with a robust foundation for constructing custom web clients and servers, fostering enhanced control over the intricacies of the HTTP protocol. Its well-designed architecture and comprehensive feature set, including support for asynchronous operations, make it a go-to choice for crafting high-performance networking solutions in Rust. On the other hand, reqwest, a user-friendly HTTP client built on top of Hyper, offers a simplified yet potent interface for effortlessly crafting and executing HTTP requests. This crate's intuitive design and seamless integration with asynchronous programming patterns empower developers to create responsive and scalable applications while minimizing the complexity of handling network interactions. By utilizing these crates, Rust developers can optimize their code for performance, reliability, and maintainability in the realm of web communication and networking, bolstering their competitive edge in modern software development landscapes. Let’s explore some key differences between the reqwest and hyper crate.
The reqwest crate offers a higher-level HTTP client. By using request you sacrifice control for convenience. The reqwest crate can get you up and running making HTTP requests in Rust with ease without dealing with the minutia.
The hyper crate is a low-level crate that allows developers more granular control. This crate is ideal for building libraries and applications.
Asynchronous and Synchronous Requests
The request crate offers ways to make both synchronous and asynchronous requests while the hyper crate is asynchronous.
In asynchronous programming, multiple tasks can run concurrently on one thread.
In synchronous programming tasks are queued and each task remains idle while the first one completes. A synchronous operation is also referred to as a blocking operation as it blocks the thread until completion.
Both crates utilize the tokio asynchronous runtime for rust for its asynchronous operations
async, the Future trait, & .await
The async keyword transforms a rust function into an asynchronous function. In rust asynchronous functions implement the Future trait. A Future is an asynchronous computation that can produce a value (although that value may be empty, e.g. ()
). If we call .await on a Future the code will attempt to run to completion.
Reqwest
First I will show you how to use the reqwest crate in order to make various HTTP requests. Be sure to have the following dependencies in your Cargo.toml file!
Reqwest - Synchronous (Blocking) GET Request
The request::blocking module will block the current thread to execute.
Reqwest - Asynchronous GET Request
As we’ve covered previously an asynchronous request allows us to make concurrent requests on a single thread. We will be utilizing the tokio asynchronous runtime for rust in order to implement this denoted by our #[tokio::main] macro declaration. Notice we’ve also added the async keyword before our main function.
Reqwest - Synchronous (Blocking) POST Request
In this example, we will be making a synchronous POST request using the reqwest library. We will be utilizing the json helper method provided by reqwest to POST JSON to a web server.
Reqwest - Asynchronous POST Request
If you need more granular control over your requests at a lower level then hyper is for you. Hyber does not have a blocking method we can use like reqwests so we are full on asynchronous here. Be sure to have the following dependencies in your Cargo.toml file!
Hyper - GET Request
First, we are utilizing the hyper::client module which is the main way to send HTTP requests with hyper.
Second, we are leveraging the hyper::Request::builder() constructor method to create an object to manufacture our request. We can use the builder to set things such as methods, headers, body, etc. Here we are setting the the-awesome-agent/007 as our user-agent.
Third, we pass our hyper::Request::builder() object to our client which dispatches our constructed request.
Since we are in an async function that gives us a Future we can simply .await the future to resolve the response.
Lastly, we have the response body to resolve. One caveat to using hyper is that we are responsible for processing the response body. The response body in hyper is streamed asynchronously. To get the response body we can consume the request by returning the body with the hyper::Request::into_body() method and passing it to the hyper::body::to_bytes() method which concatenates the buffer from a body into a single Bytes object. We can simply .await the full response to have access to the completed response.
Hyper - POST Request
In those POST example we are adding the content-type: application/json header to our request by passing it to the hyper::Request::builder() constructor method. Then we pass in the JSON body to the body with hyper::Body::from().
Conclusion
All the examples in this blog post can be downloaded from the rust-webrequest-samples Github repo. I hope you found this post helpful on your journey in learning the many interesting features and crates in the rust ecosystem. If you have any questions feel free to contact me or leave a comment below. How will you use web requests in rust?