Skip to content

Commit 622985f

Browse files
committed
Update libraries to latest releases
The libraries on the project were outdated, and in order to integrate with recent release libraries, sych as latest `hyper` or `axum`, they needed upgrades. The major change is related to the `http 0.2 -> http 1.0` breaking change migrations, which affects the whole ecosystem as typical types such as `Uri` and `Method` are exposed to requests. Another major issue is `hyper` major release, where the `hyper::Body` became a [`Trait`]. This meant change a few type annotations and boxing values to interop with Multipart and Empty bodies under the same struct. This commit: - Upgrade all dependencies to their latest release - Adapt code on Hyper to address breaking changes - Adapt code on Actix to address newer libraries In the future, the Actix code should be revisited when a new `actix-http 5.0` gets released, as there is some pending `http 0.2 -> http 1.0` migration tidbits required for compilation. This commit also ensures all examples can compile, and were checked on a Windows and Linux box.
1 parent 6e6e97c commit 622985f

11 files changed

Lines changed: 115 additions & 57 deletions

File tree

ipfs-api-backend-actix/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ with-builder = ["ipfs-api-prelude/with-builder"]
2020

2121
[dependencies]
2222
actix-http = "3"
23-
actix-multipart-rfc7578 = "0.10"
23+
actix-multipart-rfc7578 = "0.11"
2424
actix-tls = "3"
2525
awc = "3"
2626
async-trait = "0.1"
2727
bytes = "1"
2828
futures = "0.3"
29-
http = "0.2"
29+
http = "1"
30+
http_02 = { version = "0.2", package = "http" } # pending https://github.com/actix/actix-web/issues/3384
3031
ipfs-api-prelude = { version = "0.6", path = "../ipfs-api-prelude" }
31-
thiserror = "1"
32+
thiserror = "2"

ipfs-api-backend-actix/src/backend.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use http::{
1818
};
1919
use ipfs_api_prelude::{ApiRequest, Backend, BoxStream, TryFromUri};
2020
use multipart::client::multipart;
21-
use std::time::Duration;
21+
use std::{borrow::Borrow, time::Duration};
2222

2323
const ACTIX_REQUEST_TIMEOUT: Duration = Duration::from_secs(90);
2424

@@ -66,6 +66,14 @@ impl ActixBackend {
6666
}
6767
}
6868

