git_internal/protocol/
utils.rs1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use super::types::{PKT_LINE_END_MARKER, TransportProtocol};
4
5pub fn read_pkt_line(bytes: &mut Bytes) -> (usize, Bytes) {
11 if bytes.is_empty() {
12 return (0, Bytes::new());
13 }
14
15 if bytes.len() < 4 {
17 return (0, Bytes::new());
18 }
19
20 let pkt_length = bytes.copy_to_bytes(4);
21 let pkt_length_str = match core::str::from_utf8(&pkt_length) {
22 Ok(s) => s,
23 Err(_) => {
24 tracing::warn!("Invalid UTF-8 in packet length: {:?}", pkt_length);
25 return (0, Bytes::new());
26 }
27 };
28
29 let pkt_length = match usize::from_str_radix(pkt_length_str, 16) {
30 Ok(len) => len,
31 Err(_) => {
32 tracing::warn!("Invalid hex packet length: {:?}", pkt_length_str);
33 return (0, Bytes::new());
34 }
35 };
36
37 if pkt_length == 0 {
38 return (4, Bytes::new()); }
40
41 if pkt_length < 4 {
42 tracing::warn!("Invalid packet length: {} (must be >= 4)", pkt_length);
43 return (0, Bytes::new());
44 }
45
46 let data_length = pkt_length - 4;
47 if bytes.len() < data_length {
48 tracing::warn!(
49 "Insufficient data: need {} bytes, have {}",
50 data_length,
51 bytes.len()
52 );
53 return (0, Bytes::new());
54 }
55
56 let pkt_line = bytes.copy_to_bytes(data_length);
58 tracing::debug!("pkt line: {:?}", pkt_line);
59
60 (pkt_length, pkt_line)
61}
62
63pub fn add_pkt_line_string(pkt_line_stream: &mut BytesMut, buf_str: String) {
67 let buf_str_length = buf_str.len() + 4;
68 pkt_line_stream.put(Bytes::from(format!("{buf_str_length:04x}")));
69 pkt_line_stream.put(buf_str.as_bytes());
70}
71
72pub fn read_until_white_space(bytes: &mut Bytes) -> String {
76 let mut buf = Vec::new();
77 while bytes.has_remaining() {
78 let c = bytes.get_u8();
79 if c.is_ascii_whitespace() || c == 0 {
80 break;
81 }
82 buf.push(c);
83 }
84 match String::from_utf8(buf) {
85 Ok(s) => s,
86 Err(e) => {
87 tracing::warn!("Invalid UTF-8 in protocol data: {}", e);
88 String::new() }
90 }
91}
92
93pub fn build_smart_reply(
94 transport_protocol: TransportProtocol,
95 ref_list: &[String],
96 service: String,
97) -> BytesMut {
98 let mut pkt_line_stream = BytesMut::new();
99 if transport_protocol == TransportProtocol::Http {
100 add_pkt_line_string(&mut pkt_line_stream, format!("# service={service}\n"));
101 pkt_line_stream.put(&PKT_LINE_END_MARKER[..]);
102 }
103
104 for ref_line in ref_list {
105 add_pkt_line_string(&mut pkt_line_stream, ref_line.to_string());
106 }
107 pkt_line_stream.put(&PKT_LINE_END_MARKER[..]);
108 pkt_line_stream
109}
110
111pub fn search_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
113 haystack
114 .windows(needle.len())
115 .position(|window| window == needle)
116}