diff --git a/Cargo.lock b/Cargo.lock index 5f6591960..6af97d718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,28 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "actix-files" +version = "0.6.0-beta.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b864740ed79d26e6e3c33fd2a1e03a071daaa43c88e6900ff1f9378fca88ce" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", +] + [[package]] name = "actix-http" version = "3.0.0-beta.8" @@ -233,6 +255,12 @@ version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" +[[package]] +name = "askama_escape" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb" + [[package]] name = "autocfg" version = "1.0.1" @@ -625,6 +653,12 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-range" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee9694f83d9b7c09682fdb32213682939507884e5bcf227be9aff5d644b90dc" + [[package]] name = "httparse" version = "1.4.1" @@ -793,6 +827,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -1132,6 +1176,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" name = "robyn" version = "0.5.3" dependencies = [ + "actix-files", "actix-web", "anyhow", "dashmap", @@ -1496,6 +1541,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.5" diff --git a/Cargo.toml b/Cargo.toml index a46e787c1..ec3ff9016 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ dashmap = {git = "https://github.com/quake/dashmap", branch = "parking_lot", fea pyo3-asyncio = { version="0.14.0" , features = ["attributes", "tokio-runtime"] } anyhow = "1.0.38" actix-web = "4.0.0-beta.8" +actix-files = "0.6.0-beta.4" futures-util = "0.3.15" [package.metadata.maturin] diff --git a/robyn/__init__.py b/robyn/__init__.py index c4f7fe1d9..398e14628 100644 --- a/robyn/__init__.py +++ b/robyn/__init__.py @@ -44,6 +44,9 @@ def add_route(self, route_type, endpoint, handler): route_type, endpoint, handler, asyncio.iscoroutinefunction(handler) ) + def add_directory(self, route, directory_path, index_file=None, show_files_listing=False): + self.server.add_directory(route, directory_path, index_file, show_files_listing) + def add_header(self, key, value): self.server.add_header(key, value) diff --git a/src/lib.rs b/src/lib.rs index 873ca5c7a..c3113a562 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,18 +9,9 @@ use server::Server; use pyo3::prelude::*; use pyo3::wrap_pyfunction; -#[pyfunction] -pub fn start_server() { - // this is a wrapper function for python - // to start a server - Server::new(); -} - #[pymodule] pub fn robyn(_py: Python<'_>, m: &PyModule) -> PyResult<()> { // the pymodule class to make the rustPyFunctions available - // in python - m.add_wrapped(wrap_pyfunction!(start_server))?; m.add_class::()?; pyo3::prepare_freethreaded_python(); Ok(()) diff --git a/src/server.rs b/src/server.rs index 2f8f8eb97..54ab1126e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,9 +1,10 @@ use crate::processor::{apply_headers, handle_request}; use crate::router::Router; use crate::types::Headers; +use actix_files::Files; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering::{Relaxed, SeqCst}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::thread; // pyO3 module use actix_web::*; @@ -14,10 +15,19 @@ use pyo3::types::PyAny; // hyper modules static STARTED: AtomicBool = AtomicBool::new(false); +#[derive(Clone)] +struct Directory { + route: String, + directory_path: String, + index_file: Option, + show_files_listing: bool, +} + #[pyclass] pub struct Server { router: Arc, headers: Arc>, + directories: Arc>>, } #[pymethods] @@ -27,6 +37,7 @@ impl Server { Self { router: Arc::new(Router::new()), headers: Arc::new(DashMap::new()), + directories: Arc::new(Mutex::new(Vec::new())), } } @@ -41,6 +52,7 @@ impl Server { let router = self.router.clone(); let headers = self.headers.clone(); + let directories = self.directories.clone(); let asyncio = py.import("asyncio").unwrap(); @@ -56,9 +68,29 @@ impl Server { let addr = format!("127.0.0.1:{}", port); HttpServer::new(move || { + let mut app = App::new(); + let directories = directories.lock().unwrap(); + for directory in directories.iter() { + if let Some(index_file) = &directory.index_file { + app = app.service( + Files::new(&directory.route, &directory.directory_path) + .index_file(index_file) + .redirect_to_slash_directory(), // .show_files_listing(), // .index_file(index_file), + ); + } else if directory.show_files_listing { + app = app.service( + Files::new(&directory.route, &directory.directory_path) + .redirect_to_slash_directory() + .show_files_listing(), + ); + } else { + app = app + .service(Files::new(&directory.route, &directory.directory_path)); + } + } + let event_loop_hdl = event_loop_hdl.clone(); - App::new() - .app_data(web::Data::new(router.clone())) + app.app_data(web::Data::new(router.clone())) .app_data(web::Data::new(headers.clone())) .default_service(web::route().to(move |router, headers, payload, req| { pyo3_asyncio::tokio::scope_local(event_loop_hdl.clone(), async move { @@ -77,6 +109,21 @@ impl Server { event_loop.call_method0("run_forever").unwrap(); } + pub fn add_directory( + &mut self, + route: String, + directory_path: String, + index_file: Option, + show_files_listing: bool, + ) { + self.directories.lock().unwrap().push(Directory { + route, + directory_path, + index_file, + show_files_listing, + }); + } + /// Adds a new header to our concurrent hashmap /// this can be called after the server has started. pub fn add_header(&self, key: &str, value: &str) { @@ -110,7 +157,7 @@ async fn index( handle_request(handler_function, &headers, &mut payload, &req).await } None => { - let mut response = HttpResponse::NotFound(); + let mut response = HttpResponse::Ok(); apply_headers(&mut response, &headers); response.finish() } diff --git a/test.py b/test.py index 9caf3df21..d1f032db4 100644 --- a/test.py +++ b/test.py @@ -54,4 +54,5 @@ def blocker(): if __name__ == "__main__": app.add_header("server", "robyn") - app.start(port=5001) + app.add_directory(route="/",directory_path="./test_dir/build", index_file="index.html") + app.start(port=5000) diff --git a/test_fast_api.py b/test_fast_api.py deleted file mode 100644 index 423a50285..000000000 --- a/test_fast_api.py +++ /dev/null @@ -1,15 +0,0 @@ -from fastapi import FastAPI -import asyncio - -app = FastAPI() - - -@app.get("/") -async def root(): - return {"message": "Hello World"} - -@app.get("/sleeper") -async def sleeper(): - await asyncio.sleep(5) - return {"message": "Hello World"} -