mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-04-02 18:55:32 +00:00
ws load test: rewrite with glommio and futures-rustls
This commit is contained in:
parent
eb2c294300
commit
839f516dcb
4 changed files with 197 additions and 272 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -267,16 +267,19 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"aquatic_cli_helpers",
|
"aquatic_cli_helpers",
|
||||||
"aquatic_ws_protocol",
|
"aquatic_ws_protocol",
|
||||||
|
"async-tungstenite",
|
||||||
|
"futures",
|
||||||
|
"futures-rustls",
|
||||||
|
"glommio",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"mio",
|
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"rand",
|
"rand",
|
||||||
"rand_distr",
|
"rand_distr",
|
||||||
|
"rustls",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"slab",
|
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,19 @@ name = "aquatic_ws_load_test"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
async-tungstenite = "0.15"
|
||||||
aquatic_cli_helpers = "0.1.0"
|
aquatic_cli_helpers = "0.1.0"
|
||||||
aquatic_ws_protocol = "0.1.0"
|
aquatic_ws_protocol = "0.1.0"
|
||||||
|
futures = "0.3"
|
||||||
|
futures-rustls = "0.22"
|
||||||
|
glommio = { git = "https://github.com/DataDog/glommio.git", rev = "4e6b14772da2f4325271fbcf12d24cf91ed466e5" }
|
||||||
hashbrown = { version = "0.11.2", features = ["serde"] }
|
hashbrown = { version = "0.11.2", features = ["serde"] }
|
||||||
mimalloc = { version = "0.1", default-features = false }
|
mimalloc = { version = "0.1", default-features = false }
|
||||||
mio = { version = "0.7", features = ["udp", "os-poll", "os-util"] }
|
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
rand_distr = "0.4"
|
rand_distr = "0.4"
|
||||||
|
rustls = { version = "0.20", features = ["dangerous_configuration"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
slab = "0.4"
|
|
||||||
tungstenite = "0.15"
|
tungstenite = "0.15"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::sync::{atomic::Ordering, Arc};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use glommio::LocalExecutorBuilder;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand_distr::Pareto;
|
use rand_distr::Pareto;
|
||||||
|
|
||||||
|
|
@ -48,11 +49,18 @@ fn run(config: Config) -> ::anyhow::Result<()> {
|
||||||
pareto: Arc::new(pareto),
|
pareto: Arc::new(pareto),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tls_config = create_tls_config().unwrap();
|
||||||
|
|
||||||
for _ in 0..config.num_workers {
|
for _ in 0..config.num_workers {
|
||||||
let config = config.clone();
|
let config = config.clone();
|
||||||
|
let tls_config = tls_config.clone();
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
|
|
||||||
thread::spawn(move || run_socket_thread(&config, state));
|
LocalExecutorBuilder::default()
|
||||||
|
.spawn(|| async move {
|
||||||
|
run_socket_thread(config, tls_config, state).await.unwrap();
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor_statistics(state, &config);
|
monitor_statistics(state, &config);
|
||||||
|
|
@ -60,6 +68,36 @@ fn run(config: Config) -> ::anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FakeCertificateVerifier;
|
||||||
|
|
||||||
|
impl rustls::client::ServerCertVerifier for FakeCertificateVerifier {
|
||||||
|
fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
_end_entity: &rustls::Certificate,
|
||||||
|
_intermediates: &[rustls::Certificate],
|
||||||
|
_server_name: &rustls::ServerName,
|
||||||
|
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||||
|
_ocsp_response: &[u8],
|
||||||
|
_now: std::time::SystemTime,
|
||||||
|
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
|
||||||
|
Ok(rustls::client::ServerCertVerified::assertion())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_tls_config() -> anyhow::Result<Arc<rustls::ClientConfig>> {
|
||||||
|
let mut config = rustls::ClientConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_root_certificates(rustls::RootCertStore::empty())
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
config
|
||||||
|
.dangerous()
|
||||||
|
.set_certificate_verifier(Arc::new(FakeCertificateVerifier));
|
||||||
|
|
||||||
|
Ok(Arc::new(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn monitor_statistics(state: LoadTestState, config: &Config) {
|
fn monitor_statistics(state: LoadTestState, config: &Config) {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let mut report_avg_response_vec: Vec<f64> = Vec::new();
|
let mut report_avg_response_vec: Vec<f64> = Vec::new();
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,141 @@
|
||||||
use std::io::ErrorKind;
|
use std::{
|
||||||
use std::sync::atomic::Ordering;
|
cell::RefCell,
|
||||||
use std::time::Duration;
|
convert::TryInto,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{atomic::Ordering, Arc},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use aquatic_ws_protocol::{OfferId, OutMessage, PeerId};
|
||||||
use mio::{net::TcpStream, Events, Interest, Poll, Token};
|
use async_tungstenite::{WebSocketStream, client_async};
|
||||||
use rand::{prelude::*, rngs::SmallRng};
|
use futures::{StreamExt, SinkExt};
|
||||||
use tungstenite::{handshake::MidHandshake, ClientHandshake, HandshakeError, WebSocket};
|
use futures_rustls::{TlsConnector, client::TlsStream};
|
||||||
|
use glommio::net::TcpStream;
|
||||||
|
use glommio::{prelude::*, timer::TimerActionRepeat};
|
||||||
|
use rand::{Rng, SeedableRng, prelude::SmallRng};
|
||||||
|
|
||||||
use crate::common::*;
|
use crate::{common::LoadTestState, config::Config, utils::create_random_request};
|
||||||
use crate::config::*;
|
|
||||||
use crate::utils::create_random_request;
|
|
||||||
|
|
||||||
// Allow large enum variant WebSocket because it should be very common
|
pub async fn run_socket_thread(
|
||||||
#[allow(clippy::large_enum_variant)]
|
config: Config,
|
||||||
pub enum ConnectionState {
|
tls_config: Arc<rustls::ClientConfig>,
|
||||||
TcpStream(TcpStream),
|
load_test_state: LoadTestState,
|
||||||
WebSocket(WebSocket<TcpStream>),
|
) -> anyhow::Result<()> {
|
||||||
MidHandshake(MidHandshake<ClientHandshake<TcpStream>>),
|
let config = Rc::new(config);
|
||||||
|
let num_active_connections = Rc::new(RefCell::new(0usize));
|
||||||
|
|
||||||
|
TimerActionRepeat::repeat(move || {
|
||||||
|
periodically_open_connections(
|
||||||
|
config.clone(),
|
||||||
|
tls_config.clone(),
|
||||||
|
load_test_state.clone(),
|
||||||
|
num_active_connections.clone(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
futures::future::pending::<bool>().await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionState {
|
async fn periodically_open_connections(
|
||||||
fn advance(self, config: &Config) -> Option<Self> {
|
config: Rc<Config>,
|
||||||
match self {
|
tls_config: Arc<rustls::ClientConfig>,
|
||||||
Self::TcpStream(stream) => {
|
load_test_state: LoadTestState,
|
||||||
let req = format!(
|
num_active_connections: Rc<RefCell<usize>>,
|
||||||
|
) -> Option<Duration> {
|
||||||
|
if *num_active_connections.borrow() < config.num_connections {
|
||||||
|
spawn_local(async move {
|
||||||
|
if let Err(err) =
|
||||||
|
Connection::run(config, tls_config, load_test_state, num_active_connections).await
|
||||||
|
{
|
||||||
|
eprintln!("connection creation error: {:?}", err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Duration::from_secs(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Connection {
|
||||||
|
config: Rc<Config>,
|
||||||
|
load_test_state: LoadTestState,
|
||||||
|
rng: SmallRng,
|
||||||
|
can_send: bool,
|
||||||
|
peer_id: PeerId,
|
||||||
|
send_answer: Option<(PeerId, OfferId)>,
|
||||||
|
stream: WebSocketStream<TlsStream<TcpStream>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connection {
|
||||||
|
async fn run(
|
||||||
|
config: Rc<Config>,
|
||||||
|
tls_config: Arc<rustls::ClientConfig>,
|
||||||
|
load_test_state: LoadTestState,
|
||||||
|
num_active_connections: Rc<RefCell<usize>>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut rng = SmallRng::from_entropy();
|
||||||
|
let peer_id = PeerId(rng.gen());
|
||||||
|
let stream = TcpStream::connect(config.server_address)
|
||||||
|
.await
|
||||||
|
.map_err(|err| anyhow::anyhow!("connect: {:?}", err))?;
|
||||||
|
let stream = TlsConnector::from(tls_config).connect("example.com".try_into().unwrap(), stream).await?;
|
||||||
|
let request = format!(
|
||||||
"ws://{}:{}",
|
"ws://{}:{}",
|
||||||
config.server_address.ip(),
|
config.server_address.ip(),
|
||||||
config.server_address.port()
|
config.server_address.port()
|
||||||
);
|
);
|
||||||
|
let (stream, _) = client_async(request, stream).await?;
|
||||||
|
|
||||||
match ::tungstenite::client(req, stream) {
|
let mut connection = Connection {
|
||||||
Ok((ws, _)) => Some(ConnectionState::WebSocket(ws)),
|
config,
|
||||||
Err(HandshakeError::Interrupted(handshake)) => {
|
load_test_state,
|
||||||
Some(ConnectionState::MidHandshake(handshake))
|
rng,
|
||||||
}
|
stream,
|
||||||
Err(HandshakeError::Failure(err)) => {
|
can_send: true,
|
||||||
eprintln!("handshake error: {:?}", err);
|
peer_id,
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::MidHandshake(handshake) => match handshake.handshake() {
|
|
||||||
Ok((ws, _)) => Some(ConnectionState::WebSocket(ws)),
|
|
||||||
Err(HandshakeError::Interrupted(handshake)) => {
|
|
||||||
Some(ConnectionState::MidHandshake(handshake))
|
|
||||||
}
|
|
||||||
Err(HandshakeError::Failure(err)) => {
|
|
||||||
eprintln!("handshake error: {:?}", err);
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Self::WebSocket(ws) => Some(Self::WebSocket(ws)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Connection {
|
|
||||||
stream: ConnectionState,
|
|
||||||
peer_id: PeerId,
|
|
||||||
can_send: bool,
|
|
||||||
send_answer: Option<(PeerId, OfferId)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Connection {
|
|
||||||
pub fn create_and_register(
|
|
||||||
config: &Config,
|
|
||||||
rng: &mut impl Rng,
|
|
||||||
connections: &mut ConnectionMap,
|
|
||||||
poll: &mut Poll,
|
|
||||||
token_counter: &mut usize,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let mut stream = TcpStream::connect(config.server_address)?;
|
|
||||||
|
|
||||||
poll.registry()
|
|
||||||
.register(
|
|
||||||
&mut stream,
|
|
||||||
Token(*token_counter),
|
|
||||||
Interest::READABLE | Interest::WRITABLE,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let connection = Connection {
|
|
||||||
stream: ConnectionState::TcpStream(stream),
|
|
||||||
peer_id: PeerId(rng.gen()),
|
|
||||||
can_send: false,
|
|
||||||
send_answer: None,
|
send_answer: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
connections.insert(*token_counter, connection);
|
*num_active_connections.borrow_mut() += 1;
|
||||||
|
|
||||||
*token_counter += 1;
|
println!("run connection");
|
||||||
|
|
||||||
|
if let Err(err) = connection.run_connection_loop().await {
|
||||||
|
eprintln!("connection error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
*num_active_connections.borrow_mut() -= 1;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn advance(self, config: &Config) -> Option<Self> {
|
async fn run_connection_loop(&mut self) -> anyhow::Result<()> {
|
||||||
if let Some(stream) = self.stream.advance(config) {
|
|
||||||
let can_send = matches!(stream, ConnectionState::WebSocket(_));
|
|
||||||
|
|
||||||
Some(Self {
|
|
||||||
stream,
|
|
||||||
peer_id: self.peer_id,
|
|
||||||
can_send,
|
|
||||||
send_answer: None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_responses(&mut self, state: &LoadTestState) -> bool {
|
|
||||||
// bool = drop connection
|
|
||||||
if let ConnectionState::WebSocket(ref mut ws) = self.stream {
|
|
||||||
loop {
|
loop {
|
||||||
match ws.read_message() {
|
if self.can_send {
|
||||||
Ok(message) => match OutMessage::from_ws_message(message) {
|
let request =
|
||||||
|
create_random_request(&self.config, &self.load_test_state, &mut self.rng, self.peer_id).to_ws_message();
|
||||||
|
|
||||||
|
self.stream.send(request).await?;
|
||||||
|
self.stream.flush().await?;
|
||||||
|
|
||||||
|
self.load_test_state
|
||||||
|
.statistics
|
||||||
|
.requests
|
||||||
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
||||||
|
self.can_send = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.read_message().await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_message(&mut self) -> anyhow::Result<()> {
|
||||||
|
match OutMessage::from_ws_message(self.stream.next().await.unwrap()?) {
|
||||||
Ok(OutMessage::Offer(offer)) => {
|
Ok(OutMessage::Offer(offer)) => {
|
||||||
state
|
self.load_test_state
|
||||||
.statistics
|
.statistics
|
||||||
.responses_offer
|
.responses_offer
|
||||||
.fetch_add(1, Ordering::SeqCst);
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
@ -128,7 +145,7 @@ impl Connection {
|
||||||
self.can_send = true;
|
self.can_send = true;
|
||||||
}
|
}
|
||||||
Ok(OutMessage::Answer(_)) => {
|
Ok(OutMessage::Answer(_)) => {
|
||||||
state
|
self.load_test_state
|
||||||
.statistics
|
.statistics
|
||||||
.responses_answer
|
.responses_answer
|
||||||
.fetch_add(1, Ordering::SeqCst);
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
@ -136,7 +153,7 @@ impl Connection {
|
||||||
self.can_send = true;
|
self.can_send = true;
|
||||||
}
|
}
|
||||||
Ok(OutMessage::AnnounceResponse(_)) => {
|
Ok(OutMessage::AnnounceResponse(_)) => {
|
||||||
state
|
self.load_test_state
|
||||||
.statistics
|
.statistics
|
||||||
.responses_announce
|
.responses_announce
|
||||||
.fetch_add(1, Ordering::SeqCst);
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
@ -144,7 +161,7 @@ impl Connection {
|
||||||
self.can_send = true;
|
self.can_send = true;
|
||||||
}
|
}
|
||||||
Ok(OutMessage::ScrapeResponse(_)) => {
|
Ok(OutMessage::ScrapeResponse(_)) => {
|
||||||
state
|
self.load_test_state
|
||||||
.statistics
|
.statistics
|
||||||
.responses_scrape
|
.responses_scrape
|
||||||
.fetch_add(1, Ordering::SeqCst);
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
@ -152,7 +169,7 @@ impl Connection {
|
||||||
self.can_send = true;
|
self.can_send = true;
|
||||||
}
|
}
|
||||||
Ok(OutMessage::ErrorResponse(response)) => {
|
Ok(OutMessage::ErrorResponse(response)) => {
|
||||||
state
|
self.load_test_state
|
||||||
.statistics
|
.statistics
|
||||||
.responses_error
|
.responses_error
|
||||||
.fetch_add(1, Ordering::SeqCst);
|
.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
@ -164,144 +181,8 @@ impl Connection {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("error deserializing offer: {:?}", err);
|
eprintln!("error deserializing offer: {:?}", err);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(tungstenite::Error::Io(err)) if err.kind() == ErrorKind::WouldBlock => {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Ok(())
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_request(
|
|
||||||
&mut self,
|
|
||||||
config: &Config,
|
|
||||||
state: &LoadTestState,
|
|
||||||
rng: &mut impl Rng,
|
|
||||||
) -> bool {
|
|
||||||
// bool = remove connection
|
|
||||||
if !self.can_send {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ConnectionState::WebSocket(ref mut ws) = self.stream {
|
|
||||||
let request = create_random_request(&config, &state, rng, self.peer_id);
|
|
||||||
|
|
||||||
// If self.send_answer is set and request is announce request, make
|
|
||||||
// the request an offer answer
|
|
||||||
let request = if let InMessage::AnnounceRequest(mut r) = request {
|
|
||||||
if let Some((peer_id, offer_id)) = self.send_answer {
|
|
||||||
r.to_peer_id = Some(peer_id);
|
|
||||||
r.offer_id = Some(offer_id);
|
|
||||||
r.answer = Some(JsonValue(::serde_json::json!(
|
|
||||||
{"sdp": "abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-abcdefg-"}
|
|
||||||
)));
|
|
||||||
r.event = None;
|
|
||||||
r.offers = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.send_answer = None;
|
|
||||||
|
|
||||||
InMessage::AnnounceRequest(r)
|
|
||||||
} else {
|
|
||||||
request
|
|
||||||
};
|
|
||||||
|
|
||||||
match ws.write_message(request.to_ws_message()) {
|
|
||||||
Ok(()) => {
|
|
||||||
state.statistics.requests.fetch_add(1, Ordering::SeqCst);
|
|
||||||
|
|
||||||
self.can_send = false;
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Err(tungstenite::Error::Io(err)) if err.kind() == ErrorKind::WouldBlock => false,
|
|
||||||
Err(_) => true,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("send request can't send to non-ws stream");
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ConnectionMap = HashMap<usize, Connection>;
|
|
||||||
|
|
||||||
pub fn run_socket_thread(config: &Config, state: LoadTestState) {
|
|
||||||
let timeout = Duration::from_micros(config.network.poll_timeout_microseconds);
|
|
||||||
let create_conn_interval = 2 ^ config.network.connection_creation_interval;
|
|
||||||
|
|
||||||
let mut connections: ConnectionMap = HashMap::with_capacity(config.num_connections);
|
|
||||||
let mut poll = Poll::new().expect("create poll");
|
|
||||||
let mut events = Events::with_capacity(config.network.poll_event_capacity);
|
|
||||||
let mut rng = SmallRng::from_entropy();
|
|
||||||
|
|
||||||
let mut token_counter = 0usize;
|
|
||||||
let mut iter_counter = 0usize;
|
|
||||||
|
|
||||||
let mut drop_keys = Vec::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
poll.poll(&mut events, Some(timeout))
|
|
||||||
.expect("failed polling");
|
|
||||||
|
|
||||||
for event in events.iter() {
|
|
||||||
let token = event.token();
|
|
||||||
|
|
||||||
if event.is_readable() {
|
|
||||||
if let Some(connection) = connections.get_mut(&token.0) {
|
|
||||||
if let ConnectionState::WebSocket(_) = connection.stream {
|
|
||||||
let drop_connection = connection.read_responses(&state);
|
|
||||||
|
|
||||||
if drop_connection {
|
|
||||||
connections.remove(&token.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(connection) = connections.remove(&token.0) {
|
|
||||||
if let Some(connection) = connection.advance(config) {
|
|
||||||
connections.insert(token.0, connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k, connection) in connections.iter_mut() {
|
|
||||||
let drop_connection = connection.send_request(config, &state, &mut rng);
|
|
||||||
|
|
||||||
if drop_connection {
|
|
||||||
drop_keys.push(*k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k in drop_keys.drain(..) {
|
|
||||||
connections.remove(&k);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slowly create new connections
|
|
||||||
if connections.len() < config.num_connections && iter_counter % create_conn_interval == 0 {
|
|
||||||
let res = Connection::create_and_register(
|
|
||||||
config,
|
|
||||||
&mut rng,
|
|
||||||
&mut connections,
|
|
||||||
&mut poll,
|
|
||||||
&mut token_counter,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(err) = res {
|
|
||||||
eprintln!("create connection error: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iter_counter = iter_counter.wrapping_add(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue