1use std::net::SocketAddr;
7use std::ops::{Deref, DerefMut};
8
9use crate::client::HttpClient;
10use crate::error::HttpError;
11
12pub struct PoolConfig {
14 pub addr: SocketAddr,
16 pub host: String,
18 pub pool_size: usize,
20 pub protocol: Protocol,
22 pub connect_timeout_ms: u64,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Protocol {
29 H2,
31 H1,
33 H1Plain,
35}
36
37enum Slot {
38 Connected(Box<HttpClient>),
39 Disconnected,
40}
41
42pub struct Pool {
44 addr: SocketAddr,
45 host: String,
46 protocol: Protocol,
47 slots: Vec<Slot>,
48 next: usize,
49 connect_timeout_ms: u64,
50}
51
52impl Pool {
53 pub fn new(config: PoolConfig) -> Self {
55 let mut slots = Vec::with_capacity(config.pool_size);
56 for _ in 0..config.pool_size {
57 slots.push(Slot::Disconnected);
58 }
59 Pool {
60 addr: config.addr,
61 host: config.host,
62 protocol: config.protocol,
63 slots,
64 next: 0,
65 connect_timeout_ms: config.connect_timeout_ms,
66 }
67 }
68
69 pub async fn connect_all(&mut self) -> Result<(), HttpError> {
71 for i in 0..self.slots.len() {
72 let client = self.do_connect().await?;
73 self.slots[i] = Slot::Connected(Box::new(client));
74 }
75 Ok(())
76 }
77
78 pub async fn client(&mut self) -> Result<PooledClient<'_>, HttpError> {
89 let size = self.slots.len();
90 for _ in 0..size {
91 let idx = self.next;
92 self.next = (self.next + 1) % size;
93
94 match &self.slots[idx] {
95 Slot::Connected(_) => {
96 return Ok(PooledClient { pool: self, idx });
97 }
98 Slot::Disconnected => {
99 if let Ok(client) = self.do_connect().await {
100 self.slots[idx] = Slot::Connected(Box::new(client));
101 return Ok(PooledClient { pool: self, idx });
102 }
103 }
104 }
105 }
106 Err(HttpError::AllConnectionsFailed)
107 }
108
109 pub fn mark_disconnected(&mut self, idx: usize) {
111 if idx < self.slots.len() {
112 if let Slot::Connected(client) = &self.slots[idx] {
113 client.close();
114 }
115 self.slots[idx] = Slot::Disconnected;
116 }
117 }
118
119 pub fn close_all(&mut self) {
121 for slot in &mut self.slots {
122 if let Slot::Connected(client) = slot {
123 client.close();
124 }
125 *slot = Slot::Disconnected;
126 }
127 }
128
129 pub fn connected_count(&self) -> usize {
131 self.slots
132 .iter()
133 .filter(|s| matches!(s, Slot::Connected(_)))
134 .count()
135 }
136
137 pub fn pool_size(&self) -> usize {
139 self.slots.len()
140 }
141
142 async fn do_connect(&self) -> Result<HttpClient, HttpError> {
143 match self.protocol {
144 Protocol::H2 => {
145 if self.connect_timeout_ms > 0 {
146 HttpClient::connect_h2_with_timeout(
147 self.addr,
148 &self.host,
149 self.connect_timeout_ms,
150 )
151 .await
152 } else {
153 HttpClient::connect_h2(self.addr, &self.host).await
154 }
155 }
156 Protocol::H1 => HttpClient::connect_h1(self.addr, &self.host).await,
157 Protocol::H1Plain => HttpClient::connect_h1_plain(self.addr, &self.host).await,
158 }
159 }
160}
161
162pub struct PooledClient<'a> {
171 pool: &'a mut Pool,
172 idx: usize,
173}
174
175impl PooledClient<'_> {
176 pub fn slot(&self) -> usize {
180 self.idx
181 }
182}
183
184impl Deref for PooledClient<'_> {
185 type Target = HttpClient;
186
187 fn deref(&self) -> &HttpClient {
188 match &self.pool.slots[self.idx] {
189 Slot::Connected(client) => client,
190 Slot::Disconnected => unreachable!("PooledClient over Disconnected slot"),
194 }
195 }
196}
197
198impl DerefMut for PooledClient<'_> {
199 fn deref_mut(&mut self) -> &mut HttpClient {
200 match &mut self.pool.slots[self.idx] {
201 Slot::Connected(client) => client,
202 Slot::Disconnected => unreachable!("PooledClient over Disconnected slot"),
203 }
204 }
205}
206
207impl Drop for PooledClient<'_> {
208 fn drop(&mut self) {
209 let should_recycle = match &self.pool.slots[self.idx] {
210 Slot::Connected(client) => client.peer_will_close(),
211 Slot::Disconnected => false,
212 };
213 if should_recycle {
214 self.pool.mark_disconnected(self.idx);
215 }
216 }
217}