Skip to content

Commit daf16b5

Browse files
committed
build(deps): migrate from hyper 0.14 to hyper 1.x
1 parent a4c476f commit daf16b5

12 files changed

Lines changed: 219 additions & 313 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ regex = "1.12.3"
2727
serde = { version = "1.0.228", features = ["derive"] }
2828
cookie = "0.18.1"
2929
futures-lite = "2.6.1"
30-
hyper = { version = "0.14.31", features = ["full"] }
30+
hyper = { version = "1.9.0", features = ["http1", "server"] }
31+
http-body-util = "0.1.3"
32+
hyper-util = { version = "0.1.20", features = ["tokio"] }
33+
bytes = "1"
3134
wreq = { version = "6.0.0-rc.28", features = ["brotli", "gzip", "deflate", "zstd", "json", "stream", "socks"] }
3235
wreq-util = { version = "3.0.0-rc.10" }
3336
percent-encoding = "2.3.2"

src/client.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use arc_swap::ArcSwap;
22
use cached::proc_macro::cached;
33
use futures_lite::future::block_on;
44
use futures_lite::{future::Boxed, FutureExt};
5-
use hyper::{body::Buf, header, Body, Method, Response};
5+
use http_body_util::BodyExt;
6+
use hyper::{header, Method, Response};
67
use log::{error, info, trace, warn};
78
use percent_encoding::{percent_encode, CONTROLS};
89
use serde_json::Value;
@@ -86,21 +87,19 @@ pub fn build_client() -> WreqClient {
8687
.expect("Should always be able to build a wreq client")
8788
}
8889

90+
use crate::server::{full, Body};
91+
8992
/// Convert a wreq Response into a hyper Response<Body>.
90-
/// This bridge lets the rest of the codebase stay unchanged.
93+
/// wreq and hyper now both use http v1.x, so header types are directly compatible.
9194
trait IntoHyperResponse {
9295
async fn into_hyper(self) -> Result<Response<Body>, String>;
9396
}
9497

9598
impl IntoHyperResponse for wreq::Response {
9699
async fn into_hyper(self) -> Result<Response<Body>, String> {
97-
// wreq uses http v1.x; hyper uses http v0.2.x. Convert via primitives.
98-
let status_u16 = self.status().as_u16();
99-
let status = hyper::StatusCode::from_u16(status_u16).map_err(|e| e.to_string())?;
100+
let status = self.status();
100101

101-
// Snapshot headers before consuming self (bytes() moves self).
102-
// Use SmallVec-style stack storage: most responses have < 32 headers.
103-
let mut builder = hyper::Response::builder().status(status);
102+
let mut builder = hyper::Response::builder().status(status.as_u16());
104103
for (k, v) in self.headers() {
105104
// Skip headers that hyper rejects (e.g. non-ASCII bytes in CDN headers)
106105
// rather than aborting the entire response.
@@ -120,7 +119,7 @@ impl IntoHyperResponse for wreq::Response {
120119
h.insert(header::CONTENT_LENGTH, bytes.len().into());
121120
}
122121

123-
builder.body(Body::from(bytes)).map_err(|e| e.to_string())
122+
builder.body(full(bytes)).map_err(|e| e.to_string())
124123
}
125124
}
126125

@@ -382,12 +381,12 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
382381
None
383382
};
384383

385-
// Aggregate the body
386-
match hyper::body::aggregate(response).await {
387-
Ok(body) => {
388-
let has_remaining = body.has_remaining();
384+
// Collect the body bytes
385+
match response.collect().await {
386+
Ok(collected) => {
387+
let body_bytes = collected.to_bytes();
389388

390-
if !has_remaining {
389+
if body_bytes.is_empty() {
391390
tokio::spawn(force_refresh_token());
392391
return match reset {
393392
Some(val) => Err(format!(
@@ -398,7 +397,7 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
398397
};
399398
}
400399

401-
match serde_json::from_reader(body.reader()) {
400+
match serde_json::from_slice(&body_bytes) {
402401
Ok(value) => {
403402
let json: Value = value;
404403

src/duplicates.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use crate::subreddit::{can_access_quarantine, quarantine};
66
use crate::utils::{error, filter_posts, nsfw_landing, parse_post, template, Post, Preferences};
77

88
use askama::Template;
9-
use hyper::{Body, Request, Response};
9+
use hyper::{Request, Response};
10+
use crate::server::Body;
1011
use serde_json::Value;
1112
use std::borrow::ToOwned;
1213
use std::collections::HashSet;

src/instance_info.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use crate::{
55
};
66
use askama::Template;
77
use build_html::{Container, Html, HtmlContainer, Table};
8-
use hyper::{http::Error, Body, Request, Response};
8+
use hyper::{http::Error, Request, Response};
9+
use crate::server::{full, Body};
910
use serde::{Deserialize, Serialize};
1011
use std::sync::LazyLock;
1112
use time::OffsetDateTime;
@@ -33,42 +34,39 @@ pub async fn instance_info(req: Request<Body>) -> Result<Response<Body>, String>
3334
}
3435
.render()
3536
.unwrap();
36-
Response::builder().status(404).header("content-type", "text/html; charset=utf-8").body(error.into())
37+
Response::builder().status(404).header("content-type", "text/html; charset=utf-8").body(full(error))
3738
}
3839
};
3940
response.map_err(|err| format!("{err}"))
4041
}
4142

4243
fn info_json() -> Result<Response<Body>, Error> {
4344
if let Ok(body) = serde_json::to_string(&*INSTANCE_INFO) {
44-
Response::builder().status(200).header("content-type", "application/json").body(body.into())
45+
Response::builder().status(200).header("content-type", "application/json").body(full(body))
4546
} else {
4647
Response::builder()
4748
.status(500)
4849
.header("content-type", "text/plain")
49-
.body(Body::from("Error serializing JSON"))
50+
.body(full("Error serializing JSON"))
5051
}
5152
}
5253

5354
fn info_yaml() -> Result<Response<Body>, Error> {
5455
if let Ok(body) = serde_yaml_ng::to_string(&*INSTANCE_INFO) {
55-
// We can use `application/yaml` as media type, though there is no guarantee
56-
// that browsers will honor it. But we'll do it anyway. See:
57-
// https://github.com/ietf-wg-httpapi/mediatypes/blob/main/draft-ietf-httpapi-yaml-mediatypes.md#media-type-applicationyaml-application-yaml
58-
Response::builder().status(200).header("content-type", "application/yaml").body(body.into())
56+
Response::builder().status(200).header("content-type", "application/yaml").body(full(body))
5957
} else {
6058
Response::builder()
6159
.status(500)
6260
.header("content-type", "text/plain")
63-
.body(Body::from("Error serializing YAML."))
61+
.body(full("Error serializing YAML."))
6462
}
6563
}
6664

6765
fn info_txt() -> Result<Response<Body>, Error> {
6866
Response::builder()
6967
.status(200)
7068
.header("content-type", "text/plain")
71-
.body(Body::from(INSTANCE_INFO.to_string(&StringType::Raw)))
69+
.body(full(INSTANCE_INFO.to_string(&StringType::Raw)))
7270
}
7371
fn info_html(req: &Request<Body>) -> Result<Response<Body>, Error> {
7472
let message = MessageTemplate {
@@ -79,7 +77,7 @@ fn info_html(req: &Request<Body>) -> Result<Response<Body>, Error> {
7977
}
8078
.render()
8179
.unwrap();
82-
Response::builder().status(200).header("content-type", "text/html; charset=utf8").body(Body::from(message))
80+
Response::builder().status(200).header("content-type", "text/html; charset=utf8").body(full(message))
8381
}
8482
#[derive(Serialize, Deserialize, Default)]
8583
pub struct InstanceInfo {

src/main.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use clap::{Arg, ArgAction, Command};
77
use std::sync::LazyLock;
88

99
use futures_lite::FutureExt;
10-
use hyper::{header::HeaderValue, Body, Request, Response};
10+
use hyper::{header::HeaderValue, Request, Response};
11+
use redlib::server::{full, Body};
1112
use log::{info, warn};
1213
use redlib::client::{canonical_path, proxy, rate_limit_check, MEDIA_CLIENT};
1314
use redlib::server::{self, RequestExt};
@@ -24,7 +25,7 @@ async fn pwa_logo() -> Result<Response<Body>, String> {
2425
Response::builder()
2526
.status(200)
2627
.header("content-type", "image/png")
27-
.body(include_bytes!("../static/logo.png").as_ref().into())
28+
.body(full(include_bytes!("../static/logo.png").as_ref()))
2829
.unwrap_or_default(),
2930
)
3031
}
@@ -35,7 +36,7 @@ async fn iphone_logo() -> Result<Response<Body>, String> {
3536
Response::builder()
3637
.status(200)
3738
.header("content-type", "image/png")
38-
.body(include_bytes!("../static/apple-touch-icon.png").as_ref().into())
39+
.body(full(include_bytes!("../static/apple-touch-icon.png").as_ref()))
3940
.unwrap_or_default(),
4041
)
4142
}
@@ -46,7 +47,7 @@ async fn favicon() -> Result<Response<Body>, String> {
4647
.status(200)
4748
.header("content-type", "image/vnd.microsoft.icon")
4849
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
49-
.body(include_bytes!("../static/favicon.ico").as_ref().into())
50+
.body(full(include_bytes!("../static/favicon.ico").as_ref()))
5051
.unwrap_or_default(),
5152
)
5253
}
@@ -57,7 +58,7 @@ async fn font() -> Result<Response<Body>, String> {
5758
.status(200)
5859
.header("content-type", "font/woff2")
5960
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
60-
.body(include_bytes!("../static/Inter.var.woff2").as_ref().into())
61+
.body(full(include_bytes!("../static/Inter.var.woff2").as_ref()))
6162
.unwrap_or_default(),
6263
)
6364
}
@@ -68,7 +69,7 @@ async fn opensearch() -> Result<Response<Body>, String> {
6869
.status(200)
6970
.header("content-type", "application/opensearchdescription+xml")
7071
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
71-
.body(include_bytes!("../static/opensearch.xml").as_ref().into())
72+
.body(full(include_bytes!("../static/opensearch.xml").as_ref()))
7273
.unwrap_or_default(),
7374
)
7475
}
@@ -77,7 +78,7 @@ async fn resource(body: &str, content_type: &str, cache: bool) -> Result<Respons
7778
let mut res = Response::builder()
7879
.status(200)
7980
.header("content-type", content_type)
80-
.body(body.to_string().into())
81+
.body(full(body.to_string()))
8182
.unwrap_or_default();
8283

8384
if cache {
@@ -105,7 +106,7 @@ async fn style() -> Result<Response<Body>, String> {
105106
.status(200)
106107
.header("content-type", "text/css")
107108
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
108-
.body(Body::from(STYLE_CSS.clone()))
109+
.body(full(STYLE_CSS.clone()))
109110
.unwrap_or_default(),
110111
)
111112
}
@@ -428,7 +429,7 @@ pub async fn proxy_commit_info() -> Result<Response<Body>, String> {
428429
Response::builder()
429430
.status(200)
430431
.header("content-type", "application/atom+xml")
431-
.body(Body::from(fetch_commit_info().await))
432+
.body(full(fetch_commit_info().await))
432433
.unwrap_or_default(),
433434
)
434435
}
@@ -452,7 +453,7 @@ pub async fn proxy_instances() -> Result<Response<Body>, String> {
452453
Response::builder()
453454
.status(200)
454455
.header("content-type", "application/json")
455-
.body(Body::from(fetch_instances().await))
456+
.body(full(fetch_instances().await))
456457
.unwrap_or_default(),
457458
)
458459
}

src/post.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use crate::subreddit::{can_access_quarantine, quarantine};
88
use crate::utils::{
99
error, format_num, nsfw_landing, param, parse_post, rewrite_emotes, setting, template, time, val, Author, Awards, Comment, Flair, FlairPart, Post, Preferences,
1010
};
11-
use hyper::{Body, Request, Response};
11+
use hyper::{Request, Response};
12+
use crate::server::Body;
1213

1314
use askama::Template;
1415
use regex::Regex;

src/search.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use crate::{
88
subreddit::{can_access_quarantine, quarantine},
99
};
1010
use askama::Template;
11-
use hyper::{Body, Request, Response};
11+
use hyper::{Request, Response};
12+
use crate::server::Body;
1213
use regex::Regex;
1314
use std::sync::LazyLock;
1415

0 commit comments

Comments
 (0)