http_adapter/
lib.rs

1//! # Adapter for HTTP client agnostic requests
2//!
3//! This crate allows the async libraries to be HTTP client agnostic and not force a particular choice of an async runtime because
4//! of it. It is mostly useful when you develop a crate that provides access to an HTTP API.
5//!
6//! If you need to do somewhat simple HTTP requests (multiple HTTP methods, header/cookie management is included), want to be HTTP
7//! client agnostic and not lock the downstream users to a particular async runtime (think `tokio`, `async-std`, `smol`) then you
8//! need to make sure that your API client accepts an instance of the generic type implementing [`HttpClientAdapter`] and use it
9//! to make HTTP requests. Users of your crate will then use a particular adapter based on the actual HTTP client (e.g. `request`,
10//! `ureq`, `surf`, etc.) and supply it when creating your API client.
11//!
12//! # Usage
13//!
14//! The [`HttpClientAdapter`] trait exposes a single async function [`HttpClientAdapter::execute()`]. It takes an instance of
15//! [`http::Request`] that encodes the necessary request parameters like HTTP method and URL and executes it. Then it returns an
16//! instance of [`http::Response`] with the server response. The request and response types come from the
17//! [`http`](https://crates.io/crates/http) crate. The body of the request and response are expected to be `Vec<u8>`.
18//!
19//! # Adapter implementation
20//!
21//! To create a new implementation of [`HttpClientAdapter`] for an HTTP client library please refer to the following crates:
22//!   * [`http-adapter-reqwest`][1] - async wrapper, simple case because `reqwest` is using `http` types internally
23//!   * [`http-adapter-surf`][2] - async wrapper, more complicated case because of the need to convert types
24//!   * [`http-adapter-ureq`][3] - sync wrapper, complex case because of the need to wrap a sync client in an runtime-agnostic fashion
25//!
26//! # Simple APIClient example
27//!
28//! ```
29//! use http_adapter::http::Request;
30//! use http_adapter::HttpClientAdapter;
31//!
32//! struct APIClient<HttpClient> {
33//!     http_client: HttpClient,
34//! }
35//!
36//! impl<HttpClient: HttpClientAdapter> APIClient<HttpClient> {
37//!     /// Create new `APIClient` by supplying an HTTP client implementation
38//!     pub fn new(http_client: HttpClient) -> Self {
39//!         Self { http_client }
40//!     }
41//!
42//!     pub async fn create_entry(&self) -> Result<(), HttpClient::Error> {
43//!         let request = Request::post("http://localhost")
44//!             .header(http::header::AUTHORIZATION, "Bearer 12345")
45//!             .body(r#"{ "value": 42 }"#.as_bytes().to_vec())
46//!             .expect("Can't create request");
47//!         let response = self.http_client.execute(request).await?;
48//!         Ok(())
49//!     }
50//! }
51//!
52//! /// Default implementation for cases where adapter implements `Default`
53//! impl<HttpClient: HttpClientAdapter + Default> Default for APIClient<HttpClient> {
54//!     fn default() -> Self {
55//!         Self::new(HttpClient::default())
56//!     }
57//! }
58//! ```
59//!
60//! [1]: <https://crates.io/crates/http-adapter-reqwest>
61//! [2]: <https://crates.io/crates/http-adapter-surf>
62//! [3]: <https://crates.io/crates/http-adapter-ureq>
63
64pub use async_trait;
65pub use http;
66pub use http::{Request, Response};
67
68/// Adapter to allow different HTTP clients to be used to issue an HTTP request.
69/// To properly implement this trait, use [async_trait](https://crates.io/crates/async-trait).
70#[async_trait::async_trait]
71pub trait HttpClientAdapter {
72	/// Error type used by the underlying HTTP library
73	type Error;
74
75	/// Fetch the specified URL using the specified request method
76	///
77	/// Returns the text contents of the resource located at the indicated URL
78	async fn execute(&self, request: Request<Vec<u8>>) -> Result<Response<Vec<u8>>, Self::Error>;
79}