Skip to content

Commit 017da40

Browse files
authored
Merge pull request #14 from ionut-arm/ipc
Factor IPC handling out of the request handler
2 parents 28d7e6f + 647cfde commit 017da40

File tree

9 files changed

+169
-97
lines changed

9 files changed

+169
-97
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ parsec-interface = "0.10.0"
1616
num = "0.2.1"
1717
rand = "0.7.3"
1818
log = "0.4.8"
19-
derivative = "2.0.2"
19+
derivative = "2.1.0"
2020

2121
[dev-dependencies]
2222
mockstream = "0.0.3"

src/core/ipc_client/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2020 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! Types implementing an abstraction over IPC channels
4+
use crate::error::Result;
5+
use std::io::{Read, Write};
6+
7+
pub mod unix_socket;
8+
9+
/// This trait is created to allow the iterator returned by incoming to iterate over a trait object
10+
/// that implements both Read and Write.
11+
pub trait ReadWrite: Read + Write {}
12+
// Automatically implements ReadWrite for all types that implement Read and Write.
13+
impl<T: Read + Write> ReadWrite for T {}
14+
15+
/// Trait that must be implemented by any IPC client
16+
///
17+
/// The trait is used by the request handler for obtaining a stream to the service.
18+
pub trait Connect {
19+
/// Connect to underlying IPC and return a readable and writeable stream
20+
fn connect(&self) -> Result<Box<dyn ReadWrite>>;
21+
}

src/core/ipc_client/unix_socket.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2020 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! Handler for Unix domain sockets
4+
use super::{Connect, ReadWrite};
5+
use crate::error::{ClientErrorKind, Result};
6+
use std::os::unix::net::UnixStream;
7+
use std::path::PathBuf;
8+
use std::time::Duration;
9+
10+
const DEFAULT_SOCKET_PATH: &str = "/tmp/security-daemon-socket";
11+
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(1);
12+
13+
/// IPC client for Unix domain sockets
14+
#[derive(Debug, Clone)]
15+
pub struct Client {
16+
/// Path at which the socket can be found
17+
path: PathBuf,
18+
/// Timeout for reads and writes on the streams
19+
timeout: Option<Duration>,
20+
}
21+
22+
impl Connect for Client {
23+
fn connect(&self) -> Result<Box<dyn ReadWrite>> {
24+
let stream = UnixStream::connect(self.path.clone()).map_err(ClientErrorKind::Ipc)?;
25+
26+
stream
27+
.set_read_timeout(self.timeout)
28+
.map_err(ClientErrorKind::Ipc)?;
29+
stream
30+
.set_write_timeout(self.timeout)
31+
.map_err(ClientErrorKind::Ipc)?;
32+
33+
Ok(Box::from(stream))
34+
}
35+
}
36+
37+
impl Client {
38+
/// Create new client using given socket path and timeout duration
39+
pub fn new(path: PathBuf, timeout: Option<Duration>) -> Self {
40+
Client { path, timeout }
41+
}
42+
}
43+
44+
impl Default for Client {
45+
fn default() -> Self {
46+
Client {
47+
path: DEFAULT_SOCKET_PATH.into(),
48+
timeout: Some(DEFAULT_TIMEOUT),
49+
}
50+
}
51+
}

src/core/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// Copyright 2020 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
33
//! Client library for integration with the Parsec service
4-
4+
pub mod ipc_client;
55
mod operation_handler;
66
mod request_handler;
7+
mod testing;
78

89
use crate::auth::AuthenticationData;
910
use crate::error::{ClientErrorKind, Error, Result};

src/core/request_handler.rs

Lines changed: 11 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,40 @@
11
// Copyright 2020 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
3-
#![allow(dead_code)]
4-
3+
use super::ipc_client::{unix_socket, Connect};
54
use crate::error::{ClientErrorKind, Result};
6-
#[cfg(test)]
7-
use mockstream::SyncMockStream;
5+
use derivative::Derivative;
86
use parsec_interface::requests::{Request, Response};
9-
use std::io::{Read, Write};
10-
#[cfg(not(test))]
11-
use std::os::unix::net::UnixStream;
12-
use std::path::PathBuf;
13-
use std::time::Duration;
147

158
const DEFAULT_MAX_BODY_SIZE: usize = usize::max_value();
16-
const DEFAULT_SOCKET_PATH: &str = "/tmp/security-daemon-socket";
179

1810
/// Low level client structure to send a `Request` and get a `Response`.
19-
#[derive(Clone)]
20-
#[cfg_attr(not(test), derive(Debug))]
11+
#[derive(Derivative)]
12+
#[derivative(Debug)]
2113
pub struct RequestHandler {
2214
pub max_body_size: usize,
23-
pub timeout: Option<Duration>,
24-
pub socket_path: PathBuf,
25-
#[cfg(test)]
26-
pub mock_stream: SyncMockStream,
15+
#[derivative(Debug = "ignore")]
16+
pub ipc_client: Box<dyn Connect>,
2717
}
2818

