Skip to content

Commit

Permalink
WAYK-2298: remove saphir dummy HTTP listener (#129)
Browse files Browse the repository at this point in the history
* WAYK-2298: remove saphir dummy HTTP listener

* Panic if no HTTP listener is provided

* Update saphir dependency

Co-authored-by: Benoît CORTIER <benoit.cortier@fried-world.eu>
  • Loading branch information
bcortier-devolutions and CBenoit authored Dec 15, 2020
1 parent 5c7580e commit 9333ef7
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 179 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ spsc-bip-buffer = { git = "https://github.com/Devolutions/spsc-bip-buffer.git",
indexmap = "1.0"

[dependencies.saphir]
git = "https://github.com/richerarc/saphir.git"
rev = "d0fcd6adc0a8a5e22c095d41a9a2c6c0d0d91316"
git = "https://github.com/richerarc/saphir"
rev = "5f3602d5165a224b83678a5641e34c77b2325bca"
default-features = false
features = ["https", "json", "macro", "form"]

Expand Down
45 changes: 8 additions & 37 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,10 @@ use std::{
};
use url::Url;

const DEFAULT_HTTP_LISTENER_PORT: u32 = 10256;

const ARG_API_KEY: &str = "api-key";
const ARG_APPLICATION_PROTOCOLS: &str = "application-protocols";
const ARG_UNRESTRICTED: &str = "unrestricted";
const ARG_LISTENERS: &str = "listeners";
const ARG_API_LISTENER: &str = "api-listener";
const ARG_HOSTNAME: &str = "hostname";
const ARG_FARM_NAME: &str = "farm-name";
const ARG_CERTIFICATE_FILE: &str = "certificate-file";
Expand Down Expand Up @@ -96,7 +93,6 @@ pub struct Config {
pub log_file: Option<String>,
pub application_protocols: Vec<String>,
pub certificate: CertificateConfig,
pub api_listener: Url,
pub provisioner_public_key: Option<PublicKey>,
pub delegation_private_key: Option<PrivateKey>,
}
Expand All @@ -105,11 +101,6 @@ impl Default for Config {
fn default() -> Self {
let default_hostname = get_default_hostname().unwrap_or_else(|| "localhost".to_string());

let default_api_listener_url = format!("http://0.0.0.0:{}", DEFAULT_HTTP_LISTENER_PORT);
let api_listener = default_api_listener_url
.parse::<Url>()
.unwrap_or_else(|e| panic!("API listener URL is invalid: {}", e));

Config {
service_mode: false,
service_name: SERVICE_NAME.to_string(),
Expand All @@ -132,7 +123,6 @@ impl Default for Config {
private_key_file: None,
private_key_data: None,
},
api_listener,
provisioner_public_key: None,
delegation_private_key: None,
}
Expand Down Expand Up @@ -215,8 +205,6 @@ pub struct ConfigFile {
pub capture_path: Option<String>,
#[serde(rename = "Unrestricted")]
pub unrestricted: Option<bool>,
#[serde(rename = "ApiListener")]
pub api_listener: Option<String>,
}

fn get_config_path() -> PathBuf {
Expand Down Expand Up @@ -322,14 +310,6 @@ impl Config {
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name(ARG_API_LISTENER)
.long("api-listener")
.value_name("URL")
.env("DGATEWAY_API_LISTENER")
.help("API HTTP listener URL.")
.takes_value(true),
)
.arg(
Arg::with_name(ARG_FARM_NAME)
.long("farm-name")
Expand Down Expand Up @@ -517,12 +497,6 @@ impl Config {
config.unrestricted = true;
}

if let Some(api_listener) = matches.value_of(ARG_API_LISTENER) {
config.api_listener = api_listener
.parse::<Url>()
.unwrap_or_else(|e| panic!("API listener URL is invalid: {}", e));
}

if let Some(farm_name) = matches.value_of(ARG_FARM_NAME) {
config.farm_name = farm_name.to_owned();
}
Expand Down Expand Up @@ -676,6 +650,14 @@ impl Config {
panic!("At least one listener has to be specified.");
}

if !config
.listeners
.iter()
.any(|listener| matches!(listener.internal_url.scheme(), "http" | "https" | "ws" | "wss"))
{
panic!("At least one HTTP listener is required");
}

// early fail if we start as restricted without provisioner key
if !config.unrestricted && config.provisioner_public_key.is_none() {
panic!("provisioner public key is missing in unrestricted mode");
Expand Down Expand Up @@ -776,16 +758,6 @@ impl Config {
let unrestricted = config_file.unrestricted.unwrap_or(true);
let capture_path = config_file.capture_path;

// We always create a dummy API listener because Saphir needs one.
// However, this API listener is unable to process WebSocket traffic.
// Create the API listener as a dummy listener to make Saphir happy,
// but in fact we just ignore it and use only our gateway listeners.
let default_api_listener_url = format!("http://0.0.0.0:{}", DEFAULT_HTTP_LISTENER_PORT);
let api_listener_url = config_file.api_listener.unwrap_or(default_api_listener_url);
let api_listener = api_listener_url
.parse::<Url>()
.unwrap_or_else(|e| panic!("API listener URL is invalid: {}", e));

Some(Config {
unrestricted,
api_key,
Expand All @@ -800,7 +772,6 @@ impl Config {
private_key_file,
..Default::default()
},
api_listener,
provisioner_public_key,
delegation_private_key,
..Default::default()
Expand Down
147 changes: 31 additions & 116 deletions src/http/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,125 +6,40 @@ use crate::{
},
jet_client::JetAssociationsMap,
};
use futures::FutureExt;
use saphir::{
error::SaphirError,
server::{Server as SaphirServer, SslConfig},
};
use saphir::server::Server as SaphirServer;
use slog_scope::info;
use std::sync::{Arc, Mutex};
use tokio_02::{runtime::Runtime, sync::Notify, task::JoinHandle};

pub struct HttpServer {
pub server: Mutex<Option<SaphirServer>>,
server_runtime: Mutex<Option<Runtime>>,
shutdown_notification: Arc<Notify>,
join_handle: Mutex<Option<JoinHandle<Result<(), SaphirError>>>>,
}

impl HttpServer {
pub fn new(config: Arc<Config>, jet_associations: JetAssociationsMap) -> HttpServer {
let shutdown_notification = Arc::new(Notify::new());
let shutdown_notification_clone = shutdown_notification.clone();

let on_shutdown_check = async move {
shutdown_notification_clone.notified().await;
info!("HTTP server was gracefully stopped");
}
.boxed();

let http_server = SaphirServer::builder()
.configure_middlewares(|middlewares| {
info!("Loading HTTP middlewares");

// Only the "create association" should requires authorization.
let mut auth_include_path = vec!["/jet/association"];
let mut auth_exclude_path = vec!["/jet/association/<association_id>/<anything>"];

if config.unrestricted {
auth_exclude_path.push("/jet/association/<association_id>");
} else {
auth_include_path.push("/jet/association/<association_id>");
}
use std::sync::Arc;

middlewares.apply(
AuthMiddleware::new(config.clone()),
auth_include_path,
Some(auth_exclude_path),
)
})
.configure_router(|router| {
info!("Loading HTTP controllers");
let health = HealthController::new(config.clone());
let jet = JetController::new(config.clone(), jet_associations.clone());
let session = SessionsController::default();
info!("Configuring HTTP router");
router.controller(health).controller(jet).controller(session)
})
.configure_listener(|listener| {
let listener_host = config.api_listener.host().expect("API listener should be specified");
pub fn configure_http_server(config: Arc<Config>, jet_associations: JetAssociationsMap) -> Result<(), String> {
SaphirServer::builder()
.configure_middlewares(|middlewares| {
info!("Loading HTTP middlewares");

let listener_port = config.api_listener.port().unwrap_or(8080);
let interface = format!("{}:{}", listener_host, listener_port);
// Only the "create association" should requires authorization.
let mut auth_include_path = vec!["/jet/association"];
let mut auth_exclude_path = vec!["/jet/association/<association_id>/<anything>"];

let listener_config = listener
.interface(&interface)
.server_name("Saphir Server")
.shutdown(on_shutdown_check, true);

let cert_config_opt = if let Some(cert_path) = &config.certificate.certificate_file {
Some(SslConfig::FilePath(cert_path.into()))
} else if let Some(cert_data) = &config.certificate.certificate_data {
Some(SslConfig::FileData(cert_data.into()))
} else {
None
};

let key_config_opt = if let Some(key_path) = &config.certificate.private_key_file {
Some(SslConfig::FilePath(key_path.into()))
} else if let Some(key_data) = &config.certificate.private_key_data {
Some(SslConfig::FileData(key_data.into()))
} else {
None
};

if let (Some(cert_config), Some(key_config)) = (cert_config_opt, key_config_opt) {
listener_config.set_ssl_config(cert_config, key_config)
} else {
listener_config
}
})
.build();
let runtime = Runtime::new().expect("failed to create runtime for HTTP server");
HttpServer {
server: Mutex::new(Some(http_server)),
server_runtime: Mutex::new(Some(runtime)),
shutdown_notification,
join_handle: Mutex::new(None),
}
}

pub fn start(&self) {
let server = {
let mut guard = self.server.lock().unwrap();
guard.take().expect("Start server can't be called twice")
};

let runtime_guard = self.server_runtime.lock().unwrap();
let runtime = runtime_guard
.as_ref()
.expect("HTTP server runtime must be present on start");
let join_handle = runtime.spawn(server.run());
self.join_handle.lock().unwrap().replace(join_handle);
}

pub fn stop(&self) {
self.shutdown_notification.notify();

if let Some(mut runtime) = self.server_runtime.lock().unwrap().take() {
if let Some(join_handle) = self.join_handle.lock().unwrap().take() {
let _ = runtime.block_on(join_handle);
if config.unrestricted {
auth_exclude_path.push("/jet/association/<association_id>");
} else {
auth_include_path.push("/jet/association/<association_id>");
}
}
}

middlewares.apply(
AuthMiddleware::new(config.clone()),
auth_include_path,
Some(auth_exclude_path),
)
})
.configure_router(|router| {
info!("Loading HTTP controllers");
let health = HealthController::new(config.clone());
let jet = JetController::new(config.clone(), jet_associations.clone());
let session = SessionsController::default();
info!("Configuring HTTP router");
router.controller(health).controller(jet).controller(session)
})
.configure_listener(|listener| listener.server_name("Devolutions Gateway"))
.build_stack_only()
.map_err(|e| e.to_string())
}
Loading

0 comments on commit 9333ef7

Please sign in to comment.