Last active
March 6, 2024 20:05
-
-
Save Ciantic/aa97c7a72f8356d7980756c819563566 to your computer and use it in GitHub Desktop.
This example shows how to stream a file or shell execution stdout using Hyper and Futures (Rust)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "yourpackage" | |
version = "0.1.0" | |
authors = ["John Doe"] | |
edition = "2018" | |
[[bin]] | |
name = "example" | |
path = "stream-a-file-using-rust-hyper.rs" | |
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
[dependencies] | |
hyper = "0.13" | |
tokio = { version = "0.2", features = ["full"] } | |
futures-util = "0.3.1" | |
tokio-util = "0.2.0" | |
bytes = "0.5.3" | |
futures = "0.3.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::convert::Infallible; | |
use std::net::SocketAddr; | |
use tokio::fs::File; | |
use futures_util::TryStreamExt; | |
use futures_util::TryFutureExt; | |
use hyper::{Body, Request, Response, Server}; | |
use hyper::service::{make_service_fn, service_fn}; | |
use hyper::{Method, StatusCode}; | |
use bytes::BytesMut; | |
use tokio_util::codec::{BytesCodec, FramedRead}; | |
use tokio::process::Command; | |
use std::process::Stdio; | |
async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible> { | |
match (req.method(), req.uri().path()) { | |
// Stream a file from a disk | |
(&Method::GET, "/file") => { | |
let stream = File::open("C:\\Source\\Backup_Ignore.txt") | |
.map_ok(|file| FramedRead::new(file, BytesCodec::new()).map_ok(BytesMut::freeze)) | |
.try_flatten_stream(); | |
let s = Body::wrap_stream(stream); | |
let response = Response::new(s); | |
return Ok(response); | |
}, | |
// Stream from shell execute, e.g. using "curl" executable | |
// | |
// Borrows from: https://github.com/tokio-rs/tokio/blob/master/tokio/src/process/mod.rs | |
(&Method::GET, "/exec") => { | |
let mut cmd = Command::new("curl"); | |
cmd.arg("https://example.com/") | |
.stdout(Stdio::piped()) | |
.stderr(Stdio::piped()); | |
let mut child = cmd.spawn().expect("panic! failed to spawn"); | |
let stdout = child.stdout().take().expect("panic! stdout failed!"); | |
let st = FramedRead::new(stdout, BytesCodec::new()).map_ok(BytesMut::freeze); | |
let s = Body::wrap_stream(st); | |
let response = Response::new(s); | |
// // Ensure the child process is spawned in the runtime so it can | |
// // make progress on its own while we await for any output. | |
// tokio::spawn(async { | |
// let status = child.await | |
// .expect("child process encountered an error"); | |
// println!("child status was: {}", status); | |
// }); | |
return Ok(response); | |
}, | |
// 404 not found | |
_ => { | |
let mut response = Response::new(Body::empty()); | |
*response.status_mut() = StatusCode::NOT_FOUND; | |
return Ok(response); | |
}, | |
}; | |
} | |
#[tokio::main] | |
async fn main() { | |
let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); | |
let make_svc = make_service_fn(|_conn| async { | |
Ok::<_, Infallible>(service_fn(handle_request)) | |
}); | |
let server = Server::bind(&addr).serve(make_svc); | |
if let Err(e) = server.await { | |
eprintln!("server error: {}", e); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment