diff --git a/Cargo.toml b/Cargo.toml index 876a720e..cb54ac38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,8 @@ edition = "2018" r2d2 = "^0.8.7" redis = { version = "^0.21.1", features = ['r2d2'] } log = "^0.4" -iron = "^0.6.1" -urlencoded = "^0.6" -router = "^0.6" +routerify = "3.0.0" +hyper = "^0.14" serde = "^1.0" serde_json = "^1.0" spaceapi = "^0.8.1" @@ -38,3 +37,4 @@ quick-error = "2.0" [dev-dependencies] env_logger = "^0.9.0" +tokio = { version = "*", features = ["full"] } diff --git a/examples/simple.rs b/examples/simple.rs index b693ab50..ba5b22a7 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,7 +1,8 @@ use spaceapi_server::api; use spaceapi_server::SpaceapiServerBuilder; -fn main() { +#[tokio::main] +async fn main() { // Create new minimal Status instance compatible with v0.13 and v14 let status = api::StatusBuilder::mixed("coredump") .logo("https://www.coredump.ch/logo.png") @@ -29,5 +30,8 @@ fn main() { .unwrap(); // Serve! - let _ = server.serve("127.0.0.1:8000"); + let _ = server + .serve("127.0.0.1:8000") + .await + .expect("Could not start the server"); } diff --git a/examples/with_custom_redis_pool.rs b/examples/with_custom_redis_pool.rs index fe5277e9..a9a3c98b 100644 --- a/examples/with_custom_redis_pool.rs +++ b/examples/with_custom_redis_pool.rs @@ -32,7 +32,8 @@ impl StatusModifier for OpenStatusFromRedisModifier { } } -fn main() { +#[tokio::main] +async fn main() { env_logger::init(); let status = api::StatusBuilder::mixed("Mittelab") @@ -81,5 +82,6 @@ fn main() { // Serve! server .serve("127.0.0.1:8000") + .await .expect("Could not start the server"); } diff --git a/examples/with_sensors.rs b/examples/with_sensors.rs index 1aea88b8..a563f97d 100644 --- a/examples/with_sensors.rs +++ b/examples/with_sensors.rs @@ -3,7 +3,8 @@ use spaceapi_server::api::sensors::{PeopleNowPresentSensorTemplate, TemperatureS use spaceapi_server::modifiers::StateFromPeopleNowPresent; use spaceapi_server::SpaceapiServerBuilder; -fn main() { +#[tokio::main] +async fn main() { env_logger::init(); // Create new minimal Status instance compatible with v0.13 and v14 @@ -63,5 +64,6 @@ fn main() { // Serve! server .serve("127.0.0.1:8000") + .await .expect("Could not start the server"); } diff --git a/src/lib.rs b/src/lib.rs index 95eb0ea3..3258258a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,15 +188,14 @@ pub use spaceapi as api; -pub use iron::error::HttpResult; -pub use iron::Listening; +//pub use iron::error::HttpResult; +//pub use iron::Listening; mod errors; pub mod modifiers; mod sensors; mod server; mod types; - pub use crate::errors::SpaceapiServerError; pub use crate::server::SpaceapiServer; pub use crate::server::SpaceapiServerBuilder; diff --git a/src/server/handlers.rs b/src/server/handlers.rs index 65556c10..601d77d9 100644 --- a/src/server/handlers.rs +++ b/src/server/handlers.rs @@ -1,15 +1,20 @@ //! Handlers for the server. -use iron::modifiers::Header; -use iron::prelude::*; -use iron::{headers, middleware, status}; +use hyper::header::{ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE}; +use std::convert::Infallible; +// use iron::modifiers::Header; +// use iron::prelude::*; +// use iron::{headers, middleware, status}; +use hyper::{Body, Request, Response, Server, StatusCode}; use log::{debug, error, info, warn}; -use router::Router; +use routerify::prelude::*; +// use router::Router; use serde::ser::{Serialize, SerializeMap, Serializer}; use crate::api; -use crate::modifiers; +//use crate::modifiers; use crate::sensors; +use crate::server::SpaceapiServer; use crate::types::RedisPool; #[derive(Debug)] @@ -29,96 +34,57 @@ impl Serialize for ErrorResponse { } } -pub(crate) struct ReadHandler { - status: api::Status, - redis_pool: RedisPool, - sensor_specs: sensors::SafeSensorSpecs, - status_modifiers: Vec>, -} - -impl ReadHandler { - pub(crate) fn new( - status: api::Status, - redis_pool: RedisPool, - sensor_specs: sensors::SafeSensorSpecs, - status_modifiers: Vec>, - ) -> ReadHandler { - ReadHandler { - status, - redis_pool, - sensor_specs, - status_modifiers, - } - } - - fn build_response_json(&self) -> String { - // Create a mutable copy of the status struct - let mut status_copy = self.status.clone(); - - // Process registered sensors - for sensor_spec in self.sensor_specs.iter() { - match sensor_spec.get_sensor_value(&self.redis_pool) { - // Value could be read successfullly - Ok(value) => { - if status_copy.sensors.is_none() { - status_copy.sensors = Some(api::Sensors { - people_now_present: vec![], - temperature: vec![], - }); - } - sensor_spec - .template - .to_sensor(&value, &mut status_copy.sensors.as_mut().unwrap()); +pub async fn json_response_handler(req: Request) -> Result, Infallible> { + // Create a mutable copy of the status struct + let state = req.data::().unwrap(); + let mut status_copy = state.status.clone(); + + // Process registered sensors + for sensor_spec in state.sensor_specs.iter() { + match sensor_spec.get_sensor_value(&state.redis_pool) { + // Value could be read successfullly + Ok(value) => { + if status_copy.sensors.is_none() { + status_copy.sensors = Some(api::Sensors { + people_now_present: vec![], + temperature: vec![], + }); } + sensor_spec + .template + .to_sensor(&value, &mut status_copy.sensors.as_mut().unwrap()); + } - // Value could not be read, do error logging - Err(err) => { - warn!( - "Could not retrieve key '{}' from Redis, omiting the sensor", - &sensor_spec.data_key - ); - match err { - sensors::SensorError::Redis(e) => debug!("Error: {:?}", e), - sensors::SensorError::R2d2(e) => debug!("Error: {:?}", e), - sensors::SensorError::UnknownSensor(e) => warn!("Error: {:?}", e), - } + // Value could not be read, do error logging + Err(err) => { + warn!( + "Could not retrieve key '{}' from Redis, omiting the sensor", + &sensor_spec.data_key + ); + match err { + sensors::SensorError::Redis(e) => debug!("Error: {:?}", e), + sensors::SensorError::R2d2(e) => debug!("Error: {:?}", e), + sensors::SensorError::UnknownSensor(e) => warn!("Error: {:?}", e), } } } - - for status_modifier in &self.status_modifiers { - status_modifier.modify(&mut status_copy); - } - - // Serialize to JSON - serde_json::to_string(&status_copy).expect( - "Status object could not be serialized to JSON. \ - Please open an issue at https://github.com/spaceapi-community/spaceapi-server-rs/issues", - ) } -} -impl middleware::Handler for ReadHandler { - /// Return the current status JSON. - fn handle(&self, req: &mut Request) -> IronResult { - info!("{} /{} from {}", req.method, req.url.path()[0], req.remote_addr); - - // Get response body - let body = self.build_response_json(); - - // Create response - let response = Response::with((status::Ok, body)) - // Set headers - .set(Header(headers::ContentType( - "application/json; charset=utf-8".parse().unwrap(), - ))) - .set(Header(headers::CacheControl(vec![ - headers::CacheDirective::NoCache, - ]))) - .set(Header(headers::AccessControlAllowOrigin::Any)); - - Ok(response) + for status_modifier in &state.status_modifiers { + status_modifier.modify(&mut status_copy); } + + // Serialize to JSON + let response = Response::builder() + .status(StatusCode::OK) + .header(CONTENT_TYPE, "application/json; charset=utf-8") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Body::from(serde_json::to_string(&status_copy).expect( + "Status object could not be serialized to JSON. \ + Please open an issue at https://github.com/spaceapi-community/spaceapi-server-rs/issues", + ))) + .unwrap(); + Ok(response) } pub(crate) struct UpdateHandler { @@ -147,6 +113,7 @@ impl UpdateHandler { sensor_spec.set_sensor_value(&self.redis_pool, value) } + /* /// Build an OK response with the `HTTP 204 No Content` status code. fn ok_response(&self) -> Response { Response::with(status::NoContent) @@ -176,8 +143,10 @@ impl UpdateHandler { ]))) .set(Header(headers::AccessControlAllowOrigin::Any)) } + */ } +/* impl middleware::Handler for UpdateHandler { /// Update the sensor, return correct status code. fn handle(&self, req: &mut Request) -> IronResult { @@ -226,6 +195,7 @@ impl middleware::Handler for UpdateHandler { Ok(self.ok_response()) } } +*/ #[cfg(test)] mod tests { diff --git a/src/server/mod.rs b/src/server/mod.rs index d309e9c2..e13b028f 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,13 +1,16 @@ //! The SpaceAPI server struct. +use std::convert::Infallible; +use std::error::Error; use std::net::ToSocketAddrs; use std::sync::Arc; use std::time::Duration; -use iron::Iron; +// use iron::Iron; +use hyper::{Body, Request, Response, Server}; use log::debug; use redis::{ConnectionInfo, IntoConnectionInfo}; -use router::Router; +use routerify::{Router, RouterService}; use serde_json::map::Map; use serde_json::Value; @@ -171,27 +174,19 @@ pub struct SpaceapiServer { impl SpaceapiServer { /// Create and return a Router instance. - fn route(self) -> Router { - let mut router = Router::new(); - - router.get( - "/", - handlers::ReadHandler::new( - self.status.clone(), - self.redis_pool.clone(), - self.sensor_specs.clone(), - self.status_modifiers, - ), - "root", - ); - - router.put( + fn route(self) -> Router { + Router::builder() + .data(self) + .get("/", handlers::json_response_handler) + .build() + .unwrap() + /* + .put( "/sensors/:sensor/", handlers::UpdateHandler::new(self.redis_pool.clone(), self.sensor_specs), "sensors", - ); - - router + ) + */ } /// Start a HTTP server listening on ``self.host:self.port``. @@ -199,13 +194,16 @@ impl SpaceapiServer { /// The call returns an `HttpResult` object, see /// http://ironframework.io/doc/hyper/server/struct.Listening.html /// for more information. - pub fn serve(self, socket_addr: S) -> crate::HttpResult { + pub async fn serve(self, socket_addr: S) -> Result<(), Box> { // Launch server process let router = self.route(); println!("Starting HTTP server on:"); for a in socket_addr.to_socket_addrs()? { println!("\thttp://{}", a); } - Iron::new(router).http(socket_addr) + let service = RouterService::new(router).unwrap(); + + let server = Server::bind(&socket_addr.to_socket_addrs().unwrap().next().unwrap()).serve(service); + Ok(server.await?) } }