From a00f8fbe3b70239cbcc7625db3a100aeb22a131f Mon Sep 17 00:00:00 2001 From: shimun Date: Sun, 5 Apr 2020 19:24:23 +0200 Subject: [PATCH] added timeout option --- examples/multiple.rs | 4 +++- src/error.rs | 2 ++ src/util.rs | 55 +++++++++++++++++++++++++++++++------------- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/examples/multiple.rs b/examples/multiple.rs index cf090d9..17ef755 100644 --- a/examples/multiple.rs +++ b/examples/multiple.rs @@ -6,6 +6,7 @@ use ctap::{ use hex; use std::env::args; +use std::time::Duration; const RP_ID: &str = "ctap_demo"; @@ -40,7 +41,8 @@ fn main() -> ctap::FidoResult<()> { .map(|handle| FidoDevice::new(&handle)) .collect::>>()?; // run with --features request_multiple - let (cred, _) = ctap::get_assertion_devices(&req, devices.iter_mut())?; + let (cred, _) = + ctap::get_assertion_devices(&req, devices.iter_mut(), Some(Duration::from_secs(10)))?; println!("Success, got assertion for: {}", hex::encode(&cred.id)); Ok(()) } diff --git a/src/error.rs b/src/error.rs index def7432..a022d98 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,6 +23,8 @@ pub struct CborErrorCode(u8); pub enum FidoErrorKind { #[fail(display = "Read/write error with device.")] Io, + #[fail(display = "Operation timed out")] + Timeout, #[fail(display = "Error while reading packet from device.")] ReadPacket, #[fail(display = "Error while writing packet to device.")] diff --git a/src/util.rs b/src/util.rs index d739866..750b27e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,7 +7,8 @@ use crate::{ use crossbeam::thread; #[cfg(feature = "request_multiple")] use std::sync::mpsc::channel; - +#[cfg(feature = "request_multiple")] +use std::time::Duration; #[cfg(feature = "request_multiple")] pub fn request_multiple_devices< 'a, @@ -15,6 +16,7 @@ pub fn request_multiple_devices< F: Fn(&mut FidoDevice) -> FidoResult + 'a + Sync, >( devices: impl Iterator, + timeout: Option, ) -> FidoResult { thread::scope(|scope| -> FidoResult { let (tx, rx) = channel(); @@ -26,22 +28,41 @@ pub fn request_multiple_devices< Ok((cancel, thread_handle)) }) .collect::>>()?; - let mut err = None; - for res in rx.iter().take(handles.len()) { - match res { - Ok(_) => { - for (mut cancel, join) in handles { - // Canceling out of courtesy don't care if it fails - let _ = cancel.cancel(); - let _ = join.join(); - } - return res; - } - e => err = Some(e), + let mut slept = Duration::from_millis(0); + let interval = Duration::from_millis(10); + let mut received = 0usize; + let res = loop { + if timeout.map(|t| t < slept).unwrap_or(true) { + break if let Some(cause) = err { + cause + } else { + Err(FidoErrorKind::Timeout.into()) + }; } + if received == handles.len() { + break err.unwrap(); + } + if let Ok(msg) = rx.recv_timeout(interval) { + received += 1; + match msg { + e @ Err(_) => { + err = Some(e); + } + res @ Ok(_) => { + break res; + } + } + } else { + slept += interval; + } + }; + for (mut cancel, join) in handles { + // Canceling out of courtesy don't care if it fails + let _ = cancel.cancel(); + let _ = join.join(); } - err.unwrap_or(Err(FidoErrorKind::DeviceUnsupported.into())) + res }) .unwrap() } @@ -51,9 +72,10 @@ pub fn request_multiple_devices< pub fn get_assertion_devices<'a>( assertion_request: &'a FidoAssertionRequest, devices: impl Iterator, + timeout: Option, ) -> FidoResult<(&'a FidoCredential, AuthenticatorData)> { let get_assertion = |device: &mut FidoDevice| device.get_assertion(assertion_request); - request_multiple_devices(devices.map(|device| (device, &get_assertion))) + request_multiple_devices(devices.map(|device| (device, &get_assertion)), timeout) } /// Will send the `credential_request` to all supplied `devices` and return either the first credential or the last error @@ -61,7 +83,8 @@ pub fn get_assertion_devices<'a>( pub fn make_credential_devices<'a>( credential_request: &'a FidoCredentialRequest, devices: impl Iterator, + timeout: Option, ) -> FidoResult { let make_credential = |device: &mut FidoDevice| device.make_credential(credential_request); - request_multiple_devices(devices.map(|device| (device, &make_credential))) + request_multiple_devices(devices.map(|device| (device, &make_credential)), timeout) }