diff --git a/src/ping_clients/ping_client.rs b/src/ping_clients/ping_client.rs index d0826f6..012ad89 100644 --- a/src/ping_clients/ping_client.rs +++ b/src/ping_clients/ping_client.rs @@ -2,12 +2,31 @@ use std::time::Duration; use std::net::SocketAddr; use async_trait::async_trait; +#[derive(thiserror::Error, Debug)] +pub enum PingClientWarning { + #[error("{0}")] + DisconnectFailed(Box), + + #[error("{0}")] + AppHandshakeFailed(Box), +} + +#[derive(thiserror::Error, Debug)] +pub enum PingClientError { + #[error("{0}")] + PreparationFailed(Box), + + #[error("{0}")] + PingFailed(Box), +} + + #[derive(Debug)] pub struct PingClientPingResultDetails { pub actual_local_addr: Option, pub round_trip_time: Duration, pub is_timeout: bool, - pub handshake_error: Option>, + pub warning: Option, } impl PingClientPingResultDetails { @@ -15,26 +34,17 @@ impl PingClientPingResultDetails { actual_local_addr: Option, round_trip_time: Duration, is_timeout: bool, - handshake_error: Option>, + warning: Option, ) -> PingClientPingResultDetails { PingClientPingResultDetails { actual_local_addr, round_trip_time, is_timeout, - handshake_error, + warning, } } } -#[derive(thiserror::Error, Debug)] -pub enum PingClientError { - #[error("{0}")] - PreparationFailed(Box), - - #[error("{0}")] - PingFailed(Box), -} - pub type PingClientResult = std::result::Result; #[async_trait] diff --git a/src/ping_clients/ping_client_quic.rs b/src/ping_clients/ping_client_quic.rs index 7d0ca24..d4dad53 100644 --- a/src/ping_clients/ping_client_quic.rs +++ b/src/ping_clients/ping_client_quic.rs @@ -1,6 +1,4 @@ -use crate::ping_clients::ping_client::{ - PingClient, PingClientError, PingClientPingResultDetails, PingClientResult, -}; +use crate::ping_clients::ping_client::{PingClient, PingClientError, PingClientPingResultDetails, PingClientResult, PingClientWarning}; use crate::PingClientConfig; use async_trait::async_trait; use quinn::{ClientConfigBuilder, Endpoint, EndpointError, ConnectionError}; @@ -94,9 +92,11 @@ impl PingClientQuic { Err(e) => match e { ConnectionError::TimedOut => Err(PingClientError::PingFailed(Box::new(e))), ConnectionError::LocallyClosed => Err(PingClientError::PingFailed(Box::new(e))), - _ => { - return Ok(PingClientPingResultDetails::new(None, rtt, false, Some(Box::new(e)))) - }, + _ => return Ok(PingClientPingResultDetails::new( + None, + rtt, + false, + Some(PingClientWarning::AppHandshakeFailed(Box::new(e))))), } }?; diff --git a/src/ping_clients/ping_client_tcp.rs b/src/ping_clients/ping_client_tcp.rs index 47400dd..3a5c9e7 100644 --- a/src/ping_clients/ping_client_tcp.rs +++ b/src/ping_clients/ping_client_tcp.rs @@ -1,4 +1,4 @@ -use crate::ping_clients::ping_client::{PingClient, PingClientResult, PingClientPingResultDetails, PingClientError}; +use crate::ping_clients::ping_client::{PingClient, PingClientResult, PingClientPingResultDetails, PingClientError, PingClientWarning}; use crate::PingClientConfig; use socket2::{Domain, SockAddr, Socket, Type}; use std::io; @@ -36,19 +36,19 @@ impl PingClientTcp { let local_addr = socket.local_addr(); // Check closing connection as well as opening connection + let mut warning: Option = None; if self.config.check_disconnect { - let shutdown_result = self.shutdown_connection(socket); - match shutdown_result { - Err(e) => return Err(PingClientError::PingFailed(Box::new(e))), - Ok(_) => (), + warning = match self.shutdown_connection(socket) { + Err(e) => Some(PingClientWarning::DisconnectFailed(Box::new(e))), + Ok(_) => None, } } // If getting local address failed, we ignore it. // The worse case we can get is to output a 0.0.0.0 as source IP, which is not critical to what we are trying to do. return match local_addr { - Ok(addr) => Ok(PingClientPingResultDetails::new(Some(addr.as_socket().unwrap()), rtt, false, None)), - Err(_) => Ok(PingClientPingResultDetails::new(None, rtt, false, None)), + Ok(addr) => Ok(PingClientPingResultDetails::new(Some(addr.as_socket().unwrap()), rtt, false, warning)), + Err(_) => Ok(PingClientPingResultDetails::new(None, rtt, false, warning)), }; } diff --git a/src/ping_result.rs b/src/ping_result.rs index 93214cd..3a06a8f 100644 --- a/src/ping_result.rs +++ b/src/ping_result.rs @@ -1,7 +1,8 @@ -use crate::ping_clients::ping_client::PingClientError::{self, PingFailed, PreparationFailed}; +use crate::ping_clients::ping_client::PingClientError; use chrono::{offset::Utc, DateTime}; use std::{net::SocketAddr, time::Duration}; use contracts::requires; +use crate::ping_clients::ping_client::PingClientWarning; #[derive(Debug)] pub struct PingResult { @@ -14,14 +15,14 @@ pub struct PingResult { is_succeeded: bool, round_trip_time: Duration, is_timed_out: bool, + warning: Option, error: Option, - handshake_error: Option>, } impl PingResult { #[requires(is_succeeded -> !is_timed_out && error.is_none())] - #[requires(handshake_error.is_some() -> is_succeeded)] - #[requires(!is_succeeded -> (is_timed_out || error.is_some()) && handshake_error.is_none())] + #[requires(warning.is_some() -> is_succeeded)] + #[requires(!is_succeeded -> (is_timed_out || error.is_some()) && warning.is_none())] pub fn new( time: &DateTime, worker_id: u32, @@ -32,8 +33,8 @@ impl PingResult { is_succeeded: bool, round_trip_time: Duration, is_timed_out: bool, + warning: Option, error: Option, - handshake_error: Option>, ) -> PingResult { PingResult { ping_time: time.clone(), @@ -45,8 +46,8 @@ impl PingResult { is_succeeded, round_trip_time, is_timed_out, + warning, error, - handshake_error, } } @@ -77,14 +78,14 @@ impl PingResult { pub fn is_timed_out(&self) -> bool { self.is_timed_out } - pub fn handshake_error(&self) -> &Option> { - &self.handshake_error + pub fn warning(&self) -> &Option { + &self.warning } pub fn error(&self) -> &Option { &self.error } pub fn is_preparation_error(&self) -> bool { - if let Some(PreparationFailed(_)) = self.error() { + if let Some(PingClientError::PreparationFailed(_)) = self.error() { true } else { false @@ -107,7 +108,7 @@ impl PingResult { if let Some(error) = self.error() { return match error { - PreparationFailed(e) => { + PingClientError::PreparationFailed(e) => { format!( "Unable to perform ping to {} {} from {}{}, because failed preparing to ping: Error = {}", self.protocol(), @@ -117,7 +118,7 @@ impl PingResult { e) } - PingFailed(e) => { + PingClientError::PingFailed(e) => { format!( "Reaching {} {} from {}{} failed: {}", self.protocol(), @@ -130,16 +131,32 @@ impl PingResult { }; } - if let Some(handshake_error) = self.handshake_error() { - return format!( - "Reaching {} {} from {}{} succeeded, but handshake failed: RTT={:.2}ms, Error = {}", - self.protocol(), - self.target(), - self.source(), - warmup_sign, - self.round_trip_time().as_micros() as f64 / 1000.0, - handshake_error, - ); + if let Some(warning) = self.warning() { + match warning { + PingClientWarning::AppHandshakeFailed(e) => { + return format!( + "Reaching {} {} from {}{} succeeded, but handshake failed: RTT={:.2}ms, Error = {}", + self.protocol(), + self.target(), + self.source(), + warmup_sign, + self.round_trip_time().as_micros() as f64 / 1000.0, + e, + ); + } + + PingClientWarning::DisconnectFailed(e) => { + return format!( + "Reaching {} {} from {}{} succeeded, but disconnect failed: RTT={:.2}ms, Error = {}", + self.protocol(), + self.target(), + self.source(), + warmup_sign, + self.round_trip_time().as_micros() as f64 / 1000.0, + e, + ); + } + } } return format!( @@ -154,19 +171,23 @@ impl PingResult { pub fn format_as_json_string(&self) -> String { let preparation_error = self.error().as_ref().map_or(String::from(""), |e| { - if let PreparationFailed(pe) = e { pe.to_string() } else { String::from("") } + if let PingClientError::PreparationFailed(pe) = e { pe.to_string() } else { String::from("") } }); let ping_error = self.error().as_ref().map_or(String::from(""), |e| { - if let PingFailed(pe) = e { pe.to_string() } else { String::from("") } + if let PingClientError::PingFailed(pe) = e { pe.to_string() } else { String::from("") } + }); + + let handshake_error = self.warning().as_ref().map_or(String::from(""), |w| { + if let PingClientWarning::AppHandshakeFailed(hw) = w { hw.to_string() } else { String::from("") } }); - let handshake_error = self.handshake_error().as_ref().map_or(String::from(""), |e| { - e.to_string() + let disconnect_error = self.warning().as_ref().map_or(String::from(""), |w| { + if let PingClientWarning::DisconnectFailed(dw) = w { dw.to_string() } else { String::from("") } }); let json = format!( - "{{\"utcTime\":\"{:?}\",\"protocol\":\"{}\",\"workerId\":{},\"targetIp\":\"{}\",\"targetPort\":{},\"sourceIp\":\"{}\",\"sourcePort\":{},\"isWarmup\":{},\"isSucceeded\":{},\"rttInMs\":{:.2},\"isTimedOut\":{},\"preparationError\":\"{}\",\"pingError\":\"{}\",\"handshakeError\":\"{}\"}}", + "{{\"utcTime\":\"{:?}\",\"protocol\":\"{}\",\"workerId\":{},\"targetIp\":\"{}\",\"targetPort\":{},\"sourceIp\":\"{}\",\"sourcePort\":{},\"isWarmup\":{},\"isSucceeded\":{},\"rttInMs\":{:.2},\"isTimedOut\":{},\"preparationError\":\"{}\",\"pingError\":\"{}\",\"handshakeError\":\"{}\",\"disconnectError\":\"{}\"}}", self.ping_time(), self.protocol(), self.worker_id(), @@ -181,6 +202,7 @@ impl PingResult { preparation_error, ping_error, handshake_error, + disconnect_error, ); return json; @@ -188,19 +210,23 @@ impl PingResult { pub fn format_as_csv_string(&self) -> String { let preparation_error = self.error().as_ref().map_or(String::from(""), |e| { - if let PreparationFailed(pe) = e { pe.to_string() } else { String::from("") } + if let PingClientError::PreparationFailed(pe) = e { pe.to_string() } else { String::from("") } }); let ping_error = self.error().as_ref().map_or(String::from(""), |e| { - if let PingFailed(pe) = e { pe.to_string() } else { String::from("") } + if let PingClientError::PingFailed(pe) = e { pe.to_string() } else { String::from("") } + }); + + let handshake_error = self.warning().as_ref().map_or(String::from(""), |w| { + if let PingClientWarning::AppHandshakeFailed(hw) = w { hw.to_string() } else { String::from("") } }); - let handshake_error = self.handshake_error().as_ref().map_or(String::from(""), |e| { - e.to_string() + let disconnect_error = self.warning().as_ref().map_or(String::from(""), |w| { + if let PingClientWarning::DisconnectFailed(dw) = w { dw.to_string() } else { String::from("") } }); let csv = format!( - "{:?},{},{},{},{},{},{},{},{},{:.2},{},\"{}\",\"{}\",\"{}\"", + "{:?},{},{},{},{},{},{},{},{},{:.2},{},\"{}\",\"{}\",\"{}\",\"{}\"", self.ping_time(), self.worker_id(), self.protocol(), @@ -215,6 +241,7 @@ impl PingResult { preparation_error, ping_error, handshake_error, + disconnect_error, ); return csv; @@ -254,7 +281,7 @@ mod tests { assert!(r.is_succeeded()); assert_eq!(Duration::from_millis(10), r.round_trip_time()); assert!(r.error().is_none()); - assert!(r.handshake_error().is_none()); + assert!(r.warning().is_none()); } #[test] @@ -265,6 +292,7 @@ mod tests { "Reaching TCP 1.2.3.4:443 from 5.6.7.8:8080 (warmup) succeeded: RTT=10.00ms", "Reaching TCP 1.2.3.4:443 from 5.6.7.8:8080 failed: Timed out, RTT = 1000.00ms", "Reaching TCP 1.2.3.4:443 from 5.6.7.8:8080 succeeded, but handshake failed: RTT=20.00ms, Error = connect aborted", + "Reaching TCP 1.2.3.4:443 from 5.6.7.8:8080 succeeded, but disconnect failed: RTT=20.00ms, Error = disconnect timeout", "Reaching TCP 1.2.3.4:443 from 5.6.7.8:8080 failed: connect failed", "Unable to perform ping to TCP 1.2.3.4:443 from 5.6.7.8:8080, because failed preparing to ping: Error = address in use", ], @@ -280,11 +308,12 @@ mod tests { let results = rnp_test_common::generate_ping_result_test_samples(); assert_eq!( vec![ - "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":true,\"isSucceeded\":true,\"rttInMs\":10.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"\"}", - "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":false,\"rttInMs\":1000.00,\"isTimedOut\":true,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"\"}", - "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":true,\"rttInMs\":20.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"connect aborted\"}", - "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":false,\"rttInMs\":0.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"connect failed\",\"handshakeError\":\"\"}", - "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":false,\"rttInMs\":0.00,\"isTimedOut\":false,\"preparationError\":\"address in use\",\"pingError\":\"\",\"handshakeError\":\"\"}", + "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":true,\"isSucceeded\":true,\"rttInMs\":10.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"\",\"disconnectError\":\"\"}", + "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":false,\"rttInMs\":1000.00,\"isTimedOut\":true,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"\",\"disconnectError\":\"\"}", + "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":true,\"rttInMs\":20.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"connect aborted\",\"disconnectError\":\"\"}", + "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":true,\"rttInMs\":20.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"\",\"handshakeError\":\"\",\"disconnectError\":\"disconnect timeout\"}", + "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":false,\"rttInMs\":0.00,\"isTimedOut\":false,\"preparationError\":\"\",\"pingError\":\"connect failed\",\"handshakeError\":\"\",\"disconnectError\":\"\"}", + "{\"utcTime\":\"2021-07-06T09:10:11.012Z\",\"protocol\":\"TCP\",\"workerId\":1,\"targetIp\":\"1.2.3.4\",\"targetPort\":443,\"sourceIp\":\"5.6.7.8\",\"sourcePort\":8080,\"isWarmup\":false,\"isSucceeded\":false,\"rttInMs\":0.00,\"isTimedOut\":false,\"preparationError\":\"address in use\",\"pingError\":\"\",\"handshakeError\":\"\",\"disconnectError\":\"\"}", ], results.into_iter().map(|x| x.format_as_json_string()).collect::>() ); @@ -295,11 +324,12 @@ mod tests { let results = rnp_test_common::generate_ping_result_test_samples(); assert_eq!( vec![ - "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,true,true,10.00,false,\"\",\"\",\"\"", - "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,false,1000.00,true,\"\",\"\",\"\"", - "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,true,20.00,false,\"\",\"\",\"connect aborted\"", - "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,false,0.00,false,\"\",\"connect failed\",\"\"", - "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,false,0.00,false,\"address in use\",\"\",\"\"", + "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,true,true,10.00,false,\"\",\"\",\"\",\"\"", + "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,false,1000.00,true,\"\",\"\",\"\",\"\"", + "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,true,20.00,false,\"\",\"\",\"connect aborted\",\"\"", + "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,true,20.00,false,\"\",\"\",\"\",\"disconnect timeout\"", + "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,false,0.00,false,\"\",\"connect failed\",\"\",\"\"", + "2021-07-06T09:10:11.012Z,1,TCP,1.2.3.4,443,5.6.7.8,8080,false,false,0.00,false,\"address in use\",\"\",\"\",\"\"", ], results .into_iter() diff --git a/src/ping_result_processors/ping_result_processor_console_logger.rs b/src/ping_result_processors/ping_result_processor_console_logger.rs index 0ba609f..df5c667 100644 --- a/src/ping_result_processors/ping_result_processor_console_logger.rs +++ b/src/ping_result_processors/ping_result_processor_console_logger.rs @@ -1,4 +1,5 @@ use crate::ping_result_processors::ping_result_processor::PingResultProcessor; +use crate::ping_clients::ping_client::PingClientWarning; use crate::PingResult; use std::io::{stdout, Write}; use std::net::SocketAddr; @@ -9,10 +10,13 @@ pub struct PingResultProcessorConsoleLogger { no_console_log: bool, last_console_flush_time: Instant, + protocol: Option, target: Option, ping_count: u32, success_count: u32, failure_count: u32, + handshake_failed_count: u32, + disconnect_failed_count: u32, min_latency_in_us: u128, max_latency_in_us: u128, average_latency_in_us: f64, @@ -24,10 +28,13 @@ impl PingResultProcessorConsoleLogger { return PingResultProcessorConsoleLogger { no_console_log, last_console_flush_time: Instant::now(), + protocol: None, target: None, ping_count: 0, success_count: 0, failure_count: 0, + handshake_failed_count: 0, + disconnect_failed_count: 0, min_latency_in_us: u128::MAX, max_latency_in_us: u128::MIN, average_latency_in_us: 0.0, @@ -45,8 +52,9 @@ impl PingResultProcessorConsoleLogger { return; } - // Save the target for outputting summary. + // Save some info for outputting summary. if self.target.is_none() { + self.protocol = Some(ping_result.protocol().to_string()); self.target = Some(ping_result.target()); } @@ -56,6 +64,13 @@ impl PingResultProcessorConsoleLogger { None => self.success_count += 1, } + if let Some(warning) = ping_result.warning() { + match warning { + PingClientWarning::AppHandshakeFailed(_) => self.handshake_failed_count += 1, + PingClientWarning::DisconnectFailed(_) => self.disconnect_failed_count += 1, + } + }; + let latency_in_us = ping_result.round_trip_time().as_micros(); if latency_in_us == 0 { // Latency data not set. @@ -118,14 +133,24 @@ impl PingResultProcessor for PingResultProcessorConsoleLogger { } println!( - "\n=== TCP connect statistics for {:?} ===", - self.target.unwrap() + "\n=== Connect statistics for {} {:?} ===", + self.protocol.as_ref().unwrap(), + self.target.as_ref().unwrap(), ); + let mut warning: String = String::from(""); + if self.handshake_failed_count > 0 || self.disconnect_failed_count > 0 { + let mut warning_messages = Vec::new(); + if self.handshake_failed_count > 0 { warning_messages.push(format!("App Handshake Failed = {}", self.handshake_failed_count)); } + if self.disconnect_failed_count > 0 { warning_messages.push(format!("Disconnect Failed = {}", self.disconnect_failed_count)); } + warning = format!(" ({})", warning_messages.join(",")); + } + println!( - "- Packets: Sent = {}, Received = {}, Lost = {} ({:.2}% loss).", + "- Connects: Sent = {}, Succeeded = {}{}, Failed = {} ({:.2}%).", self.ping_count, self.success_count, + warning, self.failure_count, (self.failure_count as f64 * 100.0) / (self.ping_count as f64), ); diff --git a/src/ping_result_processors/ping_result_processor_csv_logger.rs b/src/ping_result_processors/ping_result_processor_csv_logger.rs index f848bc5..ebabdfe 100644 --- a/src/ping_result_processors/ping_result_processor_csv_logger.rs +++ b/src/ping_result_processors/ping_result_processor_csv_logger.rs @@ -29,7 +29,7 @@ impl PingResultProcessor for PingResultProcessorCsvLogger { fn initialize(&mut self) { // Writer CSV header self.log_file - .write("UtcTime,WorkerId,Protocol,TargetIp,TargetPort,SourceIp,SourcePort,IsWarmup,IsSucceeded,RttInMs,IsTimedOut,PreparationError,PingError,HandshakeError\n".as_bytes()) + .write("UtcTime,WorkerId,Protocol,TargetIp,TargetPort,SourceIp,SourcePort,IsWarmup,IsSucceeded,RttInMs,IsTimedOut,PreparationError,PingError,HandshakeError,DisconnectError\n".as_bytes()) .expect(&format!( "Failed to write logs to csv file! Path = {}", self.log_path.display() @@ -85,7 +85,8 @@ mod tests { rtt_in_ms: 10f64, preparation_error: "".to_string(), ping_error: "".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, PingResultCsvDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -101,7 +102,8 @@ mod tests { rtt_in_ms: 1000f64, preparation_error: "".to_string(), ping_error: "".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, PingResultCsvDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -117,7 +119,25 @@ mod tests { rtt_in_ms: 20f64, preparation_error: "".to_string(), ping_error: "".to_string(), - handshake_error: "connect aborted".to_string() + handshake_error: "connect aborted".to_string(), + disconnect_error: "".to_string(), + }, + PingResultCsvDto { + utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), + worker_id: 1, + protocol: "TCP".to_string(), + target_ip: "1.2.3.4".parse().unwrap(), + target_port: 443, + source_ip: "5.6.7.8".parse().unwrap(), + source_port: 8080, + is_warmup: false, + is_succeeded: true, + is_timed_out: false, + rtt_in_ms: 20f64, + preparation_error: "".to_string(), + ping_error: "".to_string(), + handshake_error: "".to_string(), + disconnect_error: "disconnect timeout".to_string(), }, PingResultCsvDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -133,7 +153,8 @@ mod tests { rtt_in_ms: 0f64, preparation_error: "".to_string(), ping_error: "connect failed".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, PingResultCsvDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -149,7 +170,8 @@ mod tests { rtt_in_ms: 0f64, preparation_error: "address in use".to_string(), ping_error: "".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, ], actual_logged_records, diff --git a/src/ping_result_processors/ping_result_processor_json_logger.rs b/src/ping_result_processors/ping_result_processor_json_logger.rs index 8708a72..2ec87f0 100644 --- a/src/ping_result_processors/ping_result_processor_json_logger.rs +++ b/src/ping_result_processors/ping_result_processor_json_logger.rs @@ -99,7 +99,8 @@ mod tests { rtt_in_ms: 10f64, preparation_error: "".to_string(), ping_error: "".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, PingResultJsonDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -115,7 +116,8 @@ mod tests { rtt_in_ms: 1000f64, preparation_error: "".to_string(), ping_error: "".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, PingResultJsonDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -131,7 +133,25 @@ mod tests { rtt_in_ms: 20f64, preparation_error: "".to_string(), ping_error: "".to_string(), - handshake_error: "connect aborted".to_string() + handshake_error: "connect aborted".to_string(), + disconnect_error: "".to_string(), + }, + PingResultJsonDto { + utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), + worker_id: 1, + protocol: "TCP".to_string(), + target_ip: "1.2.3.4".parse().unwrap(), + target_port: 443, + source_ip: "5.6.7.8".parse().unwrap(), + source_port: 8080, + is_warmup: false, + is_succeeded: true, + is_timed_out: false, + rtt_in_ms: 20f64, + preparation_error: "".to_string(), + ping_error: "".to_string(), + handshake_error: "".to_string(), + disconnect_error: "disconnect timeout".to_string(), }, PingResultJsonDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -147,7 +167,8 @@ mod tests { rtt_in_ms: 0f64, preparation_error: "".to_string(), ping_error: "connect failed".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, PingResultJsonDto { utc_time: Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), @@ -163,7 +184,8 @@ mod tests { rtt_in_ms: 0f64, preparation_error: "address in use".to_string(), ping_error: "".to_string(), - handshake_error: "".to_string() + handshake_error: "".to_string(), + disconnect_error: "".to_string(), }, ], actual_logged_records, diff --git a/src/ping_result_processors/ping_result_processor_latency_bucket_logger.rs b/src/ping_result_processors/ping_result_processor_latency_bucket_logger.rs index 6fbc711..656463c 100644 --- a/src/ping_result_processors/ping_result_processor_latency_bucket_logger.rs +++ b/src/ping_result_processors/ping_result_processor_latency_bucket_logger.rs @@ -123,7 +123,7 @@ mod tests { .iter() .for_each(|x| logger.update_statistics(x)); - assert_eq!(3, logger.total_hit_count); + assert_eq!(4, logger.total_hit_count); assert_eq!(1, logger.timed_out_hit_count); assert_eq!(1, logger.failed_hit_count); } diff --git a/src/ping_worker.rs b/src/ping_worker.rs index eb6e309..7381709 100644 --- a/src/ping_worker.rs +++ b/src/ping_worker.rs @@ -113,8 +113,8 @@ impl PingWorker { !ping_result.is_timeout, ping_result.round_trip_time, ping_result.is_timeout, + ping_result.warning, None, - ping_result.handshake_error, ); self.result_sender.send(result).await.unwrap(); @@ -139,8 +139,8 @@ impl PingWorker { false, Duration::from_millis(0), false, - Some(error), None, + Some(error), ); self.result_sender.send(result).await.unwrap(); diff --git a/src/rnp_dto.rs b/src/rnp_dto.rs index 61f8151..f50ec9a 100644 --- a/src/rnp_dto.rs +++ b/src/rnp_dto.rs @@ -19,6 +19,7 @@ pub struct PingResultJsonDto { pub preparation_error: String, pub ping_error: String, pub handshake_error: String, + pub disconnect_error: String, } #[derive(Debug, Serialize, Deserialize, PartialOrd, PartialEq)] @@ -38,4 +39,5 @@ pub struct PingResultCsvDto { pub preparation_error: String, pub ping_error: String, pub handshake_error: String, + pub disconnect_error: String, } \ No newline at end of file diff --git a/src/rnp_test_common.rs b/src/rnp_test_common.rs index c5609d5..189b99b 100644 --- a/src/rnp_test_common.rs +++ b/src/rnp_test_common.rs @@ -3,6 +3,7 @@ use crate::ping_result::PingResult; use chrono::{TimeZone, Utc}; use std::io; use std::time::Duration; +use crate::ping_clients::ping_client::PingClientWarning; pub fn generate_ping_result_test_samples() -> Vec { vec![ @@ -47,11 +48,29 @@ pub fn generate_ping_result_test_samples() -> Vec { true, Duration::from_millis(20), false, - None, - Some(Box::new(io::Error::new( + Some(PingClientWarning::AppHandshakeFailed(Box::new(io::Error::new( io::ErrorKind::ConnectionAborted, "connect aborted", - ))), + )))), + None, + ), + + // Reachable but disconnect connection timed out + PingResult::new( + &Utc.ymd(2021, 7, 6).and_hms_milli(9, 10, 11, 12), + 1, + "TCP", + "1.2.3.4:443".parse().unwrap(), + "5.6.7.8:8080".parse().unwrap(), + false, + true, + Duration::from_millis(20), + false, + Some(PingClientWarning::DisconnectFailed(Box::new(io::Error::new( + io::ErrorKind::TimedOut, + "disconnect timeout", + )))), + None, ), // Failed to reach remote @@ -65,11 +84,11 @@ pub fn generate_ping_result_test_samples() -> Vec { false, Duration::from_millis(0), false, + None, Some(PingFailed(Box::new(io::Error::new( io::ErrorKind::ConnectionRefused, "connect failed", )))), - None, ), // Failed to create local resources for ping, such as cannot bind address @@ -83,11 +102,11 @@ pub fn generate_ping_result_test_samples() -> Vec { false, Duration::from_millis(0), false, + None, Some(PreparationFailed(Box::new(io::Error::new( io::ErrorKind::AddrInUse, "address in use", )))), - None, ), ] }