anni_fetch/
io.rs

1/// https://git-scm.com/docs/protocol-common
2
3use std::io::{Read, Write};
4use std::convert::TryInto;
5
6pub(crate) fn take_sized<R: Read>(reader: &mut R, len: usize) -> std::io::Result<(Vec<u8>, u64)> {
7    let mut r = Vec::with_capacity(len);
8    let got = std::io::copy(&mut reader.take(len as u64), &mut r)?;
9    Ok((r, got))
10}
11
12pub(crate) fn token<R: Read>(reader: &mut R, token: &[u8]) -> std::io::Result<()> {
13    use std::io::{Error, ErrorKind};
14    let (got, read) = take_sized(reader, token.len())?;
15    if read != token.len() as u64 {
16        Err(Error::new(ErrorKind::InvalidInput, "more data needed"))
17    } else if got[..] == token[..] {
18        Ok(())
19    } else {
20        Err(Error::new(ErrorKind::InvalidData, "token mismatch"))
21    }
22}
23
24#[inline]
25pub(crate) fn u8<R: Read>(reader: &mut R) -> std::io::Result<u8> {
26    let mut buf = [0; 1];
27    reader.read_exact(&mut buf)?;
28    Ok(buf[0])
29}
30
31#[inline]
32pub(crate) fn u32_be<R: Read>(reader: &mut R) -> std::io::Result<u32> {
33    let mut buf = [0; 4];
34    reader.read_exact(&mut buf)?;
35    Ok(u32::from_be_bytes(buf[..].try_into().unwrap()))
36}
37
38fn read_len<R: Read>(reader: &mut R) -> std::io::Result<usize> {
39    let (next, got) = take_sized(reader, 4)?;
40    if got != 4 {
41        // EOF, return 0x10000(>0xffff)
42        Ok(0x10000)
43    } else {
44        let str = String::from_utf8_lossy(&next);
45        let len = usize::from_str_radix(str.as_ref(), 16).unwrap();
46        Ok(len)
47    }
48}
49
50/// Read pkgline from a reader
51pub fn read_pktline<R: Read>(reader: &mut R) -> std::io::Result<(Vec<u8>, usize)> {
52    let mut len = read_len(reader)?;
53    let data = if len == 0x10000 {
54        len = 0;
55        Vec::new()
56    } else if len >= 4 {
57        let (data, _) = take_sized(reader, len - 4)?;
58        data
59    } else {
60        format!("{:04x}", len).as_bytes().to_vec()
61    };
62    Ok((data, len))
63}
64
65///  When the grammar indicate PKT-LINE(...), unless otherwise noted the usual pkt-line LF rules apply:
66///  the sender SHOULD include a LF, but the receiver MUST NOT complain if it is not present.
67pub fn write_pktline<W: Write>(writer: &mut W, data: &str) -> std::io::Result<()> {
68    writer.write(format!("{:04x}", data.len() + 1 + 4).as_bytes())?;
69    writer.write(data.as_bytes())?;
70    writer.write(b"\n")?;
71    Ok(())
72}
73
74/// Write pkt line without the padding LF character
75pub fn write_pktline_nolf<W: Write>(writer: &mut W, data: &str) -> std::io::Result<()> {
76    writer.write(format!("{:04x}", data.len() + 4).as_bytes())?;
77    writer.write(data.as_bytes())?;
78    Ok(())
79}
80
81/// 0000 Flush Packet
82/// 0001 Delimiter Packet
83/// 0002 Response End Packet
84pub(crate) fn write_packet<W: Write>(writer: &mut W, data: u8) -> std::io::Result<()> {
85    writer.write(format!("{:04x}", data).as_bytes())?;
86    Ok(())
87}
88
89#[cfg(test)]
90mod tests {
91    use crate::io::{write_pktline, read_pktline, take_sized, token, u8, u32_be, read_len, write_pktline_nolf, write_packet};
92    use std::io::{Read, Cursor};
93
94    #[test]
95    fn test_take_sized() {
96        let v = [1, 2, 3];
97        let mut c = Cursor::new(v);
98        let (v, got) = take_sized(&mut c, 2).unwrap();
99        assert_eq!(v, vec![1, 2]);
100        assert_eq!(got, 2);
101
102        let (v, got) = take_sized(&mut c, 2).unwrap();
103        assert_eq!(v, vec![3]);
104        assert_eq!(got, 1);
105    }
106
107    #[test]
108    fn test_token() {
109        let t = b"fLaCtest";
110        let mut c = Cursor::new(t);
111        token(&mut c, b"fLaC").expect("fLaC token error");
112        token(&mut c, b"2333").expect_err("token not match");
113    }
114
115    #[test]
116    fn test_u8() {
117        let v = [1, 2, 3];
118        let mut c = Cursor::new(v);
119        assert_eq!(u8(&mut c).unwrap(), 1);
120        assert_eq!(u8(&mut c).unwrap(), 2);
121        assert_eq!(u8(&mut c).unwrap(), 3);
122        u8(&mut c).expect_err("fn should have no byte to read");
123    }
124
125    #[test]
126    fn test_u32_be() {
127        let v = [0xaa, 0xbb, 0xcc, 0xdd];
128        let mut c = Cursor::new(v);
129        assert_eq!(u32_be(&mut c).unwrap(), 0xaabbccdd);
130    }
131
132    #[test]
133    fn test_read_len() {
134        let v = b"00100000";
135        let mut c = Cursor::new(v);
136        assert_eq!(read_len(&mut c).unwrap(), 0x10);
137        assert_eq!(read_len(&mut c).unwrap(), 0);
138        assert_eq!(read_len(&mut c).unwrap(), 0x10000);
139    }
140
141    #[test]
142    fn test_pktline_read() {
143        let data = [
144            0x30, 0x30, 0x31, 0x65, 0x23, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3d, 0x67, 0x69,
145            0x74, 0x2d, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2d, 0x70, 0x61, 0x63, 0x6b, 0x0a, 0x30, 0x30,
146            0x30, 0x30, 0x30, 0x30, 0x30, 0x65, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x0a,
147            0x30, 0x30, 0x32, 0x33, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x67, 0x69, 0x74, 0x2f, 0x67, 0x69,
148            0x74, 0x68, 0x75, 0x62, 0x2d, 0x67, 0x31, 0x38, 0x63, 0x33, 0x31, 0x39, 0x39, 0x33, 0x39, 0x34,
149            0x61, 0x63, 0x0a, 0x30, 0x30, 0x30, 0x63, 0x6c, 0x73, 0x2d, 0x72, 0x65, 0x66, 0x73, 0x0a, 0x30,
150            0x30, 0x31, 0x39, 0x66, 0x65, 0x74, 0x63, 0x68, 0x3d, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77,
151            0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0a, 0x30, 0x30, 0x31, 0x32, 0x73, 0x65, 0x72, 0x76,
152            0x65, 0x72, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x30, 0x30, 0x31, 0x37, 0x6f, 0x62,
153            0x6a, 0x65, 0x63, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3d, 0x73, 0x68, 0x61, 0x31,
154            0x0a, 0x30, 0x30, 0x30, 0x30,
155        ];
156        let mut cursor = std::io::Cursor::new(data);
157        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
158        assert_eq!(r, b"# service=git-upload-pack\n");
159        assert_eq!(s, 0x001e);
160
161        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
162        assert_eq!(r, b"0000");
163        assert_eq!(s, 0x0000);
164
165        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
166        assert_eq!(r, b"version 2\n");
167        assert_eq!(s, 0x000e);
168
169        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
170        assert_eq!(r, b"agent=git/github-g18c3199394ac\n");
171        assert_eq!(s, 0x0023);
172
173        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
174        assert_eq!(r, b"ls-refs\n");
175        assert_eq!(s, 0x000c);
176
177        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
178        assert_eq!(r, b"fetch=shallow filter\n");
179        assert_eq!(s, 0x0019);
180
181        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
182        assert_eq!(r, b"server-option\n");
183        assert_eq!(s, 0x0012);
184
185        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
186        assert_eq!(r, b"object-format=sha1\n");
187        assert_eq!(s, 0x0017);
188
189        let (r, s) = read_pktline(&mut cursor).expect("failed to read");
190        assert_eq!(r, b"0000");
191        assert_eq!(s, 0x0000);
192
193        let mut r = [0; 0];
194        let r = cursor.read(&mut r).expect("failed to read");
195        assert_eq!(r, 0);
196    }
197
198    #[test]
199    fn test_pktline_write() {
200        let out = Vec::with_capacity(100);
201        let mut cursor = std::io::Cursor::new(out);
202        write_pktline(&mut cursor, "test").expect("failed to write pktline");
203        write_pktline_nolf(&mut cursor, "another_test").expect("failed to write pktline");
204        write_packet(&mut cursor, 0).expect("failed to write packet");
205        assert_eq!(cursor.into_inner(), b"0009test\n0010another_test0000")
206    }
207}