xref: /xiu/protocol/hls/src/server.rs (revision bd35295b)
122fbd29aSHarlanC use {
2a3d19cccSHarlanC     hyper::{
3a3d19cccSHarlanC         service::{make_service_fn, service_fn},
4a3d19cccSHarlanC         Body, Request, Response, Server, StatusCode,
5a3d19cccSHarlanC     },
6a3d19cccSHarlanC     tokio::fs::File,
7a3d19cccSHarlanC     tokio_util::codec::{BytesCodec, FramedRead},
822fbd29aSHarlanC };
922fbd29aSHarlanC 
1022fbd29aSHarlanC type GenericError = Box<dyn std::error::Error + Send + Sync>;
1122fbd29aSHarlanC type Result<T> = std::result::Result<T, GenericError>;
1222fbd29aSHarlanC static NOTFOUND: &[u8] = b"Not Found";
1322fbd29aSHarlanC 
handle_connection(req: Request<Body>) -> Result<Response<Body>>1422fbd29aSHarlanC async fn handle_connection(req: Request<Body>) -> Result<Response<Body>> {
1522fbd29aSHarlanC     let path = req.uri().path();
1622fbd29aSHarlanC 
1722fbd29aSHarlanC     let mut file_path: String = String::from("");
1822fbd29aSHarlanC 
1922fbd29aSHarlanC     if path.ends_with(".m3u8") {
2022fbd29aSHarlanC         //http://127.0.0.1/app_name/stream_name/stream_name.m3u8
2122fbd29aSHarlanC         let m3u8_index = path.find(".m3u8").unwrap();
2222fbd29aSHarlanC 
2322fbd29aSHarlanC         if m3u8_index > 0 {
2422fbd29aSHarlanC             let (left, _) = path.split_at(m3u8_index);
2585c0af6aSLuca Barbato             let rv: Vec<_> = left.split('/').collect();
2622fbd29aSHarlanC 
2722fbd29aSHarlanC             let app_name = String::from(rv[1]);
2822fbd29aSHarlanC             let stream_name = String::from(rv[2]);
2922fbd29aSHarlanC 
30*bd35295bSHarlan             file_path = format!("./{app_name}/{stream_name}/{stream_name}.m3u8");
3122fbd29aSHarlanC         }
3222fbd29aSHarlanC     } else if path.ends_with(".ts") {
3322fbd29aSHarlanC         //http://127.0.0.1/app_name/stream_name/ts_name.m3u8
3422fbd29aSHarlanC         let ts_index = path.find(".ts").unwrap();
3522fbd29aSHarlanC 
3622fbd29aSHarlanC         if ts_index > 0 {
3722fbd29aSHarlanC             let (left, _) = path.split_at(ts_index);
3888325f54SHarlanC 
3985c0af6aSLuca Barbato             let rv: Vec<_> = left.split('/').collect();
4022fbd29aSHarlanC 
4122fbd29aSHarlanC             let app_name = String::from(rv[1]);
4222fbd29aSHarlanC             let stream_name = String::from(rv[2]);
4322fbd29aSHarlanC             let ts_name = String::from(rv[3]);
4422fbd29aSHarlanC 
45*bd35295bSHarlan             file_path = format!("./{app_name}/{stream_name}/{ts_name}.ts");
4622fbd29aSHarlanC         }
4722fbd29aSHarlanC     }
4822fbd29aSHarlanC 
4985c0af6aSLuca Barbato     simple_file_send(file_path.as_str()).await
5022fbd29aSHarlanC }
5122fbd29aSHarlanC 
5222fbd29aSHarlanC /// HTTP status code 404
not_found() -> Response<Body>5322fbd29aSHarlanC fn not_found() -> Response<Body> {
5422fbd29aSHarlanC     Response::builder()
5522fbd29aSHarlanC         .status(StatusCode::NOT_FOUND)
5622fbd29aSHarlanC         .body(NOTFOUND.into())
5722fbd29aSHarlanC         .unwrap()
5822fbd29aSHarlanC }
5922fbd29aSHarlanC 
simple_file_send(filename: &str) -> Result<Response<Body>>6022fbd29aSHarlanC async fn simple_file_send(filename: &str) -> Result<Response<Body>> {
6122fbd29aSHarlanC     // Serve a file by asynchronously reading it by chunks using tokio-util crate.
6222fbd29aSHarlanC 
6322fbd29aSHarlanC     if let Ok(file) = File::open(filename).await {
6422fbd29aSHarlanC         let stream = FramedRead::new(file, BytesCodec::new());
6522fbd29aSHarlanC         let body = Body::wrap_stream(stream);
6622fbd29aSHarlanC         return Ok(Response::new(body));
6722fbd29aSHarlanC     }
6822fbd29aSHarlanC 
6922fbd29aSHarlanC     Ok(not_found())
7022fbd29aSHarlanC }
7122fbd29aSHarlanC 
run(port: usize) -> Result<()>7223a14778SHarlan pub async fn run(port: usize) -> Result<()> {
73*bd35295bSHarlan     let listen_address = format!("0.0.0.0:{port}");
7453a2e033SHarlanC     let sock_addr = listen_address.parse().unwrap();
7522fbd29aSHarlanC 
7623a14778SHarlan     let new_service =
7723a14778SHarlan         make_service_fn(move |_| async { Ok::<_, GenericError>(service_fn(handle_connection)) });
7822fbd29aSHarlanC 
7953a2e033SHarlanC     let server = Server::bind(&sock_addr).serve(new_service);
8088325f54SHarlanC     log::info!("Hls server listening on http://{}", sock_addr);
8122fbd29aSHarlanC     server.await?;
8222fbd29aSHarlanC 
8322fbd29aSHarlanC     Ok(())
8422fbd29aSHarlanC }
85