diff --git a/Cargo.toml b/Cargo.toml index dea357e..75914b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ log = "0.4.11" derivative = "2.1.1" zeroize = "1.1.0" users = "0.10.0" +url = "2.2.0" [dev-dependencies] mockstream = "0.0.3" diff --git a/src/core/basic_client.rs b/src/core/basic_client.rs index 42a8d90..18e2682 100644 --- a/src/core/basic_client.rs +++ b/src/core/basic_client.rs @@ -202,7 +202,7 @@ impl BasicClient { /// [`set_default_provider`](#method.set_default_provider) pub fn new(app_name: Option) -> Result { let mut client = BasicClient { - op_client: Default::default(), + op_client: OperationClient::new()?, auth_data: Authentication::None, implicit_provider: ProviderID::Core, }; diff --git a/src/core/ipc_handler/mod.rs b/src/core/ipc_handler/mod.rs index 1447ca3..406a147 100644 --- a/src/core/ipc_handler/mod.rs +++ b/src/core/ipc_handler/mod.rs @@ -1,12 +1,16 @@ // Copyright 2020 Contributors to the Parsec project. // SPDX-License-Identifier: Apache-2.0 //! Types implementing an abstraction over IPC channels -use crate::error::Result; +use crate::error::{ClientErrorKind, Error, Result}; use std::io::{Read, Write}; use std::time::Duration; +use url::Url; pub mod unix_socket; +/// Default timeout for client IPC requests. +pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); + /// This trait is created to allow the iterator returned by incoming to iterate over a trait object /// that implements both Read and Write. pub trait ReadWrite: Read + Write {} @@ -23,3 +27,14 @@ pub trait Connect { /// Set timeout for all produced streams. fn set_timeout(&mut self, timeout: Option); } + +/// Create an implementation of `Connect` from the socket URL +pub fn connector_from_url(socket_url: Url) -> Result> { + match socket_url.scheme() { + "unix" => Ok(Box::from(unix_socket::Handler::new( + socket_url.path().into(), + Some(DEFAULT_TIMEOUT), + )?)), + _ => Err(Error::Client(ClientErrorKind::InvalidSocketUrl)), + } +} diff --git a/src/core/ipc_handler/unix_socket.rs b/src/core/ipc_handler/unix_socket.rs index 3fde8c9..7d1199a 100644 --- a/src/core/ipc_handler/unix_socket.rs +++ b/src/core/ipc_handler/unix_socket.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 //! Handler for Unix domain sockets use super::{Connect, ReadWrite}; -use crate::error::{ClientErrorKind, Result}; +use crate::error::{ClientErrorKind, Error, Result}; +use std::os::unix::fs::FileTypeExt; use std::os::unix::net::UnixStream; use std::path::PathBuf; use std::time::Duration; -const DEFAULT_SOCKET_PATH: &str = "/run/parsec/parsec.sock"; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); +/// Default socket path used by the service. +pub const DEFAULT_SOCKET_PATH: &str = "/run/parsec/parsec.sock"; /// IPC handler for Unix domain sockets #[derive(Debug, Clone)] @@ -40,8 +41,17 @@ impl Connect for Handler { impl Handler { /// Create new client using given socket path and timeout duration - pub fn new(path: PathBuf, timeout: Option) -> Self { - Handler { path, timeout } + pub fn new(path: PathBuf, timeout: Option) -> Result { + if path.exists() + && std::fs::metadata(&path) + .map_err(|_| Error::Client(ClientErrorKind::InvalidSocketAddress))? + .file_type() + .is_socket() + { + Ok(Handler { path, timeout }) + } else { + Err(Error::Client(ClientErrorKind::InvalidSocketAddress)) + } } } @@ -49,7 +59,7 @@ impl Default for Handler { fn default() -> Self { Handler { path: DEFAULT_SOCKET_PATH.into(), - timeout: Some(DEFAULT_TIMEOUT), + timeout: Some(super::DEFAULT_TIMEOUT), } } } diff --git a/src/core/operation_client.rs b/src/core/operation_client.rs index 48d2e5a..e213bdf 100644 --- a/src/core/operation_client.rs +++ b/src/core/operation_client.rs @@ -40,8 +40,11 @@ impl OperationClient { /// seconds on reads and writes on the socket. It uses the version 1.0 wire protocol /// to form requests, the direct authentication method and protobuf format as /// content type. - pub fn new() -> OperationClient { - Default::default() + pub fn new() -> Result { + Ok(OperationClient { + request_client: RequestClient::new()?, + ..Default::default() + }) } fn operation_to_request( diff --git a/src/core/request_client.rs b/src/core/request_client.rs index 013fe5b..82ff904 100644 --- a/src/core/request_client.rs +++ b/src/core/request_client.rs @@ -1,10 +1,11 @@ // Copyright 2020 Contributors to the Parsec project. // SPDX-License-Identifier: Apache-2.0 //! Request-level client -use super::ipc_handler::{unix_socket, Connect}; +use super::ipc_handler::{self, unix_socket, Connect}; use crate::error::{ClientErrorKind, Result}; use derivative::Derivative; use parsec_interface::requests::{Request, Response}; +use std::env; use std::time::Duration; const DEFAULT_MAX_BODY_SIZE: usize = usize::max_value(); @@ -29,6 +30,18 @@ pub struct RequestClient { } impl RequestClient { + /// Creates a new `RequestClient`, bootstrapping the socket + /// location. + pub fn new() -> Result { + Ok(RequestClient { + ipc_handler: ipc_handler::connector_from_url(url::Url::parse( + &env::var("PARSEC_SERVICE_ENDPOINT") + .unwrap_or(format!("unix:{}", unix_socket::DEFAULT_SOCKET_PATH)), + )?)?, + max_body_size: DEFAULT_MAX_BODY_SIZE, + }) + } + /// Send a request and get a response. pub fn process_request(&self, request: Request) -> Result { // Try to connect once, wait for a timeout until trying again. diff --git a/src/error.rs b/src/error.rs index d7b924a..81ee5f9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -44,6 +44,10 @@ pub enum ClientErrorKind { MissingParam, /// The requested resource was not found. NotFound, + /// The socket address provided is not valid + InvalidSocketAddress, + /// The socket URL is invalid + InvalidSocketUrl, } impl From for Error { @@ -52,6 +56,12 @@ impl From for Error { } } +impl From for Error { + fn from(_: url::ParseError) -> Self { + Error::Client(ClientErrorKind::InvalidSocketUrl) + } +} + impl fmt::Display for ClientErrorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -67,7 +77,9 @@ impl fmt::Display for ClientErrorKind { ClientErrorKind::NoProvider => write!(f, "client is missing an implicit provider"), ClientErrorKind::NoAuthenticator => write!(f, "service is not reporting any authenticators or none of the reported ones are supported by the client"), ClientErrorKind::MissingParam => write!(f, "one of the `Option` parameters was required but was not provided"), - ClientErrorKind::NotFound => write!(f, "one of the resources required in the operation was not found") + ClientErrorKind::NotFound => write!(f, "one of the resources required in the operation was not found"), + ClientErrorKind::InvalidSocketAddress => write!(f, "the socket address provided in the URL is not valid"), + ClientErrorKind::InvalidSocketUrl => write!(f, "the socket URL is invalid"), } } }