diff --git a/nomos-services/http/Cargo.toml b/nomos-services/http/Cargo.toml index bda40f42..3f073cfb 100644 --- a/nomos-services/http/Cargo.toml +++ b/nomos-services/http/Cargo.toml @@ -6,11 +6,15 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axum = "0.6" async-trait = "0.1" +clap = { version = "4", features = ["derive", "env"], optional = true } overwatch-rs = { git = "https://github.com/logos-co/Overwatch", branch = "main" } +overwatch-derive = { git = "https://github.com/logos-co/Overwatch", branch = "main" } serde = { version = "1.0", features = ["derive"] } tracing = "0.1" tracing-appender = "0.2" tracing-subscriber = { version = "0.3", features = ["json"] } tracing-gelf = "0.7" +tower-http = { version = "0.3", features = ["cors", "trace"] } futures = "0.3" diff --git a/nomos-services/http/src/bin/main.rs b/nomos-services/http/src/bin/main.rs new file mode 100644 index 00000000..a840a083 --- /dev/null +++ b/nomos-services/http/src/bin/main.rs @@ -0,0 +1,73 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use clap::Parser; +use nomos_http::*; +use overwatch_rs::{ + overwatch::OverwatchRunner, + services::{ + handle::{ServiceHandle, ServiceStateHandle}, + relay::{NoMessage, Relay}, + state::{NoOperator, NoState}, + ServiceCore, ServiceData, ServiceId, + }, DynError, +}; + +pub struct Foo; + +impl ServiceData for Foo { + const SERVICE_ID: ServiceId = "Foo"; + + type Settings = (); + + type State = NoState<()>; + + type StateOperator = NoOperator; + + type Message = NoMessage; +} + + +#[async_trait::async_trait] +impl ServiceCore for Foo { + /// Initialize the service with the given state + fn init(service_state: ServiceStateHandle) -> Result { + Ok(Self) + } + + /// Service main loop + async fn run(mut self) -> Result<(), DynError> { + Ok(()) + } +} + +#[derive(overwatch_derive::Services)] +struct Services { + http: ServiceHandle>, +} + +#[derive(clap::Parser)] +pub struct Args { + #[clap(flatten)] + http: ServerSettings, +} + +fn main() -> Result<(), Box> { + let settings = Args::parse(); + let graphql = OverwatchRunner::::run( + ServicesServiceSettings { + http: settings.http, + }, + None, + )?; + + // tracing_subscriber::fmt::fmt() + // // .with_env_filter(std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned())) + // .with_file(false) + // .init(); + + graphql.wait_finished(); + Ok(()) +} diff --git a/nomos-services/http/src/lib.rs b/nomos-services/http/src/lib.rs index e69de29b..a0c26b1d 100644 --- a/nomos-services/http/src/lib.rs +++ b/nomos-services/http/src/lib.rs @@ -0,0 +1,109 @@ +// std + +// crates +use axum::{ + http::{ + header::{CONTENT_TYPE, USER_AGENT}, + HeaderValue, + }, + routing::{post, get}, + Router, Server, response::Html, +}; +use tower_http::{ + cors::{Any, CorsLayer}, + trace::TraceLayer, +}; + +// internal +use overwatch_rs::services::relay::Relay; +use overwatch_rs::services::{ + handle::ServiceStateHandle, + relay::NoMessage, + state::{NoOperator, NoState}, + ServiceCore, ServiceData, ServiceId, +}; +/// Configuration for the Http Server +#[derive(Debug, Clone, clap::Args, serde::Deserialize, serde::Serialize)] +pub struct ServerSettings { + /// Socket where the server will be listening on for incoming requests. + #[arg(short, long = "addr", default_value_t = std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)), 8080), env = "HTTP_ADDRESS")] + pub address: std::net::SocketAddr, + /// Allowed origins for this server deployment requests. + #[arg(long = "cors-origin")] + pub cors_origins: Vec, +} + +pub trait Data { + +} + +pub trait Abstration { + +} + +#[derive(Clone)] +pub struct HttpServer { + settings: ServerSettings, + http_abstraction_channel: Relay, +} + +impl ServiceData for HttpServer { + const SERVICE_ID: ServiceId = "HttpServer"; + + type Settings = ServerSettings; + + type State = NoState; + + type StateOperator = NoOperator; + + type Message = NoMessage; +} + +async fn handler() -> Html<&'static str> { + Html("

Hello, World!

") +} + +#[async_trait::async_trait] +impl ServiceCore for HttpServer +{ + fn init(service_state: ServiceStateHandle) -> Result { + let settings = service_state.settings_reader.get_updated_settings(); + let http_abstraction_channel: Relay = + service_state.overwatch_handle.relay(); + Ok(Self { + settings, + http_abstraction_channel, + }) + } + + async fn run(mut self) -> Result<(), overwatch_rs::DynError> { + let mut builder = CorsLayer::new(); + if self.settings.cors_origins.is_empty() { + builder = builder.allow_origin(Any); + } + for origin in &self.settings.cors_origins { + builder = builder.allow_origin( + origin + .as_str() + .parse::() + .expect("fail to parse origin"), + ); + } + let cors = builder + .allow_headers([CONTENT_TYPE, USER_AGENT]) + .allow_methods(Any); + + let addr = self.settings.address; + + let router = Router::new() + .route("/", get(handler)) + .layer(cors) + .layer(TraceLayer::new_for_http()); + + tracing::info!("HTTP server listening: {}", addr); + let mut srv = Server::bind(&addr).serve(router.into_make_service()); + let conn = self.http_abstraction_channel.connect().await?; + + Ok(()) + } +} \ No newline at end of file