Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reqwest Client (Tokio 1.0 support) #92

Merged
merged 13 commits into from
Oct 4, 2021
12 changes: 7 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ jobs:
components: "rustfmt,clippy"
- name: Check code formatting
run: cargo fmt --all -- --check
- name: Check Clippy lints
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Check Clippy lints (reqwest)
run: cargo clippy --all-targets --no-default-features --features use-serde,derive,reqwest-client -- -D warnings
- name: Check Clippy lints (surf)
run: cargo clippy --all-targets --no-default-features --features use-serde,derive,hyper-client -- -D warnings

compile:
name: Compile (${{ matrix.rust_release }}/${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
rust_release: [1.45, stable, nightly]
rust_release: [1.46, stable, nightly]
os: [ubuntu-latest, windows-latest, macOS-latest]

steps:
Expand All @@ -50,7 +52,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
http-backend: [curl-client, h1-client, h1-client-rustls, hyper-client]
http-backend: [curl-client, h1-client, h1-client-rustls, hyper-client, reqwest-client, reqwest-client-rustls]
services:
influxdb:
image: influxdb:1.8
Expand Down Expand Up @@ -122,7 +124,7 @@ jobs:
cargo tarpaulin -v \
--target-dir target/tarpaulin \
--workspace \
--all-features \
--features use-serde,derive \
--exclude-files 'derive/*' \
--exclude-files 'target/*' \
--ignore-panics --ignore-tests \
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- reqwest-based http client (enabled by default)

## [0.4.0] - 2021-03-08

### Fixed
Expand Down
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
<a href="https://www.rust-lang.org/en-US/">
<img src="https://img.shields.io/badge/Made%20with-Rust-orange.svg" alt='Build with Rust' />
</a>
<a href="https://blog.rust-lang.org/2020/03/12/Rust-1.45.html">
<img src="https://img.shields.io/badge/rustc-1.45+-yellow.svg" alt='Minimum Rust Version' />
<a href="https://blog.rust-lang.org/2020/08/27/Rust-1.46.0.html">
<img src="https://img.shields.io/badge/rustc-1.46+-yellow.svg" alt='Minimum Rust Version' />
</a>
</p>

Expand Down Expand Up @@ -62,8 +62,8 @@ use influxdb::{Client, Query, Timestamp};
use influxdb::InfluxDbWriteable;
use chrono::{DateTime, Utc};

#[async_std::main]
// or #[tokio::main] if you prefer
#[tokio::main]
// or #[async_std::main] if you prefer
async fn main() {
// Connect to db `test` on `http://localhost:8086`
let client = Client::new("http://localhost:8086", "test");
Expand Down Expand Up @@ -101,11 +101,21 @@ in the repository.

## Choice of HTTP backend

To communicate with InfluxDB, you can choose the HTTP backend to be used configuring the appropriate feature:
To communicate with InfluxDB, you can choose the HTTP backend to be used configuring the appropriate feature. We recommend sticking with the default reqwest-based client, unless you really need async-std compatibility.

- **[hyper](https://github.com/hyperium/hyper)** (used by default)
- **[hyper](https://github.com/hyperium/hyper)** (through reqwest, used by default), with [rustls](https://github.com/ctz/rustls)
```toml
influxdb = { version = "0.4.0", features = ["derive"] }
```

- **[hyper](https://github.com/hyperium/hyper)** (through reqwest), with native TLS (OpenSSL)
```toml
influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "reqwest-client"] }
```

- **[hyper](https://github.com/hyperium/hyper)** (through surf), use this if you need tokio 0.2 compatibility
```toml
influxdb = { version = "0.4.0", features = ["derive"] }
influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "curl-client"] }
```
- **[curl](https://github.com/alexcrichton/curl-rust)**, using [libcurl](https://curl.se/libcurl/)
```toml
Expand Down
6 changes: 3 additions & 3 deletions README.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
<a href="https://www.rust-lang.org/en-US/">
<img src="https://img.shields.io/badge/Made%20with-Rust-orange.svg" alt='Build with Rust' />
</a>
<a href="https://blog.rust-lang.org/2020/03/12/Rust-1.45.html">
<img src="https://img.shields.io/badge/rustc-1.45+-yellow.svg" alt='Minimum Rust Version' />
<a href="https://blog.rust-lang.org/2020/08/27/Rust-1.46.0.html">
<img src="https://img.shields.io/badge/rustc-1.46+-yellow.svg" alt='Minimum Rust Version' />
</a>
</p>

{{readme}}

@ 2020 Gero Gerke and [contributors](https://github.com/Empty2k12/influxdb-rust/graphs/contributors).
@ 2020 Gero Gerke and [contributors](https://github.com/Empty2k12/influxdb-rust/graphs/contributors).
28 changes: 17 additions & 11 deletions influxdb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,30 @@ travis-ci = { repository = "Empty2k12/influxdb-rust", branch = "master" }
[dependencies]
chrono = { version = "0.4.11", features = ["serde"] }
futures = "0.3.4"
lazy_static = "1.4.0"
http = "0.2.4"
influxdb_derive = { version = "0.4.0", optional = true }
lazy_static = "1.4.0"
regex = "1.3.5"
surf = { version = "2.2.0", default-features = false }
reqwest = { version = "0.11.4", default-features = false, optional = true }
surf = { version = "2.2.0", default-features = false, optional = true }
serde = { version = "1.0.104", features = ["derive"], optional = true }
serde_json = { version = "1.0.48", optional = true }
thiserror = "1.0"

[features]
use-serde = ["serde", "serde_json"]
curl-client = ["surf/curl-client"]
h1-client = ["surf/h1-client"]
h1-client-rustls = ["surf/h1-client-rustls"]
hyper-client = ["surf/hyper-client"]
wasm-client = ["surf/wasm-client"]
default = ["use-serde", "hyper-client"]
default = ["use-serde", "reqwest-client-rustls"]
derive = ["influxdb_derive"]
use-serde = ["serde", "serde_json"]

# http clients
curl-client = ["surf", "surf/curl-client"]
h1-client = ["surf", "surf/h1-client"]
h1-client-rustls = ["surf", "surf/h1-client-rustls"]
hyper-client = ["surf", "surf/hyper-client"]
reqwest-client = ["reqwest", "reqwest/native-tls-alpn"]
reqwest-client-rustls = ["reqwest", "reqwest/rustls-tls-webpki-roots"]
wasm-client = ["surf", "surf/wasm-client"]

[dev-dependencies]
async-std = { version = "1.6.5", features = ["attributes"] }
tokio = { version = "0.2.22", features = ["rt-threaded", "macros"] }
async-std = { version = "1.6.5", features = ["attributes", "tokio02", "tokio1"] }
tokio = { version = "1.7", features = ["macros", "rt-multi-thread"] }
75 changes: 52 additions & 23 deletions influxdb/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
//! ```

use futures::prelude::*;
use surf::{self, Client as SurfClient, StatusCode};
use http::StatusCode;
#[cfg(feature = "reqwest")]
use reqwest::{Client as HttpClient, Response as HttpResponse};
#[cfg(feature = "surf")]
use surf::{Client as HttpClient, Response as HttpResponse};

use crate::query::QueryType;
use crate::Error;
Expand All @@ -29,7 +33,7 @@ use std::sync::Arc;
pub struct Client {
pub(crate) url: Arc<String>,
pub(crate) parameters: Arc<HashMap<&'static str, String>>,
pub(crate) client: SurfClient,
pub(crate) client: HttpClient,
}

impl Client {
Expand Down Expand Up @@ -57,7 +61,7 @@ impl Client {
Client {
url: Arc::new(url.into()),
parameters: Arc::new(parameters),
client: SurfClient::new(),
client: HttpClient::new(),
}
}

Expand Down Expand Up @@ -112,10 +116,25 @@ impl Client {
error: format!("{}", err),
})?;

let build = res.header("X-Influxdb-Build").unwrap().as_str();
let version = res.header("X-Influxdb-Version").unwrap().as_str();
const BUILD_HEADER: &str = "X-Influxdb-Build";
const VERSION_HEADER: &str = "X-Influxdb-Version";

Ok((build.to_owned(), version.to_owned()))
#[cfg(feature = "reqwest")]
let (build, version) = {
let hdrs = res.headers();
(
hdrs.get(BUILD_HEADER).and_then(|value| value.to_str().ok()),
hdrs.get(VERSION_HEADER)
.and_then(|value| value.to_str().ok()),
)
};

#[cfg(feature = "surf")]
let build = res.header(BUILD_HEADER).map(|value| value.as_str());
#[cfg(feature = "surf")]
let version = res.header(VERSION_HEADER).map(|value| value.as_str());

Ok((build.unwrap().to_owned(), version.unwrap().to_owned()))
}

/// Sends a [`ReadQuery`](crate::ReadQuery) or [`WriteQuery`](crate::WriteQuery) to the InfluxDB Server.
Expand Down Expand Up @@ -184,32 +203,31 @@ impl Client {

self.client.post(url).body(query.get()).query(&parameters)
}
}
.map_err(|err| Error::UrlConstructionError {
};

#[cfg(feature = "surf")]
let request_builder = request_builder.map_err(|err| Error::UrlConstructionError {
error: err.to_string(),
})?;

let request = request_builder.build();
let mut res = self
.client
.send(request)
let res = request_builder
.send()
.map_err(|err| Error::ConnectionError {
error: err.to_string(),
})
.await?;
check_status(&res)?;

match res.status() {
StatusCode::Unauthorized => return Err(Error::AuthorizationError),
StatusCode::Forbidden => return Err(Error::AuthenticationError),
_ => {}
}
#[cfg(feature = "reqwest")]
let body = res.text();
#[cfg(feature = "surf")]
let mut res = res;
#[cfg(feature = "surf")]
let body = res.body_string();

let s = res
.body_string()
.await
.map_err(|_| Error::DeserializationError {
error: "response could not be converted to UTF-8".to_string(),
})?;
let s = body.await.map_err(|_| Error::DeserializationError {
error: "response could not be converted to UTF-8".to_string(),
})?;

// todo: improve error parsing without serde
if s.contains("\"error\"") {
Expand All @@ -222,6 +240,17 @@ impl Client {
}
}

pub(crate) fn check_status(res: &HttpResponse) -> Result<(), Error> {
let status = res.status();
if status == StatusCode::UNAUTHORIZED.as_u16() {
Err(Error::AuthorizationError)
} else if status == StatusCode::FORBIDDEN.as_u16() {
Err(Error::AuthenticationError)
} else {
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::Client;
Expand Down
37 changes: 17 additions & 20 deletions influxdb/src/integrations/serde_integration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@

mod de;

use surf::StatusCode;

use serde::{de::DeserializeOwned, Deserialize};

use crate::{Client, Error, Query, ReadQuery};
use crate::{client::check_status, Client, Error, Query, ReadQuery};

#[derive(Deserialize)]
#[doc(hidden)]
Expand Down Expand Up @@ -143,30 +141,29 @@ impl Client {
let url = &format!("{}/query", &self.url);
let mut parameters = self.parameters.as_ref().clone();
parameters.insert("q", read_query);
let request = self
.client
.get(url)
.query(&parameters)
.map_err(|err| Error::UrlConstructionError {
error: err.to_string(),
})?
.build();
let request_builder = self.client.get(url).query(&parameters);

#[cfg(feature = "surf")]
let request_builder = request_builder.map_err(|err| Error::UrlConstructionError {
error: err.to_string(),
})?;

let mut res = self
.client
.send(request)
let res = request_builder
.send()
.await
.map_err(|err| Error::ConnectionError {
error: err.to_string(),
})?;
check_status(&res)?;

match res.status() {
StatusCode::Unauthorized => return Err(Error::AuthorizationError),
StatusCode::Forbidden => return Err(Error::AuthenticationError),
_ => {}
}
#[cfg(feature = "reqwest")]
let body = res.bytes();
#[cfg(feature = "surf")]
let mut res = res;
#[cfg(feature = "surf")]
let body = res.body_bytes();

let body = res.body_bytes().await.map_err(|err| Error::ProtocolError {
let body = body.await.map_err(|err| Error::ProtocolError {
error: err.to_string(),
})?;

Expand Down
26 changes: 21 additions & 5 deletions influxdb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
//! use influxdb::InfluxDbWriteable;
//! use chrono::{DateTime, Utc};
//!
//! #[async_std::main]
//! // or #[tokio::main] if you prefer
//! #[tokio::main]
//! // or #[async_std::main] if you prefer
//! async fn main() {
//! // Connect to db `test` on `http://localhost:8086`
//! let client = Client::new("http://localhost:8086", "test");
Expand Down Expand Up @@ -69,11 +69,21 @@
//!
//! # Choice of HTTP backend
//!
//! To communicate with InfluxDB, you can choose the HTTP backend to be used configuring the appropriate feature:
//! To communicate with InfluxDB, you can choose the HTTP backend to be used configuring the appropriate feature. We recommend sticking with the default reqwest-based client, unless you really need async-std compatibility.
//!
//! - **[hyper](https://github.com/hyperium/hyper)** (used by default)
//! - **[hyper](https://github.com/hyperium/hyper)** (through reqwest, used by default), with [rustls](https://github.com/ctz/rustls)
//! ```toml
//! influxdb = { version = "0.4.0", features = ["derive"] }
//! ```
//!
//! - **[hyper](https://github.com/hyperium/hyper)** (through reqwest), with native TLS (OpenSSL)
//! ```toml
//! influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "reqwest-client"] }
//! ```
//!
//! - **[hyper](https://github.com/hyperium/hyper)** (through surf), use this if you need tokio 0.2 compatibility
//! ```toml
//! influxdb = { version = "0.4.0", features = ["derive"] }
//! influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "curl-client"] }
//! ```
//! - **[curl](https://github.com/alexcrichton/curl-rust)**, using [libcurl](https://curl.se/libcurl/)
//! ```toml
Expand All @@ -99,6 +109,12 @@
#![allow(clippy::needless_doctest_main)]
#![allow(clippy::needless_lifetimes)] // False positive in client/mod.rs query fn

#[cfg(all(feature = "reqwest", feature = "surf"))]
compile_error!("You need to choose between reqwest and surf; enabling both is not supported");

#[cfg(not(any(feature = "reqwest", feature = "surf")))]
compile_error!("You need to choose an http client; consider not disabling default features");

mod client;
mod error;
mod query;
Expand Down
Loading