Skip to content

Commit

Permalink
Consolidate HTTP client errors and flatten exports
Browse files Browse the repository at this point in the history
This change replaces the per-HTTP-client error types with a single
generic `HttpClientError` type shared by all the built-in clients.
This allows us to remove the public `reqwest`, `curl`, and `ureq`
modules and directly re-export those crates from the root, which
allows imports such as `oauth2::reqwest` instead of
`oauth2::reqwest::reqwest`.

BREAKING CHANGES:
 - Removed `reqwest`, `curl`, and `ureq` modules. The corresponding
   crates are now re-exported from the root of this crate.
 - `CurlHttpClient` is now exported from the root of this crate when
   the `curl` feature is enabled.
 - Per-client error enums (e.g., `oauth2::reqwest::Error`) have been
   replaced with a generic `HttpClientError` type (e.g.,
   `oauth2::HttpClientError<reqwest::Error>`).
  • Loading branch information
ramosbugs committed Mar 3, 2024
1 parent 5ab6607 commit 4391eed
Show file tree
Hide file tree
Showing 14 changed files with 163 additions and 170 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
- name: Doc tests
run: |
cargo test --doc
cargo test --doc --no-default-features
cargo test --doc --all-features
- name: Test with all features enabled
run: cargo test --all-features
Expand Down
2 changes: 1 addition & 1 deletion examples/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//!
use oauth2::basic::BasicClient;
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::{
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope,
TokenResponse, TokenUrl,
Expand Down
2 changes: 1 addition & 1 deletion examples/github_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//!
use oauth2::basic::BasicClient;
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::{
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope,
TokenResponse, TokenUrl,
Expand Down
2 changes: 1 addition & 1 deletion examples/google.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! ...and follow the instructions.
//!
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::{basic::BasicClient, StandardRevocableToken, TokenResponse};
use oauth2::{
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl,
Expand Down
2 changes: 1 addition & 1 deletion examples/google_devicecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//!
use oauth2::basic::BasicClient;
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::{
AuthType, AuthUrl, ClientId, ClientSecret, DeviceAuthorizationResponse, DeviceAuthorizationUrl,
ExtraDeviceAuthorizationFields, Scope, TokenUrl,
Expand Down
2 changes: 1 addition & 1 deletion examples/microsoft_devicecode_tenant_user.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use oauth2::basic::BasicClient;
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::StandardDeviceAuthorizationResponse;
use oauth2::{AuthUrl, ClientId, DeviceAuthorizationUrl, Scope, TokenUrl};

Expand Down
2 changes: 1 addition & 1 deletion examples/msgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//!
use oauth2::basic::BasicClient;
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::{
AuthType, AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge,
RedirectUrl, Scope, TokenUrl,
Expand Down
2 changes: 1 addition & 1 deletion examples/wunderlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use oauth2::basic::{
BasicErrorResponse, BasicRevocationErrorResponse, BasicTokenIntrospectionResponse,
BasicTokenType,
};
use oauth2::reqwest::reqwest;
use oauth2::reqwest;
use oauth2::{
AccessToken, AuthUrl, AuthorizationCode, Client, ClientId, ClientSecret, CsrfToken,
EmptyExtraTokenFields, EndpointNotSet, ExtraTokenFields, RedirectUrl, RefreshToken, Scope,
Expand Down
87 changes: 0 additions & 87 deletions src/curl.rs

This file was deleted.

80 changes: 80 additions & 0 deletions src/curl_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{HttpClientError, HttpRequest, HttpResponse, SyncHttpClient};

use curl::easy::Easy;
use http::header::{HeaderValue, CONTENT_TYPE};
use http::method::Method;
use http::status::StatusCode;

use std::io::Read;

/// A synchronous HTTP client using [`curl`].
pub struct CurlHttpClient;
impl SyncHttpClient for CurlHttpClient {
type Error = HttpClientError<curl::Error>;

fn call(&self, request: HttpRequest) -> Result<HttpResponse, Self::Error> {
let mut easy = Easy::new();
easy.url(&request.uri().to_string()[..]).map_err(Box::new)?;

let mut headers = curl::easy::List::new();
for (name, value) in request.headers() {
headers
.append(&format!(
"{}: {}",
name,
// TODO: Unnecessary fallibility, curl uses a CString under the hood
value.to_str().map_err(|_| HttpClientError::Other(format!(
"invalid `{name}` header value {:?}",
value.as_bytes()
)))?
))
.map_err(Box::new)?
}

easy.http_headers(headers).map_err(Box::new)?;

if let Method::POST = *request.method() {
easy.post(true).map_err(Box::new)?;
easy.post_field_size(request.body().len() as u64)
.map_err(Box::new)?;
} else {
assert_eq!(*request.method(), Method::GET);
}

let mut form_slice = &request.body()[..];
let mut data = Vec::new();
{
let mut transfer = easy.transfer();

transfer
.read_function(|buf| Ok(form_slice.read(buf).unwrap_or(0)))
.map_err(Box::new)?;

transfer
.write_function(|new_data| {
data.extend_from_slice(new_data);
Ok(new_data.len())
})
.map_err(Box::new)?;

transfer.perform().map_err(Box::new)?;
}

let mut builder = http::Response::builder().status(
StatusCode::from_u16(easy.response_code().map_err(Box::new)? as u16)
.map_err(http::Error::from)?,
);

if let Some(content_type) = easy
.content_type()
.map_err(Box::new)?
.map(HeaderValue::from_str)
.transpose()
.map_err(http::Error::from)?
{
builder = builder.header(CONTENT_TYPE, content_type);
}

builder.body(data).map_err(HttpClientError::Http)
}
}
Loading

0 comments on commit 4391eed

Please sign in to comment.