1use std::net::IpAddr;
18
19use async_trait::async_trait;
20use bytes::{Buf, BufMut, BytesMut};
21use futures::{SinkExt, TryStreamExt, sink};
22use itertools::Itertools;
23use mz_adapter_types::connection::ConnectionId;
24use mz_ore::future::OreSinkExt;
25use mz_ore::netio::AsyncReady;
26use mz_pgwire_common::{
27 ChannelBinding, Conn, Cursor, DecodeState, ErrorResponse, FrontendMessage, GS2Header, Pgbuf,
28 SASLClientFinalResponse, SASLInitialResponse, input_err, parse_frame_len,
29};
30use tokio::io::{self, AsyncRead, AsyncWrite, Interest, Ready};
31use tokio::time::{self, Duration};
32use tokio_util::codec::{Decoder, Encoder, Framed};
33use tracing::trace;
34
35use crate::message::{BackendMessage, BackendMessageKind, SASLServerFinalMessageKinds};
36
37pub struct FramedConn<A> {
39 conn_id: ConnectionId,
40 peer_addr: Option<IpAddr>,
41 inner: sink::Buffer<Framed<Conn<A>, Codec>, BackendMessage>,
42}
43
44impl<A> FramedConn<A>
45where
46 A: AsyncRead + AsyncWrite + Unpin,
47{
48 pub fn new(conn_id: ConnectionId, peer_addr: Option<IpAddr>, inner: Conn<A>) -> FramedConn<A> {
57 FramedConn {
58 conn_id,
59 peer_addr,
60 inner: Framed::new(inner, Codec::new()).buffer(32),
61 }
62 }
63
64 pub async fn recv(&mut self) -> Result<Option<FrontendMessage>, io::Error> {
78 let message = self.inner.try_next().await?;
79 match &message {
80 Some(message) => trace!("cid={} recv_name={}", self.conn_id, message.name()),
81 None => trace!("cid={} recv=<eof>", self.conn_id),
82 }
83 Ok(message)
84 }
85
86 pub async fn send<M>(&mut self, message: M) -> Result<(), io::Error>
95 where
96 M: Into<BackendMessage>,
97 {
98 let message = message.into();
99 trace!(
100 "cid={} send={:?}",
101 self.conn_id,
102 BackendMessageKind::from(&message)
103 );
104 self.inner.enqueue(message).await
105 }
106
107 pub async fn send_all(
114 &mut self,
115 messages: impl IntoIterator<Item = BackendMessage>,
116 ) -> Result<(), io::Error> {
117 for m in messages {
120 self.send(m).await?;
121 }
122 Ok(())
123 }
124
125 pub async fn flush(&mut self) -> Result<(), io::Error> {
127 self.inner.flush().await
128 }
129
130 pub fn set_encode_state(
139 &mut self,
140 encode_state: Vec<(mz_pgrepr::Type, mz_pgwire_common::Format)>,
141 text_settings: mz_pgrepr::TextEncodeSettings,
142 ) {
143 let codec = self.inner.get_mut().codec_mut();
144 codec.encode_state = encode_state;
145 codec.text_settings = text_settings;
146 }
147
148 pub async fn wait_closed(&self) -> io::Error
162 where
163 A: AsyncReady + Send + Sync,
164 {
165 loop {
166 time::sleep(Duration::from_secs(1)).await;
167
168 match self.ready(Interest::READABLE | Interest::WRITABLE).await {
169 Ok(ready) if ready.is_read_closed() || ready.is_write_closed() => {
170 return io::Error::new(io::ErrorKind::Other, "connection closed");
171 }
172 Ok(_) => (),
173 Err(err) => return err,
174 }
175 }
176 }
177
178 pub fn conn_id(&self) -> &ConnectionId {
180 &self.conn_id
181 }
182
183 pub fn peer_addr(&self) -> &Option<IpAddr> {
185 &self.peer_addr
186 }
187}
188
189impl<A> FramedConn<A>
190where
191 A: AsyncRead + AsyncWrite + Unpin,
192{
193 pub fn inner(&self) -> &Conn<A> {
194 self.inner.get_ref().get_ref()
195 }
196}
197
198#[async_trait]
199impl<A> AsyncReady for FramedConn<A>
200where
201 A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
202{
203 async fn ready(&self, interest: Interest) -> io::Result<Ready> {
204 self.inner.get_ref().get_ref().ready(interest).await
205 }
206}
207
208pub struct Codec {
209 decode_state: DecodeState,
210 encode_state: Vec<(mz_pgrepr::Type, mz_pgwire_common::Format)>,
211 text_settings: mz_pgrepr::TextEncodeSettings,
213}
214
215impl Codec {
216 pub fn new() -> Codec {
218 Codec {
219 decode_state: DecodeState::Head,
220 encode_state: vec![],
221 text_settings: mz_pgrepr::TextEncodeSettings::STABLE,
222 }
223 }
224}
225
226impl Default for Codec {
227 fn default() -> Codec {
228 Codec::new()
229 }
230}
231
232impl Encoder<BackendMessage> for Codec {
233 type Error = io::Error;
234
235 fn encode(&mut self, msg: BackendMessage, dst: &mut BytesMut) -> Result<(), io::Error> {
238 let start = dst.len();
242 match self.encode_inner(msg, dst) {
243 Ok(()) => Ok(()),
244 Err(e) => {
245 dst.truncate(start);
246 Err(e)
247 }
248 }
249 }
250}
251
252impl Codec {
253 fn encode_inner(&self, msg: BackendMessage, dst: &mut BytesMut) -> Result<(), io::Error> {
256 let byte = match &msg {
258 BackendMessage::AuthenticationOk => b'R',
259 BackendMessage::AuthenticationCleartextPassword
260 | BackendMessage::AuthenticationSASL
261 | BackendMessage::AuthenticationSASLContinue(_)
262 | BackendMessage::AuthenticationSASLFinal(_) => b'R',
263 BackendMessage::RowDescription(_) => b'T',
264 BackendMessage::DataRow(_) => b'D',
265 BackendMessage::CommandComplete { .. } => b'C',
266 BackendMessage::EmptyQueryResponse => b'I',
267 BackendMessage::ReadyForQuery(_) => b'Z',
268 BackendMessage::NoData => b'n',
269 BackendMessage::ParameterStatus(_, _) => b'S',
270 BackendMessage::PortalSuspended => b's',
271 BackendMessage::BackendKeyData { .. } => b'K',
272 BackendMessage::ParameterDescription(_) => b't',
273 BackendMessage::ParseComplete => b'1',
274 BackendMessage::BindComplete => b'2',
275 BackendMessage::CloseComplete => b'3',
276 BackendMessage::ErrorResponse(r) => {
277 if r.severity.is_error() {
278 b'E'
279 } else {
280 b'N'
281 }
282 }
283 BackendMessage::CopyInResponse { .. } => b'G',
284 BackendMessage::CopyOutResponse { .. } => b'H',
285 BackendMessage::CopyData(_) => b'd',
286 BackendMessage::CopyDone => b'c',
287 };
288 dst.put_u8(byte);
289
290 let base = dst.len();
292 dst.put_u32(0);
293
294 match msg {
296 BackendMessage::CopyInResponse {
297 overall_format,
298 column_formats,
299 }
300 | BackendMessage::CopyOutResponse {
301 overall_format,
302 column_formats,
303 } => {
304 dst.put_format_i8(overall_format);
305 if column_formats.len() > usize::try_from(i16::MAX).expect("i16::MAX is positive") {
306 return Err(io::Error::new(
307 io::ErrorKind::InvalidData,
308 format!(
309 "{} columns in COPY response, which exceeds {}",
310 column_formats.len(),
311 i16::MAX
312 ),
313 ));
314 }
315 dst.put_length_i16(column_formats.len())?;
316 for format in column_formats {
317 dst.put_format_i16(format);
318 }
319 }
320 BackendMessage::CopyData(data) => {
321 dst.put_slice(&data);
322 }
323 BackendMessage::CopyDone => (),
324 BackendMessage::AuthenticationOk => {
325 dst.put_u32(0);
326 }
327 BackendMessage::AuthenticationCleartextPassword => {
328 dst.put_u32(3);
329 }
330 BackendMessage::AuthenticationSASL => {
331 dst.put_u32(10);
332 dst.put_string("SCRAM-SHA-256");
333 dst.put_u8(b'\0');
334 }
335 BackendMessage::AuthenticationSASLContinue(data) => {
336 dst.put_u32(11);
337 let data = format!(
338 "r={},s={},i={}",
339 data.nonce, data.salt, data.iteration_count
340 );
341 dst.put_slice(data.as_bytes());
342 }
343 BackendMessage::AuthenticationSASLFinal(data) => {
344 dst.put_u32(12);
345 let res = match data.kind {
346 SASLServerFinalMessageKinds::Verifier(verifier) => {
347 format!("v={}", verifier)
348 }
349 };
350 dst.put_slice(res.as_bytes());
351 if !data.extensions.is_empty() {
352 dst.put_slice(b",");
353 dst.put_slice(data.extensions.join(",").as_bytes());
354 }
355 }
356 BackendMessage::RowDescription(fields) => {
357 if fields.len() > usize::try_from(i16::MAX).expect("i16::MAX is positive") {
358 return Err(io::Error::new(
359 io::ErrorKind::InvalidData,
360 format!(
361 "{} fields in row description, which exceeds {}",
362 fields.len(),
363 i16::MAX
364 ),
365 ));
366 }
367 dst.put_length_i16(fields.len())?;
368 for f in &fields {
369 dst.put_string(&f.name.to_string());
370 dst.put_u32(f.table_id);
371 dst.put_u16(f.column_id);
372 dst.put_u32(f.type_oid);
373 dst.put_i16(f.type_len);
374 dst.put_i32(f.type_mod);
375 dst.put_format_i16(f.format);
377 }
378 }
379 BackendMessage::DataRow(fields) => {
380 if fields.len() > usize::try_from(i16::MAX).expect("i16::MAX is positive") {
381 return Err(io::Error::new(
382 io::ErrorKind::InvalidData,
383 format!(
384 "{} fields in data row, which exceeds {}",
385 fields.len(),
386 i16::MAX
387 ),
388 ));
389 }
390 dst.put_length_i16(fields.len())?;
391 for (f, (ty, format)) in fields.iter().zip_eq(&self.encode_state) {
392 if let Some(f) = f {
393 let base = dst.len();
394 dst.put_u32(0);
395 f.encode(ty, *format, dst, self.text_settings)?;
396 let len = dst.len() - base - 4;
397 let len = i32::try_from(len).map_err(|_| {
398 io::Error::new(
399 io::ErrorKind::InvalidData,
400 "length of encoded data row field does not fit into an i32",
401 )
402 })?;
403 dst[base..base + 4].copy_from_slice(&len.to_be_bytes());
404 } else {
405 dst.put_i32(-1);
406 }
407 }
408 }
409 BackendMessage::CommandComplete { tag } => {
410 dst.put_string(&tag);
411 }
412 BackendMessage::ParseComplete => (),
413 BackendMessage::BindComplete => (),
414 BackendMessage::CloseComplete => (),
415 BackendMessage::EmptyQueryResponse => (),
416 BackendMessage::ReadyForQuery(status) => {
417 dst.put_u8(status.into());
418 }
419 BackendMessage::ParameterStatus(name, value) => {
420 dst.put_string(name);
421 dst.put_string(&value);
422 }
423 BackendMessage::PortalSuspended => (),
424 BackendMessage::NoData => (),
425 BackendMessage::BackendKeyData {
426 conn_id,
427 secret_key,
428 } => {
429 dst.put_u32(conn_id);
430 dst.put_u32(secret_key);
431 }
432 BackendMessage::ParameterDescription(params) => {
433 if params.len() > usize::try_from(i16::MAX).expect("i16::MAX is positive") {
434 return Err(io::Error::new(
435 io::ErrorKind::InvalidData,
436 format!(
437 "{} params in parameter description, which exceeds {}",
438 params.len(),
439 i16::MAX
440 ),
441 ));
442 }
443 dst.put_length_i16(params.len())?;
444 for param in params {
445 dst.put_u32(param.oid());
446 }
447 }
448 BackendMessage::ErrorResponse(ErrorResponse {
449 severity,
450 code,
451 message,
452 detail,
453 hint,
454 position,
455 }) => {
456 dst.put_u8(b'S');
457 dst.put_string(severity.as_str());
458 dst.put_u8(b'C');
459 dst.put_string(code.code());
460 dst.put_u8(b'M');
461 dst.put_string(&message);
462 if let Some(detail) = &detail {
463 dst.put_u8(b'D');
464 dst.put_string(detail);
465 }
466 if let Some(hint) = &hint {
467 dst.put_u8(b'H');
468 dst.put_string(hint);
469 }
470 if let Some(position) = &position {
471 dst.put_u8(b'P');
472 dst.put_string(&position.to_string());
473 }
474 dst.put_u8(b'\0');
475 }
476 }
477
478 let len = dst.len() - base;
479
480 let len = i32::try_from(len).map_err(|_| {
482 io::Error::new(
483 io::ErrorKind::InvalidData,
484 "length of encoded message does not fit into an i32",
485 )
486 })?;
487 dst[base..base + 4].copy_from_slice(&len.to_be_bytes());
488
489 Ok(())
490 }
491}
492
493impl Decoder for Codec {
494 type Item = FrontendMessage;
495 type Error = io::Error;
496
497 fn decode(&mut self, src: &mut BytesMut) -> Result<Option<FrontendMessage>, io::Error> {
498 loop {
499 match self.decode_state {
500 DecodeState::Head => {
501 if src.len() < 5 {
502 return Ok(None);
503 }
504 let msg_type = src[0];
505 let frame_len = parse_frame_len(&src[1..])?;
506 src.advance(5);
507 src.reserve(frame_len);
508 self.decode_state = DecodeState::Data(msg_type, frame_len);
509 }
510
511 DecodeState::Data(msg_type, frame_len) => {
512 if src.len() < frame_len {
513 return Ok(None);
514 }
515 let buf = src.split_to(frame_len).freeze();
516 let buf = Cursor::new(&buf);
517 let msg = match msg_type {
518 b'Q' => decode_query(buf)?,
520
521 b'P' => decode_parse(buf)?,
523 b'D' => decode_describe(buf)?,
524 b'B' => decode_bind(buf)?,
525 b'E' => decode_execute(buf)?,
526 b'H' => decode_flush(buf)?,
527 b'S' => decode_sync(buf)?,
528 b'C' => decode_close(buf)?,
529
530 b'X' => decode_terminate(buf)?,
532
533 b'p' => decode_auth(buf)?,
535
536 b'f' => decode_copy_fail(buf)?,
538 b'd' => decode_copy_data(buf, frame_len)?,
539 b'c' => decode_copy_done(buf)?,
540
541 _ => {
543 return Err(io::Error::new(
544 io::ErrorKind::InvalidData,
545 format!("unknown message type {}", msg_type),
546 ));
547 }
548 };
549 src.reserve(5);
550 self.decode_state = DecodeState::Head;
551 return Ok(Some(msg));
552 }
553 }
554 }
555 }
556}
557
558fn decode_terminate(mut _buf: Cursor) -> Result<FrontendMessage, io::Error> {
559 Ok(FrontendMessage::Terminate)
561}
562
563fn decode_auth(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
564 let mut value = Vec::new();
565 while let Ok(b) = buf.read_byte() {
566 value.push(b);
567 }
568 Ok(FrontendMessage::RawAuthentication(value))
569}
570
571fn expect(buf: &mut Cursor, expected: &[u8]) -> Result<(), io::Error> {
572 for i in 0..expected.len() {
573 if buf.read_byte()? != expected[i] {
574 return Err(input_err(format!(
575 "Invalid SASL initial response: expected '{}'",
576 std::str::from_utf8(expected).unwrap_or("invalid UTF-8")
577 )));
578 }
579 }
580 Ok(())
581}
582
583fn read_until_comma(buf: &mut Cursor) -> Result<Vec<u8>, io::Error> {
584 let mut v = Vec::new();
585 while let Ok(b) = buf.peek_byte() {
586 if b == b',' {
587 break;
588 }
589 v.push(buf.read_byte()?);
590 }
591 Ok(v)
592}
593
594pub fn decode_sasl_client_first_message(mut buf: Cursor) -> Result<SASLInitialResponse, io::Error> {
625 let cbind_flag = match buf.read_byte()? {
627 b'n' => ChannelBinding::None,
628 b'y' => ChannelBinding::ClientSupported,
629 b'p' => {
630 expect(&mut buf, b"=")?;
632 let cbname = String::from_utf8(read_until_comma(&mut buf)?)
633 .map_err(|_| input_err("invalid cbname utf8"))?;
634 ChannelBinding::Required(cbname)
635 }
636 other => {
637 return Err(input_err(format!(
638 "Invalid channel binding flag: {}",
639 other
640 )));
641 }
642 };
643 expect(&mut buf, b",")?;
644
645 let mut authzid = None;
647 if buf.peek_byte()? == b'a' {
648 expect(&mut buf, b"a=")?;
649 let a = String::from_utf8(read_until_comma(&mut buf)?)
650 .map_err(|_| input_err("invalid authzid utf8"))?;
651 authzid = Some(a);
652 }
653 expect(&mut buf, b",")?;
654
655 let mut client_first_message_bare_raw = String::new();
656
657 let mut reserved_mext = None;
659 if buf.peek_byte()? == b'm' {
660 expect(&mut buf, b"m=")?;
661 let mext_val = String::from_utf8(read_until_comma(&mut buf)?)
662 .map_err(|_| input_err("invalid m ext utf8"))?;
663 client_first_message_bare_raw.push_str(&format!("m={},", mext_val));
664 reserved_mext = Some(mext_val);
665 expect(&mut buf, b",")?;
666 }
667
668 expect(&mut buf, b"n=")?;
670 let username = String::from_utf8(read_until_comma(&mut buf)?)
672 .map_err(|_| input_err("invalid username utf8"))?;
673 expect(&mut buf, b",")?;
674 client_first_message_bare_raw.push_str(&format!("n={},", username));
675
676 expect(&mut buf, b"r=")?;
678 let nonce = String::from_utf8(read_until_comma(&mut buf)?)
679 .map_err(|_| input_err("invalid nonce utf8"))?;
680 client_first_message_bare_raw.push_str(&format!("r={}", nonce));
681
682 let mut extensions = Vec::new();
684 while let Ok(b',') = buf.peek_byte().map(|b| b) {
685 expect(&mut buf, b",")?;
686 let ext = String::from_utf8(read_until_comma(&mut buf)?)
687 .map_err(|_| input_err("invalid ext utf8"))?;
688 if !ext.is_empty() {
689 client_first_message_bare_raw.push_str(&format!(",{}", ext));
690 extensions.push(ext);
691 }
692 }
693
694 Ok(SASLInitialResponse {
695 gs2_header: GS2Header {
696 cbind_flag,
697 authzid,
698 },
699 nonce,
700 extensions,
701 reserved_mext,
702 client_first_message_bare_raw,
703 })
704}
705
706pub fn decode_sasl_initial_response(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
707 let mechanism = buf.read_cstr()?;
708 let initial_resp_len = buf.read_i32()?;
709 if initial_resp_len < 0 {
710 return Err(input_err("No initial response"));
712 }
713
714 let initial_response = decode_sasl_client_first_message(buf)?;
715 Ok(FrontendMessage::SASLInitialResponse {
716 gs2_header: initial_response.gs2_header.clone(),
717 mechanism: mechanism.to_owned(),
718 initial_response,
719 })
720}
721
722pub fn decode_sasl_response(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
733 let mut client_final_message_bare_raw = String::new();
735 expect(&mut buf, b"c=")?;
737 let channel_binding = String::from_utf8(read_until_comma(&mut buf)?)
738 .map_err(|_| input_err("invalid channel-binding utf8"))?;
739 expect(&mut buf, b",")?;
740 client_final_message_bare_raw.push_str(&format!("c={},", channel_binding));
741
742 expect(&mut buf, b"r=")?;
744 let nonce = String::from_utf8(read_until_comma(&mut buf)?)
745 .map_err(|_| input_err("invalid nonce utf8"))?;
746 client_final_message_bare_raw.push_str(&format!("r={}", nonce));
747
748 let mut extensions = Vec::new();
750
751 while buf.peek_byte()? == b',' {
753 expect(&mut buf, b",")?;
754 if buf.peek_byte()? == b'p' {
755 break;
756 }
757 let ext = String::from_utf8(read_until_comma(&mut buf)?)
758 .map_err(|_| input_err("invalid extension utf8"))?;
759 if !ext.is_empty() {
760 client_final_message_bare_raw.push_str(&format!(",{}", ext));
761 extensions.push(ext);
762 }
763 }
764
765 expect(&mut buf, b"p=")?;
767 let proof = String::from_utf8(read_until_comma(&mut buf)?)
768 .map_err(|_| input_err("invalid proof utf8"))?;
769
770 Ok(FrontendMessage::SASLResponse(SASLClientFinalResponse {
771 channel_binding,
772 nonce,
773 extensions,
774 proof,
775 client_final_message_bare_raw,
776 }))
777}
778
779pub fn decode_password(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
780 Ok(FrontendMessage::Password {
781 password: buf.read_cstr()?.to_owned(),
782 })
783}
784
785fn decode_query(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
786 Ok(FrontendMessage::Query {
787 sql: buf.read_cstr()?.to_string(),
788 })
789}
790
791fn decode_parse(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
792 let name = buf.read_cstr()?;
793 let sql = buf.read_cstr()?;
794
795 let mut param_types = vec![];
796 for _ in 0..buf.read_i16()? {
797 param_types.push(buf.read_u32()?);
798 }
799
800 Ok(FrontendMessage::Parse {
801 name: name.into(),
802 sql: sql.into(),
803 param_types,
804 })
805}
806
807fn decode_close(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
808 match buf.read_byte()? {
809 b'S' => Ok(FrontendMessage::CloseStatement {
810 name: buf.read_cstr()?.to_owned(),
811 }),
812 b'P' => Ok(FrontendMessage::ClosePortal {
813 name: buf.read_cstr()?.to_owned(),
814 }),
815 b => Err(input_err(format!(
816 "invalid type byte in close message: {}",
817 b
818 ))),
819 }
820}
821
822fn decode_describe(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
823 let first_char = buf.read_byte()?;
824 let name = buf.read_cstr()?.to_string();
825 match first_char {
826 b'S' => Ok(FrontendMessage::DescribeStatement { name }),
827 b'P' => Ok(FrontendMessage::DescribePortal { name }),
828 other => Err(input_err(format!("Invalid describe type: {:#x?}", other))),
829 }
830}
831
832fn decode_bind(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
833 let portal_name = buf.read_cstr()?.to_string();
834 let statement_name = buf.read_cstr()?.to_string();
835
836 let mut param_formats = Vec::new();
837 for _ in 0..buf.read_i16()? {
838 param_formats.push(buf.read_format()?);
839 }
840
841 let mut raw_params = Vec::new();
842 for _ in 0..buf.read_i16()? {
843 let len = buf.read_i32()?;
844 if len == -1 {
845 raw_params.push(None); } else {
847 let mut value = Vec::new();
849 for _ in 0..len {
850 value.push(buf.read_byte()?);
851 }
852 raw_params.push(Some(value));
853 }
854 }
855
856 let mut result_formats = Vec::new();
857 for _ in 0..buf.read_i16()? {
858 result_formats.push(buf.read_format()?);
859 }
860
861 Ok(FrontendMessage::Bind {
862 portal_name,
863 statement_name,
864 param_formats,
865 raw_params,
866 result_formats,
867 })
868}
869
870fn decode_execute(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
871 let portal_name = buf.read_cstr()?.to_string();
872 let max_rows = buf.read_i32()?;
873 Ok(FrontendMessage::Execute {
874 portal_name,
875 max_rows,
876 })
877}
878
879fn decode_flush(mut _buf: Cursor) -> Result<FrontendMessage, io::Error> {
880 Ok(FrontendMessage::Flush)
882}
883
884fn decode_sync(mut _buf: Cursor) -> Result<FrontendMessage, io::Error> {
885 Ok(FrontendMessage::Sync)
887}
888
889fn decode_copy_data(mut buf: Cursor, frame_len: usize) -> Result<FrontendMessage, io::Error> {
890 let mut data = Vec::with_capacity(frame_len);
891 for _ in 0..frame_len {
892 data.push(buf.read_byte()?);
893 }
894 Ok(FrontendMessage::CopyData(data))
895}
896
897fn decode_copy_done(mut _buf: Cursor) -> Result<FrontendMessage, io::Error> {
898 Ok(FrontendMessage::CopyDone)
900}
901
902fn decode_copy_fail(mut buf: Cursor) -> Result<FrontendMessage, io::Error> {
903 Ok(FrontendMessage::CopyFail(buf.read_cstr()?.to_string()))
904}