2919
impl RequestHandler {
3020
/// Send a request and get a response.
3121
pub fn process_request(&self, request: Request) -> Result<Response> {
3222
// Try to connect once, wait for a timeout until trying again.
33-
let mut stream = self.connect_to_socket()?;
23+
let mut stream = self.ipc_client.connect()?;
3424

3525
request
3626
.write_to_stream(&mut stream)
3727
.map_err(ClientErrorKind::Interface)?;
3828
Ok(Response::read_from_stream(&mut stream, self.max_body_size)
3929
.map_err(ClientErrorKind::Interface)?)
4030
}
41-
42-
#[cfg(test)]
43-
fn connect_to_socket(&self) -> Result<impl Read + Write> {
44-
Ok(self.mock_stream.clone())
45-
}
46-
47-
#[cfg(not(test))]
48-
fn connect_to_socket(&self) -> Result<impl Read + Write> {
49-
let stream = UnixStream::connect(&self.socket_path).map_err(ClientErrorKind::Ipc)?;
50-
51-
stream
52-
.set_read_timeout(self.timeout)
53-
.map_err(ClientErrorKind::Ipc)?;
54-
stream
55-
.set_write_timeout(self.timeout)
56-
.map_err(ClientErrorKind::Ipc)?;
57-
58-
Ok(stream)
59-
}
6031
}
6132

6233
impl Default for RequestHandler {
6334
fn default() -> Self {
6435
RequestHandler {
6536
max_body_size: DEFAULT_MAX_BODY_SIZE,
66-
timeout: None,
67-
socket_path: DEFAULT_SOCKET_PATH.into(),
68-
#[cfg(test)]
69-
mock_stream: SyncMockStream::new(),
37+
ipc_client: Box::from(unix_socket::Client::default()),
7038
}
7139
}
7240
}
@@ -77,33 +45,8 @@ impl crate::CoreClient {
7745
self.op_handler.request_handler.max_body_size = max_body_size;
7846
}
7947

80-
/// Set the timeout allowed for operations on the IPC used for communicating with the service.
81-
///
82-
/// A value of `None` represents "no timeout"
83-
pub fn set_ipc_timeout(&mut self, timeout: Option<Duration>) {
84-
self.op_handler.request_handler.timeout = timeout;
85-
}
86-
87-
/// Set the location of the Unix Socket path where the service socket can be found
88-
pub fn set_socket_path(&mut self, socket_path: PathBuf) {
89-
self.op_handler.request_handler.socket_path = socket_path;
90-
}
91-
92-
/// Test helper used to set the data to be returned from the mock stream
93-
#[cfg(test)]
94-
pub fn set_mock_read(&mut self, data: &[u8]) {
95-
self.op_handler
96-
.request_handler
97-
.mock_stream
98-
.push_bytes_to_read(data);
99-
}
100-
101-
/// Test helper used to verify the data written to the mock stream
102-
#[cfg(test)]
103-
pub fn get_mock_write(&mut self) -> Vec<u8> {
104-
self.op_handler
105-
.request_handler
106-
.mock_stream
107-
.pop_bytes_written()
48+
/// Set the IPC handler used for communication with the service
49+
pub fn set_ipc_client(&mut self, ipc_client: Box<dyn Connect>) {
50+
self.op_handler.request_handler.ipc_client = ipc_client;
10851
}
10952
}

src/tests/core_tests.rs renamed to src/core/testing/core_tests.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2020 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
3+
use super::{TestCoreClient, DEFAULT_APP_NAME};
34
use crate::error::{ClientErrorKind, Error};
4-
use crate::{auth::AuthenticationData, CoreClient};
55
use mockstream::MockStream;
66
use parsec_interface::operations;
77
use parsec_interface::operations::list_providers::ProviderInfo;
@@ -27,7 +27,6 @@ const REQ_HEADER: RequestHeader = RequestHeader {
2727
auth_type: AuthType::NoAuth,
2828
opcode: Opcode::Ping,
2929
};
30-
const APP_NAME: &str = "test-app";
3130

