1use super::Capabilities;
2use git_zero_core::{
3 Error, ObjectFormat, ObjectId, Result, pktline, repository::validate_ref_name_bytes,
4};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub struct Command<'a> {
8 pub old: ObjectId,
9 pub new: ObjectId,
10 pub name: &'a [u8],
11}
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub struct Limits {
15 pub max_bytes: u64,
16 pub max_commands: u32,
17 pub max_shallow: u32,
18 pub max_options: u32,
19}
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum ControlEvent<'a> {
23 NeedInput,
24 Shallow(ObjectId),
25 Command(Command<'a>),
26 Options,
28 PushOption(&'a [u8]),
29 End {
31 pack_required: bool,
32 },
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub struct ControlProgress<'a> {
37 pub consumed: usize,
38 pub event: ControlEvent<'a>,
39}
40
41#[derive(Clone, Copy, Eq, PartialEq)]
42enum Phase {
43 Commands,
44 Options,
45 End,
46}
47
48pub struct ControlDecoder<'a> {
53 frame: Frame<'a>,
54 state: ControlState,
55 failure: Option<Error>,
56}
57
58struct ControlState {
59 format: ObjectFormat,
60 offered: Capabilities,
61 selected: Capabilities,
62 limits: Limits,
63 phase: Phase,
64 commands: u32,
65 shallow: u32,
66 options: u32,
67 pack_required: bool,
68}
69
70impl<'a> ControlDecoder<'a> {
71 pub fn new(
72 storage: &'a mut [u8],
73 format: ObjectFormat,
74 offered: Capabilities,
75 limits: Limits,
76 ) -> Result<Self> {
77 if storage.len() < 4 {
78 return Err(Error::BufferTooSmall);
79 }
80 Ok(Self {
81 frame: Frame {
82 storage,
83 filled: 0,
84 target: 4,
85 ready: false,
86 total: 0,
87 limit: limits.max_bytes,
88 },
89 state: ControlState {
90 format,
91 offered,
92 selected: Capabilities::default(),
93 limits,
94 phase: Phase::Commands,
95 commands: 0,
96 shallow: 0,
97 options: 0,
98 pack_required: false,
99 },
100 failure: None,
101 })
102 }
103
104 pub fn capabilities(&self) -> Capabilities {
105 self.state.selected
106 }
107
108 pub fn read(&mut self, input: &[u8], end_of_input: bool) -> Result<ControlProgress<'_>> {
112 if let Some(error) = self.failure {
113 return Err(error);
114 }
115 if self.state.phase == Phase::End {
116 return Ok(ControlProgress {
117 consumed: 0,
118 event: ControlEvent::End {
119 pack_required: self.state.pack_required,
120 },
121 });
122 }
123 let frame = match self.frame.read(input) {
124 Ok(frame) => frame,
125 Err(error) => {
126 self.failure = Some(error);
127 return Err(error);
128 }
129 };
130 let event = match frame.event {
131 FrameEvent::NeedInput if end_of_input => Err(Error::InvalidPacket),
132 event => self.state.accept(event),
133 };
134 match event {
135 Ok(event) => Ok(ControlProgress {
136 consumed: frame.consumed,
137 event,
138 }),
139 Err(error) => {
140 self.failure = Some(error);
141 Err(error)
142 }
143 }
144 }
145}
146
147impl ControlState {
148 fn accept<'a>(&mut self, event: FrameEvent<'a>) -> Result<ControlEvent<'a>> {
149 match event {
150 FrameEvent::NeedInput => Ok(ControlEvent::NeedInput),
151 FrameEvent::Flush => Ok(self.flush()),
152 FrameEvent::Data(bytes) if self.phase == Phase::Options => self.option(bytes),
153 FrameEvent::Data(bytes) => {
154 let bytes = bytes.strip_suffix(b"\n").unwrap_or(bytes);
155 if let Some(hex) = bytes.strip_prefix(b"shallow ") {
156 self.boundary(hex)
157 } else {
158 self.command(bytes)
159 }
160 }
161 }
162 }
163
164 fn flush(&mut self) -> ControlEvent<'static> {
165 if self.phase == Phase::Commands && self.selected.contains(Capabilities::PUSH_OPTIONS) {
166 self.phase = Phase::Options;
167 ControlEvent::Options
168 } else {
169 self.phase = Phase::End;
170 ControlEvent::End {
171 pack_required: self.pack_required,
172 }
173 }
174 }
175
176 fn option<'a>(&mut self, bytes: &'a [u8]) -> Result<ControlEvent<'a>> {
177 if self.options == self.limits.max_options {
178 return Err(Error::LimitExceeded);
179 }
180 if bytes.iter().any(|byte| matches!(byte, 0 | b'\n' | b'\r')) {
181 return Err(Error::InvalidRequest);
182 }
183 self.options += 1;
184 Ok(ControlEvent::PushOption(bytes))
185 }
186
187 fn boundary(&mut self, hex: &[u8]) -> Result<ControlEvent<'static>> {
188 if self.commands != 0 {
189 return Err(Error::InvalidRequest);
190 }
191 if self.shallow == self.limits.max_shallow {
192 return Err(Error::LimitExceeded);
193 }
194 let id = object_id(hex, self.format)?;
195 if id.is_zero() {
196 return Err(Error::InvalidRequest);
197 }
198 self.shallow += 1;
199 Ok(ControlEvent::Shallow(id))
200 }
201
202 fn command<'a>(&mut self, bytes: &'a [u8]) -> Result<ControlEvent<'a>> {
203 if self.commands == self.limits.max_commands {
204 return Err(Error::LimitExceeded);
205 }
206 let (command, capabilities) =
207 command(bytes, self.format, self.commands == 0, self.offered)?;
208 if command.new.is_zero() && !self.offered.contains(Capabilities::DELETE_REFS) {
209 return Err(Error::Unsupported);
210 }
211 if self.commands == 0 {
212 self.selected = capabilities;
213 }
214 self.pack_required |= !command.new.is_zero();
215 self.commands += 1;
216 Ok(ControlEvent::Command(command))
217 }
218}
219
220fn object_id(bytes: &[u8], format: ObjectFormat) -> Result<ObjectId> {
221 let id = ObjectId::from_hex(bytes).map_err(|_| Error::InvalidRequest)?;
222 if id.format() != format {
223 return Err(Error::InvalidRequest);
224 }
225 Ok(id)
226}
227
228fn command(
229 bytes: &[u8],
230 format: ObjectFormat,
231 first: bool,
232 offered: Capabilities,
233) -> Result<(Command<'_>, Capabilities)> {
234 let (line, selected) = match bytes.iter().position(|&byte| byte == 0) {
235 Some(at) if first => (
236 &bytes[..at],
237 Capabilities::parse(&bytes[at + 1..], format, offered)?,
238 ),
239 Some(_) => return Err(Error::InvalidRequest),
240 None if first && format == ObjectFormat::Sha256 => return Err(Error::InvalidRequest),
241 None => (bytes, Capabilities::default()),
242 };
243 let width = format.hex_length();
244 if line.get(width) != Some(&b' ') || line.get(width * 2 + 1) != Some(&b' ') {
245 return Err(Error::InvalidRequest);
246 }
247 let old = object_id(&line[..width], format)?;
248 let new = object_id(&line[width + 1..width * 2 + 1], format)?;
249 let name = &line[width * 2 + 2..];
250 validate_ref_name_bytes(name)?;
251 if !new.is_zero() {
252 super::advertisement::validate_first_name(format, offered, name)?;
253 }
254 Ok((Command { old, new, name }, selected))
255}
256
257enum FrameEvent<'a> {
258 NeedInput,
259 Flush,
260 Data(&'a [u8]),
261}
262struct FrameProgress<'a> {
263 consumed: usize,
264 event: FrameEvent<'a>,
265}
266struct Frame<'a> {
267 storage: &'a mut [u8],
268 filled: usize,
269 target: usize,
270 ready: bool,
271 total: u64,
272 limit: u64,
273}
274
275impl Frame<'_> {
276 fn read(&mut self, input: &[u8]) -> Result<FrameProgress<'_>> {
277 if self.ready {
278 self.filled = 0;
279 self.target = 4;
280 self.ready = false;
281 }
282 let mut consumed = 0;
283 while self.filled < self.target {
284 let count = (self.target - self.filled).min(input.len() - consumed);
285 if count == 0 {
286 return Ok(FrameProgress {
287 consumed,
288 event: FrameEvent::NeedInput,
289 });
290 }
291 let total = self
292 .total
293 .checked_add(count as u64)
294 .filter(|&n| n <= self.limit)
295 .ok_or(Error::LimitExceeded)?;
296 self.storage[self.filled..self.filled + count]
297 .copy_from_slice(&input[consumed..consumed + count]);
298 self.filled += count;
299 consumed += count;
300 self.total = total;
301 if self.filled == 4 && self.target == 4 {
302 let length = pktline::length(&self.storage[..4])?;
303 if length == 0 {
304 self.ready = true;
305 return Ok(FrameProgress {
306 consumed,
307 event: FrameEvent::Flush,
308 });
309 }
310 if length > self.storage.len() {
311 return Err(Error::BufferTooSmall);
312 }
313 self.target = length;
314 }
315 }
316 self.ready = true;
317 Ok(FrameProgress {
318 consumed,
319 event: FrameEvent::Data(&self.storage[4..self.target]),
320 })
321 }
322}