1use std::collections::VecDeque;
20
21use bytes::{Bytes, BytesMut};
22use kafka_protocol::messages::{ApiKey, RequestHeader, ResponseHeader};
23use kafka_protocol::protocol::{encode_request_header_into_buffer, Decodable, Encodable, StrBytes};
24
25use crate::frame::{self, FrameDecoder};
26use crate::{Error, Result};
27
28#[derive(Debug, Clone, Copy)]
30struct Pending {
31 api_key: ApiKey,
32 version: i16,
33 correlation_id: i32,
34}
35
36#[derive(Debug)]
42pub struct PendingResponse {
43 pub api_key: ApiKey,
44 pub version: i16,
45 pub correlation_id: i32,
46 pub body: Bytes,
48}
49
50#[derive(Debug)]
52pub struct Connection {
53 client_id: StrBytes,
54 next_correlation_id: i32,
55 in_flight: VecDeque<Pending>,
56 decoder: FrameDecoder,
57}
58
59impl Connection {
60 #[must_use]
61 pub fn new(client_id: impl Into<StrBytes>) -> Self {
62 Self {
63 client_id: client_id.into(),
64 next_correlation_id: 0,
65 in_flight: VecDeque::new(),
66 decoder: FrameDecoder::default(),
67 }
68 }
69
70 #[must_use]
76 pub fn in_flight(&self) -> usize {
77 self.in_flight.len()
78 }
79
80 pub fn request<R: Encodable>(
86 &mut self,
87 api_key: ApiKey,
88 version: i16,
89 req: &R,
90 ) -> Result<Bytes> {
91 self.next_correlation_id = self.next_correlation_id.wrapping_add(1);
92 let correlation_id = self.next_correlation_id;
93
94 let mut header = RequestHeader::default();
95 header.request_api_key = api_key as i16;
96 header.request_api_version = version;
97 header.correlation_id = correlation_id;
98 header.client_id = Some(self.client_id.clone());
99
100 let mut body = BytesMut::new();
101 encode_request_header_into_buffer(&mut body, &header)
104 .map_err(|e| Error::Codec(format!("encode header: {e}")))?;
105 req.encode(&mut body, version)
106 .map_err(|e| Error::Codec(format!("encode {api_key:?} v{version}: {e}")))?;
107
108 self.in_flight.push_back(Pending {
109 api_key,
110 version,
111 correlation_id,
112 });
113 frame::frame(&body)
114 }
115
116 pub fn push_bytes(&mut self, bytes: &[u8]) {
118 self.decoder.push(bytes);
119 }
120
121 #[must_use]
124 pub fn needed(&self) -> usize {
125 self.decoder.needed()
126 }
127
128 pub fn next_response(&mut self) -> Result<Option<PendingResponse>> {
135 let Some(mut frame) = self.decoder.next_frame()? else {
136 return Ok(None);
137 };
138 let pending = self.in_flight.front().copied().ok_or(Error::Unsolicited)?;
139
140 let header = ResponseHeader::decode(
141 &mut frame,
142 pending.api_key.response_header_version(pending.version),
143 )
144 .map_err(|e| Error::Codec(format!("decode response header: {e}")))?;
145
146 if header.correlation_id != pending.correlation_id {
147 return Err(Error::Correlation {
150 got: header.correlation_id,
151 expected: pending.correlation_id,
152 });
153 }
154 self.in_flight.pop_front();
155
156 Ok(Some(PendingResponse {
157 api_key: pending.api_key,
158 version: pending.version,
159 correlation_id: header.correlation_id,
160 body: frame,
161 }))
162 }
163
164 pub fn decode<R: Decodable>(resp: &PendingResponse) -> Result<R> {
169 let mut body = resp.body.clone();
170 R::decode(&mut body, resp.version).map_err(|e| {
171 Error::Codec(format!(
172 "decode {:?} v{} response: {e}",
173 resp.api_key, resp.version
174 ))
175 })
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use kafka_protocol::messages::{ApiVersionsRequest, ApiVersionsResponse};
183
184 fn broker_response<R: Encodable>(
187 api_key: ApiKey,
188 version: i16,
189 correlation_id: i32,
190 resp: &R,
191 ) -> Bytes {
192 let mut header = ResponseHeader::default();
193 header.correlation_id = correlation_id;
194 let mut body = BytesMut::new();
195 header
196 .encode(&mut body, api_key.response_header_version(version))
197 .unwrap();
198 resp.encode(&mut body, version).unwrap();
199 frame::frame(&body).unwrap()
200 }
201
202 fn api_versions_response(api_count: usize) -> ApiVersionsResponse {
203 let mut resp = ApiVersionsResponse::default();
204 resp.api_keys = (0..api_count)
205 .map(|i| {
206 let mut k = kafka_protocol::messages::api_versions_response::ApiVersion::default();
207 k.api_key = i as i16;
208 k.max_version = 1;
209 k
210 })
211 .collect();
212 resp
213 }
214
215 #[test]
216 fn a_request_and_its_response_are_matched() {
217 let mut conn = Connection::new(StrBytes::from_static_str("test"));
218 let req = ApiVersionsRequest::default();
219 let _wire = conn.request(ApiKey::ApiVersions, 3, &req).unwrap();
220 assert_eq!(conn.in_flight(), 1);
221
222 conn.push_bytes(&broker_response(
223 ApiKey::ApiVersions,
224 3,
225 1,
226 &api_versions_response(2),
227 ));
228 let resp = conn.next_response().unwrap().expect("a response");
229 assert_eq!(resp.correlation_id, 1);
230 assert_eq!(conn.in_flight(), 0);
231
232 let decoded: ApiVersionsResponse = Connection::decode(&resp).unwrap();
233 assert_eq!(decoded.api_keys.len(), 2);
234 }
235
236 #[test]
239 fn pipelined_requests_match_in_order() {
240 let mut conn = Connection::new(StrBytes::from_static_str("test"));
241 for _ in 0..3 {
242 conn.request(ApiKey::ApiVersions, 3, &ApiVersionsRequest::default())
243 .unwrap();
244 }
245 assert_eq!(conn.in_flight(), 3);
246
247 for id in 1..=3 {
248 conn.push_bytes(&broker_response(
249 ApiKey::ApiVersions,
250 3,
251 id,
252 &api_versions_response(id as usize),
253 ));
254 }
255 for id in 1..=3 {
256 let resp = conn.next_response().unwrap().expect("a response");
257 assert_eq!(resp.correlation_id, id);
258 let decoded: ApiVersionsResponse = Connection::decode(&resp).unwrap();
259 assert_eq!(decoded.api_keys.len(), id as usize);
260 }
261 assert_eq!(conn.in_flight(), 0);
262 }
263
264 #[test]
268 fn a_correlation_mismatch_is_fatal_and_does_not_advance() {
269 let mut conn = Connection::new(StrBytes::from_static_str("test"));
270 conn.request(ApiKey::ApiVersions, 3, &ApiVersionsRequest::default())
271 .unwrap();
272 conn.push_bytes(&broker_response(
273 ApiKey::ApiVersions,
274 3,
275 99,
276 &api_versions_response(1),
277 ));
278 assert!(matches!(
279 conn.next_response(),
280 Err(Error::Correlation {
281 got: 99,
282 expected: 1
283 })
284 ));
285 assert_eq!(conn.in_flight(), 1);
286 }
287
288 #[test]
289 fn a_response_with_nothing_in_flight_is_an_error() {
290 let mut conn = Connection::new(StrBytes::from_static_str("test"));
291 conn.push_bytes(&broker_response(
292 ApiKey::ApiVersions,
293 3,
294 1,
295 &api_versions_response(1),
296 ));
297 assert!(matches!(conn.next_response(), Err(Error::Unsolicited)));
298 }
299
300 #[test]
302 fn a_partial_response_yields_nothing_yet() {
303 let mut conn = Connection::new(StrBytes::from_static_str("test"));
304 conn.request(ApiKey::ApiVersions, 3, &ApiVersionsRequest::default())
305 .unwrap();
306 let wire = broker_response(ApiKey::ApiVersions, 3, 1, &api_versions_response(4));
307 conn.push_bytes(&wire[..wire.len() - 1]);
308 assert!(conn.next_response().unwrap().is_none());
309 conn.push_bytes(&wire[wire.len() - 1..]);
310 assert!(conn.next_response().unwrap().is_some());
311 }
312
313 #[test]
316 fn correlation_ids_advance() {
317 let mut conn = Connection::new(StrBytes::from_static_str("test"));
318 conn.request(ApiKey::ApiVersions, 3, &ApiVersionsRequest::default())
319 .unwrap();
320 conn.request(ApiKey::ApiVersions, 3, &ApiVersionsRequest::default())
321 .unwrap();
322 conn.push_bytes(&broker_response(
323 ApiKey::ApiVersions,
324 3,
325 1,
326 &api_versions_response(1),
327 ));
328 conn.push_bytes(&broker_response(
329 ApiKey::ApiVersions,
330 3,
331 2,
332 &api_versions_response(1),
333 ));
334 assert_eq!(conn.next_response().unwrap().unwrap().correlation_id, 1);
335 assert_eq!(conn.next_response().unwrap().unwrap().correlation_id, 2);
336 }
337}