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 changelogcargo-fmt
parent
69269e183a
commit
60212ae451
|
@ -12,13 +12,6 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
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_21
|
||||
- opentelemetry_0_22
|
||||
|
@ -61,13 +54,6 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
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_21
|
||||
- opentelemetry_0_22
|
||||
|
@ -102,13 +88,6 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
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_21
|
||||
- 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]
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
# ...
|
||||
[dependencies]
|
||||
reqwest = "0.11"
|
||||
reqwest-middleware = "0.1.6"
|
||||
reqwest-retry = "0.1.5"
|
||||
reqwest-tracing = "0.2.3"
|
||||
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
||||
reqwest = { version = "0.12", features = ["rustls-tls"] }
|
||||
reqwest-middleware = "0.3"
|
||||
reqwest-retry = "0.5"
|
||||
reqwest-tracing = "0.5"
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||
```
|
||||
|
||||
```rust
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "reqwest-middleware"
|
||||
version = "0.2.5"
|
||||
version = "0.3.0"
|
||||
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
||||
edition = "2018"
|
||||
description = "Wrapper around reqwest to allow for client middleware chains."
|
||||
|
@ -10,17 +10,21 @@ keywords = ["reqwest", "http", "middleware"]
|
|||
categories = ["web-programming::http-client"]
|
||||
readme = "../README.md"
|
||||
|
||||
[features]
|
||||
multipart = ["reqwest/multipart"]
|
||||
json = ["reqwest/json"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.0"
|
||||
async-trait = "0.1.51"
|
||||
http = "0.2.0"
|
||||
reqwest = { version = "0.11.10", default-features = false, features = ["json", "multipart"] }
|
||||
http = "1.0.0"
|
||||
reqwest = { version = "0.12.0", default-features = false }
|
||||
serde = "1.0.106"
|
||||
task-local-extensions = "0.1.4"
|
||||
thiserror = "1.0.21"
|
||||
tower-service = "0.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
reqwest-retry = { path = "../reqwest-retry" }
|
||||
reqwest-tracing = { path = "../reqwest-tracing" }
|
||||
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::multipart::Form;
|
||||
use reqwest::{Body, Client, IntoUrl, Method, Request, Response};
|
||||
use serde::Serialize;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::Arc;
|
||||
use task_local_extensions::Extensions;
|
||||
|
||||
#[cfg(feature = "multipart")]
|
||||
use reqwest::multipart;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::middleware::{Middleware, Next};
|
||||
|
@ -16,8 +18,8 @@ use crate::RequestInitialiser;
|
|||
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
|
||||
pub struct ClientBuilder {
|
||||
client: Client,
|
||||
middleware_stack: Vec<Arc<dyn Middleware>>,
|
||||
initialiser_stack: Vec<Arc<dyn RequestInitialiser>>,
|
||||
middleware_stack: Vec<Box<dyn Middleware>>,
|
||||
initialiser_stack: Vec<Box<dyn RequestInitialiser>>,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
|
@ -31,40 +33,40 @@ impl ClientBuilder {
|
|||
|
||||
/// 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
|
||||
where
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
/// 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
|
||||
where
|
||||
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
|
||||
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
|
||||
}
|
||||
|
@ -73,8 +75,8 @@ impl ClientBuilder {
|
|||
pub fn build(self) -> ClientWithMiddleware {
|
||||
ClientWithMiddleware {
|
||||
inner: self.client,
|
||||
middleware_stack: self.middleware_stack.into_boxed_slice(),
|
||||
initialiser_stack: self.initialiser_stack.into_boxed_slice(),
|
||||
middleware_stack: self.middleware_stack.into(),
|
||||
initialiser_stack: self.initialiser_stack.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,73 +86,127 @@ impl ClientBuilder {
|
|||
#[derive(Clone)]
|
||||
pub struct ClientWithMiddleware {
|
||||
inner: reqwest::Client,
|
||||
middleware_stack: Box<[Arc<dyn Middleware>]>,
|
||||
initialiser_stack: Box<[Arc<dyn RequestInitialiser>]>,
|
||||
middleware_stack: Arc<[Box<dyn Middleware>]>,
|
||||
initialiser_stack: Arc<[Box<dyn RequestInitialiser>]>,
|
||||
}
|
||||
|
||||
impl ClientWithMiddleware {
|
||||
/// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances.
|
||||
pub fn new<T>(client: Client, middleware_stack: T) -> Self
|
||||
where
|
||||
T: Into<Box<[Arc<dyn Middleware>]>>,
|
||||
T: Into<Arc<[Box<dyn Middleware>]>>,
|
||||
{
|
||||
ClientWithMiddleware {
|
||||
inner: client,
|
||||
middleware_stack: middleware_stack.into(),
|
||||
// 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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
let req = RequestBuilder {
|
||||
inner: self.inner.request(method, url),
|
||||
client: self.clone(),
|
||||
extensions: Extensions::new(),
|
||||
middleware_stack: self.middleware_stack.clone(),
|
||||
initialiser_stack: self.initialiser_stack.clone(),
|
||||
};
|
||||
self.initialiser_stack
|
||||
.iter()
|
||||
.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> {
|
||||
let mut ext = Extensions::new();
|
||||
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(
|
||||
&self,
|
||||
req: Request,
|
||||
|
@ -166,8 +222,8 @@ impl From<Client> for ClientWithMiddleware {
|
|||
fn from(client: Client) -> Self {
|
||||
ClientWithMiddleware {
|
||||
inner: client,
|
||||
middleware_stack: Box::new([]),
|
||||
initialiser_stack: Box::new([]),
|
||||
middleware_stack: Arc::from(vec![]),
|
||||
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.
|
||||
#[must_use = "RequestBuilder does nothing until you 'send' it"]
|
||||
pub struct RequestBuilder {
|
||||
inner: reqwest::RequestBuilder,
|
||||
client: ClientWithMiddleware,
|
||||
middleware_stack: Arc<[Box<dyn Middleware>]>,
|
||||
initialiser_stack: Arc<[Box<dyn RequestInitialiser>]>,
|
||||
extensions: Extensions,
|
||||
}
|
||||
|
||||
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
|
||||
where
|
||||
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 {
|
||||
RequestBuilder {
|
||||
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
|
||||
where
|
||||
U: Display,
|
||||
|
@ -229,6 +388,7 @@ impl RequestBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enable HTTP bearer authentication.
|
||||
pub fn bearer_auth<T>(self, token: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
|
@ -239,6 +399,7 @@ impl RequestBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the request body.
|
||||
pub fn body<T: Into<Body>>(self, body: T) -> Self {
|
||||
RequestBuilder {
|
||||
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 {
|
||||
RequestBuilder {
|
||||
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 {
|
||||
inner: self.inner.multipart(multipart),
|
||||
..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 {
|
||||
RequestBuilder {
|
||||
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 {
|
||||
RequestBuilder {
|
||||
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 {
|
||||
RequestBuilder {
|
||||
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 {
|
||||
RequestBuilder {
|
||||
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> {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
@ -304,14 +560,31 @@ impl RequestBuilder {
|
|||
&mut self.extensions
|
||||
}
|
||||
|
||||
pub async fn send(self) -> Result<Response> {
|
||||
let Self {
|
||||
inner,
|
||||
client,
|
||||
mut extensions,
|
||||
} = self;
|
||||
let req = inner.build()?;
|
||||
client.execute_with_extensions(req, &mut extensions).await
|
||||
/// Constructs the Request and sends it to the target URL, returning a
|
||||
/// future Response.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method fails if there was an error while sending request,
|
||||
/// redirect loop was detected or redirect limit was exhausted.
|
||||
///
|
||||
/// # 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.
|
||||
|
@ -319,13 +592,26 @@ impl RequestBuilder {
|
|||
/// `None` is returned if the RequestBuilder can not be cloned,
|
||||
/// i.e. if the request body is a stream.
|
||||
///
|
||||
/// # Extensions
|
||||
/// Note that extensions are not preserved through cloning.
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # 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> {
|
||||
self.inner.try_clone().map(|inner| RequestBuilder {
|
||||
inner,
|
||||
client: self.client.clone(),
|
||||
extensions: Extensions::new(),
|
||||
middleware_stack: self.middleware_stack.clone(),
|
||||
initialiser_stack: self.initialiser_stack.clone(),
|
||||
extensions: self.extensions.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//! ```
|
||||
//! use reqwest::{Client, Request, Response};
|
||||
//! use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
||||
//! use task_local_extensions::Extensions;
|
||||
//! use http::Extensions;
|
||||
//!
|
||||
//! struct LoggingMiddleware;
|
||||
//!
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use http::Extensions;
|
||||
use reqwest::{Client, Request, Response};
|
||||
use std::sync::Arc;
|
||||
use task_local_extensions::Extensions;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
|
@ -12,7 +11,7 @@ use crate::error::{Error, Result};
|
|||
/// ```
|
||||
/// use reqwest::{Client, Request, Response};
|
||||
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result};
|
||||
/// use task_local_extensions::Extensions;
|
||||
/// use http::Extensions;
|
||||
///
|
||||
/// struct TransparentMiddleware;
|
||||
///
|
||||
|
@ -74,7 +73,7 @@ where
|
|||
#[derive(Clone)]
|
||||
pub struct Next<'a> {
|
||||
client: &'a Client,
|
||||
middlewares: &'a [Arc<dyn Middleware>],
|
||||
middlewares: &'a [Box<dyn Middleware>],
|
||||
}
|
||||
|
||||
#[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>>;
|
||||
|
||||
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 {
|
||||
client,
|
||||
middlewares,
|
||||
|
@ -97,7 +96,7 @@ impl<'a> Next<'a> {
|
|||
) -> BoxFuture<'a, Result<Response>> {
|
||||
if let Some((current, rest)) = self.middlewares.split_first() {
|
||||
self.middlewares = rest;
|
||||
Box::pin(current.handle(req, extensions, self))
|
||||
current.handle(req, extensions, self)
|
||||
} else {
|
||||
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
|
||||
///
|
||||
/// ```
|
||||
/// use reqwest::{Client, Request, Response};
|
||||
/// use reqwest_middleware::{ClientBuilder, Middleware, Next, Result, Extension};
|
||||
/// use task_local_extensions::Extensions;
|
||||
/// use http::Extensions;
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// 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/),
|
||||
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
|
||||
### Changed
|
||||
- `retry-policies` upgraded to 0.2.0
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "reqwest-retry"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
||||
edition = "2018"
|
||||
description = "Retry middleware for reqwest."
|
||||
|
@ -10,20 +10,19 @@ keywords = ["reqwest", "http", "middleware", "retry"]
|
|||
categories = ["web-programming::http-client"]
|
||||
|
||||
[dependencies]
|
||||
reqwest-middleware = { version = "0.2.0", path = "../reqwest-middleware" }
|
||||
reqwest-middleware = { version = "0.3.0", path = "../reqwest-middleware" }
|
||||
|
||||
anyhow = "1.0.0"
|
||||
async-trait = "0.1.51"
|
||||
chrono = { version = "0.4.19", features = ["clock"], default-features = false }
|
||||
futures = "0.3.0"
|
||||
http = "0.2.0"
|
||||
reqwest = { version = "0.11.0", default-features = false }
|
||||
http = "1.0"
|
||||
reqwest = { version = "0.12.0", default-features = false }
|
||||
retry-policies = "0.3.0"
|
||||
task-local-extensions = "0.1.4"
|
||||
tracing = "0.1.26"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
hyper = "0.14.0"
|
||||
hyper = "1.0"
|
||||
tokio = { version = "1.6.0", features = ["time"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
|
@ -34,5 +33,5 @@ getrandom = { version = "0.2.0", features = ["js"] }
|
|||
[dev-dependencies]
|
||||
paste = "1.0.0"
|
||||
tokio = { version = "1.0.0", features = ["full"] }
|
||||
wiremock = "0.5.0"
|
||||
wiremock = "0.6.0"
|
||||
futures = "0.3.0"
|
||||
|
|
|
@ -3,10 +3,10 @@ use crate::retryable_strategy::RetryableStrategy;
|
|||
use crate::{retryable::Retryable, retryable_strategy::DefaultRetryableStrategy};
|
||||
use anyhow::anyhow;
|
||||
use chrono::Utc;
|
||||
use http::Extensions;
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{Error, Middleware, Next, Result};
|
||||
use retry_policies::RetryPolicy;
|
||||
use task_local_extensions::Extensions;
|
||||
|
||||
/// `RetryTransientMiddleware` offers retry logic for requests that fail in a transient manner
|
||||
/// 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::{Request, Response};
|
||||
/// 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
|
||||
/// struct LoggingMiddleware;
|
||||
|
|
|
@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [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
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "reqwest-tracing"
|
||||
version = "0.4.8"
|
||||
version = "0.5.0"
|
||||
authors = ["Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"]
|
||||
edition = "2018"
|
||||
description = "Opentracing middleware for reqwest."
|
||||
|
@ -10,46 +10,25 @@ keywords = ["reqwest", "http", "middleware", "opentelemetry", "tracing"]
|
|||
categories = ["web-programming::http-client"]
|
||||
|
||||
[features]
|
||||
opentelemetry_0_13 = ["opentelemetry_0_13_pkg", "tracing-opentelemetry_0_12_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_20 = ["opentelemetry_0_20_pkg", "tracing-opentelemetry_0_21_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"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
reqwest-middleware = { version = "0.2.0", path = "../reqwest-middleware" }
|
||||
reqwest-middleware = { version = "0.3.0", path = "../reqwest-middleware" }
|
||||
|
||||
anyhow = "1.0.70"
|
||||
async-trait = "0.1.51"
|
||||
matchit = "0.7.0"
|
||||
reqwest = { version = "0.11.0", default-features = false }
|
||||
task-local-extensions = "0.1.4"
|
||||
matchit = "0.7.3"
|
||||
http = "1"
|
||||
reqwest = { version = "0.12.0", default-features = false }
|
||||
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_21_pkg = { package = "opentelemetry", version = "0.21.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_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_21_pkg = { package = "tracing-opentelemetry", version = "0.21.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 }
|
||||
|
||||
|
@ -58,9 +37,9 @@ getrandom = { version = "0.2.0", features = ["js"] }
|
|||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.0.0", features = ["macros"] }
|
||||
tracing_subscriber_0_2 = { package = "tracing-subscriber", version = "0.2.0" }
|
||||
tracing_subscriber_0_3 = { package = "tracing-subscriber", version = "0.3.0" }
|
||||
wiremock = "0.5.0"
|
||||
tracing_subscriber = { package = "tracing-subscriber", version = "0.3.0" }
|
||||
wiremock = "0.6.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_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
|
||||
# ...
|
||||
[dependencies]
|
||||
opentelemetry = "0.18"
|
||||
reqwest = "0.11"
|
||||
reqwest-middleware = "0.1.1"
|
||||
reqwest-retry = "0.1.1"
|
||||
reqwest-tracing = { version = "0.3.1", features = ["opentelemetry_0_18"] }
|
||||
opentelemetry = "0.22"
|
||||
reqwest = { version = "0.12", features = ["rustls-tls"] }
|
||||
reqwest-middleware = "0.3"
|
||||
reqwest-retry = "0.5"
|
||||
reqwest-tracing = { version = "0.5", features = ["opentelemetry_0_22"] }
|
||||
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
|
||||
tracing = "0.1"
|
||||
tracing-opentelemetry = "0.18"
|
||||
tracing-opentelemetry = "0.23"
|
||||
tracing-subscriber = "0.3"
|
||||
task-local-extensions = "0.1.4"
|
||||
```
|
||||
|
||||
```rust,skip
|
||||
|
@ -34,7 +33,7 @@ use opentelemetry::sdk::export::trace::stdout;
|
|||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{ClientBuilder, Result};
|
||||
use std::time::Instant;
|
||||
use task_local_extensions::Extensions;
|
||||
use http::Extensions;
|
||||
use tracing::Span;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::Registry;
|
||||
|
@ -89,12 +88,10 @@ an opentelemetry version feature:
|
|||
```toml
|
||||
[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`,
|
||||
`opentelemetry_0_19`, `opentelemetry_0_18`, `opentelemetry_0_17`, `opentelemetry_0_16`,
|
||||
`opentelemetry_0_15`, `opentelemetry_0_14` and `opentelemetry_0_13`.
|
||||
Available opentelemetry features are `opentelemetry_0_22`, `opentelemetry_0_21`, and `opentelemetry_0_20`,
|
||||
|
||||
#### License
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
//! Note that Opentelemetry tracks start and stop already, there is no need to have a custom builder like this.
|
||||
//! ```rust
|
||||
//! use reqwest_middleware::Result;
|
||||
//! use task_local_extensions::Extensions;
|
||||
//! use http::Extensions;
|
||||
//! use reqwest::{Request, Response};
|
||||
//! use reqwest_middleware::ClientBuilder;
|
||||
//! use reqwest_tracing::{
|
||||
|
@ -84,13 +84,6 @@
|
|||
|
||||
mod middleware;
|
||||
#[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_21",
|
||||
feature = "opentelemetry_0_22",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use http::Extensions;
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{Middleware, Next, Result};
|
||||
use task_local_extensions::Extensions;
|
||||
use tracing::Instrument;
|
||||
|
||||
use crate::{DefaultSpanBackend, ReqwestOtelSpanBackend};
|
||||
|
@ -46,18 +46,11 @@ where
|
|||
|
||||
let outcome_future = async {
|
||||
#[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_21",
|
||||
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.
|
||||
// Spans added by downstream consumers will be part of the same trace.
|
||||
crate::otel::inject_opentelemetry_context_into_request(req)
|
||||
|
|
|
@ -3,75 +3,26 @@ use reqwest::Request;
|
|||
use std::str::FromStr;
|
||||
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.
|
||||
pub fn inject_opentelemetry_context_into_request(mut request: Request) -> Request {
|
||||
#[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))
|
||||
});
|
||||
|
||||
|
@ -94,97 +45,125 @@ impl<'a> RequestCarrier<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Injector for RequestCarrier<'a> {
|
||||
fn set(&mut self, key: &str, value: String) {
|
||||
impl<'a> RequestCarrier<'a> {
|
||||
fn set_inner(&mut self, key: &str, value: String) {
|
||||
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");
|
||||
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)]
|
||||
mod test {
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use super::*;
|
||||
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_middleware::{ClientBuilder, ClientWithMiddleware, Extension};
|
||||
use tracing::{info_span, Instrument, Level};
|
||||
#[cfg(any(
|
||||
feature = "opentelemetry_0_13",
|
||||
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 tracing_subscriber::{filter, layer::SubscriberExt, Registry};
|
||||
use wiremock::{matchers::any, Mock, MockServer, ResponseTemplate};
|
||||
|
||||
async fn make_echo_request_in_otel_context(client: ClientWithMiddleware) -> Response {
|
||||
static TELEMETRY: OnceLock<()> = OnceLock::new();
|
||||
|
||||
TELEMETRY.get_or_init(|| {
|
||||
#[cfg(all(
|
||||
not(feature = "opentelemetry_0_20"),
|
||||
not(feature = "opentelemetry_0_21"),
|
||||
not(feature = "opentelemetry_0_22")
|
||||
))]
|
||||
let tracer = opentelemetry::sdk::export::trace::stdout::new_pipeline()
|
||||
.with_writer(std::io::sink())
|
||||
.install_simple();
|
||||
#[cfg(any(
|
||||
feature = "opentelemetry_0_20",
|
||||
feature = "opentelemetry_0_21",
|
||||
feature = "opentelemetry_0_22"
|
||||
))]
|
||||
let tracer = {
|
||||
use opentelemetry::trace::TracerProvider;
|
||||
let subscriber = Registry::default().with(
|
||||
filter::Targets::new().with_target("reqwest_tracing::otel::test", Level::DEBUG),
|
||||
);
|
||||
|
||||
#[cfg(feature = "opentelemetry_0_20")]
|
||||
let subscriber = {
|
||||
use opentelemetry_0_20_pkg::trace::TracerProvider;
|
||||
use opentelemetry_stdout_0_1::SpanExporterBuilder;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
let exporter = SpanExporterBuilder::default()
|
||||
.with_writer(std::io::sink())
|
||||
.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()
|
||||
.with_simple_exporter(exporter)
|
||||
.build();
|
||||
|
||||
let tracer = provider.versioned_tracer("reqwest", None::<&str>, None::<&str>, None);
|
||||
let _ = global::set_tracer_provider(provider);
|
||||
tracer
|
||||
let _ = opentelemetry_0_22_pkg::global::set_tracer_provider(provider);
|
||||
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();
|
||||
global::set_text_map_propagator(TraceContextPropagator::new());
|
||||
});
|
||||
|
||||
// Mock server - sends all request headers back in the response
|
||||
|
|
|
@ -4,7 +4,7 @@ use matchit::Router;
|
|||
use reqwest::header::{HeaderMap, HeaderValue};
|
||||
use reqwest::{Request, Response, StatusCode as RequestStatusCode, Url};
|
||||
use reqwest_middleware::{Error, Result};
|
||||
use task_local_extensions::Extensions;
|
||||
use http::Extensions;
|
||||
use tracing::{warn, Span};
|
||||
|
||||
use crate::reqwest_otel_span;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
///
|
||||
/// ```rust
|
||||
/// use reqwest_middleware::Result;
|
||||
/// use task_local_extensions::Extensions;
|
||||
/// use http::Extensions;
|
||||
/// use reqwest::{Request, Response};
|
||||
/// use reqwest_tracing::{
|
||||
/// default_on_request_end, reqwest_otel_span, ReqwestOtelSpanBackend
|
||||
|
|
Loading…
Reference in New Issue