1#[cfg(feature = "read")]
4pub mod preparation;
5
6use crate::{
7 Request, Response,
8 http_backend::{self, Plan},
9 upload_pack,
10};
11use git_zero_core::{Error, Repository, Result, io::SliceWriter, pack};
12
13pub struct RequestBody<'a> {
17 storage: &'a mut [u8],
18 length: usize,
19 expected: Option<usize>,
20 cancelled: bool,
21}
22
23impl<'a> RequestBody<'a> {
24 pub fn new(storage: &'a mut [u8], expected: Option<usize>) -> Result<Self> {
25 if expected.is_some_and(|length| length > storage.len()) {
26 return Err(Error::BufferTooSmall);
27 }
28 Ok(Self {
29 storage,
30 length: 0,
31 expected,
32 cancelled: false,
33 })
34 }
35
36 pub fn push(&mut self, bytes: &[u8]) -> Result<()> {
38 if self.cancelled {
39 return Err(Error::InvalidRequest);
40 }
41 let end = self
42 .length
43 .checked_add(bytes.len())
44 .ok_or(Error::BufferTooSmall)?;
45 if self.expected.is_some_and(|length| end > length) {
46 return Err(Error::InvalidRequest);
47 }
48 self.storage
49 .get_mut(self.length..end)
50 .ok_or(Error::BufferTooSmall)?
51 .copy_from_slice(bytes);
52 self.length = end;
53 Ok(())
54 }
55
56 pub fn received(&self) -> usize {
57 self.length
58 }
59
60 pub fn cancel(&mut self) {
62 self.cancelled = true;
63 }
64
65 pub fn finish(self) -> Result<&'a [u8]> {
66 if self.cancelled || self.expected.is_some_and(|length| length != self.length) {
67 return Err(Error::InvalidRequest);
68 }
69 Ok(&self.storage[..self.length])
70 }
71}
72
73#[allow(clippy::large_enum_variant)]
75enum Body<'a> {
76 Buffered,
77 Pack(pack::Encoder<'a>),
78 Loose(pack::LooseEncoder),
79 #[cfg(feature = "read")]
80 Cached {
81 bytes: &'a [u8],
82 position: usize,
83 },
84}
85
86pub struct ResponseEncoder<'a> {
93 response: Response,
94 prefix: &'a [u8],
95 position: usize,
96 body: Body<'a>,
97 band: u8,
98 packet: [u8; 1029],
99 packet_position: usize,
100 packet_length: usize,
101 terminated: bool,
102 cancelled: bool,
103 started: bool,
104}
105
106impl<'a> ResponseEncoder<'a> {
107 pub fn new(
109 repository: &impl Repository,
110 request: Request<'_>,
111 control: &'a mut [u8],
112 ) -> Result<Self> {
113 let mut output = SliceWriter::new(control);
114 let plan = http_backend::prepare(repository, request, &mut output, false)?;
115 let Plan::Upload(version) = plan else {
116 return Self::ordinary_response(repository, plan, output.finish());
117 };
118 let upload = upload_pack::prepare_complete(repository, request.body, version)?;
119 upload.acknowledge(repository, &mut output)?;
120 let mut encoder = Self::buffered(upload_pack::response(), output.finish());
121 if upload.done {
122 encoder.body = Body::Pack(pack::Encoder::new(repository, None)?);
123 encoder.band = upload.band();
124 }
125 Ok(encoder)
126 }
127
128 #[cfg(feature = "read")]
132 pub fn with_selection<R: Repository + ?Sized>(
133 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
134 request: Request<'_>,
135 storage: &'a mut crate::SelectionStorage<'_>,
136 control: &'a mut [u8],
137 ) -> Result<Self> {
138 Self::with_selection_storage(repository, request, storage.reborrow(), control)
139 }
140
141 #[cfg(feature = "read")]
144 pub fn with_selection_storage<R: Repository + ?Sized>(
145 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
146 request: Request<'_>,
147 storage: crate::SelectionStorage<'a>,
148 control: &'a mut [u8],
149 ) -> Result<Self> {
150 Self::with_selection_storage_policy(
151 repository,
152 request,
153 storage,
154 crate::WantOptions::STRICT,
155 control,
156 )
157 }
158
159 #[cfg(feature = "read")]
162 pub fn with_selection_policy<R: Repository + ?Sized>(
163 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
164 request: Request<'_>,
165 storage: &'a mut crate::SelectionStorage<'_>,
166 options: crate::WantOptions,
167 control: &'a mut [u8],
168 ) -> Result<Self> {
169 Self::with_selection_storage_policy(
170 repository,
171 request,
172 storage.reborrow(),
173 options,
174 control,
175 )
176 }
177
178 #[cfg(feature = "read")]
179 pub fn with_selection_storage_policy<R: Repository + ?Sized>(
180 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
181 request: Request<'_>,
182 storage: crate::SelectionStorage<'a>,
183 options: crate::WantOptions,
184 control: &'a mut [u8],
185 ) -> Result<Self> {
186 Self::selected_storage(repository, request, storage, options, None, control)
187 }
188
189 #[cfg(feature = "read")]
192 pub fn with_revisions<R: Repository + ?Sized>(
193 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
194 request: Request<'_>,
195 storage: &'a mut crate::RevisionStorage<'_>,
196 control: &'a mut [u8],
197 ) -> Result<Self> {
198 Self::with_revision_storage(repository, request, storage.reborrow(), control)
199 }
200
201 #[cfg(feature = "read")]
202 pub fn with_revision_storage<R: Repository + ?Sized>(
203 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
204 request: Request<'_>,
205 storage: crate::RevisionStorage<'a>,
206 control: &'a mut [u8],
207 ) -> Result<Self> {
208 Self::with_revision_storage_policy(
209 repository,
210 request,
211 storage,
212 crate::WantOptions::STRICT,
213 control,
214 )
215 }
216
217 #[cfg(feature = "read")]
220 pub fn with_revision_storage_policy<R: Repository + ?Sized>(
221 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
222 request: Request<'_>,
223 storage: crate::RevisionStorage<'a>,
224 options: crate::WantOptions,
225 control: &'a mut [u8],
226 ) -> Result<Self> {
227 Self::selected_storage(
228 repository,
229 request,
230 storage.selection,
231 options,
232 Some((storage.state, storage.limits)),
233 control,
234 )
235 }
236
237 #[cfg(feature = "read")]
238 fn selected_storage<R: Repository + ?Sized>(
239 repository: &git_zero_core::ValidatedRepository<'_, '_, R>,
240 request: Request<'_>,
241 storage: crate::SelectionStorage<'a>,
242 options: crate::WantOptions,
243 revision: Option<(&mut [u8], git_zero_core::RevisionLimits)>,
244 control: &'a mut [u8],
245 ) -> Result<Self> {
246 let mut output = SliceWriter::new(control);
247 let plan = upload_pack::prepare_route(
248 repository,
249 request,
250 options,
251 revision.is_some(),
252 &mut output,
253 )?;
254 let Plan::Upload(version) = plan else {
255 return Self::ordinary_response(repository, plan, output.finish());
256 };
257 let selected = upload_pack::prepare_fetch(
258 repository,
259 request,
260 version,
261 storage,
262 options,
263 revision,
264 &mut output,
265 )?;
266 let mut encoder = Self::buffered(upload_pack::response(), output.finish());
267 if let Some(indices) = selected.indices {
268 encoder.body = Body::Pack(pack::Encoder::new(repository, Some(indices))?);
269 encoder.band = selected.upload.band();
270 }
271 Ok(encoder)
272 }
273
274 fn buffered(response: Response, prefix: &'a [u8]) -> Self {
275 Self {
276 response,
277 prefix,
278 position: 0,
279 body: Body::Buffered,
280 band: 0,
281 packet: [0; 1029],
282 packet_position: 0,
283 packet_length: 0,
284 terminated: false,
285 cancelled: false,
286 started: false,
287 }
288 }
289
290 fn ordinary_response(
291 repository: &impl Repository,
292 plan: Plan,
293 prefix: &'a [u8],
294 ) -> Result<Self> {
295 match plan {
296 Plan::Buffered(response) => Ok(Self::buffered(response, prefix)),
297 Plan::Loose(index) => {
298 let mut encoder = Self::buffered(
299 Response {
300 status: 200,
301 content_type: "application/x-git-loose-object",
302 },
303 prefix,
304 );
305 encoder.body = Body::Loose(pack::LooseEncoder::new(repository, index)?);
306 Ok(encoder)
307 }
308 Plan::Upload(_) => unreachable!(),
309 }
310 }
311
312 pub fn response(&self) -> Response {
313 self.response
314 }
315
316 pub fn cancel(&mut self) {
319 self.cancelled = true;
320 }
321
322 pub fn read(&mut self, repository: &impl Repository, output: &mut [u8]) -> Result<usize> {
325 if self.cancelled {
326 return Err(Error::InvalidRequest);
327 }
328 self.started = true;
329 if self.position < self.prefix.len() {
330 return copy(self.prefix, &mut self.position, output);
331 }
332 if self.band == 0 {
333 return self.read_body(repository, output);
334 }
335 if self.packet_position == self.packet_length && !self.terminated {
336 if output.is_empty() {
337 return Err(Error::BufferTooSmall);
338 }
339 self.next_packet(repository)?;
340 }
341
342 if self.packet_position == self.packet_length {
343 return Ok(0);
344 }
345 copy(
346 &self.packet[..self.packet_length],
347 &mut self.packet_position,
348 output,
349 )
350 }
351
352 fn next_packet(&mut self, repository: &impl Repository) -> Result<()> {
353 let capacity = if self.band == 1 { 995 } else { 1024 };
354 let length = match &mut self.body {
355 Body::Pack(pack) => pack.read(repository, &mut self.packet[5..5 + capacity])?,
356 #[cfg(feature = "read")]
357 Body::Cached { bytes, position } => {
358 if *position == bytes.len() {
359 0
360 } else {
361 copy(bytes, position, &mut self.packet[5..5 + capacity])?
362 }
363 }
364 _ => unreachable!(),
365 };
366 self.packet_position = 0;
367 if length == 0 {
368 self.packet[..4].copy_from_slice(b"0000");
369 self.packet_length = 4;
370 self.terminated = true;
371 } else {
372 let size = length + 5;
373 for (index, byte) in self.packet[..4].iter_mut().enumerate() {
374 *byte = b"0123456789abcdef"[(size >> ((3 - index) * 4)) & 15];
375 }
376 self.packet[4] = 1;
377 self.packet_length = size;
378 }
379 Ok(())
380 }
381
382 fn read_body(&mut self, repository: &impl Repository, output: &mut [u8]) -> Result<usize> {
383 match &mut self.body {
384 Body::Buffered => Ok(0),
385 Body::Pack(pack) => pack.read(repository, output),
386 Body::Loose(loose) => loose.read(repository, output),
387 #[cfg(feature = "read")]
388 Body::Cached { bytes, position } => {
389 if *position == bytes.len() {
390 Ok(0)
391 } else {
392 copy(bytes, position, output)
393 }
394 }
395 }
396 }
397}
398
399fn copy(bytes: &[u8], position: &mut usize, output: &mut [u8]) -> Result<usize> {
400 if output.is_empty() {
401 return Err(Error::BufferTooSmall);
402 }
403 let count = output.len().min(bytes.len() - *position);
404 output[..count].copy_from_slice(&bytes[*position..*position + count]);
405 *position += count;
406 Ok(count)
407}