1use std::io::{self, Cursor, Read, Write};
8use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender, TrySendError};
9use std::sync::Arc;
10use std::time::Duration;
11
12use crate::error::{Error, Result};
13use crate::objects::ObjectId;
14use crate::progress::{Cancel, NeverCancel};
15use crate::transport::http::HttpClient;
16use crate::transport::rpc_channel::RpcChannel;
17use crate::transport::{Connection, Service};
18
19struct ErrRead {
21 err: io::Error,
22}
23
24impl Read for ErrRead {
25 fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
26 Err(std::mem::replace(
27 &mut self.err,
28 io::Error::other("transport error already reported"),
29 ))
30 }
31}
32
33const LARGE_POST: usize = 1024 * 1024;
37
38const STREAM_CHUNK: usize = 256 * 1024;
40
41const STREAM_QUEUE: usize = 4;
43
44enum BodyChunk {
46 Data(Vec<u8>),
47 End,
48}
49
50struct ChunkReader {
52 chunks: Receiver<BodyChunk>,
53 chunk: Vec<u8>,
54 pos: usize,
55}
56
57impl Read for ChunkReader {
58 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
59 while self.pos == self.chunk.len() {
60 match self.chunks.recv() {
61 Ok(BodyChunk::Data(chunk)) => {
62 self.chunk = chunk;
63 self.pos = 0;
64 }
65 Ok(BodyChunk::End) => return Ok(0),
66 Err(_) => {
69 return Err(io::Error::new(
70 io::ErrorKind::UnexpectedEof,
71 "request body abandoned",
72 ))
73 }
74 }
75 }
76 let n = buf.len().min(self.chunk.len() - self.pos);
77 buf[..n].copy_from_slice(&self.chunk[self.pos..self.pos + n]);
78 self.pos += n;
79 Ok(n)
80 }
81}
82
83const CANCEL_POLL: Duration = Duration::from_millis(20);
85
86struct ReadResponse {
89 bytes: Cursor<Vec<u8>>,
90 error: Option<io::Error>,
91}
92
93impl ReadResponse {
94 fn read_from(mut reader: Box<dyn Read + Send>) -> Self {
95 let mut bytes = Vec::new();
96 let error = reader.read_to_end(&mut bytes).err();
97 Self {
98 bytes: Cursor::new(bytes),
99 error,
100 }
101 }
102}
103
104impl Read for ReadResponse {
105 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
106 match self.bytes.read(buf)? {
107 0 if !buf.is_empty() => self.error.take().map_or(Ok(0), Err),
108 n => Ok(n),
109 }
110 }
111}
112
113struct PendingResponse(Receiver<Result<ReadResponse>>);
116
117impl PendingResponse {
118 fn spawn<F>(request: F) -> io::Result<Self>
119 where
120 F: FnOnce() -> Result<Box<dyn Read + Send>> + Send + 'static,
121 {
122 let (tx, rx) = std::sync::mpsc::channel();
123 std::thread::Builder::new()
124 .name("grit-http-post".to_owned())
125 .spawn(move || {
126 let _ = tx.send(request().map(ReadResponse::read_from));
127 })?;
128 Ok(Self(rx))
129 }
130
131 fn wait(&self, cancel: &dyn Cancel) -> Result<ReadResponse> {
134 loop {
135 match self.0.recv_timeout(CANCEL_POLL) {
136 Ok(response) => return response,
137 Err(RecvTimeoutError::Timeout) if cancel.is_cancelled() => {
138 return Err(Error::Cancelled)
139 }
140 Err(RecvTimeoutError::Timeout) => {}
141 Err(RecvTimeoutError::Disconnected) => {
142 return Err(Error::Message("HTTP upload thread panicked".to_owned()))
143 }
144 }
145 }
146 }
147}
148
149struct StreamingPost {
151 chunks: SyncSender<BodyChunk>,
152 response: PendingResponse,
153}
154
155impl StreamingPost {
156 fn start<C: HttpClient + 'static>(
157 client: Arc<C>,
158 url: String,
159 content_type: String,
160 accept: String,
161 ) -> io::Result<Self> {
162 let (chunks, rx) = std::sync::mpsc::sync_channel(STREAM_QUEUE);
163 let response = PendingResponse::spawn(move || {
164 let mut body = ChunkReader {
165 chunks: rx,
166 chunk: Vec::new(),
167 pos: 0,
168 };
169 client.post_streaming(&url, &content_type, &accept, &mut body, None)
170 })?;
171 Ok(Self { chunks, response })
172 }
173
174 fn send(self, chunk: Vec<u8>) -> std::result::Result<Self, Error> {
176 match self.chunks.send(BodyChunk::Data(chunk)) {
177 Ok(()) => Ok(self),
178 Err(_) => Err(self.ended_early()),
179 }
180 }
181
182 fn send_until(&self, mut item: BodyChunk, cancel: &dyn Cancel) -> Result<()> {
184 loop {
185 match self.chunks.try_send(item) {
186 Ok(()) => return Ok(()),
187 Err(TrySendError::Full(back)) => {
188 if cancel.is_cancelled() {
189 return Err(Error::Cancelled);
190 }
191 item = back;
192 std::thread::sleep(CANCEL_POLL);
193 }
194 Err(TrySendError::Disconnected(_)) => return Err(self.ended_early()),
195 }
196 }
197 }
198
199 fn ended_early(&self) -> Error {
201 match self.response.wait(&NeverCancel) {
202 Err(e) => e,
203 Ok(_) => Error::Message("server answered before the request body was sent".into()),
204 }
205 }
206
207 fn finish(self, chunk: Vec<u8>, cancel: &dyn Cancel) -> Result<ReadResponse> {
210 self.send_until(BodyChunk::Data(chunk), cancel)?;
211 self.send_until(BodyChunk::End, cancel)?;
212 self.response.wait(cancel)
213 }
214}
215
216pub struct StatelessHttpConnection<C: HttpClient> {
223 client: Arc<C>,
224 service: Service,
225 repo_url: String,
226 post_url: String,
227 content_type: String,
228 accept: String,
229 git_protocol: Option<String>,
230 adv_refs: Vec<(String, ObjectId)>,
231 caps: Vec<String>,
232 head_symref: Option<String>,
233 protocol_version: u8,
234 object_format: String,
235 sent_state: Vec<u8>,
237 pending: Vec<u8>,
238 response: Option<Box<dyn Read + Send>>,
239 fail_read: Option<ErrRead>,
240 empty_response: Cursor<Vec<u8>>,
241 recv_body: Vec<u8>,
242 recv_stream: Option<StreamingPost>,
243 recv_posted: bool,
244}
245
246impl<C: HttpClient + 'static> StatelessHttpConnection<C> {
247 #[allow(clippy::too_many_arguments)]
249 pub fn new(
250 client: C,
251 service: Service,
252 repo_url: String,
253 protocol_version: u8,
254 adv_refs: Vec<(String, ObjectId)>,
255 caps: Vec<String>,
256 head_symref: Option<String>,
257 object_format: String,
258 git_protocol: Option<String>,
259 ) -> Self {
260 let wire = service.wire_name();
261 let base = repo_url.trim_end_matches('/');
262 let post_url = format!("{base}/{wire}");
263 let content_type = format!("application/x-{wire}-request");
264 let accept = format!("application/x-{wire}-result");
265 Self {
266 client: Arc::new(client),
267 service,
268 repo_url,
269 post_url,
270 content_type,
271 accept,
272 git_protocol,
273 adv_refs,
274 caps,
275 head_symref,
276 protocol_version,
277 object_format,
278 sent_state: Vec::new(),
279 pending: Vec::new(),
280 response: None,
281 fail_read: None,
282 empty_response: Cursor::new(Vec::new()),
283 recv_body: Vec::new(),
284 recv_stream: None,
285 recv_posted: false,
286 }
287 }
288
289 #[must_use]
291 pub fn repo_url(&self) -> &str {
292 &self.repo_url
293 }
294
295 #[must_use]
297 pub fn object_format(&self) -> &str {
298 &self.object_format
299 }
300
301 fn drain_response(&mut self) {
302 if let Some(mut r) = self.response.take() {
303 let _ = crate::pack_receive::copy_reader_to_writer(&mut r, &mut io::sink());
304 }
305 }
306
307 fn set_transport_err(&mut self, err: &Error) {
308 self.fail_read = Some(ErrRead {
309 err: io::Error::other(err.to_string()),
310 });
311 self.response = None;
312 }
313
314 fn upload_pack_post_due(&self) -> bool {
315 if self.pending.is_empty() {
316 return false;
317 }
318 self.pending.ends_with(b"0000") || self.pending.ends_with(b"done\n")
321 }
322
323 fn maybe_post_on_flush(&mut self) -> Result<()> {
324 if self.service != Service::UploadPack {
325 return Ok(());
326 }
327 if self.upload_pack_post_due() {
328 self.post_upload_pack_round()?;
329 }
330 Ok(())
331 }
332
333 fn post_upload_pack_round(&mut self) -> Result<()> {
334 if self.pending.is_empty() {
335 return Ok(());
336 }
337 let body = if self.protocol_version >= 2 {
338 std::mem::take(&mut self.pending)
341 } else {
342 let mut body = self.sent_state.clone();
343 body.extend_from_slice(&self.pending);
344 self.sent_state.extend_from_slice(&self.pending);
345 self.pending.clear();
346 body
347 };
348 self.response = Some(self.request(&body)?);
349 Ok(())
350 }
351
352 fn post_receive_pack(&mut self, cancel: &dyn Cancel) -> Result<()> {
355 if self.recv_posted {
356 return Ok(());
357 }
358 self.recv_posted = true;
359 let body = std::mem::take(&mut self.recv_body);
360 let response = match self.recv_stream.take() {
361 Some(stream) => stream.finish(body, cancel),
362 None => {
363 let client = Arc::clone(&self.client);
364 let (url, content_type, accept) = (
365 self.post_url.clone(),
366 self.content_type.clone(),
367 self.accept.clone(),
368 );
369 PendingResponse::spawn(move || {
370 client.post_into_reader(&url, &content_type, &accept, &body, None)
371 })
372 .map_err(Error::Io)
373 .and_then(|pending| pending.wait(cancel))
374 }
375 };
376 let response = response.inspect_err(|e| self.set_transport_err(e))?;
377 self.response = Some(Box::new(response));
378 Ok(())
379 }
380
381 fn write_receive_pack(&mut self, buf: &[u8]) -> Result<()> {
384 if self.recv_posted {
385 return Err(Error::Message(
386 "receive-pack request already sent".to_owned(),
387 ));
388 }
389 self.recv_body.extend_from_slice(buf);
390 if self.recv_stream.is_none() {
391 if self.recv_body.len() <= LARGE_POST {
392 return Ok(());
393 }
394 self.probe_receive_pack()?;
395 self.recv_stream = Some(StreamingPost::start(
396 Arc::clone(&self.client),
397 self.post_url.clone(),
398 self.content_type.clone(),
399 self.accept.clone(),
400 )?);
401 }
402 if self.recv_body.len() < STREAM_CHUNK {
403 return Ok(());
404 }
405 let chunk = std::mem::replace(&mut self.recv_body, Vec::with_capacity(STREAM_CHUNK));
406 if let Some(stream) = self.recv_stream.take() {
407 match stream.send(chunk) {
408 Ok(stream) => self.recv_stream = Some(stream),
409 Err(e) => {
410 self.recv_posted = true;
411 self.set_transport_err(&e);
412 return Err(e);
413 }
414 }
415 }
416 Ok(())
417 }
418
419 fn probe_receive_pack(&mut self) -> Result<()> {
422 let mut reply = self.client.post_into_reader(
423 &self.post_url,
424 &self.content_type,
425 &self.accept,
426 b"0000",
427 None,
428 )?;
429 io::copy(&mut reply, &mut io::sink()).map_err(Error::Io)?;
430 Ok(())
431 }
432
433 fn ensure_response_for_read(&mut self) -> Result<()> {
434 if self.service == Service::ReceivePack && !self.recv_posted {
435 self.post_receive_pack(&NeverCancel)?;
436 }
437 Ok(())
438 }
439}
440
441impl<C: HttpClient + 'static> RpcChannel for StatelessHttpConnection<C> {
442 fn request(&mut self, body: &[u8]) -> Result<Box<dyn Read + Send>> {
443 self.drain_response();
444 self.client
445 .post_into_reader(
446 &self.post_url,
447 &self.content_type,
448 &self.accept,
449 body,
450 self.git_protocol.as_deref(),
451 )
452 .inspect_err(|e| {
453 self.set_transport_err(e);
454 })
455 }
456}
457
458impl<C: HttpClient + 'static> Write for StatelessHttpConnection<C> {
459 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
460 match self.service {
461 Service::UploadPack => {
462 self.pending.extend_from_slice(buf);
463 self.maybe_post_on_flush()
464 .map_err(|e| io::Error::other(e.to_string()))?;
465 Ok(buf.len())
466 }
467 Service::ReceivePack => {
468 let taken = &buf[..buf.len().min(STREAM_CHUNK)];
471 self.write_receive_pack(taken)
472 .map_err(|e| io::Error::other(e.to_string()))?;
473 Ok(taken.len())
474 }
475 }
476 }
477
478 fn flush(&mut self) -> io::Result<()> {
479 if self.service == Service::UploadPack {
480 self.maybe_post_on_flush()
481 .map_err(|e| io::Error::other(e.to_string()))?;
482 }
483 Ok(())
484 }
485}
486
487impl<C: HttpClient + 'static> Connection for StatelessHttpConnection<C> {
488 fn reader(&mut self) -> &mut dyn Read {
489 if self.fail_read.is_none() {
490 if let Err(e) = self.ensure_response_for_read() {
491 self.fail_read = Some(ErrRead {
492 err: io::Error::other(e.to_string()),
493 });
494 }
495 }
496 if let Some(r) = self.fail_read.as_mut() {
497 return r;
498 }
499 match self.response.as_mut() {
500 Some(r) => r.as_mut(),
501 None => &mut self.empty_response,
502 }
503 }
504
505 fn writer(&mut self) -> &mut dyn Write {
506 self
507 }
508
509 fn advertised_refs(&self) -> &[(String, ObjectId)] {
510 &self.adv_refs
511 }
512
513 fn capabilities(&self) -> &[String] {
514 &self.caps
515 }
516
517 fn head_symref(&self) -> Option<&str> {
518 self.head_symref.as_deref()
519 }
520
521 fn protocol_version(&self) -> u8 {
522 self.protocol_version
523 }
524
525 fn stateless_rpc(&self) -> bool {
526 true
527 }
528
529 fn finish_send(&mut self) {
530 if self.service == Service::ReceivePack && !self.recv_posted {
531 if let Err(e) = self.post_receive_pack(&NeverCancel) {
532 self.set_transport_err(&e);
533 }
534 }
535 }
536
537 fn wait_for_response(&mut self, cancel: &dyn Cancel) -> Result<()> {
538 if self.service == Service::ReceivePack {
539 self.post_receive_pack(cancel)?;
540 }
541 Ok(())
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548 use std::sync::mpsc;
549 use std::sync::Mutex;
550 use std::time::Duration;
551
552 #[derive(Default)]
554 struct Seen {
555 buffered: Vec<Vec<u8>>,
557 streamed: Option<Vec<u8>>,
559 largest_read: usize,
561 }
562
563 struct FakeServer {
565 seen: Arc<Mutex<Seen>>,
566 refuse_stream: bool,
568 body_end: Option<Mutex<mpsc::Sender<io::Result<usize>>>>,
570 }
571
572 impl FakeServer {
573 fn new() -> (Self, Arc<Mutex<Seen>>) {
574 let seen = Arc::new(Mutex::new(Seen::default()));
575 let server = Self {
576 seen: Arc::clone(&seen),
577 refuse_stream: false,
578 body_end: None,
579 };
580 (server, seen)
581 }
582 }
583
584 impl HttpClient for FakeServer {
585 fn get(&self, _: &str, _: Option<&str>) -> Result<Vec<u8>> {
586 Err(Error::Message("no GETs here".to_owned()))
587 }
588
589 fn post(&self, _: &str, _: &str, _: &str, body: &[u8], _: Option<&str>) -> Result<Vec<u8>> {
590 self.seen
591 .lock()
592 .unwrap_or_else(|e| e.into_inner())
593 .buffered
594 .push(body.to_vec());
595 Ok(b"report".to_vec())
596 }
597
598 fn post_streaming(
599 &self,
600 _: &str,
601 _: &str,
602 _: &str,
603 body: &mut dyn Read,
604 _: Option<&str>,
605 ) -> Result<Box<dyn Read + Send>> {
606 if self.refuse_stream {
607 return Err(Error::Message("HTTP 500 from the fake server".to_owned()));
608 }
609 let mut got = Vec::new();
610 let mut largest = 0;
611 let mut buf = vec![0u8; 1 << 20];
612 let end = loop {
613 match body.read(&mut buf) {
614 Ok(0) => break Ok(got.len()),
615 Ok(n) => {
616 largest = largest.max(n);
617 got.extend_from_slice(&buf[..n]);
618 }
619 Err(e) => break Err(e),
620 }
621 };
622 let failed = end.is_err();
623 if let Some(tx) = &self.body_end {
624 let _ = tx.lock().unwrap_or_else(|e| e.into_inner()).send(end);
625 }
626 if failed {
627 return Err(Error::Message("body did not finish".to_owned()));
628 }
629 let mut seen = self.seen.lock().unwrap_or_else(|e| e.into_inner());
630 seen.streamed = Some(got);
631 seen.largest_read = largest;
632 Ok(Box::new(Cursor::new(b"streamed report".to_vec())))
633 }
634 }
635
636 fn receive_pack(server: FakeServer) -> StatelessHttpConnection<FakeServer> {
637 StatelessHttpConnection::new(
638 server,
639 Service::ReceivePack,
640 "http://h/r.git".to_owned(),
641 0,
642 Vec::new(),
643 Vec::new(),
644 None,
645 "sha1".to_owned(),
646 None,
647 )
648 }
649
650 fn body(len: usize) -> Vec<u8> {
651 (0..len).map(|i| (i % 251) as u8).collect()
652 }
653
654 fn read_reply(conn: &mut StatelessHttpConnection<FakeServer>) -> io::Result<Vec<u8>> {
655 let mut reply = Vec::new();
656 conn.reader().read_to_end(&mut reply)?;
657 Ok(reply)
658 }
659
660 #[test]
661 fn small_receive_pack_bodies_are_posted_once_whole() {
662 let (server, seen) = FakeServer::new();
663 let mut conn = receive_pack(server);
664 let data = body(4096);
665 conn.writer().write_all(&data).unwrap();
666 assert_eq!(read_reply(&mut conn).unwrap(), b"report");
667 let seen = seen.lock().unwrap();
668 assert_eq!(seen.buffered, vec![data]);
669 assert!(seen.streamed.is_none());
670 }
671
672 #[test]
673 fn large_receive_pack_bodies_stream_after_a_probe_without_being_held() {
674 let (server, seen) = FakeServer::new();
675 let mut conn = receive_pack(server);
676 let data = body(6 * LARGE_POST + 12345);
677 for piece in data.chunks(10_000) {
678 conn.writer().write_all(piece).unwrap();
679 assert!(
680 conn.recv_body.len() <= LARGE_POST + 10_000,
681 "the connection holds at most one post buffer"
682 );
683 }
684 assert!(conn.recv_stream.is_some());
685 assert_eq!(read_reply(&mut conn).unwrap(), b"streamed report");
686
687 let seen = seen.lock().unwrap();
688 assert_eq!(
689 seen.buffered,
690 vec![b"0000".to_vec()],
691 "only the probe is buffered"
692 );
693 assert_eq!(seen.streamed.as_deref(), Some(&data[..]));
694 assert!(seen.largest_read <= LARGE_POST + 10_000);
695 }
696
697 #[test]
698 fn one_huge_write_is_streamed_a_chunk_at_a_time() {
699 let (server, seen) = FakeServer::new();
700 let mut conn = receive_pack(server);
701 let data = body(8 * LARGE_POST);
702 let first = conn.writer().write(&data).unwrap();
703 assert!(first <= STREAM_CHUNK, "took {first} bytes in one write");
704 conn.writer().write_all(&data[first..]).unwrap();
705 assert!(conn.recv_body.len() <= STREAM_CHUNK);
706 assert_eq!(read_reply(&mut conn).unwrap(), b"streamed report");
707 let seen = seen.lock().unwrap();
708 assert_eq!(seen.streamed.as_deref(), Some(&data[..]));
709 assert!(seen.largest_read <= LARGE_POST + STREAM_CHUNK);
710 }
711
712 #[test]
713 fn a_refused_stream_fails_the_write_with_the_server_error() {
714 let (mut server, _) = FakeServer::new();
715 server.refuse_stream = true;
716 let mut conn = receive_pack(server);
717 let data = body(LARGE_POST);
718 let mut failure = None;
719 for _ in 0..(STREAM_QUEUE + 8) {
720 if let Err(e) = conn.writer().write_all(&data) {
721 failure = Some(e);
722 break;
723 }
724 }
725 let failure = failure.expect("writes fail once the request is refused");
726 assert!(failure.to_string().contains("HTTP 500"), "{failure}");
727 let after = read_reply(&mut conn).unwrap_err();
728 assert!(after.to_string().contains("HTTP 500"), "{after}");
729 }
730
731 #[test]
732 fn dropping_a_connection_mid_body_fails_the_request_instead_of_ending_it() {
733 let (mut server, _) = FakeServer::new();
734 let (tx, rx) = mpsc::channel();
735 server.body_end = Some(Mutex::new(tx));
736 let mut conn = receive_pack(server);
737 conn.writer().write_all(&body(2 * LARGE_POST)).unwrap();
738 drop(conn);
739 let end = rx
740 .recv_timeout(Duration::from_secs(30))
741 .expect("the request thread reports how the body ended");
742 let err = end.expect_err("an abandoned body must not look complete");
743 assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
744 }
745}