netget/server/git/
pktline.rs1pub fn encode(payload: &[u8]) -> Vec<u8> {
9 let len = payload.len() + 4;
10 debug_assert!(len <= 65520, "pkt-line payload too large: {len}");
11 let mut out = format!("{:04x}", len).into_bytes();
12 out.extend_from_slice(payload);
13 out
14}
15
16pub const FLUSH: &[u8] = b"0000";
18
19#[derive(Debug, Default, Clone)]
21pub struct UploadPackRequest {
22 pub wants: Vec<String>,
24 pub haves: Vec<String>,
26 pub capabilities: Vec<String>,
28 pub done: bool,
30}
31
32pub fn parse_upload_pack_request(body: &[u8]) -> UploadPackRequest {
43 let mut request = UploadPackRequest::default();
44 let mut offset = 0usize;
45
46 while offset + 4 <= body.len() {
47 let header = match std::str::from_utf8(&body[offset..offset + 4]) {
48 Ok(h) => h,
49 Err(_) => break,
50 };
51 let len = match usize::from_str_radix(header, 16) {
52 Ok(l) => l,
53 Err(_) => break,
54 };
55 offset += 4;
56
57 if len == 0 || len == 1 {
59 continue;
60 }
61 if len < 4 {
62 break;
63 }
64 let payload_len = len - 4;
65 if offset + payload_len > body.len() {
66 break;
67 }
68 let payload = &body[offset..offset + payload_len];
69 offset += payload_len;
70
71 let line = String::from_utf8_lossy(payload);
72 let line = line.trim_end_matches(['\n', '\r']);
73
74 if let Some(rest) = line.strip_prefix("want ") {
75 let mut parts = rest.split(' ');
76 if let Some(oid) = parts.next() {
77 if is_object_id(oid) {
78 request.wants.push(oid.to_ascii_lowercase());
79 }
80 }
81 if request.capabilities.is_empty() {
83 request.capabilities = parts.map(|c| c.to_string()).collect();
84 }
85 } else if let Some(oid) = line.strip_prefix("have ") {
86 let oid = oid.split(' ').next().unwrap_or("");
87 if is_object_id(oid) {
88 request.haves.push(oid.to_ascii_lowercase());
89 }
90 } else if line == "done" {
91 request.done = true;
92 }
93 }
94
95 request
96}
97
98fn is_object_id(value: &str) -> bool {
99 value.len() == 40 && value.bytes().all(|b| b.is_ascii_hexdigit())
100}