xref: /xiu/protocol/hls/src/server.rs (revision bd35295b)
1 use {
2     hyper::{
3         service::{make_service_fn, service_fn},
4         Body, Request, Response, Server, StatusCode,
5     },
6     tokio::fs::File,
7     tokio_util::codec::{BytesCodec, FramedRead},
8 };
9 
10 type GenericError = Box<dyn std::error::Error + Send + Sync>;
11 type Result<T> = std::result::Result<T, GenericError>;
12 static NOTFOUND: &[u8] = b"Not Found";
13 
handle_connection(req: Request<Body>) -> Result<Response<Body>>14 async fn handle_connection(req: Request<Body>) -> Result<Response<Body>> {
15     let path = req.uri().path();
16 
17     let mut file_path: String = String::from("");
18 
19     if path.ends_with(".m3u8") {
20         //http://127.0.0.1/app_name/stream_name/stream_name.m3u8
21         let m3u8_index = path.find(".m3u8").unwrap();
22 
23         if m3u8_index > 0 {
24             let (left, _) = path.split_at(m3u8_index);
25             let rv: Vec<_> = left.split('/').collect();
26 
27             let app_name = String::from(rv[1]);
28             let stream_name = String::from(rv[2]);
29 
30             file_path = format!("./{app_name}/{stream_name}/{stream_name}.m3u8");
31         }
32     } else if path.ends_with(".ts") {
33         //http://127.0.0.1/app_name/stream_name/ts_name.m3u8
34         let ts_index = path.find(".ts").unwrap();
35 
36         if ts_index > 0 {
37             let (left, _) = path.split_at(ts_index);
38 
39             let rv: Vec<_> = left.split('/').collect();
40 
41             let app_name = String::from(rv[1]);
42             let stream_name = String::from(rv[2]);
43             let ts_name = String::from(rv[3]);
44 
45             file_path = format!("./{app_name}/{stream_name}/{ts_name}.ts");
46         }
47     }
48 
49     simple_file_send(file_path.as_str()).await
50 }
51 
52 /// HTTP status code 404
not_found() -> Response<Body>53 fn not_found() -> Response<Body> {
54     Response::builder()
55         .status(StatusCode::NOT_FOUND)
56         .body(NOTFOUND.into())
57         .unwrap()
58 }
59 
simple_file_send(filename: &str) -> Result<Response<Body>>60 async fn simple_file_send(filename: &str) -> Result<Response<Body>> {
61     // Serve a file by asynchronously reading it by chunks using tokio-util crate.
62 
63     if let Ok(file) = File::open(filename).await {
64         let stream = FramedRead::new(file, BytesCodec::new());
65         let body = Body::wrap_stream(stream);
66         return Ok(Response::new(body));
67     }
68 
69     Ok(not_found())
70 }
71 
run(port: usize) -> Result<()>72 pub async fn run(port: usize) -> Result<()> {
73     let listen_address = format!("0.0.0.0:{port}");
74     let sock_addr = listen_address.parse().unwrap();
75 
76     let new_service =
77         make_service_fn(move |_| async { Ok::<_, GenericError>(service_fn(handle_connection)) });
78 
79     let server = Server::bind(&sock_addr).serve(new_service);
80     log::info!("Hls server listening on http://{}", sock_addr);
81     server.await?;
82 
83     Ok(())
84 }
85