mirror of
https://github.com/TrueLayer/reqwest-middleware.git
synced 2024-12-26 10:56:32 +00:00
reqwest 0.12 and other breaking changes (#135)
* update reqwest and http crates remove task_local_extensions * remove older opentelemetry packages * remove more legacy and add new otel * attempt to make features additive * features are additive * delete commented out code * build split * docs * more uniform with reqwest::Client * remove arcs * slight optimisation * update readmes * update changelog
This commit is contained in:
parent
69269e183a
commit
60212ae451
20 changed files with 516 additions and 282 deletions
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
|
@ -12,13 +12,6 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
otel_version:
|
otel_version:
|
||||||
- opentelemetry_0_13
|
|
||||||
- opentelemetry_0_14
|
|
||||||
- opentelemetry_0_15
|
|
||||||
- opentelemetry_0_16
|
|
||||||
- opentelemetry_0_17
|
|
||||||
- opentelemetry_0_18
|
|
||||||
- opentelemetry_0_19
|
|
||||||
- opentelemetry_0_20
|
- opentelemetry_0_20
|
||||||
- opentelemetry_0_21
|
- opentelemetry_0_21
|
||||||
- opentelemetry_0_22
|
- opentelemetry_0_22
|
||||||
|
@ -61,13 +54,6 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
otel_version:
|
otel_version:
|
||||||
- opentelemetry_0_13
|
|
||||||
- opentelemetry_0_14
|
|
||||||
- opentelemetry_0_15
|
|
||||||
- opentelemetry_0_16
|
|
||||||
- opentelemetry_0_17
|
|
||||||
- opentelemetry_0_18
|
|
||||||
- opentelemetry_0_19
|
|
||||||
- opentelemetry_0_20
|
- opentelemetry_0_20
|
||||||
- opentelemetry_0_21
|
- opentelemetry_0_21
|
||||||
- opentelemetry_0_22
|
- opentelemetry_0_22
|
||||||
|
@ -102,13 +88,6 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
otel_version:
|
otel_version:
|
||||||
- opentelemetry_0_13
|
|
||||||
- opentelemetry_0_14
|
|
||||||
- opentelemetry_0_15
|
|
||||||
- opentelemetry_0_16
|
|
||||||
- opentelemetry_0_17
|
|
||||||
- opentelemetry_0_18
|
|
||||||
- opentelemetry_0_19
|
|
||||||
- opentelemetry_0_20
|
- opentelemetry_0_20
|
||||||
- opentelemetry_0_21
|
- opentelemetry_0_21
|
||||||
- opentelemetry_0_22
|
- opentelemetry_0_22
|
||||||
|
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
- Updated `reqwest` to `0.12.0`.
|
||||||
|
- Removed `task_local_extensions` in favour of `http::Extensions`.
|
||||||
|
- All extensions must be `Clone` now.
|
||||||
|
- Replaced `with_arc` and `with_arc_init` methods with `with_box` and `with_box_init`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `RequestBuilder::try_clone` now clones the extensions.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- implemented `Service` for `ClientWithMiddleware` to have more feature parity with `reqwest`.
|
||||||
|
- Added more methods like `build_split` to have more feature parity with `reqwest.`
|
||||||
|
- Added a lot more documentation
|
||||||
|
|
||||||
### [0.2.5] - 2024-03-15
|
### [0.2.5] - 2024-03-15
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
10
README.md
10
README.md
|
@ -29,11 +29,11 @@ The `reqwest-middleware` client exposes the same interface as a plain `reqwest`
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
# ...
|
# ...
|
||||||
[dependencies]
|
[dependencies]
|
||||||
reqwest = "0.11"
|
reqwest = { version = "0.12", features = ["rustls-tls"] }
|
||||||
reqwest-middleware = "0.1.6"
|
reqwest-middleware = "0.3"
|
||||||
reqwest-retry = "0.1.5"
|
reqwest-retry = "0.5"
|
||||||
reqwest-tracing = "0.2.3"
|
reqwest-tracing = "0.5"
|
||||||
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "reqwest-middleware"
|
name = "reqwest-middleware"
|
||||||
version = "0.2.5"
|
version = "0.3.0"
|
||||||
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Wrapper around reqwest to allow for client middleware chains."
|
description = "Wrapper around reqwest to allow for client middleware chains."
|
||||||
|
@ -10,17 +10,21 @@ keywords = ["reqwest", "http", "middleware"]
|
||||||
categories = ["web-programming::http-client"]
|
categories = ["web-programming::http-client"]
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
multipart = ["reqwest/multipart"]
|
||||||
|
json = ["reqwest/json"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.0"
|
anyhow = "1.0.0"
|
||||||
async-trait = "0.1.51"
|
async-trait = "0.1.51"
|
||||||
http = "0.2.0"
|
http = "1.0.0"
|
||||||
reqwest = { version = "0.11.10", default-features = false, features = ["json", "multipart"] }
|
reqwest = { version = "0.12.0", default-features = false }
|
||||||
serde = "1.0.106"
|
serde = "1.0.106"
|
||||||
task-local-extensions = "0.1.4"
|
|
||||||
thiserror = "1.0.21"
|
thiserror = "1.0.21"
|
||||||
|
tower-service = "0.3.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
reqwest-retry = { path = "../reqwest-retry" }
|
reqwest-retry = { path = "../reqwest-retry" }
|
||||||
reqwest-tracing = { path = "../reqwest-tracing" }
|
reqwest-tracing = { path = "../reqwest-tracing" }
|
||||||
tokio = { version = "1.0.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.0.0", features = ["macros", "rt-multi-thread"] }
|
||||||
wiremock = "0.5.0"
|
wiremock = "0.6.0"
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
use http::Extensions;
|
||||||
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||||
use reqwest::multipart::Form;
|
|
||||||
use reqwest::{Body, Client, IntoUrl, Method, Request, Response};
|
use reqwest::{Body, Client, IntoUrl, Method, Request, Response};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use task_local_extensions::Extensions;
|
|
||||||
|
#[cfg(feature = "multipart")]
|
||||||
|
use reqwest::multipart;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::middleware::{Middleware, Next};
|
use crate::middleware::{Middleware, Next};
|
||||||
|
@ -16,8 +18,8 @@ use crate::RequestInitialiser;
|
||||||
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
|
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
|
||||||
pub struct ClientBuilder {
|
pub struct ClientBuilder {
|
||||||
client: Client,
|
client: Client,
|
||||||
middleware_stack: Vec<Arc<dyn Middleware>>,
|
middleware_stack: Vec<Box<dyn Middleware>>,
|
||||||
initialiser_stack: Vec<Arc<dyn RequestInitialiser>>,
|
initialiser_stack: Vec<Box<dyn RequestInitialiser>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientBuilder {
|
impl ClientBuilder {
|
||||||
|
@ -31,40 +33,40 @@ impl ClientBuilder {
|
||||||
|
|
||||||
/// Convenience method to attach middleware.
|
/// Convenience method to attach middleware.
|
||||||
///
|
///
|
||||||
/// If you need to keep a reference to the middleware after attaching, use [`with_arc`].
|
/// If you need to keep a reference to the middleware after attaching, use [`with_box`].
|
||||||
///
|
///
|
||||||
/// [`with_arc`]: Self::with_arc
|
/// [`with_box`]: Self::with_box
|
||||||
pub fn with<M>(self, middleware: M) -> Self
|
pub fn with<M>(self, middleware: M) -> Self
|
||||||
where
|
where
|
||||||
M: Middleware,
|
M: Middleware,
|
||||||
{
|
{
|
||||||
self.with_arc(Arc::new(middleware))
|
self.with_box(Box::new(middleware))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add middleware to the chain. [`with`] is more ergonomic if you don't need the `Arc`.
|
/// Add middleware to the chain. [`with`] is more ergonomic if you don't need the `Box`.
|
||||||
///
|
///
|
||||||
/// [`with`]: Self::with
|
/// [`with`]: Self::with
|
||||||
pub fn with_arc(mut self, middleware: Arc<dyn Middleware>) -> Self {
|
pub fn with_box(mut self, middleware: Box<dyn Middleware>) -> Self {
|
||||||
self.middleware_stack.push(middleware);
|
self.middleware_stack.push(middleware);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method to attach a request initialiser.
|
/// Convenience method to attach a request initialiser.
|
||||||
///
|
///
|
||||||
/// If you need to keep a reference to the initialiser after attaching, use [`with_arc_init`].
|
/// If you need to keep a reference to the initialiser after attaching, use [`with_box_init`].
|
||||||
///
|
///
|
||||||
/// [`with_arc_init`]: Self::with_arc_init
|
/// [`with_box_init`]: Self::with_box_init
|
||||||
pub fn with_init<I>(self, initialiser: I) -> Self
|
pub fn with_init<I>(self, initialiser: I) -> Self
|
||||||
where
|
where
|
||||||
I: RequestInitialiser,
|
I: RequestInitialiser,
|
||||||
{
|
{
|
||||||
self.with_arc_init(Arc::new(initialiser))
|
self.with_box_init(Box::new(initialiser))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a request initialiser to the chain. [`with_init`] is more ergonomic if you don't need the `Arc`.
|
/// Add a request initialiser to the chain. [`with_init`] is more ergonomic if you don't need the `Box`.
|
||||||
///
|
///
|
||||||
/// [`with_init`]: Self::with_init
|
/// [`with_init`]: Self::with_init
|
||||||
pub fn with_arc_init(mut self, initialiser: Arc<dyn RequestInitialiser>) -> Self {
|
pub fn with_box_init(mut self, initialiser: Box<dyn RequestInitialiser>) -> Self {
|
||||||
self.initialiser_stack.push(initialiser);
|
self.initialiser_stack.push(initialiser);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -73,8 +75,8 @@ impl ClientBuilder {
|
||||||
pub fn build(self) -> ClientWithMiddleware {
|
pub fn build(self) -> ClientWithMiddleware {
|
||||||
ClientWithMiddleware {
|
ClientWithMiddleware {
|
||||||
inner: self.client,
|
inner: self.client,
|
||||||
middleware_stack: self.middleware_stack.into_boxed_slice(),
|
middleware_stack: self.middleware_stack.into(),
|
||||||
initialiser_stack: self.initialiser_stack.into_boxed_slice(),
|
initialiser_stack: self.initialiser_stack.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,73 +86,127 @@ impl ClientBuilder {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ClientWithMiddleware {
|
pub struct ClientWithMiddleware {
|
||||||
inner: reqwest::Client,
|
inner: reqwest::Client,
|
||||||
middleware_stack: Box<[Arc<dyn Middleware>]>,
|
middleware_stack: Arc<[Box<dyn Middleware>]>,
|
||||||
initialiser_stack: Box<[Arc<dyn RequestInitialiser>]>,
|
initialiser_stack: Arc<[Box<dyn RequestInitialiser>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientWithMiddleware {
|
impl ClientWithMiddleware {
|
||||||
/// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances.
|
/// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances.
|
||||||
pub fn new<T>(client: Client, middleware_stack: T) -> Self
|
pub fn new<T>(client: Client, middleware_stack: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<Box<[Arc<dyn Middleware>]>>,
|
T: Into<Arc<[Box<dyn Middleware>]>>,
|
||||||
{
|
{
|
||||||
ClientWithMiddleware {
|
ClientWithMiddleware {
|
||||||
inner: client,
|
inner: client,
|
||||||
middleware_stack: middleware_stack.into(),
|
middleware_stack: middleware_stack.into(),
|
||||||
// TODO(conradludgate) - allow downstream code to control this manually if desired
|
// TODO(conradludgate) - allow downstream code to control this manually if desired
|
||||||
initialiser_stack: Box::new([]),
|
initialiser_stack: Arc::from(vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::get`]
|
/// Convenience method to make a `GET` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
self.request(Method::GET, url)
|
self.request(Method::GET, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::post`]
|
/// Convenience method to make a `POST` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
self.request(Method::POST, url)
|
self.request(Method::POST, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::put`]
|
/// Convenience method to make a `PUT` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
self.request(Method::PUT, url)
|
self.request(Method::PUT, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::patch`]
|
/// Convenience method to make a `PATCH` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
self.request(Method::PATCH, url)
|
self.request(Method::PATCH, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::delete`]
|
/// Convenience method to make a `DELETE` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
self.request(Method::DELETE, url)
|
self.request(Method::DELETE, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::head`]
|
/// Convenience method to make a `HEAD` request to a URL.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
|
||||||
self.request(Method::HEAD, url)
|
self.request(Method::HEAD, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::request`]
|
/// Start building a `Request` with the `Method` and `Url`.
|
||||||
|
///
|
||||||
|
/// Returns a `RequestBuilder`, which will allow setting headers and
|
||||||
|
/// the request body before sending.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails whenever the supplied `Url` cannot be parsed.
|
||||||
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
|
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
|
||||||
let req = RequestBuilder {
|
let req = RequestBuilder {
|
||||||
inner: self.inner.request(method, url),
|
inner: self.inner.request(method, url),
|
||||||
client: self.clone(),
|
|
||||||
extensions: Extensions::new(),
|
extensions: Extensions::new(),
|
||||||
|
middleware_stack: self.middleware_stack.clone(),
|
||||||
|
initialiser_stack: self.initialiser_stack.clone(),
|
||||||
};
|
};
|
||||||
self.initialiser_stack
|
self.initialiser_stack
|
||||||
.iter()
|
.iter()
|
||||||
.fold(req, |req, i| i.init(req))
|
.fold(req, |req, i| i.init(req))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`Client::execute`]
|
/// Executes a `Request`.
|
||||||
|
///
|
||||||
|
/// A `Request` can be built manually with `Request::new()` or obtained
|
||||||
|
/// from a RequestBuilder with `RequestBuilder::build()`.
|
||||||
|
///
|
||||||
|
/// You should prefer to use the `RequestBuilder` and
|
||||||
|
/// `RequestBuilder::send()`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails if there was an error while sending request,
|
||||||
|
/// redirect loop was detected or redirect limit was exhausted.
|
||||||
pub async fn execute(&self, req: Request) -> Result<Response> {
|
pub async fn execute(&self, req: Request) -> Result<Response> {
|
||||||
let mut ext = Extensions::new();
|
let mut ext = Extensions::new();
|
||||||
self.execute_with_extensions(req, &mut ext).await
|
self.execute_with_extensions(req, &mut ext).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a request with initial [`Extensions`].
|
/// Executes a `Request` with initial [`Extensions`].
|
||||||
|
///
|
||||||
|
/// A `Request` can be built manually with `Request::new()` or obtained
|
||||||
|
/// from a RequestBuilder with `RequestBuilder::build()`.
|
||||||
|
///
|
||||||
|
/// You should prefer to use the `RequestBuilder` and
|
||||||
|
/// `RequestBuilder::send()`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails if there was an error while sending request,
|
||||||
|
/// redirect loop was detected or redirect limit was exhausted.
|
||||||
pub async fn execute_with_extensions(
|
pub async fn execute_with_extensions(
|
||||||
&self,
|
&self,
|
||||||
req: Request,
|
req: Request,
|
||||||
|
@ -166,8 +222,8 @@ impl From<Client> for ClientWithMiddleware {
|
||||||
fn from(client: Client) -> Self {
|
fn from(client: Client) -> Self {
|
||||||
ClientWithMiddleware {
|
ClientWithMiddleware {
|
||||||
inner: client,
|
inner: client,
|
||||||
middleware_stack: Box::new([]),
|
middleware_stack: Arc::from(vec![]),
|
||||||
initialiser_stack: Box::new([]),
|
initialiser_stack: Arc::from(vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,15 +237,101 @@ impl fmt::Debug for ClientWithMiddleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod service {
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
use http::Extensions;
|
||||||
|
use reqwest::{Request, Response};
|
||||||
|
|
||||||
|
use crate::{middleware::BoxFuture, ClientWithMiddleware, Next};
|
||||||
|
|
||||||
|
// this is meant to be semi-private, same as reqwest's pending
|
||||||
|
pub struct Pending {
|
||||||
|
inner: BoxFuture<'static, Result<Response>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unpin for Pending {}
|
||||||
|
|
||||||
|
impl Future for Pending {
|
||||||
|
type Output = Result<Response>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
self.inner.as_mut().poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl tower_service::Service<Request> for ClientWithMiddleware {
|
||||||
|
type Response = Response;
|
||||||
|
type Error = crate::Error;
|
||||||
|
type Future = Pending;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||||
|
self.inner.poll_ready(cx).map_err(crate::Error::Reqwest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request) -> Self::Future {
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
let middlewares = self.middleware_stack.clone();
|
||||||
|
let mut extensions = Extensions::new();
|
||||||
|
Pending {
|
||||||
|
inner: Box::pin(async move {
|
||||||
|
let next = Next::new(&inner, &middlewares);
|
||||||
|
next.run(req, &mut extensions).await
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl tower_service::Service<Request> for &'_ ClientWithMiddleware {
|
||||||
|
type Response = Response;
|
||||||
|
type Error = crate::Error;
|
||||||
|
type Future = Pending;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||||
|
(&self.inner).poll_ready(cx).map_err(crate::Error::Reqwest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request) -> Self::Future {
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
let middlewares = self.middleware_stack.clone();
|
||||||
|
let mut extensions = Extensions::new();
|
||||||
|
Pending {
|
||||||
|
inner: Box::pin(async move {
|
||||||
|
let next = Next::new(&inner, &middlewares);
|
||||||
|
next.run(req, &mut extensions).await
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is a wrapper around [`reqwest::RequestBuilder`] exposing the same API.
|
/// This is a wrapper around [`reqwest::RequestBuilder`] exposing the same API.
|
||||||
#[must_use = "RequestBuilder does nothing until you 'send' it"]
|
#[must_use = "RequestBuilder does nothing until you 'send' it"]
|
||||||
pub struct RequestBuilder {
|
pub struct RequestBuilder {
|
||||||
inner: reqwest::RequestBuilder,
|
inner: reqwest::RequestBuilder,
|
||||||
client: ClientWithMiddleware,
|
middleware_stack: Arc<[Box<dyn Middleware>]>,
|
||||||
|
initialiser_stack: Arc<[Box<dyn RequestInitialiser>]>,
|
||||||
extensions: Extensions,
|
extensions: Extensions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequestBuilder {
|
impl RequestBuilder {
|
||||||
|
/// Assemble a builder starting from an existing `Client` and a `Request`.
|
||||||
|
pub fn from_parts(client: ClientWithMiddleware, request: Request) -> RequestBuilder {
|
||||||
|
let inner = reqwest::RequestBuilder::from_parts(client.inner, request);
|
||||||
|
RequestBuilder {
|
||||||
|
inner,
|
||||||
|
middleware_stack: client.middleware_stack,
|
||||||
|
initialiser_stack: client.initialiser_stack,
|
||||||
|
extensions: Extensions::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a `Header` to this Request.
|
||||||
pub fn header<K, V>(self, key: K, value: V) -> Self
|
pub fn header<K, V>(self, key: K, value: V) -> Self
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
|
@ -203,6 +345,9 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a set of Headers to the existing ones on this Request.
|
||||||
|
///
|
||||||
|
/// The headers will be merged in to any already set.
|
||||||
pub fn headers(self, headers: HeaderMap) -> Self {
|
pub fn headers(self, headers: HeaderMap) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.headers(headers),
|
inner: self.inner.headers(headers),
|
||||||
|
@ -218,6 +363,20 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable HTTP basic authentication.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use anyhow::Error;
|
||||||
|
///
|
||||||
|
/// # async fn run() -> Result<(), Error> {
|
||||||
|
/// let client = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new());
|
||||||
|
/// let resp = client.delete("http://httpbin.org/delete")
|
||||||
|
/// .basic_auth("admin", Some("good password"))
|
||||||
|
/// .send()
|
||||||
|
/// .await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
|
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
|
||||||
where
|
where
|
||||||
U: Display,
|
U: Display,
|
||||||
|
@ -229,6 +388,7 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable HTTP bearer authentication.
|
||||||
pub fn bearer_auth<T>(self, token: T) -> Self
|
pub fn bearer_auth<T>(self, token: T) -> Self
|
||||||
where
|
where
|
||||||
T: Display,
|
T: Display,
|
||||||
|
@ -239,6 +399,7 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the request body.
|
||||||
pub fn body<T: Into<Body>>(self, body: T) -> Self {
|
pub fn body<T: Into<Body>>(self, body: T) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.body(body),
|
inner: self.inner.body(body),
|
||||||
|
@ -246,7 +407,11 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
/// Enables a request timeout.
|
||||||
|
///
|
||||||
|
/// The timeout is applied from when the request starts connecting until the
|
||||||
|
/// response body has finished. It affects only this request and overrides
|
||||||
|
/// the timeout configured using `ClientBuilder::timeout()`.
|
||||||
pub fn timeout(self, timeout: std::time::Duration) -> Self {
|
pub fn timeout(self, timeout: std::time::Duration) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.timeout(timeout),
|
inner: self.inner.timeout(timeout),
|
||||||
|
@ -254,13 +419,33 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multipart(self, multipart: Form) -> Self {
|
#[cfg(feature = "multipart")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
|
||||||
|
pub fn multipart(self, multipart: multipart::Form) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.multipart(multipart),
|
inner: self.inner.multipart(multipart),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modify the query string of the URL.
|
||||||
|
///
|
||||||
|
/// Modifies the URL of this request, adding the parameters provided.
|
||||||
|
/// This method appends and does not overwrite. This means that it can
|
||||||
|
/// be called multiple times and that existing query parameters are not
|
||||||
|
/// overwritten if the same key is used. The key will simply show up
|
||||||
|
/// twice in the query string.
|
||||||
|
/// Calling `.query(&[("foo", "a"), ("foo", "b")])` gives `"foo=a&foo=b"`.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This method does not support serializing a single key-value
|
||||||
|
/// pair. Instead of using `.query(("key", "val"))`, use a sequence, such
|
||||||
|
/// as `.query(&[("key", "val")])`. It's also possible to serialize structs
|
||||||
|
/// and maps into a key-value pair.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// This method will fail if the object you provide cannot be serialized
|
||||||
|
/// into a query string.
|
||||||
pub fn query<T: Serialize + ?Sized>(self, query: &T) -> Self {
|
pub fn query<T: Serialize + ?Sized>(self, query: &T) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.query(query),
|
inner: self.inner.query(query),
|
||||||
|
@ -268,6 +453,33 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a form body.
|
||||||
|
///
|
||||||
|
/// Sets the body to the url encoded serialization of the passed value,
|
||||||
|
/// and also sets the `Content-Type: application/x-www-form-urlencoded`
|
||||||
|
/// header.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use anyhow::Error;
|
||||||
|
/// # use std::collections::HashMap;
|
||||||
|
/// #
|
||||||
|
/// # async fn run() -> Result<(), Error> {
|
||||||
|
/// let mut params = HashMap::new();
|
||||||
|
/// params.insert("lang", "rust");
|
||||||
|
///
|
||||||
|
/// let client = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new());
|
||||||
|
/// let res = client.post("http://httpbin.org")
|
||||||
|
/// .form(¶ms)
|
||||||
|
/// .send()
|
||||||
|
/// .await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This method fails if the passed value cannot be serialized into
|
||||||
|
/// url encoded format
|
||||||
pub fn form<T: Serialize + ?Sized>(self, form: &T) -> Self {
|
pub fn form<T: Serialize + ?Sized>(self, form: &T) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.form(form),
|
inner: self.inner.form(form),
|
||||||
|
@ -275,6 +487,18 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a JSON body.
|
||||||
|
///
|
||||||
|
/// # Optional
|
||||||
|
///
|
||||||
|
/// This requires the optional `json` feature enabled.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Serialization can fail if `T`'s implementation of `Serialize` decides to
|
||||||
|
/// fail, or if `T` contains a map with non-string keys.
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
|
||||||
pub fn json<T: Serialize + ?Sized>(self, json: &T) -> Self {
|
pub fn json<T: Serialize + ?Sized>(self, json: &T) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.json(json),
|
inner: self.inner.json(json),
|
||||||
|
@ -282,6 +506,15 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable CORS on fetching the request.
|
||||||
|
///
|
||||||
|
/// # WASM
|
||||||
|
///
|
||||||
|
/// This option is only effective with WebAssembly target.
|
||||||
|
///
|
||||||
|
/// The [request mode][mdn] will be set to 'no-cors'.
|
||||||
|
///
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request/mode
|
||||||
pub fn fetch_mode_no_cors(self) -> Self {
|
pub fn fetch_mode_no_cors(self) -> Self {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
inner: self.inner.fetch_mode_no_cors(),
|
inner: self.inner.fetch_mode_no_cors(),
|
||||||
|
@ -289,12 +522,35 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a `Request`, which can be inspected, modified and executed with
|
||||||
|
/// `ClientWithMiddleware::execute()`.
|
||||||
pub fn build(self) -> reqwest::Result<Request> {
|
pub fn build(self) -> reqwest::Result<Request> {
|
||||||
self.inner.build()
|
self.inner.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a `Request`, which can be inspected, modified and executed with
|
||||||
|
/// `ClientWithMiddleware::execute()`.
|
||||||
|
///
|
||||||
|
/// This is similar to [`RequestBuilder::build()`], but also returns the
|
||||||
|
/// embedded `Client`.
|
||||||
|
pub fn build_split(self) -> (ClientWithMiddleware, reqwest::Result<Request>) {
|
||||||
|
let Self {
|
||||||
|
inner,
|
||||||
|
middleware_stack,
|
||||||
|
initialiser_stack,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let (inner, req) = inner.build_split();
|
||||||
|
let client = ClientWithMiddleware {
|
||||||
|
inner,
|
||||||
|
middleware_stack,
|
||||||
|
initialiser_stack,
|
||||||
|
};
|
||||||
|
(client, req)
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts the extension into this request builder
|
/// Inserts the extension into this request builder
|
||||||
pub fn with_extension<T: Send + Sync + 'static>(mut self, extension: T) -> Self {
|
pub fn with_extension<T: Send + Sync + Clone + 'static>(mut self, extension: T) -> Self {
|
||||||
self.extensions.insert(extension);
|
self.extensions.insert(extension);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -304,14 +560,31 @@ impl RequestBuilder {
|
||||||
&mut self.extensions
|
&mut self.extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(self) -> Result<Response> {
|
/// Constructs the Request and sends it to the target URL, returning a
|
||||||
let Self {
|
/// future Response.
|
||||||
inner,
|
///
|
||||||
client,
|
/// # Errors
|
||||||
mut extensions,
|
///
|
||||||
} = self;
|
/// This method fails if there was an error while sending request,
|
||||||
let req = inner.build()?;
|
/// redirect loop was detected or redirect limit was exhausted.
|
||||||
client.execute_with_extensions(req, &mut extensions).await
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use anyhow::Error;
|
||||||
|
/// #
|
||||||
|
/// # async fn run() -> Result<(), Error> {
|
||||||
|
/// let response = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new())
|
||||||
|
/// .get("https://hyper.rs")
|
||||||
|
/// .send()
|
||||||
|
/// .await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn send(mut self) -> Result<Response> {
|
||||||
|
let mut extensions = std::mem::take(self.extensions());
|
||||||
|
let (client, req) = self.build_split();
|
||||||
|
client.execute_with_extensions(req?, &mut extensions).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to clone the RequestBuilder.
|
/// Attempt to clone the RequestBuilder.
|
||||||
|
@ -319,13 +592,26 @@ impl RequestBuilder {
|
||||||
/// `None` is returned if the RequestBuilder can not be cloned,
|
/// `None` is returned if the RequestBuilder can not be cloned,
|
||||||
/// i.e. if the request body is a stream.
|
/// i.e. if the request body is a stream.
|
||||||
///
|
///
|
||||||
/// # Extensions
|
/// # Examples
|
||||||
/// Note that extensions are not preserved through cloning.
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use reqwest::Error;
|
||||||
|
/// #
|
||||||
|
/// # fn run() -> Result<(), Error> {
|
||||||
|
/// let client = reqwest_middleware::ClientWithMiddleware::from(reqwest::Client::new());
|
||||||
|
/// let builder = client.post("http://httpbin.org/post")
|
||||||
|
/// .body("from a &str!");
|
||||||
|
/// let clone = builder.try_clone();
|
||||||
|
/// assert!(clone.is_some());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn try_clone(&self) -> Option<Self> {
|
pub fn try_clone(&self) -> Option<Self> {
|
||||||
self.inner.try_clone().map(|inner| RequestBuilder {
|
self.inner.try_clone().map(|inner| RequestBuilder {
|
||||||
inner,
|
inner,
|
||||||
client: self.client.clone(),
|
middleware_stack: self.middleware_stack.clone(),
|
||||||
extensions: Extensions::new(),
|
initialiser_stack: self.initialiser_stack.clone(),
|
||||||
|
extensions: self.extensions.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! use reqwest::{Client, Request, Response};
|
//! use reqwest::{Client, Request, Response};
|
||||||
//! use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
//! use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
||||||
//! use task_local_extensions::Extensions;
|
//! use http::Extensions;
|
||||||
//!
|
//!
|
||||||
//! struct LoggingMiddleware;
|
//! struct LoggingMiddleware;
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
use http::Extensions;
|
||||||
use reqwest::{Client, Request, Response};
|
use reqwest::{Client, Request, Response};
|
||||||
use std::sync::Arc;
|
|
||||||
use task_local_extensions::Extensions;
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
@ -12,7 +11,7 @@ use crate::error::{Error, Result};
|
||||||
/// ```
|
/// ```
|
||||||
/// use reqwest::{Client, Request, Response};
|
/// use reqwest::{Client, Request, Response};
|
||||||
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
||||||
/// use task_local_extensions::Extensions;
|
/// use http::Extensions;
|
||||||
///
|
///
|
||||||
/// struct TransparentMiddleware;
|
/// struct TransparentMiddleware;
|
||||||
///
|
///
|
||||||
|
@ -74,7 +73,7 @@ where
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Next<'a> {
|
pub struct Next<'a> {
|
||||||
client: &'a Client,
|
client: &'a Client,
|
||||||
middlewares: &'a [Arc<dyn Middleware>],
|
middlewares: &'a [Box<dyn Middleware>],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -83,7 +82,7 @@ pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T
|
||||||
pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + 'a>>;
|
pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + 'a>>;
|
||||||
|
|
||||||
impl<'a> Next<'a> {
|
impl<'a> Next<'a> {
|
||||||
pub(crate) fn new(client: &'a Client, middlewares: &'a [Arc<dyn Middleware>]) -> Self {
|
pub(crate) fn new(client: &'a Client, middlewares: &'a [Box<dyn Middleware>]) -> Self {
|
||||||
Next {
|
Next {
|
||||||
client,
|
client,
|
||||||
middlewares,
|
middlewares,
|
||||||
|
@ -97,7 +96,7 @@ impl<'a> Next<'a> {
|
||||||
) -> BoxFuture<'a, Result<Response>> {
|
) -> BoxFuture<'a, Result<Response>> {
|
||||||
if let Some((current, rest)) = self.middlewares.split_first() {
|
if let Some((current, rest)) = self.middlewares.split_first() {
|
||||||
self.middlewares = rest;
|
self.middlewares = rest;
|
||||||
Box::pin(current.handle(req, extensions, self))
|
current.handle(req, extensions, self)
|
||||||
} else {
|
} else {
|
||||||
Box::pin(async move { self.client.execute(req).await.map_err(Error::from) })
|
Box::pin(async move { self.client.execute(req).await.map_err(Error::from) })
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A middleware that inserts the value into the [`Extensions`](task_local_extensions::Extensions) during the call.
|
/// A middleware that inserts the value into the [`Extensions`](http::Extensions) during the call.
|
||||||
///
|
///
|
||||||
/// This is a good way to inject extensions to middleware deeper in the stack
|
/// This is a good way to inject extensions to middleware deeper in the stack
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use reqwest::{Client, Request, Response};
|
/// use reqwest::{Client, Request, Response};
|
||||||
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result, Extension};
|
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result, Extension};
|
||||||
/// use task_local_extensions::Extensions;
|
/// use http::Extensions;
|
||||||
///
|
///
|
||||||
/// #[derive(Clone)]
|
/// #[derive(Clone)]
|
||||||
/// struct LogName(&'static str);
|
/// struct LogName(&'static str);
|
||||||
|
|
|
@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
- Updated `reqwest-middleware` to `0.3.0`.
|
||||||
|
|
||||||
## [0.3.0] - 2023-09-07
|
## [0.3.0] - 2023-09-07
|
||||||
### Changed
|
### Changed
|
||||||
- `retry-policies` upgraded to 0.2.0
|
- `retry-policies` upgraded to 0.2.0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "reqwest-retry"
|
name = "reqwest-retry"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Retry middleware for reqwest."
|
description = "Retry middleware for reqwest."
|
||||||
|
@ -10,20 +10,19 @@ keywords = ["reqwest", "http", "middleware", "retry"]
|
||||||
categories = ["web-programming::http-client"]
|
categories = ["web-programming::http-client"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
reqwest-middleware = { version = "0.2.0", path = "../reqwest-middleware" }
|
reqwest-middleware = { version = "0.3.0", path = "../reqwest-middleware" }
|
||||||
|
|
||||||
anyhow = "1.0.0"
|
anyhow = "1.0.0"
|
||||||
async-trait = "0.1.51"
|
async-trait = "0.1.51"
|
||||||
chrono = { version = "0.4.19", features = ["clock"], default-features = false }
|
chrono = { version = "0.4.19", features = ["clock"], default-features = false }
|
||||||
futures = "0.3.0"
|
futures = "0.3.0"
|
||||||
http = "0.2.0"
|
http = "1.0"
|
||||||
reqwest = { version = "0.11.0", default-features = false }
|
reqwest = { version = "0.12.0", default-features = false }
|
||||||
retry-policies = "0.3.0"
|
retry-policies = "0.3.0"
|
||||||
task-local-extensions = "0.1.4"
|
|
||||||
tracing = "0.1.26"
|
tracing = "0.1.26"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
hyper = "0.14.0"
|
hyper = "1.0"
|
||||||
tokio = { version = "1.6.0", features = ["time"] }
|
tokio = { version = "1.6.0", features = ["time"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
@ -34,5 +33,5 @@ getrandom = { version = "0.2.0", features = ["js"] }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
paste = "1.0.0"
|
paste = "1.0.0"
|
||||||
tokio = { version = "1.0.0", features = ["full"] }
|
tokio = { version = "1.0.0", features = ["full"] }
|
||||||
wiremock = "0.5.0"
|
wiremock = "0.6.0"
|
||||||
futures = "0.3.0"
|
futures = "0.3.0"
|
||||||
|
|
|
@ -3,10 +3,10 @@ use crate::retryable_strategy::RetryableStrategy;
|
||||||
use crate::{retryable::Retryable, retryable_strategy::DefaultRetryableStrategy};
|
use crate::{retryable::Retryable, retryable_strategy::DefaultRetryableStrategy};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use http::Extensions;
|
||||||
use reqwest::{Request, Response};
|
use reqwest::{Request, Response};
|
||||||
use reqwest_middleware::{Error, Middleware, Next, Result};
|
use reqwest_middleware::{Error, Middleware, Next, Result};
|
||||||
use retry_policies::RetryPolicy;
|
use retry_policies::RetryPolicy;
|
||||||
use task_local_extensions::Extensions;
|
|
||||||
|
|
||||||
/// `RetryTransientMiddleware` offers retry logic for requests that fail in a transient manner
|
/// `RetryTransientMiddleware` offers retry logic for requests that fail in a transient manner
|
||||||
/// and can be safely executed again.
|
/// and can be safely executed again.
|
||||||
|
|
|
@ -18,7 +18,7 @@ use reqwest_middleware::Error;
|
||||||
/// use reqwest_retry::{default_on_request_failure, policies::ExponentialBackoff, Retryable, RetryableStrategy, RetryTransientMiddleware};
|
/// use reqwest_retry::{default_on_request_failure, policies::ExponentialBackoff, Retryable, RetryableStrategy, RetryTransientMiddleware};
|
||||||
/// use reqwest::{Request, Response};
|
/// use reqwest::{Request, Response};
|
||||||
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
||||||
/// use task_local_extensions::Extensions;
|
/// use http::Extensions;
|
||||||
///
|
///
|
||||||
/// // Log each request to show that the requests will be retried
|
/// // Log each request to show that the requests will be retried
|
||||||
/// struct LoggingMiddleware;
|
/// struct LoggingMiddleware;
|
||||||
|
|
|
@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
- Updated `reqwest-middleware` to `0.3.0`.
|
||||||
|
- Removed support for `opentelemetry` 0.13 to 0.19
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- opentelemetry features are now additive.
|
||||||
|
|
||||||
## [0.4.8] - 2024-03-11
|
## [0.4.8] - 2024-03-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "reqwest-tracing"
|
name = "reqwest-tracing"
|
||||||
version = "0.4.8"
|
version = "0.5.0"
|
||||||
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Opentracing middleware for reqwest."
|
description = "Opentracing middleware for reqwest."
|
||||||
|
@ -10,46 +10,25 @@ keywords = ["reqwest", "http", "middleware", "opentelemetry", "tracing"]
|
||||||
categories = ["web-programming::http-client"]
|
categories = ["web-programming::http-client"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
opentelemetry_0_13 = ["opentelemetry_0_13_pkg", "tracing-opentelemetry_0_12_pkg"]
|
opentelemetry_0_20 = ["opentelemetry_0_20_pkg", "tracing-opentelemetry_0_21_pkg"]
|
||||||
opentelemetry_0_14 = ["opentelemetry_0_14_pkg", "tracing-opentelemetry_0_13_pkg"]
|
|
||||||
opentelemetry_0_15 = ["opentelemetry_0_15_pkg", "tracing-opentelemetry_0_14_pkg"]
|
|
||||||
opentelemetry_0_16 = ["opentelemetry_0_16_pkg", "tracing-opentelemetry_0_16_pkg"]
|
|
||||||
opentelemetry_0_17 = ["opentelemetry_0_17_pkg", "tracing-opentelemetry_0_17_pkg"]
|
|
||||||
opentelemetry_0_18 = ["opentelemetry_0_18_pkg", "tracing-opentelemetry_0_18_pkg"]
|
|
||||||
opentelemetry_0_19 = ["opentelemetry_0_19_pkg", "tracing-opentelemetry_0_19_pkg"]
|
|
||||||
opentelemetry_0_20 = ["opentelemetry_0_20_pkg", "tracing-opentelemetry_0_20_pkg"]
|
|
||||||
opentelemetry_0_21 = ["opentelemetry_0_21_pkg", "tracing-opentelemetry_0_22_pkg"]
|
opentelemetry_0_21 = ["opentelemetry_0_21_pkg", "tracing-opentelemetry_0_22_pkg"]
|
||||||
opentelemetry_0_22 = ["opentelemetry_0_22_pkg", "tracing-opentelemetry_0_23_pkg"]
|
opentelemetry_0_22 = ["opentelemetry_0_22_pkg", "tracing-opentelemetry_0_23_pkg"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
reqwest-middleware = { version = "0.2.0", path = "../reqwest-middleware" }
|
reqwest-middleware = { version = "0.3.0", path = "../reqwest-middleware" }
|
||||||
|
|
||||||
anyhow = "1.0.70"
|
anyhow = "1.0.70"
|
||||||
async-trait = "0.1.51"
|
async-trait = "0.1.51"
|
||||||
matchit = "0.7.0"
|
matchit = "0.7.3"
|
||||||
reqwest = { version = "0.11.0", default-features = false }
|
http = "1"
|
||||||
task-local-extensions = "0.1.4"
|
reqwest = { version = "0.12.0", default-features = false }
|
||||||
tracing = "0.1.26"
|
tracing = "0.1.26"
|
||||||
|
|
||||||
opentelemetry_0_13_pkg = { package = "opentelemetry", version = "0.13.0", optional = true }
|
|
||||||
opentelemetry_0_14_pkg = { package = "opentelemetry", version = "0.14.0", optional = true }
|
|
||||||
opentelemetry_0_15_pkg = { package = "opentelemetry", version = "0.15.0", optional = true }
|
|
||||||
opentelemetry_0_16_pkg = { package = "opentelemetry", version = "0.16.0", optional = true }
|
|
||||||
opentelemetry_0_17_pkg = { package = "opentelemetry", version = "0.17.0", optional = true }
|
|
||||||
opentelemetry_0_18_pkg = { package = "opentelemetry", version = "0.18.0", optional = true }
|
|
||||||
opentelemetry_0_19_pkg = { package = "opentelemetry", version = "0.19.0", optional = true }
|
|
||||||
opentelemetry_0_20_pkg = { package = "opentelemetry", version = "0.20.0", optional = true }
|
opentelemetry_0_20_pkg = { package = "opentelemetry", version = "0.20.0", optional = true }
|
||||||
opentelemetry_0_21_pkg = { package = "opentelemetry", version = "0.21.0", optional = true }
|
opentelemetry_0_21_pkg = { package = "opentelemetry", version = "0.21.0", optional = true }
|
||||||
opentelemetry_0_22_pkg = { package = "opentelemetry", version = "0.22.0", optional = true }
|
opentelemetry_0_22_pkg = { package = "opentelemetry", version = "0.22.0", optional = true }
|
||||||
tracing-opentelemetry_0_12_pkg = { package = "tracing-opentelemetry", version = "0.12.0", optional = true }
|
tracing-opentelemetry_0_21_pkg = { package = "tracing-opentelemetry", version = "0.21.0", optional = true }
|
||||||
tracing-opentelemetry_0_13_pkg = { package = "tracing-opentelemetry", version = "0.13.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_14_pkg = { package = "tracing-opentelemetry", version = "0.14.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_16_pkg = { package = "tracing-opentelemetry", version = "0.16.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_17_pkg = { package = "tracing-opentelemetry", version = "0.17.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_18_pkg = { package = "tracing-opentelemetry", version = "0.18.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_19_pkg = { package = "tracing-opentelemetry", version = "0.19.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_20_pkg = { package = "tracing-opentelemetry", version = "0.20.0", optional = true }
|
|
||||||
tracing-opentelemetry_0_22_pkg = { package = "tracing-opentelemetry", version = "0.22.0", optional = true }
|
tracing-opentelemetry_0_22_pkg = { package = "tracing-opentelemetry", version = "0.22.0", optional = true }
|
||||||
tracing-opentelemetry_0_23_pkg = { package = "tracing-opentelemetry", version = "0.23.0", optional = true }
|
tracing-opentelemetry_0_23_pkg = { package = "tracing-opentelemetry", version = "0.23.0", optional = true }
|
||||||
|
|
||||||
|
@ -58,9 +37,9 @@ getrandom = { version = "0.2.0", features = ["js"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1.0.0", features = ["macros"] }
|
tokio = { version = "1.0.0", features = ["macros"] }
|
||||||
tracing_subscriber_0_2 = { package = "tracing-subscriber", version = "0.2.0" }
|
tracing_subscriber = { package = "tracing-subscriber", version = "0.3.0" }
|
||||||
tracing_subscriber_0_3 = { package = "tracing-subscriber", version = "0.3.0" }
|
wiremock = "0.6.0"
|
||||||
wiremock = "0.5.0"
|
reqwest = { version = "0.12.0", features = ["rustls-tls"]}
|
||||||
|
|
||||||
opentelemetry_sdk_0_21 = { package = "opentelemetry_sdk", version = "0.21.0", features = ["trace"] }
|
opentelemetry_sdk_0_21 = { package = "opentelemetry_sdk", version = "0.21.0", features = ["trace"] }
|
||||||
opentelemetry_sdk_0_22 = { package = "opentelemetry_sdk", version = "0.22.0", features = ["trace"] }
|
opentelemetry_sdk_0_22 = { package = "opentelemetry_sdk", version = "0.22.0", features = ["trace"] }
|
||||||
|
|
|
@ -16,16 +16,15 @@ Attach `TracingMiddleware` to your client to automatically trace HTTP requests:
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
# ...
|
# ...
|
||||||
[dependencies]
|
[dependencies]
|
||||||
opentelemetry = "0.18"
|
opentelemetry = "0.22"
|
||||||
reqwest = "0.11"
|
reqwest = { version = "0.12", features = ["rustls-tls"] }
|
||||||
reqwest-middleware = "0.1.1"
|
reqwest-middleware = "0.3"
|
||||||
reqwest-retry = "0.1.1"
|
reqwest-retry = "0.5"
|
||||||
reqwest-tracing = { version = "0.3.1", features = ["opentelemetry_0_18"] }
|
reqwest-tracing = { version = "0.5", features = ["opentelemetry_0_22"] }
|
||||||
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-opentelemetry = "0.18"
|
tracing-opentelemetry = "0.23"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
task-local-extensions = "0.1.4"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust,skip
|
```rust,skip
|
||||||
|
@ -34,7 +33,7 @@ use opentelemetry::sdk::export::trace::stdout;
|
||||||
use reqwest::{Request, Response};
|
use reqwest::{Request, Response};
|
||||||
use reqwest_middleware::{ClientBuilder, Result};
|
use reqwest_middleware::{ClientBuilder, Result};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use task_local_extensions::Extensions;
|
use http::Extensions;
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
use tracing_subscriber::Registry;
|
use tracing_subscriber::Registry;
|
||||||
|
@ -89,12 +88,10 @@ an opentelemetry version feature:
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# ...
|
# ...
|
||||||
reqwest-tracing = { version = "0.3.1", features = ["opentelemetry_0_18"] }
|
reqwest-tracing = { version = "0.5.0", features = ["opentelemetry_0_22"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
Available opentelemetry features are `opentelemetry_0_22`, `opentelemetry_0_21`, `opentelemetry_0_20`,
|
Available opentelemetry features are `opentelemetry_0_22`, `opentelemetry_0_21`, and `opentelemetry_0_20`,
|
||||||
`opentelemetry_0_19`, `opentelemetry_0_18`, `opentelemetry_0_17`, `opentelemetry_0_16`,
|
|
||||||
`opentelemetry_0_15`, `opentelemetry_0_14` and `opentelemetry_0_13`.
|
|
||||||
|
|
||||||
#### License
|
#### License
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
//! Note that Opentelemetry tracks start and stop already, there is no need to have a custom builder like this.
|
//! Note that Opentelemetry tracks start and stop already, there is no need to have a custom builder like this.
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use reqwest_middleware::Result;
|
//! use reqwest_middleware::Result;
|
||||||
//! use task_local_extensions::Extensions;
|
//! use http::Extensions;
|
||||||
//! use reqwest::{Request, Response};
|
//! use reqwest::{Request, Response};
|
||||||
//! use reqwest_middleware::ClientBuilder;
|
//! use reqwest_middleware::ClientBuilder;
|
||||||
//! use reqwest_tracing::{
|
//! use reqwest_tracing::{
|
||||||
|
@ -84,13 +84,6 @@
|
||||||
|
|
||||||
mod middleware;
|
mod middleware;
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "opentelemetry_0_13",
|
|
||||||
feature = "opentelemetry_0_14",
|
|
||||||
feature = "opentelemetry_0_15",
|
|
||||||
feature = "opentelemetry_0_16",
|
|
||||||
feature = "opentelemetry_0_17",
|
|
||||||
feature = "opentelemetry_0_18",
|
|
||||||
feature = "opentelemetry_0_19",
|
|
||||||
feature = "opentelemetry_0_20",
|
feature = "opentelemetry_0_20",
|
||||||
feature = "opentelemetry_0_21",
|
feature = "opentelemetry_0_21",
|
||||||
feature = "opentelemetry_0_22",
|
feature = "opentelemetry_0_22",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
use http::Extensions;
|
||||||
use reqwest::{Request, Response};
|
use reqwest::{Request, Response};
|
||||||
use reqwest_middleware::{Middleware, Next, Result};
|
use reqwest_middleware::{Middleware, Next, Result};
|
||||||
use task_local_extensions::Extensions;
|
|
||||||
use tracing::Instrument;
|
use tracing::Instrument;
|
||||||
|
|
||||||
use crate::{DefaultSpanBackend, ReqwestOtelSpanBackend};
|
use crate::{DefaultSpanBackend, ReqwestOtelSpanBackend};
|
||||||
|
@ -46,18 +46,11 @@ where
|
||||||
|
|
||||||
let outcome_future = async {
|
let outcome_future = async {
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "opentelemetry_0_13",
|
|
||||||
feature = "opentelemetry_0_14",
|
|
||||||
feature = "opentelemetry_0_15",
|
|
||||||
feature = "opentelemetry_0_16",
|
|
||||||
feature = "opentelemetry_0_17",
|
|
||||||
feature = "opentelemetry_0_18",
|
|
||||||
feature = "opentelemetry_0_19",
|
|
||||||
feature = "opentelemetry_0_20",
|
feature = "opentelemetry_0_20",
|
||||||
feature = "opentelemetry_0_21",
|
feature = "opentelemetry_0_21",
|
||||||
feature = "opentelemetry_0_22",
|
feature = "opentelemetry_0_22",
|
||||||
))]
|
))]
|
||||||
let req = if !extensions.contains::<crate::DisableOtelPropagation>() {
|
let req = if extensions.get::<crate::DisableOtelPropagation>().is_none() {
|
||||||
// Adds tracing headers to the given request to propagate the OpenTelemetry context to downstream revivers of the request.
|
// Adds tracing headers to the given request to propagate the OpenTelemetry context to downstream revivers of the request.
|
||||||
// Spans added by downstream consumers will be part of the same trace.
|
// Spans added by downstream consumers will be part of the same trace.
|
||||||
crate::otel::inject_opentelemetry_context_into_request(req)
|
crate::otel::inject_opentelemetry_context_into_request(req)
|
||||||
|
|
|
@ -3,75 +3,26 @@ use reqwest::Request;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tracing::Span;
|
use tracing::Span;
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_13")]
|
|
||||||
use opentelemetry_0_13_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_14")]
|
|
||||||
use opentelemetry_0_14_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_15")]
|
|
||||||
use opentelemetry_0_15_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_16")]
|
|
||||||
use opentelemetry_0_16_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_17")]
|
|
||||||
use opentelemetry_0_17_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_18")]
|
|
||||||
use opentelemetry_0_18_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_19")]
|
|
||||||
use opentelemetry_0_19_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_20")]
|
|
||||||
use opentelemetry_0_20_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_21")]
|
|
||||||
use opentelemetry_0_21_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_22")]
|
|
||||||
use opentelemetry_0_22_pkg as opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_13")]
|
|
||||||
pub use tracing_opentelemetry_0_12_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_14")]
|
|
||||||
pub use tracing_opentelemetry_0_13_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_15")]
|
|
||||||
pub use tracing_opentelemetry_0_14_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_16")]
|
|
||||||
pub use tracing_opentelemetry_0_16_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_17")]
|
|
||||||
pub use tracing_opentelemetry_0_17_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_18")]
|
|
||||||
pub use tracing_opentelemetry_0_18_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_19")]
|
|
||||||
pub use tracing_opentelemetry_0_19_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_20")]
|
|
||||||
pub use tracing_opentelemetry_0_20_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_21")]
|
|
||||||
pub use tracing_opentelemetry_0_22_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_22")]
|
|
||||||
pub use tracing_opentelemetry_0_23_pkg as tracing_opentelemetry;
|
|
||||||
|
|
||||||
use opentelemetry::global;
|
|
||||||
use opentelemetry::propagation::Injector;
|
|
||||||
use tracing_opentelemetry::OpenTelemetrySpanExt;
|
|
||||||
|
|
||||||
/// Injects the given OpenTelemetry Context into a reqwest::Request headers to allow propagation downstream.
|
/// Injects the given OpenTelemetry Context into a reqwest::Request headers to allow propagation downstream.
|
||||||
pub fn inject_opentelemetry_context_into_request(mut request: Request) -> Request {
|
pub fn inject_opentelemetry_context_into_request(mut request: Request) -> Request {
|
||||||
let context = Span::current().context();
|
#[cfg(feature = "opentelemetry_0_20")]
|
||||||
|
opentelemetry_0_20_pkg::global::get_text_map_propagator(|injector| {
|
||||||
|
use tracing_opentelemetry_0_21_pkg::OpenTelemetrySpanExt;
|
||||||
|
let context = Span::current().context();
|
||||||
|
injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
|
||||||
|
});
|
||||||
|
|
||||||
global::get_text_map_propagator(|injector| {
|
#[cfg(feature = "opentelemetry_0_21")]
|
||||||
|
opentelemetry_0_21_pkg::global::get_text_map_propagator(|injector| {
|
||||||
|
use tracing_opentelemetry_0_22_pkg::OpenTelemetrySpanExt;
|
||||||
|
let context = Span::current().context();
|
||||||
|
injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "opentelemetry_0_22")]
|
||||||
|
opentelemetry_0_22_pkg::global::get_text_map_propagator(|injector| {
|
||||||
|
use tracing_opentelemetry_0_23_pkg::OpenTelemetrySpanExt;
|
||||||
|
let context = Span::current().context();
|
||||||
injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
|
injector.inject_context(&context, &mut RequestCarrier::new(&mut request))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -94,97 +45,125 @@ impl<'a> RequestCarrier<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Injector for RequestCarrier<'a> {
|
impl<'a> RequestCarrier<'a> {
|
||||||
fn set(&mut self, key: &str, value: String) {
|
fn set_inner(&mut self, key: &str, value: String) {
|
||||||
let header_name = HeaderName::from_str(key).expect("Must be header name");
|
let header_name = HeaderName::from_str(key).expect("Must be header name");
|
||||||
let header_value = HeaderValue::from_str(&value).expect("Must be a header value");
|
let header_value = HeaderValue::from_str(&value).expect("Must be a header value");
|
||||||
self.request.headers_mut().insert(header_name, header_value);
|
self.request.headers_mut().insert(header_name, header_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opentelemetry_0_20")]
|
||||||
|
impl<'a> opentelemetry_0_20_pkg::propagation::Injector for RequestCarrier<'a> {
|
||||||
|
fn set(&mut self, key: &str, value: String) {
|
||||||
|
self.set_inner(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opentelemetry_0_21")]
|
||||||
|
impl<'a> opentelemetry_0_21_pkg::propagation::Injector for RequestCarrier<'a> {
|
||||||
|
fn set(&mut self, key: &str, value: String) {
|
||||||
|
self.set_inner(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opentelemetry_0_22")]
|
||||||
|
impl<'a> opentelemetry_0_22_pkg::propagation::Injector for RequestCarrier<'a> {
|
||||||
|
fn set(&mut self, key: &str, value: String) {
|
||||||
|
self.set_inner(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{DisableOtelPropagation, TracingMiddleware};
|
use crate::{DisableOtelPropagation, TracingMiddleware};
|
||||||
#[cfg(not(any(feature = "opentelemetry_0_22", feature = "opentelemetry_0_21")))]
|
|
||||||
use opentelemetry::sdk::propagation::TraceContextPropagator;
|
|
||||||
#[cfg(feature = "opentelemetry_0_21")]
|
|
||||||
use opentelemetry_sdk_0_21::propagation::TraceContextPropagator;
|
|
||||||
#[cfg(feature = "opentelemetry_0_22")]
|
|
||||||
use opentelemetry_sdk_0_22::propagation::TraceContextPropagator;
|
|
||||||
use reqwest::Response;
|
use reqwest::Response;
|
||||||
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, Extension};
|
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, Extension};
|
||||||
use tracing::{info_span, Instrument, Level};
|
use tracing::{info_span, Instrument, Level};
|
||||||
#[cfg(any(
|
|
||||||
feature = "opentelemetry_0_13",
|
use tracing_subscriber::{filter, layer::SubscriberExt, Registry};
|
||||||
feature = "opentelemetry_0_14",
|
|
||||||
feature = "opentelemetry_0_15"
|
|
||||||
))]
|
|
||||||
use tracing_subscriber_0_2::{filter, layer::SubscriberExt, Registry};
|
|
||||||
#[cfg(not(any(
|
|
||||||
feature = "opentelemetry_0_13",
|
|
||||||
feature = "opentelemetry_0_14",
|
|
||||||
feature = "opentelemetry_0_15"
|
|
||||||
)))]
|
|
||||||
use tracing_subscriber_0_3::{filter, layer::SubscriberExt, Registry};
|
|
||||||
use wiremock::{matchers::any, Mock, MockServer, ResponseTemplate};
|
use wiremock::{matchers::any, Mock, MockServer, ResponseTemplate};
|
||||||
|
|
||||||
async fn make_echo_request_in_otel_context(client: ClientWithMiddleware) -> Response {
|
async fn make_echo_request_in_otel_context(client: ClientWithMiddleware) -> Response {
|
||||||
static TELEMETRY: OnceLock<()> = OnceLock::new();
|
static TELEMETRY: OnceLock<()> = OnceLock::new();
|
||||||
|
|
||||||
TELEMETRY.get_or_init(|| {
|
TELEMETRY.get_or_init(|| {
|
||||||
#[cfg(all(
|
let subscriber = Registry::default().with(
|
||||||
not(feature = "opentelemetry_0_20"),
|
filter::Targets::new().with_target("reqwest_tracing::otel::test", Level::DEBUG),
|
||||||
not(feature = "opentelemetry_0_21"),
|
);
|
||||||
not(feature = "opentelemetry_0_22")
|
|
||||||
))]
|
#[cfg(feature = "opentelemetry_0_20")]
|
||||||
let tracer = opentelemetry::sdk::export::trace::stdout::new_pipeline()
|
let subscriber = {
|
||||||
.with_writer(std::io::sink())
|
use opentelemetry_0_20_pkg::trace::TracerProvider;
|
||||||
.install_simple();
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "opentelemetry_0_20",
|
|
||||||
feature = "opentelemetry_0_21",
|
|
||||||
feature = "opentelemetry_0_22"
|
|
||||||
))]
|
|
||||||
let tracer = {
|
|
||||||
use opentelemetry::trace::TracerProvider;
|
|
||||||
#[cfg(feature = "opentelemetry_0_20")]
|
|
||||||
use opentelemetry_stdout_0_1::SpanExporterBuilder;
|
use opentelemetry_stdout_0_1::SpanExporterBuilder;
|
||||||
#[cfg(feature = "opentelemetry_0_21")]
|
|
||||||
|
let exporter = SpanExporterBuilder::default()
|
||||||
|
.with_writer(std::io::sink())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let provider = opentelemetry_0_20_pkg::sdk::trace::TracerProvider::builder()
|
||||||
|
.with_simple_exporter(exporter)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
|
||||||
|
let _ = opentelemetry_0_20_pkg::global::set_tracer_provider(provider);
|
||||||
|
opentelemetry_0_20_pkg::global::set_text_map_propagator(
|
||||||
|
opentelemetry_0_20_pkg::sdk::propagation::TraceContextPropagator::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let telemetry = tracing_opentelemetry_0_21_pkg::layer().with_tracer(tracer);
|
||||||
|
subscriber.with(telemetry)
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "opentelemetry_0_21")]
|
||||||
|
let subscriber = {
|
||||||
|
use opentelemetry_0_21_pkg::trace::TracerProvider;
|
||||||
use opentelemetry_stdout_0_2::SpanExporterBuilder;
|
use opentelemetry_stdout_0_2::SpanExporterBuilder;
|
||||||
#[cfg(feature = "opentelemetry_0_22")]
|
|
||||||
|
let exporter = SpanExporterBuilder::default()
|
||||||
|
.with_writer(std::io::sink())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let provider = opentelemetry_sdk_0_21::trace::TracerProvider::builder()
|
||||||
|
.with_simple_exporter(exporter)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
|
||||||
|
let _ = opentelemetry_0_21_pkg::global::set_tracer_provider(provider);
|
||||||
|
opentelemetry_0_21_pkg::global::set_text_map_propagator(
|
||||||
|
opentelemetry_sdk_0_21::propagation::TraceContextPropagator::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let telemetry = tracing_opentelemetry_0_22_pkg::layer().with_tracer(tracer);
|
||||||
|
subscriber.with(telemetry)
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "opentelemetry_0_22")]
|
||||||
|
let subscriber = {
|
||||||
|
use opentelemetry_0_22_pkg::trace::TracerProvider;
|
||||||
use opentelemetry_stdout_0_3::SpanExporterBuilder;
|
use opentelemetry_stdout_0_3::SpanExporterBuilder;
|
||||||
|
|
||||||
let exporter = SpanExporterBuilder::default()
|
let exporter = SpanExporterBuilder::default()
|
||||||
.with_writer(std::io::sink())
|
.with_writer(std::io::sink())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
#[cfg(feature = "opentelemetry_0_20")]
|
|
||||||
let provider = opentelemetry::sdk::trace::TracerProvider::builder()
|
|
||||||
.with_simple_exporter(exporter)
|
|
||||||
.build();
|
|
||||||
#[cfg(feature = "opentelemetry_0_21")]
|
|
||||||
let provider = opentelemetry_sdk_0_21::trace::TracerProvider::builder()
|
|
||||||
.with_simple_exporter(exporter)
|
|
||||||
.build();
|
|
||||||
#[cfg(feature = "opentelemetry_0_22")]
|
|
||||||
let provider = opentelemetry_sdk_0_22::trace::TracerProvider::builder()
|
let provider = opentelemetry_sdk_0_22::trace::TracerProvider::builder()
|
||||||
.with_simple_exporter(exporter)
|
.with_simple_exporter(exporter)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
|
let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
|
||||||
let _ = global::set_tracer_provider(provider);
|
let _ = opentelemetry_0_22_pkg::global::set_tracer_provider(provider);
|
||||||
tracer
|
opentelemetry_0_22_pkg::global::set_text_map_propagator(
|
||||||
|
opentelemetry_sdk_0_22::propagation::TraceContextPropagator::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let telemetry = tracing_opentelemetry_0_23_pkg::layer().with_tracer(tracer);
|
||||||
|
subscriber.with(telemetry)
|
||||||
};
|
};
|
||||||
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
|
||||||
let subscriber = Registry::default()
|
|
||||||
.with(
|
|
||||||
filter::Targets::new().with_target("reqwest_tracing::otel::test", Level::DEBUG),
|
|
||||||
)
|
|
||||||
.with(telemetry);
|
|
||||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||||
global::set_text_map_propagator(TraceContextPropagator::new());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mock server - sends all request headers back in the response
|
// Mock server - sends all request headers back in the response
|
||||||
|
|
|
@ -4,7 +4,7 @@ use matchit::Router;
|
||||||
use reqwest::header::{HeaderMap, HeaderValue};
|
use reqwest::header::{HeaderMap, HeaderValue};
|
||||||
use reqwest::{Request, Response, StatusCode as RequestStatusCode, Url};
|
use reqwest::{Request, Response, StatusCode as RequestStatusCode, Url};
|
||||||
use reqwest_middleware::{Error, Result};
|
use reqwest_middleware::{Error, Result};
|
||||||
use task_local_extensions::Extensions;
|
use http::Extensions;
|
||||||
use tracing::{warn, Span};
|
use tracing::{warn, Span};
|
||||||
|
|
||||||
use crate::reqwest_otel_span;
|
use crate::reqwest_otel_span;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use reqwest_middleware::Result;
|
/// use reqwest_middleware::Result;
|
||||||
/// use task_local_extensions::Extensions;
|
/// use http::Extensions;
|
||||||
/// use reqwest::{Request, Response};
|
/// use reqwest::{Request, Response};
|
||||||
/// use reqwest_tracing::{
|
/// use reqwest_tracing::{
|
||||||
/// default_on_request_end, reqwest_otel_span, ReqwestOtelSpanBackend
|
/// default_on_request_end, reqwest_otel_span, ReqwestOtelSpanBackend
|
||||||
|
|
Loading…
Reference in a new issue