Skip to content

Commit 078e373

Browse files
committed
Upgrade hyper to 1.0
1 parent 5a650df commit 078e373

11 files changed

+727
-477
lines changed

Cargo.lock

+331-207
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hyper_cgi/Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ repository = "https://github.com/josh-project/josh"
99
readme = "README.md"
1010

1111
[dependencies]
12+
bytes = "1.5.0"
1213
futures = { workspace = true }
1314
tokio = { workspace = true }
1415
tokio-util = { workspace = true }
15-
hyper = { version = "0.14.28", features = ["stream", "tcp", "server", "http1"] }
16+
hyper = { version = "1.1.0", features = ["server", "http1"] }
17+
hyper-util = { version = "0.1.2" }
18+
http-body = "1.0.0"
19+
http-body-util = "0.1.0"
1620

1721
clap = { version = "4.5.2", optional = true }
1822
base64 = { version = "0.21.7", optional = true }

hyper_cgi/src/bin/hyper-cgi-test-server.rs

+46-30
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
#[macro_use]
22
extern crate lazy_static;
3+
use bytes::Bytes;
34
use core::iter;
45
use core::str::from_utf8;
6+
use http_body_util::Full;
7+
use hyper::server::conn::http1;
8+
use hyper::service::service_fn;
9+
use hyper_util::rt::{TokioIo, TokioTimer};
510
use rand::{distributions::Alphanumeric, thread_rng, Rng};
6-
use std::str::FromStr;
11+
use std::net::SocketAddr;
12+
use tokio::net::TcpListener;
713

814
use futures::FutureExt;
915

10-
use hyper::server::Server;
16+
use hyper::body::Incoming;
1117

1218
// Import the base64 crate Engine trait anonymously so we can
1319
// call its methods without adding to the namespace.
@@ -44,7 +50,7 @@ pub struct ServerState {
4450
users: Vec<(String, String)>,
4551
}
4652

