diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f79f04f..8acdf9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e491e7..e55969e 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/README.md b/README.md index 01871df..c3423f1 100644 --- a/README.md +++ b/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 diff --git a/reqwest-middleware/Cargo.toml b/reqwest-middleware/Cargo.toml index 36ffc91..908df82 100644 --- a/reqwest-middleware/Cargo.toml +++ b/reqwest-middleware/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reqwest-middleware" -version = "0.2.5" +version = "0.3.0" authors = ["Rodrigo Gryzinski "] 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" diff --git a/reqwest-middleware/src/client.rs b/reqwest-middleware/src/client.rs index 8a48fad..8059618 100644 --- a/reqwest-middleware/src/client.rs +++ b/reqwest-middleware/src/client.rs @@ -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>, - initialiser_stack: Vec>, + middleware_stack: Vec>, + initialiser_stack: Vec>, } 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(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) -> Self { + pub fn with_box(mut self, middleware: Box) -> 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(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) -> Self { + pub fn with_box_init(mut self, initialiser: Box) -> 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]>, - initialiser_stack: Box<[Arc]>, + middleware_stack: Arc<[Box]>, + initialiser_stack: Arc<[Box]>, } impl ClientWithMiddleware { /// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances. pub fn new(client: Client, middleware_stack: T) -> Self where - T: Into]>>, + T: Into]>>, { 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(&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(&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(&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(&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(&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(&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(&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 { 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 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>, + } + + impl Unpin for Pending {} + + impl Future for Pending { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.inner.as_mut().poll(cx) + } + } + + impl tower_service::Service for ClientWithMiddleware { + type Response = Response; + type Error = crate::Error; + type Future = Pending; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + 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 for &'_ ClientWithMiddleware { + type Response = Response; + type Error = crate::Error; + type Future = Pending; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + (&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]>, + initialiser_stack: Arc<[Box]>, 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(self, key: K, value: V) -> Self where HeaderName: TryFrom, @@ -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(self, username: U, password: Option

) -> Self where U: Display, @@ -229,6 +388,7 @@ impl RequestBuilder { } } + /// Enable HTTP bearer authentication. pub fn bearer_auth(self, token: T) -> Self where T: Display, @@ -239,6 +399,7 @@ impl RequestBuilder { } } + /// Set the request body. pub fn 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(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(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(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 { 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) { + 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(mut self, extension: T) -> Self { + pub fn with_extension(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 { - 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 { + 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.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(), }) } } diff --git a/reqwest-middleware/src/lib.rs b/reqwest-middleware/src/lib.rs index a92243d..1b21173 100644 --- a/reqwest-middleware/src/lib.rs +++ b/reqwest-middleware/src/lib.rs @@ -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; //! diff --git a/reqwest-middleware/src/middleware.rs b/reqwest-middleware/src/middleware.rs index acf94da..7230642 100644 --- a/reqwest-middleware/src/middleware.rs +++ b/reqwest-middleware/src/middleware.rs @@ -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], + middlewares: &'a [Box], } #[cfg(not(target_arch = "wasm32"))] @@ -83,7 +82,7 @@ pub type BoxFuture<'a, T> = std::pin::Pin = std::pin::Pin + 'a>>; impl<'a> Next<'a> { - pub(crate) fn new(client: &'a Client, middlewares: &'a [Arc]) -> Self { + pub(crate) fn new(client: &'a Client, middlewares: &'a [Box]) -> Self { Next { client, middlewares, @@ -97,7 +96,7 @@ impl<'a> Next<'a> { ) -> BoxFuture<'a, Result> { 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) }) } diff --git a/reqwest-middleware/src/req_init.rs b/reqwest-middleware/src/req_init.rs index 92c8167..0c0ec55 100644 --- a/reqwest-middleware/src/req_init.rs +++ b/reqwest-middleware/src/req_init.rs @@ -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); diff --git a/reqwest-retry/CHANGELOG.md b/reqwest-retry/CHANGELOG.md index 52025ad..1ada901 100644 --- a/reqwest-retry/CHANGELOG.md +++ b/reqwest-retry/CHANGELOG.md @@ -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 diff --git a/reqwest-retry/Cargo.toml b/reqwest-retry/Cargo.toml index a9dd29e..1a52f32 100644 --- a/reqwest-retry/Cargo.toml +++ b/reqwest-retry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reqwest-retry" -version = "0.4.0" +version = "0.5.0" authors = ["Rodrigo Gryzinski "] 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" diff --git a/reqwest-retry/src/middleware.rs b/reqwest-retry/src/middleware.rs index 4b3cf82..2a65fdd 100644 --- a/reqwest-retry/src/middleware.rs +++ b/reqwest-retry/src/middleware.rs @@ -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. diff --git a/reqwest-retry/src/retryable_strategy.rs b/reqwest-retry/src/retryable_strategy.rs index 982a0bf..55c5409 100644 --- a/reqwest-retry/src/retryable_strategy.rs +++ b/reqwest-retry/src/retryable_strategy.rs @@ -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; diff --git a/reqwest-tracing/CHANGELOG.md b/reqwest-tracing/CHANGELOG.md index ed6046e..7172aa2 100644 --- a/reqwest-tracing/CHANGELOG.md +++ b/reqwest-tracing/CHANGELOG.md @@ -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 diff --git a/reqwest-tracing/Cargo.toml b/reqwest-tracing/Cargo.toml index 9350388..08386f7 100644 --- a/reqwest-tracing/Cargo.toml +++ b/reqwest-tracing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reqwest-tracing" -version = "0.4.8" +version = "0.5.0" authors = ["Rodrigo Gryzinski "] 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"] } diff --git a/reqwest-tracing/README.md b/reqwest-tracing/README.md index 3bc1ca4..e64bd7a 100644 --- a/reqwest-tracing/README.md +++ b/reqwest-tracing/README.md @@ -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 diff --git a/reqwest-tracing/src/lib.rs b/reqwest-tracing/src/lib.rs index 339e62b..e0b38b9 100644 --- a/reqwest-tracing/src/lib.rs +++ b/reqwest-tracing/src/lib.rs @@ -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", diff --git a/reqwest-tracing/src/middleware.rs b/reqwest-tracing/src/middleware.rs index 345c667..2340fda 100644 --- a/reqwest-tracing/src/middleware.rs +++ b/reqwest-tracing/src/middleware.rs @@ -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::() { + let req = if extensions.get::().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) diff --git a/reqwest-tracing/src/otel.rs b/reqwest-tracing/src/otel.rs index 82c2feb..e99a637 100644 --- a/reqwest-tracing/src/otel.rs +++ b/reqwest-tracing/src/otel.rs @@ -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 { - 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)) }); @@ -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; - #[cfg(feature = "opentelemetry_0_20")] + 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; - #[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; - #[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; 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 diff --git a/reqwest-tracing/src/reqwest_otel_span_builder.rs b/reqwest-tracing/src/reqwest_otel_span_builder.rs index f2f452d..92f580f 100644 --- a/reqwest-tracing/src/reqwest_otel_span_builder.rs +++ b/reqwest-tracing/src/reqwest_otel_span_builder.rs @@ -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; diff --git a/reqwest-tracing/src/reqwest_otel_span_macro.rs b/reqwest-tracing/src/reqwest_otel_span_macro.rs index 7e32a35..3ea5287 100644 --- a/reqwest-tracing/src/reqwest_otel_span_macro.rs +++ b/reqwest-tracing/src/reqwest_otel_span_macro.rs @@ -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