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