1use std::collections::HashMap;
2
3use bytes::{Buf, BufMut, Bytes, BytesMut};
4use scylla_cql::frame::flag;
5use scylla_cql::frame::frame_errors::FrameHeaderParseError;
6use scylla_cql::frame::protocol_features::ProtocolFeatures;
7pub use scylla_cql::frame::request::RequestOpcode;
8use scylla_cql::frame::request::{RequestDeserializationError, RequestV2};
9pub use scylla_cql::frame::response::ResponseOpcode;
10use scylla_cql::frame::response::error::DbError;
11use scylla_cql::frame::types;
12use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
13
14use tracing::warn;
15
16use crate::errors::ReadFrameError;
17use crate::proxy::CompressionReader;
18
19const HEADER_SIZE: usize = 9;
20
21#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23pub struct FrameParams {
24 pub version: u8,
25 pub flags: u8,
26 pub stream: i16,
27}
28
29impl FrameParams {
30 pub const fn for_request(&self) -> FrameParams {
31 Self {
32 version: self.version & 0x7F,
33 ..*self
34 }
35 }
36 pub const fn for_response(&self) -> FrameParams {
37 Self {
38 version: 0x80 | (self.version & 0x7F),
39 ..*self
40 }
41 }
42
43 pub const fn is_compressed(&self) -> bool {
49 self.flags & flag::COMPRESSION != 0
50 }
51}
52
53#[derive(Copy, Clone, Debug)]
54pub(crate) enum FrameType {
55 Request,
56 Response,
57}
58
59#[derive(Copy, Clone, Debug, PartialEq, Eq)]
60pub(crate) enum FrameOpcode {
61 Request(RequestOpcode),
62 Response(ResponseOpcode),
63}
64
65#[derive(Clone, Debug, Eq)]
66pub struct RequestFrame {
67 pub params: FrameParams,
68 pub opcode: RequestOpcode,
69 pub body: Bytes,
71 pub wire_body_len: usize,
78}
79
80impl PartialEq for RequestFrame {
83 fn eq(&self, other: &Self) -> bool {
84 self.params == other.params && self.opcode == other.opcode && self.body == other.body
85 }
86}
87
88impl RequestFrame {
89 pub fn new(params: FrameParams, opcode: RequestOpcode, body: Bytes) -> Self {
92 Self {
93 params,
94 opcode,
95 wire_body_len: body.len(),
96 body,
97 }
98 }
99
100 pub(crate) async fn write(
101 &self,
102 writer: &mut (impl AsyncWrite + Unpin),
103 compression: &CompressionReader,
104 ) -> Result<(), tokio::io::Error> {
105 write_frame(
106 self.params,
107 FrameOpcode::Request(self.opcode),
108 &self.body,
109 writer,
110 compression,
111 )
112 .await
113 }
114
115 pub fn deserialize(
116 &self,
117 features: &ProtocolFeatures,
118 ) -> Result<RequestV2<'_>, RequestDeserializationError> {
119 RequestV2::deserialize(&mut &self.body[..], self.opcode, features)
120 }
121}
122#[derive(Clone, Debug, Eq)]
123pub struct ResponseFrame {
124 pub params: FrameParams,
125 pub opcode: ResponseOpcode,
126 pub body: Bytes,
128 pub wire_body_len: usize,
133}
134
135impl PartialEq for ResponseFrame {
138 fn eq(&self, other: &Self) -> bool {
139 self.params == other.params && self.opcode == other.opcode && self.body == other.body
140 }
141}
142
143impl ResponseFrame {
144 pub fn new(params: FrameParams, opcode: ResponseOpcode, body: Bytes) -> Self {
147 Self {
148 params,
149 opcode,
150 wire_body_len: body.len(),
151 body,
152 }
153 }
154
155 pub fn forged_error(
158 request_params: FrameParams,
159 error: DbError,
160 msg: Option<&str>,
161 ) -> Result<Self, std::num::TryFromIntError> {
162 let msg = msg.unwrap_or("Proxy-triggered error.");
163 let len_bytes = (msg.len() as u16).to_be_bytes(); let code_bytes = error.code(&ProtocolFeatures::default()).to_be_bytes(); let body_len = msg.len() + code_bytes.len() + len_bytes.len();
166 let mut buf = BytesMut::with_capacity(body_len);
167
168 buf.extend_from_slice(&code_bytes);
169 buf.extend_from_slice(&len_bytes);
170 buf.extend_from_slice(msg.as_bytes());
171
172 serialize_error_specific_fields(&mut buf, error)?;
173
174 Ok(ResponseFrame::new(
175 request_params.for_response(),
176 ResponseOpcode::Error,
177 buf.freeze(),
178 ))
179 }
180
181 pub fn forged_supported(
183 request_params: FrameParams,
184 options: &HashMap<String, Vec<String>>,
185 ) -> Result<Self, std::num::TryFromIntError> {
186 let mut buf = BytesMut::new();
187 types::write_string_multimap(options, &mut buf)?;
188
189 Ok(ResponseFrame::new(
190 request_params.for_response(),
191 ResponseOpcode::Supported,
192 buf.freeze(),
193 ))
194 }
195
196 pub fn forged_ready(request_params: FrameParams) -> Self {
197 ResponseFrame::new(
198 request_params.for_response(),
199 ResponseOpcode::Ready,
200 Bytes::new(),
201 )
202 }
203
204 pub(crate) async fn write(
205 &self,
206 writer: &mut (impl AsyncWrite + Unpin),
207 compression: &CompressionReader,
208 ) -> Result<(), tokio::io::Error> {
209 write_frame(
210 self.params,
211 FrameOpcode::Response(self.opcode),
212 &self.body,
213 writer,
214 compression,
215 )
216 .await
217 }
218}
219
220fn serialize_error_specific_fields(
221 buf: &mut BytesMut,
222 error: DbError,
223) -> Result<(), std::num::TryFromIntError> {
224 match error {
225 DbError::Unavailable {
226 consistency,
227 required,
228 alive,
229 } => {
230 types::write_consistency(consistency, buf);
231 types::write_int(required, buf);
232 types::write_int(alive, buf);
233 }
234 DbError::WriteTimeout {
235 consistency,
236 received,
237 required,
238 write_type,
239 } => {
240 types::write_consistency(consistency, buf);
241 types::write_int(received, buf);
242 types::write_int(required, buf);
243 types::write_string(write_type.as_str(), buf)?;
244 }
245 DbError::ReadTimeout {
246 consistency,
247 received,
248 required,
249 data_present,
250 } => {
251 types::write_consistency(consistency, buf);
252 types::write_int(received, buf);
253 types::write_int(required, buf);
254 buf.put_u8(u8::from(data_present));
255 }
256 DbError::ReadFailure {
257 consistency,
258 received,
259 required,
260 numfailures,
261 data_present,
262 } => {
263 types::write_consistency(consistency, buf);
264 types::write_int(received, buf);
265 types::write_int(required, buf);
266 types::write_int(numfailures, buf);
267 buf.put_u8(u8::from(data_present));
268 }
269 DbError::WriteFailure {
270 consistency,
271 received,
272 required,
273 numfailures,
274 write_type,
275 } => {
276 types::write_consistency(consistency, buf);
277 types::write_int(received, buf);
278 types::write_int(required, buf);
279 types::write_int(numfailures, buf);
280 types::write_string(write_type.as_str(), buf)?;
281 }
282 DbError::FunctionFailure {
283 keyspace,
284 function,
285 arg_types,
286 } => {
287 types::write_string(keyspace.as_str(), buf)?;
288 types::write_string(function.as_str(), buf)?;
289 types::write_string_list(&arg_types, buf)?;
290 }
291 DbError::AlreadyExists { keyspace, table } => {
292 types::write_string(keyspace.as_str(), buf)?;
293 types::write_string(table.as_str(), buf)?;
294 }
295 DbError::Unprepared { statement_id } => {
296 types::write_short_bytes(statement_id.as_ref(), buf)?;
297 }
298 _ => (),
299 }
300 Ok(())
301}
302
303pub(crate) async fn write_frame(
304 params: FrameParams,
305 opcode: FrameOpcode,
306 body: &[u8],
307 writer: &mut (impl AsyncWrite + Unpin),
308 compression: &CompressionReader,
309) -> Result<(), tokio::io::Error> {
310 let compressed_body = compression
311 .maybe_compress_body(params.flags, body)
312 .map_err(tokio::io::Error::other)?;
313
314 let body = compressed_body.as_deref().unwrap_or(body);
315
316 let mut header = [0; HEADER_SIZE];
317
318 header[0] = params.version;
319 header[1] = params.flags;
320 header[2..=3].copy_from_slice(¶ms.stream.to_be_bytes());
321 header[4] = match opcode {
322 FrameOpcode::Request(op) => op as u8,
323 FrameOpcode::Response(op) => op as u8,
324 };
325 header[5..9].copy_from_slice(&(body.len() as u32).to_be_bytes());
326
327 writer.write_all(&header).await?;
328 writer.write_all(body).await?;
329 writer.flush().await?;
330 Ok(())
331}
332
333pub(crate) async fn read_frame(
339 reader: &mut (impl AsyncRead + Unpin),
340 frame_type: FrameType,
341 compression: &CompressionReader,
342) -> Result<(FrameParams, FrameOpcode, Bytes, usize), ReadFrameError> {
343 let mut raw_header = [0u8; HEADER_SIZE];
344 reader
345 .read_exact(&mut raw_header[..])
346 .await
347 .map_err(FrameHeaderParseError::HeaderIoError)?;
348
349 let mut buf = &raw_header[..];
350
351 let version = buf.get_u8();
352 {
353 let (err, valid_direction, direction_str) = match frame_type {
354 FrameType::Request => (FrameHeaderParseError::FrameFromServer, 0x00, "request"),
355 FrameType::Response => (FrameHeaderParseError::FrameFromClient, 0x80, "response"),
356 };
357 if version & 0x80 != valid_direction {
358 return Err(err.into());
359 }
360 let protocol_version = version & 0x7F;
361 if protocol_version != 0x04 {
362 warn!(
363 "Received {} with protocol version {}.",
364 direction_str, protocol_version
365 );
366 }
367 }
368
369 let flags = buf.get_u8();
370 let stream = buf.get_i16();
371
372 let frame_params = FrameParams {
373 version,
374 flags,
375 stream,
376 };
377
378 let opcode = match frame_type {
379 FrameType::Request => FrameOpcode::Request(
380 RequestOpcode::try_from(buf.get_u8())
381 .map_err(|_| FrameHeaderParseError::FrameFromServer)?,
382 ),
383 FrameType::Response => FrameOpcode::Response(
384 ResponseOpcode::try_from(buf.get_u8())
385 .map_err(|_| FrameHeaderParseError::FrameFromClient)?,
386 ),
387 };
388
389 let length = buf.get_u32() as usize;
390
391 let mut body = Vec::with_capacity(length).limit(length);
392
393 while body.has_remaining_mut() {
394 let n = reader
395 .read_buf(&mut body)
396 .await
397 .map_err(|err| FrameHeaderParseError::BodyChunkIoError(body.remaining_mut(), err))?;
398 if n == 0 {
399 return Err(
401 FrameHeaderParseError::ConnectionClosed(body.remaining_mut(), length).into(),
402 );
403 }
404 }
405
406 let body = compression.maybe_decompress_body(flags, body.into_inner().into())?;
407
408 Ok((frame_params, opcode, body, length))
410}
411
412pub(crate) async fn read_request_frame(
413 reader: &mut (impl AsyncRead + Unpin),
414 compression: &CompressionReader,
415) -> Result<RequestFrame, ReadFrameError> {
416 read_frame(reader, FrameType::Request, compression)
417 .await
418 .map(|(params, opcode, body, wire_body_len)| RequestFrame {
419 params,
420 opcode: match opcode {
421 FrameOpcode::Request(op) => op,
422 FrameOpcode::Response(_) => unreachable!(),
423 },
424 body,
425 wire_body_len,
426 })
427}
428
429pub(crate) async fn read_response_frame(
430 reader: &mut (impl AsyncRead + Unpin),
431 compression: &CompressionReader,
432) -> Result<ResponseFrame, ReadFrameError> {
433 read_frame(reader, FrameType::Response, compression)
434 .await
435 .map(|(params, opcode, body, wire_body_len)| ResponseFrame {
436 params,
437 opcode: match opcode {
438 FrameOpcode::Request(_) => unreachable!(),
439 FrameOpcode::Response(op) => op,
440 },
441 body,
442 wire_body_len,
443 })
444}