69+
// Pending until https://github.com/actix/actix-web/issues/3384
70+
fn to_http_0_2(method: http::Method) -> http_02::Method {
71+
match method {
72+
http::Method::POST => http_02::Method::POST,
73+
_ => todo!("Not used by codebase"),
74+
}
75+
}
76+
6977
#[async_trait(?Send)]
7078
impl Backend for ActixBackend {
7179
type HttpRequest = awc::SendClientRequest;
@@ -91,7 +99,9 @@ impl Backend for ActixBackend {
9199
Req: ApiRequest,
92100
{
93101
let url = req.absolute_url(&self.base)?;
94-
let req = self.client.request(Req::METHOD, url);
102+
let req = self
103+
.client
104+
.request(to_http_0_2(Req::METHOD), url.to_string());
95105
let req = if let Some((username, password)) = &self.credentials {
96106
req.basic_auth(username, password)
97107
} else {
@@ -107,8 +117,11 @@ impl Backend for ActixBackend {
107117
Ok(req)
108118
}
109119

110-
fn get_header(res: &Self::HttpResponse, key: HeaderName) -> Option<&HeaderValue> {
111-
res.headers().get(key)
120+
fn get_header(res: &Self::HttpResponse, key: HeaderName) -> Option<impl Borrow<HeaderValue>> {
121+
// mapping is needed until https://github.com/actix/actix-web/issues/3384 is done
122+
res.headers()
123+
.get(key.as_str())
124+
.and_then(|v| HeaderValue::from_bytes(v.clone().as_bytes()).ok())
112125
}
113126

114127
async fn request_raw<Req>(
@@ -125,7 +138,12 @@ impl Backend for ActixBackend {
125138
let body = res.body().await?;
126139

127140
// FIXME: Actix compat with bytes 1.0
128-
Ok((status, body))
141+
Ok((
142+
// mapping is needed until https://github.com/actix/actix-web/issues/3384 is done
143+
StatusCode::from_u16(status.as_u16())
144+
.expect("failed mapping http 0.2 to http 1.0 status code"),
145+
body,
146+
))
129147
}
130148

131149
fn response_to_byte_stream(res: Self::HttpResponse) -> BoxStream<Bytes, Self::Error> {
@@ -146,7 +164,7 @@ impl Backend for ActixBackend {
146164
.err_into()
147165
.map_ok(move |mut res| {
148166
match res.status() {
149-
StatusCode::OK => process(res).right_stream(),
167+
http_02::StatusCode::OK => process(res).right_stream(),
150168
// If the server responded with an error status code, the body
151169
// still needs to be read so an error can be built. This block will
152170
// read the entire body stream, then immediately return an error.

ipfs-api-backend-hyper/Cargo.toml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ with-send-sync = ["ipfs-api-prelude/with-send-sync"]
2323

2424
[dependencies]
2525
async-trait = "0.1"
26-
base64 = "0.13"
26+
base64 = "0.22"
2727
bytes = "1"
2828
futures = "0.3"
29-
http = "0.2"
30-
hyper = { version = "0.14", features = ["http1", "http2", "client", "tcp"] }
31-
hyper-multipart-rfc7578 = "0.8"
32-
hyper-rustls = { version = "0.23", features = ["rustls-native-certs"], optional = true }
33-
hyper-tls = { version = "0.5", optional = true }
29+
http = "1"
30+
http-body-util = "0.1"
31+
hyper = { version = "1.8", features = ["http1", "http2", "client"] }
32+
hyper-util = { version = "0.1", features = ["http1", "http2", "client", "client-legacy"] }
33+
hyper-multipart-rfc7578 = "0.9"
34+
hyper-rustls = { version = "0.27", features = ["rustls-native-certs"], optional = true }
35+
hyper-tls = { version = "0.6", optional = true }
3436
ipfs-api-prelude = { version = "0.6", path = "../ipfs-api-prelude" }
35-
thiserror = "1"
37+
thiserror = "2"

ipfs-api-backend-hyper/src/backend.rs

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,32 @@
66
// copied, modified, or distributed except according to those terms.
77
//
88

9+
use std::borrow::Borrow;
10+
911
use crate::error::Error;
1012
use async_trait::async_trait;
13+
use base64::Engine as _;
1114
use bytes::Bytes;
1215
use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
1316
use http::{
1417
header::{HeaderName, HeaderValue},
1518
uri::Scheme,
1619
StatusCode, Uri,
1720
};
18-
use hyper::{
19-
body,
20-
client::{self, connect::Connect, Builder, HttpConnector},
21+
use http_body_util::{combinators::BoxBody, BodyExt};
22+
use hyper::body;
23+
#[cfg(not(feature = "with-hyper-rustls"))]
24+
#[cfg(not(feature = "with-hyper-tls"))]
25+
use hyper_util::client::legacy::connect::HttpConnector;
26+
use hyper_util::{
27+
client::legacy::{self as client, connect::Connect},
28+
rt::TokioExecutor,
2129
};
2230
use ipfs_api_prelude::{ApiRequest, Backend, BoxStream, TryFromUri};
2331
use multipart::client::multipart;
2432

33+
type RequestBody = BoxBody<body::Bytes, hyper_multipart_rfc7578::client::Error>;
34+
2535
macro_rules! impl_default {
2636
($http_connector:path) => {
2737
impl_default!($http_connector, <$http_connector>::new());
@@ -33,7 +43,7 @@ macro_rules! impl_default {
3343
C: Connect + Clone + Send + Sync + 'static,
3444
{
3545
base: Uri,
36-
client: client::Client<C, hyper::Body>,
46+
client: client::Client<C, RequestBody>,
3747

3848
/// Username and password
3949
credentials: Option<(String, String)>,
@@ -52,7 +62,7 @@ macro_rules! impl_default {
5262

5363
impl TryFromUri for HyperBackend<$http_connector> {
5464
fn build_with_base_uri(base: Uri) -> Self {
55-
let client = Builder::default().build($constructor);
65+
let client = client::Builder::new(TokioExecutor::new()).build($constructor);
5666

5767
HyperBackend {
5868
base,
@@ -104,7 +114,7 @@ impl<C: Connect + Clone + Send + Sync + 'static> HyperBackend<C> {
104114
fn basic_authorization(&self) -> Option<String> {
105115
self.credentials.as_ref().map(|(username, password)| {
106116
let credentials = format!("{}:{}", username, password);
107-
let encoded = base64::encode(credentials);
117+
let encoded = base64::prelude::BASE64_STANDARD.encode(credentials);
108118

109119
format!("Basic {}", encoded)
110120
})
@@ -117,9 +127,9 @@ impl<C> Backend for HyperBackend<C>
117127
where
118128
C: Connect + Clone + Send + Sync + 'static,
119129
{
120-
type HttpRequest = http::Request<hyper::Body>;
130+
type HttpRequest = http::Request<RequestBody>;
121131

122-
type HttpResponse = http::Response<hyper::Body>;
132+
type HttpResponse = http::Response<hyper::body::Incoming>;
123133

124134
type Error = Error;
125135

@@ -149,17 +159,21 @@ where
149159
} else {
150160
builder
151161
};
152-
153162
let req = if let Some(form) = form {
154-
form.set_body_convert::<hyper::Body, multipart::Body>(builder)
163+
form.set_body::<multipart::Body>(builder)?
164+
.map(|body| body.boxed())
155165
} else {
156-
builder.body(hyper::Body::empty())
157-
}?;
166+
builder.body(
167+
http_body_util::Empty::new()
168+
.map_err(|never| match never {})
169+
.boxed(),
170+
)?
171+
};
158172

159173
Ok(req)
160174
}
161175

162-
fn get_header(res: &Self::HttpResponse, key: HeaderName) -> Option<&HeaderValue> {
176+
fn get_header(res: &Self::HttpResponse, key: HeaderName) -> Option<impl Borrow<HeaderValue>> {
163177
res.headers().get(key)
164178
}
165179

@@ -174,13 +188,19 @@ where
174188
let req = self.build_base_request(req, form)?;
175189
let res = self.client.request(req).await?;
176190
let status = res.status();
177-
let body = body::to_bytes(res.into_body()).await?;
191+
let body = res.into_body().collect().await?.to_bytes();
178192

179193
Ok((status, body))
180194
}
181195

182196
fn response_to_byte_stream(res: Self::HttpResponse) -> BoxStream<Bytes, Self::Error> {
183-
Box::new(res.into_body().err_into())
197+
Box::new(
198+
res.into_body()
199+
.collect()
200+
.into_stream()
201+
.map_ok(|body| body.to_bytes())
202+
.err_into(),
203+
)
184204
}
185205

186206
fn request_stream<Res, F>(
@@ -202,10 +222,11 @@ where
202222
// still needs to be read so an error can be built. This block will
203223
// read the entire body stream, then immediately return an error.
204224
//
205-
_ => body::to_bytes(res.into_body())
206-
.boxed()
225+
_ => res
226+
.into_body()
227+
.collect()
207228
.map(|maybe_body| match maybe_body {
208-
Ok(body) => Err(Self::process_error_from_body(body)),
229+
Ok(body) => Err(Self::process_error_from_body(body.to_bytes())),
209230
Err(e) => Err(e.into()),
210231
})
211232
.into_stream()

ipfs-api-backend-hyper/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ pub enum Error {
1616
#[error("hyper client error `{0}`")]
1717
Client(#[from] hyper::Error),
1818

19+
#[error("hyper client error `{0}`")]
20+
LegacyClient(#[from] hyper_util::client::legacy::Error),
21+
22+
#[error("multipart parsing error `{0}`")]
23+
MultipartParse(#[from] multipart::client::Error),
24+
1925
#[error("http error `{0}`")]
2026
Http(#[from] http::Error),
2127

ipfs-api-examples/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ maintenance = { status = "passively-maintained" }
1717

1818
[features]
1919
default = ["with-hyper"]
20-
with-hyper = ["ipfs-api-backend-hyper", "tokio/macros", "tokio/rt-multi-thread"]
20+
with-hyper = ["ipfs-api-backend-hyper", "tokio/macros", "tokio/rt-multi-thread", "ipfs-api-backend-hyper/with-builder"]
2121
with-hyper-tls = ["with-hyper", "ipfs-api-backend-hyper/with-hyper-tls"]
2222
with-hyper-rustls = ["with-hyper", "ipfs-api-backend-hyper/with-hyper-rustls"]
23-
with-actix = ["ipfs-api-backend-actix", "actix-rt"]
23+
with-actix = ["ipfs-api-backend-actix", "actix-rt", "ipfs-api-backend-actix/with-builder"]
2424

2525
[dependencies]
2626
actix-rt = { version = "2.6", optional = true }
2727
futures = "0.3"
2828
ipfs-api-backend-actix = { version = "0.7", path = "../ipfs-api-backend-actix", optional = true }
2929
ipfs-api-backend-hyper = { version = "0.6", path = "../ipfs-api-backend-hyper", optional = true }
3030
tar = "0.4"
31-
thiserror = "1"
31+
thiserror = "2"
3232
tokio = { version = "1", features = ["time"] }
3333
tokio-stream = { version = "0.1", features = ["time"] }
3434
tracing-subscriber = { version = "0.3", features = ["fmt"] }

ipfs-api-prelude/Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,20 @@ with-send-sync = []
2323
async-trait = "0.1"
2424
bytes = "1"
2525
cfg-if = "1"
26-
common-multipart-rfc7578 = "0.6"
27-
dirs = "4"
26+
common-multipart-rfc7578 = "0.7"
27+
dirs = "6"
2828
futures = "0.3"
29-
http = "0.2"
30-
multiaddr = "0.17"
29+
http = "1"
30+
multiaddr = "0.18"
3131
multibase = "0.9"
3232
serde = { version = "1", features = ["derive"] }
3333
serde_json = "1"
3434
serde_urlencoded = "0.7"
35-
thiserror = "1"
35+
thiserror = "2"
3636
tokio = "1"
3737
tokio-util = { version = "0.7", features = ["codec"] }
3838
tracing = "0.1"
39-
typed-builder = { version = "0.10", optional = true }
39+
typed-builder = { version = "0.23", optional = true }
4040
walkdir = "2.3"
4141

4242
[dev-dependencies]

ipfs-api-prelude/src/backend.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ use http::{
2020
StatusCode,
2121
};
2222
use serde::Deserialize;
23-
use std::fmt::{Debug, Display};
23+
use std::{
24+
borrow::Borrow,
25+
fmt::{Debug, Display},
26+
};
2427
use tokio_util::codec::{Decoder, FramedRead};
2528

2629
cfg_if::cfg_if! {
@@ -64,7 +67,8 @@ pub trait Backend {
6467

6568
/// Get the value of a header from an HTTP response.
6669
///
67-
fn get_header(res: &Self::HttpResponse, key: HeaderName) -> Option<&HeaderValue>;
70+
// TODO: replace `impl Borrow` with `&` after https://github.com/actix/actix-web/issues/3384 is done
71+
fn get_header(res: &Self::HttpResponse, key: HeaderName) -> Option<impl Borrow<HeaderValue>>;
6872

6973
/// Generates a request, and returns the unprocessed response future.
7074
///
@@ -214,11 +218,11 @@ pub trait Backend {
214218
// is used to indicate that there was an error while streaming
215219
// data with Ipfs.
216220
//
217-
if trailer == X_STREAM_ERROR_KEY {
221+
if trailer.borrow() == X_STREAM_ERROR_KEY {
218222
true
219223
} else {
220224
let err = crate::Error::UnrecognizedTrailerHeader(
221-
String::from_utf8_lossy(trailer.as_ref()).into(),
225+
String::from_utf8_lossy(trailer.borrow().as_bytes()).into(),
222226
);
223227

224228
// There was an unrecognized trailer value. If that is the case,

ipfs-api-prelude/src/global_opts.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use async_trait::async_trait;
1111
use bytes::Bytes;
1212
use common_multipart_rfc7578::client::multipart;
1313
use serde::{Serialize, Serializer};
14-
use std::time::Duration;
14+
use std::{borrow::Borrow, time::Duration};
1515

1616
/// Options valid on any IPFS Api request
1717
///
@@ -85,7 +85,7 @@ impl<Back: Backend> BackendWithGlobalOptions<Back> {
8585
}
8686
}
8787

88-
fn combine<Req>(&self, req: Req) -> OptCombiner<Req>
88+
fn combine<Req>(&'_ self, req: Req) -> OptCombiner<'_, Req>
8989
where
9090
Req: ApiRequest,
9191
{
@@ -198,7 +198,7 @@ impl<Back: Backend> Backend for BackendWithGlobalOptions<Back> {
198198
fn get_header(
199199
res: &Self::HttpResponse,
200200
key: http::header::HeaderName,
201-
) -> Option<&http::HeaderValue> {
201+
) -> Option<impl Borrow<http::HeaderValue>> {
202202
Back::get_header(res, key)
203203
}
204204

ipfs-api/Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ actix-rt = "2.5"
3131
cfg-if = "1"
3232
futures = "0.3"
3333
ipfs-api-versions = { version = "0.1", path = "../ipfs-api-versions" }
34-
passivized_docker_engine_client = "0.0.8"
35-
passivized_htpasswd = "0.0.5"
36-
reqwest = "0.11"
34+
passivized_docker_engine_client = "0.0.9"
35+
passivized_htpasswd = "0.0.6"
36+
reqwest = "0.13"
3737
tempfile = "3.3"
38-
test-case = "2.2"
39-
thiserror = "1.0"
38+
test-case = "3.3"
39+
thiserror = "2.0"
4040
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
4141

4242
# Only when testing with-hyper
43-
hyper = "0.14"
43+
hyper = "1.8"
4444

4545
# Only when testing with-hyper-tls
46-
hyper-tls = "0.5"
46+
hyper-tls = "0.6"

0 commit comments

Comments
 (0)