Skip to content

Instantly share code, notes, and snippets.

@Ciantic
Last active March 6, 2024 20:05
Show Gist options
  • Save Ciantic/aa97c7a72f8356d7980756c819563566 to your computer and use it in GitHub Desktop.
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)
[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"
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