47-
pub fn parse_auth(req: &hyper::Request<hyper::Body>) -> Option<(String, String)> {
53+
pub fn parse_auth(req: &hyper::Request<Incoming>) -> Option<(String, String)> {
4854
let line = some_or!(
4955
req.headers()
5056
.get("authorization")
@@ -69,9 +75,9 @@ pub fn parse_auth(req: &hyper::Request<hyper::Body>) -> Option<(String, String)>
6975
}
7076

7177
fn auth_response(
72-
req: &hyper::Request<hyper::Body>,
78+
req: &hyper::Request<Incoming>,
7379
users: &Vec<(String, String)>,
74-
) -> Option<hyper::Response<hyper::Body>> {
80+
) -> Option<hyper::Response<Full<Bytes>>> {
7581
if users.len() == 0 {
7682
return None;
7783
}
@@ -83,7 +89,7 @@ fn auth_response(
8389
let builder = hyper::Response::builder()
8490
.header("WWW-Authenticate", "Basic realm=User Visible Realm")
8591
.status(hyper::StatusCode::UNAUTHORIZED);
86-
return Some(builder.body(hyper::Body::empty()).unwrap());
92+
return Some(builder.body(Full::new(Bytes::new())).unwrap());
8793
}
8894
};
8995

@@ -102,17 +108,16 @@ fn auth_response(
102108
.status(hyper::StatusCode::UNAUTHORIZED);
103109
return Some(
104110
builder
105-
.body(hyper::Body::empty())
111+
.body(Full::new(Bytes::new()))
106112
.unwrap_or(hyper::Response::default()),
107113
);
108114
}
109115

110116
async fn call(
111117
serv: std::sync::Arc<std::sync::Mutex<ServerState>>,
112-
req: hyper::Request<hyper::Body>,
113-
) -> hyper::Response<hyper::Body> {
118+
req: hyper::Request<Incoming>,
119+
) -> hyper::Response<Full<Bytes>> {
114120
println!("call {:?}", req.uri().path());
115-
let mut req = req;
116121

117122
let path = req.uri().path();
118123

@@ -133,21 +138,22 @@ async fn call(
133138
for (u, p) in serv.lock().unwrap().users.iter() {
134139
if username == *u {
135140
password = p.clone();
136-
return builder.body(hyper::Body::from(password)).unwrap();
141+
return builder.body(Full::new(Bytes::from(password))).unwrap();
137142
}
138143
}
139144
serv.lock()
140145
.unwrap()
141146
.users
142147
.push((username, password.clone()));
143148
println!("users: {:?}", serv.lock().unwrap().users);
144-
return builder.body(hyper::Body::from(password)).unwrap();
149+
return builder.body(Full::new(Bytes::from(password))).unwrap();
145150
}
146151

147152
if let Some(response) = auth_response(&req, &serv.lock().unwrap().users) {
148153
return response;
149154
}
150155

156+
/*
151157
if let Some(proxy) = &ARGS.get_one::<String>("proxy") {
152158
for proxy in proxy.split(",") {
153159
if let [proxy_path, proxy_target] = proxy.split("=").collect::<Vec<_>>().as_slice() {
@@ -159,13 +165,14 @@ async fn call(
159165
Ok(response) => response,
160166
Err(error) => hyper::Response::builder()
161167
.status(hyper::StatusCode::INTERNAL_SERVER_ERROR)
162-
.body(hyper::Body::from(format!("Proxy error: {:?}", error)))
168+
.body(format!("Proxy error: {:?}", error))
163169
.unwrap(),
164170
};
165171
}
166172
}
167173
}
168174
}
175+
*/
169176

170177
let workdir = std::path::PathBuf::from(
171178
ARGS.get_one::<String>("dir")
@@ -184,34 +191,43 @@ async fn call(
184191
}
185192

186193
#[tokio::main]
187-
async fn main() {
194+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
188195
let server_state = std::sync::Arc::new(std::sync::Mutex::new(ServerState { users: vec![] }));
189196

190-
let make_service = hyper::service::make_service_fn(move |_| {
191-
let server_state = server_state.clone();
192-
193-
let service = hyper::service::service_fn(move |_req| {
194-
let server_state = server_state.clone();
195-
196-
call(server_state, _req).map(Ok::<_, hyper::http::Error>)
197-
});
198-
199-
futures::future::ok::<_, hyper::http::Error>(service)
200-
});
201-
202-
let addr = format!(
197+
let addr: SocketAddr = format!(
203198
"0.0.0.0:{}",
204199
ARGS.get_one::<String>("port")
205200
.unwrap_or(&"8000".to_owned())
206201
.to_owned()
207202
)
208203
.parse()
209204
.unwrap();
210-
let server = Server::bind(&addr).serve(make_service);
205+
206+
let listener = TcpListener::bind(addr).await?;
211207
println!("Now listening on {}", addr);
208+
let server_state = server_state.clone();
209+
210+
loop {
211+
let (tcp, _) = listener.accept().await?;
212+
let io = TokioIo::new(tcp);
213+
let server_state = server_state.clone();
212214

213-
if let Err(e) = server.await {
214-
eprintln!("server error: {}", e);
215+
tokio::task::spawn(async move {
216+
if let Err(err) = http1::Builder::new()
217+
.timer(TokioTimer::new())
218+
.serve_connection(
219+
io,
220+
service_fn(move |_req| {
221+
let server_state = server_state.clone();
222+
223+
call(server_state, _req).map(Ok::<_, hyper::http::Error>)
224+
}),
225+
)
226+
.await
227+
{
228+
println!("Error serving connection: {:?}", err);
229+
}
230+
});
215231
}
216232
}
217233

hyper_cgi/src/hyper_cgi.rs

+29-24
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
//! This module implements a do_cgi function, to run CGI scripts with hyper
2-
use futures::stream::StreamExt;
2+
use bytes::Bytes;
33
use futures::TryStreamExt;
4-
use hyper::{Request, Response};
4+
use http_body_util::{BodyStream, Full};
5+
use hyper::{body::Body, Request, Response};
6+
use std::io;
57
use std::process::Stdio;
68
use std::str::FromStr;
79
use tokio::io::AsyncBufReadExt;
810
use tokio::io::AsyncReadExt;
11+
use tokio::io::AsyncWriteExt;
912
use tokio::io::BufReader;
1013
use tokio::process::Command;
1114

1215
/// do_cgi is an async function that takes an hyper request and a CGI compatible
1316
/// command, and passes the request to be executed to the command.
1417
/// It then returns an hyper response and the stderr output of the command.
15-
pub async fn do_cgi(
16-
req: Request<hyper::Body>,
18+
pub async fn do_cgi<B, E>(
19+
req: Request<B>,
1720
cmd: Command,
18-
) -> (hyper::http::Response<hyper::Body>, Vec<u8>) {
21+
) -> (hyper::http::Response<Full<Bytes>>, Vec<u8>)
22+
where
23+
B: hyper::body::Body<Error = E>,
24+
E: std::error::Error + Sync + Send + 'static,
25+
{
1926
let mut cmd = cmd;
2027
setup_cmd(&mut cmd, &req);
2128

@@ -56,22 +63,23 @@ pub async fn do_cgi(
5663
}
5764
};
5865

59-
let req_body = req
60-
.into_body()
61-
.map(|result| {
62-
result.map_err(|_error| std::io::Error::new(std::io::ErrorKind::Other, "Error!"))
63-
})
64-
.into_async_read();
66+
let stream_of_frames = BodyStream::new(req.into_body());
67+
let stream_of_bytes = stream_of_frames
68+
.try_filter_map(|frame| async move { Ok(frame.into_data().ok()) })
69+
.map_err(|err| io::Error::new(io::ErrorKind::Other, err));
70+
let async_read = tokio_util::io::StreamReader::new(stream_of_bytes);
71+
let mut req_body = std::pin::pin!(async_read);
6572

66-
let mut req_body = to_tokio_async_read(req_body);
6773
let mut err_output = vec![];
6874

6975
let mut stdout = BufReader::new(stdout);
7076

7177
let mut data = vec![];
7278
let write_stdin = async {
7379
let mut stdin = stdin;
74-
tokio::io::copy(&mut req_body, &mut stdin).await
80+
let res = tokio::io::copy(&mut req_body, &mut stdin).await;
81+
stdin.flush().await.unwrap();
82+
res
7583
};
7684

7785
let read_stderr = async {
@@ -105,7 +113,7 @@ pub async fn do_cgi(
105113
line = String::new();
106114
}
107115
stdout.read_to_end(&mut data).await?;
108-
convert_error_io_hyper(response.body(hyper::Body::from(data)))
116+
convert_error_io_hyper(response.body(Full::new(Bytes::from(data))))
109117
};
110118

111119
let wait_process = async { child.wait().await };
@@ -119,7 +127,7 @@ pub async fn do_cgi(
119127
(error_response(), err_output)
120128
}
121129

122-
fn setup_cmd(cmd: &mut Command, req: &Request<hyper::Body>) {
130+
fn setup_cmd(cmd: &mut Command, req: &Request<impl Body>) {
123131
cmd.stdout(Stdio::piped());
124132
cmd.stderr(Stdio::piped());
125133
cmd.stdin(Stdio::piped());
@@ -157,14 +165,10 @@ fn setup_cmd(cmd: &mut Command, req: &Request<hyper::Body>) {
157165
);
158166
}
159167

160-
fn to_tokio_async_read(r: impl futures::io::AsyncRead) -> impl tokio::io::AsyncRead {
161-
tokio_util::compat::FuturesAsyncReadCompatExt::compat(r)
162-
}
163-
164-
fn error_response() -> hyper::Response<hyper::Body> {
168+
fn error_response() -> hyper::Response<Full<Bytes>> {
165169
Response::builder()
166170
.status(hyper::StatusCode::INTERNAL_SERVER_ERROR)
167-
.body(hyper::Body::empty())
171+
.body(Full::new(Bytes::new()))
168172
.unwrap()
169173
}
170174

@@ -177,7 +181,8 @@ fn convert_error_io_hyper<T>(res: Result<T, hyper::http::Error>) -> Result<T, st
177181

178182
#[cfg(test)]
179183
mod tests {
180-
use hyper::body::HttpBody;
184+
use bytes::Bytes;
185+
use http_body_util::{BodyExt, Full};
181186

182187
#[tokio::test]
183188
async fn run_cmd() {
@@ -193,7 +198,7 @@ mod tests {
193198
.header("Accept-Encoding", "deflate, gzip, br")
194199
.header("Accept-Language", "en-US, *;q=0.9")
195200
.header("Pragma", "no-cache")
196-
.body(hyper::Body::from("\r\na body"))
201+
.body(Full::new(Bytes::from("\r\na body")))
197202
.unwrap();
198203

199204
let mut cmd = tokio::process::Command::new("cat");
@@ -203,7 +208,7 @@ mod tests {
203208

204209
assert_eq!(resp.status(), hyper::StatusCode::OK);
205210

206-
let resp_string = resp.into_body().data().await.unwrap().unwrap().to_vec();
211+
let resp_string = resp.into_body().collect().await.unwrap().to_bytes();
207212
let resp_string = String::from_utf8_lossy(&resp_string);
208213

209214
assert_eq!("", std::str::from_utf8(&stderr).unwrap());

josh-proxy/Cargo.toml

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ repository = "https://github.com/josh-project/josh"
1010
version = "22.4.15"
1111

1212
[dependencies]
13-
bytes = "1.5.0"
1413
base64 = "0.21.7"
14+
bytes = "1.5.0"
1515
clap = { workspace = true }
1616
futures = { workspace = true }
17-
hyper = { version = "0.14.28", features = ["stream"] }
17+
hyper = { version = "1.1.0" }
1818
hyper-reverse-proxy = { workspace = true }
19-
hyper-staticfile = "0.9.5"
20-
hyper-tls = "0.5.0"
19+
hyper-staticfile = "0.10.0"
20+
hyper-tls = "0.6.0"
2121
hyper_cgi = { path = "../hyper_cgi" }
22+
hyper-util = { version = "0.1.2", features = ["client", "client-legacy", "http1", "tokio"] }
23+
http-body-util = "0.1.0"
2224
indoc = "2.0.4"
2325
josh = {path = "../josh-core" }
2426
lazy_static = { workspace = true }

josh-proxy/src/auth.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
// call its methods without adding to the namespace.
33
use base64::engine::general_purpose::STANDARD as BASE64;
44
use base64::engine::Engine as _;
5+
use bytes::Bytes;
6+
use http_body_util::Empty;
7+
use hyper::body::Incoming;
8+
use hyper_util::client::legacy::Client;
9+
use hyper_util::rt::TokioExecutor;
510

611
lazy_static! {
712
static ref AUTH: std::sync::Mutex<std::collections::HashMap<Handle, Header>> =
@@ -90,7 +95,7 @@ pub async fn check_auth(url: &str, auth: &Handle, required: bool) -> josh::JoshR
9095
tracing::trace!("no cached auth {:?}", *AUTH_TIMERS.lock()?);
9196

9297
let https = hyper_tls::HttpsConnector::new();
93-
let client = hyper::Client::builder().build::<_, hyper::Body>(https);
98+
let client = Client::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(https);
9499

95100
let password = AUTH
96101
.lock()?
@@ -109,7 +114,7 @@ pub async fn check_auth(url: &str, auth: &Handle, required: bool) -> josh::JoshR
109114
builder
110115
};
111116

112-
let request = builder.body(hyper::Body::empty())?;
117+
let request = builder.body(Empty::new())?;
113118
let resp = client.request(request).await?;
114119

115120
let status = resp.status();
@@ -125,19 +130,16 @@ pub async fn check_auth(url: &str, auth: &Handle, required: bool) -> josh::JoshR
125130
Ok(true)
126131
} else if status == hyper::StatusCode::UNAUTHORIZED {
127132
tracing::warn!("resp.status == 401: {:?}", &err_msg);
128-
tracing::trace!(
129-
"body: {:?}",
130-
std::str::from_utf8(&hyper::body::to_bytes(resp.into_body()).await?)
131-
);
133+
tracing::trace!("body: {:?}", resp.into_body());
132134
Ok(false)
133135
} else {
134136
return Err(josh::josh_error(&err_msg));
135137
}
136138
}
137139

138140
pub fn strip_auth(
139-
req: hyper::Request<hyper::Body>,
140-
) -> josh::JoshResult<(Handle, hyper::Request<hyper::Body>)> {
141+
req: hyper::Request<Incoming>,
142+
) -> josh::JoshResult<(Handle, hyper::Request<Incoming>)> {
141143
let mut req = req;
142144
let header: Option<hyper::header::HeaderValue> =
143145
req.headers_mut().remove(hyper::header::AUTHORIZATION);

0 commit comments

Comments
 (0)