3231
fn get_response_bytes_from_result(result: NativeResult) -> Vec<u8> {
3332
let mut stream = MockStream::new();
@@ -52,13 +51,9 @@ fn get_operation_from_req_bytes(bytes: Vec<u8>) -> NativeOperation {
5251
.unwrap()
5352
}
5453

55-
fn get_client() -> CoreClient {
56-
CoreClient::new(AuthenticationData::AppIdentity(String::from(APP_NAME)))
57-
}
58-
5954
#[test]
6055
fn ping_test() {
61-
let mut client = get_client();
56+
let mut client: TestCoreClient = Default::default();
6257
client.set_mock_read(&get_response_bytes_from_result(NativeResult::Ping(
6358
operations::ping::Result {
6459
wire_protocol_version_maj: 1,
@@ -74,7 +69,7 @@ fn ping_test() {
7469

7570
#[test]
7671
fn list_provider_test() {
77-
let mut client = get_client();
72+
let mut client: TestCoreClient = Default::default();
7873
let mut provider_info = Vec::new();
7974
provider_info.push(ProviderInfo {
8075
uuid: uuid::Uuid::nil(),
@@ -101,7 +96,7 @@ fn list_provider_test() {
10196

10297
#[test]
10398
fn list_provider_operations_test() {
104-
let mut client = get_client();
99+
let mut client: TestCoreClient = Default::default();
105100
let mut opcodes = HashSet::new();
106101
let _ = opcodes.insert(Opcode::PsaDestroyKey);
107102
let _ = opcodes.insert(Opcode::PsaGenerateKey);
@@ -121,7 +116,7 @@ fn list_provider_operations_test() {
121116

122117
#[test]
123118
fn generate_key_test() {
124-
let mut client = get_client();
119+
let mut client: TestCoreClient = Default::default();
125120
client.set_mock_read(&get_response_bytes_from_result(
126121
NativeResult::PsaGenerateKey(operations::psa_generate_key::Result {}),
127122
));
@@ -165,7 +160,7 @@ fn generate_key_test() {
165160

166161
#[test]
167162
fn destroy_key_test() {
168-
let mut client = get_client();
163+
let mut client: TestCoreClient = Default::default();
169164
client.set_mock_read(&get_response_bytes_from_result(
170165
NativeResult::PsaDestroyKey(operations::psa_destroy_key::Result {}),
171166
));
@@ -188,7 +183,7 @@ fn destroy_key_test() {
188183

189184
#[test]
190185
fn import_key_test() {
191-
let mut client = get_client();
186+
let mut client: TestCoreClient = Default::default();
192187
client.set_mock_read(&get_response_bytes_from_result(NativeResult::PsaImportKey(
193188
operations::psa_import_key::Result {},
194189
)));
@@ -238,7 +233,7 @@ fn import_key_test() {
238233

239234
#[test]
240235
fn export_public_key_test() {
241-
let mut client = get_client();
236+
let mut client: TestCoreClient = Default::default();
242237
let key_data = vec![0xa5; 128];
243238
client.set_mock_read(&get_response_bytes_from_result(
244239
NativeResult::PsaExportPublicKey(operations::psa_export_public_key::Result {
@@ -266,7 +261,7 @@ fn export_public_key_test() {
266261

267262
#[test]
268263
fn sign_hash_test() {
269-
let mut client = get_client();
264+
let mut client: TestCoreClient = Default::default();
270265
let hash = vec![0x77_u8; 32];
271266
let key_name = String::from("key_name");
272267
let sign_algorithm = AsymmetricSignature::Ecdsa {
@@ -305,7 +300,7 @@ fn sign_hash_test() {
305300

306301
#[test]
307302
fn verify_hash_test() {
308-
let mut client = get_client();
303+
let mut client: TestCoreClient = Default::default();
309304
let hash = vec![0x77_u8; 32];
310305
let key_name = String::from("key_name");
311306
let sign_algorithm = AsymmetricSignature::Ecdsa {
@@ -343,7 +338,7 @@ fn verify_hash_test() {
343338

344339
#[test]
345340
fn different_response_type_test() {
346-
let mut client = get_client();
341+
let mut client: TestCoreClient = Default::default();
347342
client.set_mock_read(&get_response_bytes_from_result(
348343
NativeResult::PsaVerifyHash(operations::psa_verify_hash::Result {}),
349344
));
@@ -360,7 +355,7 @@ fn different_response_type_test() {
360355

361356
#[test]
362357
fn response_status_test() {
363-
let mut client = get_client();
358+
let mut client: TestCoreClient = Default::default();
364359
let mut stream = MockStream::new();
365360
let status = ResponseStatus::PsaErrorDataCorrupt;
366361
let mut resp = Response::from_request_header(REQ_HEADER, ResponseStatus::Success);
@@ -374,7 +369,7 @@ fn response_status_test() {
374369

375370
#[test]
376371
fn malformed_response_test() {
377-
let mut client = get_client();
372+
let mut client: TestCoreClient = Default::default();
378373
client.set_mock_read(&[0xcb_u8; 130]);
379374
let err = client.ping().expect_err("Error was expected");
380375

@@ -386,7 +381,7 @@ fn malformed_response_test() {
386381

387382
#[test]
388383
fn request_fields_test() {
389-
let mut client = get_client();
384+
let mut client: TestCoreClient = Default::default();
390385
client.set_mock_read(&get_response_bytes_from_result(NativeResult::Ping(
391386
operations::ping::Result {
392387
wire_protocol_version_maj: 1,
@@ -401,7 +396,7 @@ fn request_fields_test() {
401396

402397
#[test]
403398
fn auth_value_test() {
404-
let mut client = get_client();
399+
let mut client: TestCoreClient = Default::default();
405400
client.set_mock_read(&get_response_bytes_from_result(
406401
NativeResult::PsaDestroyKey(operations::psa_destroy_key::Result {}),
407402
));
@@ -413,6 +408,6 @@ fn auth_value_test() {
413408
let req = get_req_from_bytes(client.get_mock_write());
414409
assert_eq!(
415410
String::from_utf8(req.auth.bytes().to_owned()).unwrap(),
416-
String::from(APP_NAME)
411+
String::from(DEFAULT_APP_NAME)
417412
);
418413
}

0 commit comments

Comments
 (0)