diff --git a/src/origin/git_proxy.rs b/src/origin/git_proxy.rs index b2c0953..2f1d054 100644 --- a/src/origin/git_proxy.rs +++ b/src/origin/git_proxy.rs @@ -1,4 +1,4 @@ -use crate::error::unexpected::{self, Mappable}; +use crate::error::unexpected::{self, Intoable, Mappable}; use crate::{origin, util}; use std::{collections, sync}; @@ -7,6 +7,15 @@ struct DescrState { current_tree: gix_hash::ObjectId, } +#[derive(thiserror::Error, Clone, Debug, PartialEq)] +enum GetObjectError { + #[error("unavailable due to server-side issue")] + Unavailable, + + #[error(transparent)] + Unexpected(#[from] unexpected::Error), +} + #[derive(Default)] pub struct Proxy { client: reqwest::Client, @@ -101,7 +110,8 @@ impl Proxy { &self, descr: &origin::Descr, oid: &gix_hash::ObjectId, - ) -> unexpected::Result> { + expect_kind: gix_object::Kind, + ) -> Result { let hex = oid.to_string(); let (url, _) = Self::deconstruct_descr(descr); @@ -109,26 +119,40 @@ impl Proxy { Self::construct_url(url, format!("objects/{}/{}", &hex[..2], &hex[2..]).as_str()) .or_unexpected_while("constructing refs url")?; - Ok(self + let mut loose_object = self .client .get(object_url) .send() .await - .or_unexpected_while("performing request")? + .or(Err(GetObjectError::Unavailable))? .error_for_status() - .ok() .map(|res| { use async_compression::tokio::bufread::ZlibDecoder; use futures::stream::TryStreamExt; use std::io; - let r = tokio_util::io::StreamReader::new( + tokio::io::BufReader::new(ZlibDecoder::new(tokio_util::io::StreamReader::new( res.bytes_stream() .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), - ); + ))) + }) + .or(Err(GetObjectError::Unavailable))?; + + use tokio::io::AsyncBufReadExt; + let mut header = Vec::::new(); + loose_object + .read_until(0, &mut header) + .await + .or(Err(GetObjectError::Unavailable))?; + + let (kind, _, _) = + gix_object::decode::loose_header(&header).or(Err(GetObjectError::Unavailable))?; + + if kind != expect_kind { + return Err(GetObjectError::Unavailable); + } - util::BoxByteStream::from_async_read(ZlibDecoder::new(r)) - })) + Ok(util::BoxByteStream::from_async_read(loose_object)) } async fn get_commit_tree( @@ -137,17 +161,18 @@ impl Proxy { commit_hash: &gix_hash::ObjectId, ) -> Result { let commit_object_bytes = self - .get_object(descr, commit_hash) - .await? - .ok_or(origin::SyncError::Unavailable)? + .get_object(descr, commit_hash, gix_object::Kind::Commit) + .await + .map_err(|e| match e { + GetObjectError::Unavailable => origin::SyncError::Unavailable, + GetObjectError::Unexpected(_) => e.into_unexpected().into(), + })? .read_to_end() .await .or(Err(origin::SyncError::Unavailable))?; - let commit_object = gix_object::ObjectRef::from_loose(commit_object_bytes.as_ref()) - .or(Err(origin::SyncError::Unavailable))? - .into_commit() - .ok_or(origin::SyncError::Unavailable)?; + let commit_object = gix_object::CommitRef::from_bytes(commit_object_bytes.as_ref()) + .or(Err(origin::SyncError::Unavailable))?; Ok(commit_object.tree()) } @@ -159,17 +184,18 @@ impl Proxy { entry_name: &str, ) -> Result { let tree_object_bytes = self - .get_object(descr, tree_hash) - .await? - .ok_or(origin::GetFileError::Unavailable)? + .get_object(descr, tree_hash, gix_object::Kind::Tree) + .await + .map_err(|e| match e { + GetObjectError::Unavailable => origin::GetFileError::Unavailable, + GetObjectError::Unexpected(_) => e.into_unexpected().into(), + })? .read_to_end() .await .or(Err(origin::GetFileError::Unavailable))?; - let tree_object = gix_object::ObjectRef::from_loose(tree_object_bytes.as_ref()) - .or(Err(origin::GetFileError::Unavailable))? - .into_tree() - .ok_or(origin::GetFileError::Unavailable)?; + let tree_object = gix_object::TreeRef::from_bytes(tree_object_bytes.as_ref()) + .or(Err(origin::GetFileError::Unavailable))?; for entry in tree_object.entries { if entry.filename == entry_name { @@ -236,9 +262,7 @@ impl origin::Store for Proxy { let path = path .as_str() .parse::() - .or_unexpected_while("parsing path")? - .canonicalize() - .or_unexpected_while("canonicalizing path")?; + .or_unexpected_while("parsing path")?; let path_parts = path.iter().collect::>(); @@ -297,12 +321,14 @@ impl origin::Store for Proxy { .into()); } - Ok(self - .get_object(&descr, &entry.oid) - .await? - .map_unexpected_while(|| format!("object for entry {:?} not found", entry))?) - - // TODO this is still not correct, as it will include the git object header + self.get_object(&descr, &entry.oid, gix_object::Kind::Blob) + .await + .map_err(|e| match e { + GetObjectError::Unavailable => origin::GetFileError::Unavailable, + GetObjectError::Unexpected(_) => e + .into_unexpected_while(format!("getting object for entry {:?}", entry)) + .into(), + }) }) } }