use std::{
path::{Path, PathBuf},
time::Duration,
};
use crate::configuration::load_bundle_configuration;
use crate::runtime::paths::{BundleRuntimePaths, tmux_socket_path_for_runtime_directory};
use crate::transports::WorkerReadinessState;
mod authorization;
mod client;
mod connection;
mod constants;
mod context;
mod contract;
mod delivery;
mod drain;
mod errors;
mod handlers;
mod identity;
mod lifecycle;
mod routing;
mod startup_state;
mod stream;
mod watcher;
use self::authorization::load_authorization_context;
pub use self::client::{RelayStreamSession, request_relay};
pub use self::connection::{BundleCatalog, HostingIntent, serve_connection};
use self::constants::*;
use self::context::*;
pub use self::contract::*;
pub use self::delivery::install_pending_choice_request_for_testing;
pub use self::delivery::observability::{
ChoicesQueueEvent, subscribe_choices_queue_events, subscribe_worker_readiness,
};
pub use self::drain::{ConnectionDrainCoordinator, ConnectionDrainReport, ConnectionWorkerSlot};
use self::errors::*;
use self::identity::*;
pub use self::stream::second_claim_is_live_conflict_for_testing;
pub use self::watcher::{BundleWatcher, spawn_bundle_watcher};
pub fn handle_request(
request: RelayRequest,
configuration_root: &Path,
bundle_name: &str,
runtime_directory: &Path,
) -> Result<RelayResponse, RelayError> {
let bundle_catalog = BundleCatalog::from_paths([BundleRuntimePaths {
state_root: PathBuf::new(),
bundle_name: bundle_name.to_string(),
runtime_directory: runtime_directory.to_path_buf(),
tmux_socket: tmux_socket_path_for_runtime_directory(runtime_directory),
}]);
handle_request_with_principal(
request,
configuration_root,
bundle_name,
runtime_directory,
None,
&bundle_catalog,
)
}
fn handle_request_with_principal(
request: RelayRequest,
configuration_root: &Path,
bundle_name: &str,
runtime_directory: &Path,
principal: Option<RequestPrincipal>,
bundle_catalog: &BundleCatalog,
) -> Result<RelayResponse, RelayError> {
match request {
RelayRequest::Send { .. } => {
return handlers::handle_send_routed(
bundle_name,
request,
configuration_root,
bundle_catalog,
principal.as_ref(),
);
}
RelayRequest::Look { .. } => {
return handlers::handle_look_routed(
bundle_name,
Some(runtime_directory),
request,
configuration_root,
bundle_catalog,
principal.as_ref(),
);
}
RelayRequest::Raww { .. } => {
return handlers::handle_raww_routed(
bundle_name,
Some(runtime_directory),
request,
configuration_root,
bundle_catalog,
);
}
_ => {}
}
let bundle = load_bundle_configuration(configuration_root, bundle_name).map_err(map_config)?;
let authorization = load_authorization_context(configuration_root, Some(&bundle))?;
handlers::handle_request(
request,
&bundle,
&authorization,
runtime_directory,
principal,
bundle_catalog,
)
}
pub fn reconcile_bundle(
configuration_root: &Path,
bundle_name: &str,
tmux_socket: &Path,
) -> Result<ReconciliationReport, RelayError> {
lifecycle::reconcile_bundle(configuration_root, bundle_name, tmux_socket)
}
pub fn startup_bundle(
configuration_root: &Path,
bundle_name: &str,
runtime_directory: &Path,
) -> Result<BundleStartupReport, RelayError> {
lifecycle::startup_bundle(configuration_root, bundle_name, runtime_directory)
}
pub fn register_configured_relay_wide_principals(
configuration_root: &Path,
) -> Result<(), RelayError> {
lifecycle::register_configured_relay_wide_principals(configuration_root)
}
pub fn register_configured_bundle(
configuration_root: &Path,
bundle_name: &str,
) -> Result<(), RelayError> {
lifecycle::register_configured_bundle(configuration_root, bundle_name)
}
pub fn registered_principal_ids(namespace: &str) -> Vec<String> {
stream::list_namespace_sessions(namespace)
.into_iter()
.map(|(principal_id, _session_type, _ready)| principal_id)
.collect()
}
pub fn shutdown_bundle_runtime(tmux_socket: &Path) -> Result<ShutdownReport, RelayError> {
lifecycle::shutdown_bundle_runtime(tmux_socket)
}
pub fn load_startup_failures(
runtime_directory: &Path,
) -> Result<Vec<StartupFailureRecord>, String> {
startup_state::load_startup_failures(runtime_directory)
}
pub fn append_startup_failure(
runtime_directory: &Path,
record: StartupFailureRecord,
) -> Result<StartupFailureRecord, String> {
startup_state::append_startup_failure(runtime_directory, record)
}
#[must_use]
pub fn wait_for_async_delivery_shutdown(timeout: Duration) -> usize {
delivery::wait_for_async_delivery_shutdown(timeout)
}
#[must_use]
pub fn read_worker_readiness(
namespace: &str,
runtime_directory: &Path,
target_session: &str,
) -> Option<&'static str> {
delivery::get_worker_readiness(namespace, runtime_directory, target_session).map(|state| {
match state {
WorkerReadinessState::Initializing => "initializing",
WorkerReadinessState::Available => "available",
WorkerReadinessState::Busy => "busy",
WorkerReadinessState::Recovering => "recovering",
WorkerReadinessState::Unavailable => "unavailable",
}
})
}
pub(in crate::relay) fn dispatch_request(
request: RelayRequest,
configuration_root: &Path,
bundle_name: &str,
runtime_directory: &Path,
principal: Option<RequestPrincipal>,
bundle_catalog: &BundleCatalog,
) -> RelayResponse {
match handle_request_with_principal(
request,
configuration_root,
bundle_name,
runtime_directory,
principal,
bundle_catalog,
) {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub(in crate::relay) fn dispatch_list(
request: RelayRequest,
configuration_root: &Path,
dispatch_paths: &crate::runtime::paths::BundleRuntimePaths,
enumerate_paths: &crate::runtime::paths::BundleRuntimePaths,
) -> RelayResponse {
let result = (|| {
let RelayRequest::List { requester_session } = request else {
return Err(relay_error(
"internal_unexpected_request",
"non-list request routed to the list dispatcher",
None,
));
};
let dispatch_bundle =
load_bundle_configuration(configuration_root, &dispatch_paths.bundle_name)
.map_err(map_config)?;
let dispatch_authorization =
load_authorization_context(configuration_root, Some(&dispatch_bundle))?;
let enumerate_bundle =
load_bundle_configuration(configuration_root, &enumerate_paths.bundle_name)
.map_err(map_config)?;
handlers::handle_list_routed(
&dispatch_bundle,
&dispatch_authorization,
&enumerate_bundle,
&enumerate_paths.runtime_directory,
requester_session,
)
})();
match result {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub(in crate::relay) fn dispatch_send(
request: RelayRequest,
configuration_root: &Path,
bound_bundle: Option<&crate::runtime::paths::BundleRuntimePaths>,
principal: Option<RequestPrincipal>,
bundle_catalog: &BundleCatalog,
) -> RelayResponse {
let home_namespace = match bound_bundle {
Some(paths) => paths.bundle_name.clone(),
None => GLOBAL_NAMESPACE.to_string(),
};
match handlers::handle_send_routed(
home_namespace.as_str(),
request,
configuration_root,
bundle_catalog,
principal.as_ref(),
) {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub(in crate::relay) fn dispatch_look(
request: RelayRequest,
configuration_root: &Path,
bound_bundle: Option<&crate::runtime::paths::BundleRuntimePaths>,
principal: Option<RequestPrincipal>,
bundle_catalog: &BundleCatalog,
) -> RelayResponse {
let (home_namespace, home_runtime) = match bound_bundle {
Some(paths) => (
paths.bundle_name.clone(),
Some(paths.runtime_directory.clone()),
),
None => (GLOBAL_NAMESPACE.to_string(), None),
};
match handlers::handle_look_routed(
home_namespace.as_str(),
home_runtime.as_deref(),
request,
configuration_root,
bundle_catalog,
principal.as_ref(),
) {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub(in crate::relay) fn dispatch_raww(
request: RelayRequest,
configuration_root: &Path,
bound_bundle: Option<&crate::runtime::paths::BundleRuntimePaths>,
bundle_catalog: &BundleCatalog,
) -> RelayResponse {
let (home_namespace, home_runtime) = match bound_bundle {
Some(paths) => (
paths.bundle_name.clone(),
Some(paths.runtime_directory.clone()),
),
None => (GLOBAL_NAMESPACE.to_string(), None),
};
match handlers::handle_raww_routed(
home_namespace.as_str(),
home_runtime.as_deref(),
request,
configuration_root,
bundle_catalog,
) {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub(in crate::relay) fn dispatch_identity_admin(
request: RelayRequest,
configuration_root: &Path,
state_root: &Path,
requester_principal_id: &str,
) -> RelayResponse {
match handlers::handle_identity_admin_request(
request,
configuration_root,
state_root,
requester_principal_id,
) {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub(in crate::relay) fn dispatch_identity_introspect(
request: RelayRequest,
state_root: &Path,
principal: &RequestPrincipal,
) -> RelayResponse {
let RelayRequest::IdentityIntrospect { target_session } = request else {
return RelayResponse::Error {
error: relay_error(
"internal_unexpected_request",
"non-introspect request routed to identity introspect dispatcher",
None,
),
};
};
match handlers::handle_identity_introspect(state_root, principal, target_session.as_str()) {
Ok(value) => value,
Err(error) => RelayResponse::Error { error },
}
}
pub fn prune_principal_store(state_root: &Path) -> Result<usize, RelayError> {
let mut store = PrincipalStore::load(crate::runtime::paths::principal_store_path(state_root))?;
let pruned = store.prune_expired(time::OffsetDateTime::now_utc());
if pruned > 0 {
store.persist()?;
}
Ok(pruned)
}