git_zero_rustix/network/server/
chunked.rs1use git_zero::{Error, Result};
3
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub struct ChunkedLimits {
8 pub max_chunks: usize,
10 pub max_line_bytes: usize,
12}
13
14impl Default for ChunkedLimits {
15 fn default() -> Self {
16 Self {
17 max_chunks: 1024,
18 max_line_bytes: 32,
19 }
20 }
21}
22
23struct Intake<'a, 'read, F> {
24 receive: &'read mut F,
25 storage: &'a mut [u8],
26 length: usize,
27 position: usize,
28 wire_limit: Option<usize>,
29}
30
31impl<F: FnMut(&mut [u8]) -> Result<usize>> Intake<'_, '_, F> {
32 fn more(&mut self) -> Result<()> {
33 let capacity = self
34 .wire_limit
35 .map_or(self.storage.len(), |limit| limit.min(self.storage.len()));
36 if self.length >= capacity {
37 return Err(if self.wire_limit == Some(capacity) {
38 Error::LimitExceeded
39 } else {
40 Error::BufferTooSmall
41 });
42 }
43 let count = (self.receive)(&mut self.storage[self.length..capacity])?;
44 if count == 0 {
45 return Err(Error::InvalidRequest);
46 }
47 if count > capacity - self.length {
48 return Err(Error::Backend);
49 }
50 self.length += count;
51 Ok(())
52 }
53
54 fn byte(&mut self) -> Result<u8> {
55 if self.position == self.length {
56 self.more()?;
57 }
58 let byte = self.storage[self.position];
59 self.position += 1;
60 Ok(byte)
61 }
62
63 fn size(&mut self, limit: usize) -> Result<usize> {
64 let mut value = 0usize;
65 let mut digits = 0usize;
66 let mut consumed = 0usize;
67 loop {
68 if consumed >= limit {
69 return Err(Error::LimitExceeded);
70 }
71 let byte = self.byte()?;
72 consumed += 1;
73 if byte == b'\r' {
74 if consumed >= limit {
75 return Err(Error::LimitExceeded);
76 }
77 if self.byte()? != b'\n' || digits == 0 {
78 return Err(Error::InvalidRequest);
79 }
80 return Ok(value);
81 }
82 if byte == b';' {
83 return Err(Error::Unsupported);
84 }
85 let digit = match byte {
86 b'0'..=b'9' => byte - b'0',
87 b'a'..=b'f' => byte - b'a' + 10,
88 b'A'..=b'F' => byte - b'A' + 10,
89 _ => return Err(Error::InvalidRequest),
90 };
91 value = value
92 .checked_mul(16)
93 .and_then(|value| value.checked_add(usize::from(digit)))
94 .ok_or(Error::InvalidRequest)?;
95 digits += 1;
96 }
97 }
98}
99
100pub(super) fn read(
101 receive: &mut impl FnMut(&mut [u8]) -> Result<usize>,
102 storage: &mut [u8],
103 initial: usize,
104 wire_limit: Option<usize>,
105 body_limit: Option<usize>,
106 limits: ChunkedLimits,
107) -> Result<(usize, usize)> {
108 let mut input = Intake {
109 receive,
110 storage,
111 length: initial,
112 position: 0,
113 wire_limit,
114 };
115 let mut decoded = 0usize;
116 let mut chunks = 0usize;
117 loop {
118 let size = input.size(limits.max_line_bytes)?;
119 if size == 0 {
120 if input.byte()? != b'\r' {
123 return Err(Error::Unsupported);
124 }
125 if input.byte()? != b'\n' {
126 return Err(Error::InvalidRequest);
127 }
128 if input.length != input.position {
129 return Err(Error::InvalidRequest);
130 }
131 return Ok((input.position, decoded));
132 }
133 if chunks >= limits.max_chunks {
134 return Err(Error::LimitExceeded);
135 }
136 chunks += 1;
137 let end = decoded.checked_add(size).ok_or(Error::LimitExceeded)?;
138 if body_limit.is_some_and(|limit| end > limit) {
139 return Err(Error::LimitExceeded);
140 }
141 let wire_end = input
144 .position
145 .checked_add(size)
146 .and_then(|value| value.checked_add(2))
147 .ok_or(Error::LimitExceeded)?;
148 if wire_limit.is_some_and(|limit| wire_end > limit) {
149 return Err(Error::LimitExceeded);
150 }
151 if wire_end > input.storage.len() {
152 return Err(Error::BufferTooSmall);
153 }
154 let mut remaining = size;
155 while remaining > 0 {
156 if input.position == input.length {
157 input.more()?;
158 }
159 let count = remaining.min(input.length - input.position);
160 input
161 .storage
162 .copy_within(input.position..input.position + count, decoded);
163 input.position += count;
164 decoded += count;
165 remaining -= count;
166 }
167 if input.byte()? != b'\r' || input.byte()? != b'\n' {
168 return Err(Error::InvalidRequest);
169 }
170 }
171}