Unified · Split
| @@ -1646,7 +1646,6 @@ dependencies = [ · 1645 unmodified lines | |||
| 1646 | "git-url", | 1646 | "git-url", |
| 1647 | "maybe-async", | 1647 | "maybe-async", |
| 1648 | "pin-project-lite", | 1648 | "pin-project-lite", |
| 1649 | "quick-error", | ||
| 1650 | "serde", | 1649 | "serde", |
| 1651 | "thiserror", | 1650 | "thiserror", |
| 1652 | ] | 1651 | ] |
| @@ -56,7 +56,6 @@ git-sec = { version = "^0.3.0", path = "../git-sec" } · 55 unmodified lines | |||
| 56 | git-packetline = { version = "^0.12.5", path = "../git-packetline" } | 56 | git-packetline = { version = "^0.12.5", path = "../git-packetline" } |
| 57 | 57 | ||
| 58 | serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} | 58 | serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} |
| 59 | quick-error = "2.0.0" | ||
| 60 | bstr = { version = "0.2.13", default-features = false, features = ["std"] } | 59 | bstr = { version = "0.2.13", default-features = false, features = ["std"] } |
| 61 | 60 | ||
| 62 | # for async-client | 61 | # for async-client |
| @@ -1,61 +1,71 @@ | |||
| 1 | pub use crate::client::non_io_types::connect::Error; | 1 | pub use crate::client::non_io_types::connect::Error; |
| 2 | use crate::client::Transport; | ||
| 3 | 2 | ||
| 4 | /// A general purpose connector connecting to a repository identified by the given `url`. | 3 | pub(crate) mod function { |
| 5 | /// | 4 | use crate::client::non_io_types::connect::Error; |
| 6 | /// This includes connections to | 5 | use crate::client::Transport; |
| 7 | /// [local repositories][crate::client::file::connect()], | 6 | |
| 8 | /// [repositories over ssh][crate::client::ssh::connect()], | 7 | /// A general purpose connector connecting to a repository identified by the given `url`. |
| 9 | /// [git daemons][crate::client::git::connect()], | 8 | /// |
| 10 | /// and if compiled in connections to [git repositories over https][crate::client::http::connect()]. | 9 | /// This includes connections to |
| 11 | /// | 10 | /// [local repositories][crate::client::file::connect()], |
| 12 | /// Use `desired_version` to set the desired protocol version to use when connecting, but not that the server may downgrade it. | 11 | /// [repositories over ssh][crate::client::ssh::connect()], |
| 13 | pub fn connect(url: &[u8], desired_version: crate::Protocol) -> Result<Box<dyn Transport + Send>, Error> { | 12 | /// [git daemons][crate::client::git::connect()], |
| 14 | let urlb = url; | 13 | /// and if compiled in connections to [git repositories over https][crate::client::http::connect()]. |
| 15 | let mut url = git_url::parse(urlb)?; | 14 | /// |
| 16 | Ok(match url.scheme { | 15 | /// Use `desired_version` to set the desired protocol version to use when connecting, but not that the server may downgrade it. |
| 17 | git_url::Scheme::Radicle => return Err(Error::UnsupportedScheme(url.scheme)), | 16 | pub fn connect(url: &[u8], desired_version: crate::Protocol) -> Result<Box<dyn Transport + Send>, Error> { |
| 18 | git_url::Scheme::File => { | 17 | let urlb = url; |
| 19 | if url.user().is_some() || url.host().is_some() || url.port.is_some() { | 18 | let mut url = git_url::parse(urlb)?; |
| 20 | return Err(Error::UnsupportedUrlTokens(urlb.into(), url.scheme)); | 19 | Ok(match url.scheme { |
| 21 | } | 20 | git_url::Scheme::Radicle => return Err(Error::UnsupportedScheme(url.scheme)), |
| 22 | Box::new( | 21 | git_url::Scheme::File => { |
| 23 | crate::client::blocking_io::file::connect(url.path, desired_version) | 22 | if url.user().is_some() || url.host().is_some() || url.port.is_some() { |
| 24 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?, | 23 | return Err(Error::UnsupportedUrlTokens { |
| 25 | ) | 24 | url: urlb.into(), |
| 26 | } | 25 | scheme: url.scheme, |
| 27 | git_url::Scheme::Ssh => Box::new({ | 26 | }); |
| 28 | let path = std::mem::take(&mut url.path); | 27 | } |
| 29 | crate::client::blocking_io::ssh::connect( | 28 | Box::new( |
| 30 | url.host().expect("host is present in url"), | 29 | crate::client::blocking_io::file::connect(url.path, desired_version) |
| 31 | path, | 30 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?, |
| 32 | desired_version, | 31 | ) |
| 33 | url.user(), | ||
| 34 | url.port, | ||
| 35 | ) | ||
| 36 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)? | ||
| 37 | }), | ||
| 38 | git_url::Scheme::Git => { | ||
| 39 | if url.user().is_some() { | ||
| 40 | return Err(Error::UnsupportedUrlTokens(urlb.into(), url.scheme)); | ||
| 41 | } | 32 | } |
| 42 | Box::new({ | 33 | git_url::Scheme::Ssh => Box::new({ |
| 43 | let path = std::mem::take(&mut url.path); | 34 | let path = std::mem::take(&mut url.path); |
| 44 | crate::client::git::connect( | 35 | crate::client::blocking_io::ssh::connect( |
| 45 | url.host().expect("host is present in url"), | 36 | url.host().expect("host is present in url"), |
| 46 | path, | 37 | path, |
| 47 | desired_version, | 38 | desired_version, |
| 39 | url.user(), | ||
| 48 | url.port, | 40 | url.port, |
| 49 | ) | 41 | ) |
| 50 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)? | 42 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)? |
| 51 | }) | 43 | }), |
| 52 | } | 44 | git_url::Scheme::Git => { |
| 53 | #[cfg(not(feature = "http-client-curl"))] | 45 | if url.user().is_some() { |
| 54 | git_url::Scheme::Https | git_url::Scheme::Http => return Err(Error::CompiledWithoutHttp(url.scheme)), | 46 | return Err(Error::UnsupportedUrlTokens { |
| 55 | #[cfg(feature = "http-client-curl")] | 47 | url: urlb.into(), |
| 56 | git_url::Scheme::Https | git_url::Scheme::Http => Box::new( | 48 | scheme: url.scheme, |
| 57 | crate::client::http::connect(urlb.into(), desired_version) | 49 | }); |
| 58 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?, | 50 | } |
| 59 | ), | 51 | Box::new({ |
| 60 | }) | 52 | let path = std::mem::take(&mut url.path); |
| 53 | crate::client::git::connect( | ||
| 54 | url.host().expect("host is present in url"), | ||
| 55 | path, | ||
| 56 | desired_version, | ||
| 57 | url.port, | ||
| 58 | ) | ||
| 59 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)? | ||
| 60 | }) | ||
| 61 | } | ||
| 62 | #[cfg(not(feature = "http-client-curl"))] | ||
| 63 | git_url::Scheme::Https | git_url::Scheme::Http => return Err(Error::CompiledWithoutHttp(url.scheme)), | ||
| 64 | #[cfg(feature = "http-client-curl")] | ||
| 65 | git_url::Scheme::Https | git_url::Scheme::Http => Box::new( | ||
| 66 | crate::client::http::connect(urlb.into(), desired_version) | ||
| 67 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?, | ||
| 68 | ), | ||
| 69 | }) | ||
| 70 | } | ||
| 61 | } | 71 | } |
| @@ -182,6 +182,8 @@ pub fn new() -> ( · 181 unmodified lines | |||
| 182 | 182 | ||
| 183 | impl From<curl::Error> for http::Error { | 183 | impl From<curl::Error> for http::Error { |
| 184 | fn from(err: curl::Error) -> Self { | 184 | fn from(err: curl::Error) -> Self { |
| 185 | http::Error::Detail(err.to_string()) | 185 | http::Error::Detail { |
| 186 | description: err.to_string(), | ||
| 187 | } | ||
| 186 | } | 188 | } |
| 187 | } | 189 | } |
| @@ -2,7 +2,7 @@ use bstr::{BStr, BString}; · 1 unmodified line | |||
| 2 | use std::{ | 2 | use std::{ |
| 3 | borrow::Cow, | 3 | borrow::Cow, |
| 4 | convert::Infallible, | 4 | convert::Infallible, |
| 5 | io::{self, BufRead, Read}, | 5 | io::{BufRead, Read}, |
| 6 | }; | 6 | }; |
| 7 | 7 | ||
| 8 | use git_packetline::PacketLineRef; | 8 | use git_packetline::PacketLineRef; |
| @@ -27,9 +27,9 @@ pub type Impl = curl::Curl; · 18 unmodified lines | |||
| 27 | pub struct Transport<H: Http> { | 27 | pub struct Transport<H: Http> { |
| 28 | url: BString, | 28 | url: BString, |
| 29 | user_agent_header: &'static str, | 29 | user_agent_header: &'static str, |
| 30 | desired_version: crate::Protocol, | 30 | desired_version: Protocol, |
| 31 | supported_versions: [crate::Protocol; 1], | 31 | supported_versions: [Protocol; 1], |
| 32 | actual_version: crate::Protocol, | 32 | actual_version: Protocol, |
| 33 | http: H, | 33 | http: H, |
| 34 | service: Option<Service>, | 34 | service: Option<Service>, |
| 35 | line_provider: Option<git_packetline::StreamingPeekableIter<H::ResponseBody>>, | 35 | line_provider: Option<git_packetline::StreamingPeekableIter<H::ResponseBody>>, |
| @@ -38,7 +38,7 @@ pub struct Transport<H: Http> { · 2 unmodified lines | |||
| 38 | 38 | ||
| 39 | impl Transport<Impl> { | 39 | impl Transport<Impl> { |
| 40 | /// Create a new instance to communicate to `url` using the given `desired_version` of the `git` protocol. | 40 | /// Create a new instance to communicate to `url` using the given `desired_version` of the `git` protocol. |
| 41 | pub fn new(url: &BStr, desired_version: crate::Protocol) -> Self { | 41 | pub fn new(url: &BStr, desired_version: Protocol) -> Self { |
| 42 | Transport { | 42 | Transport { |
| 43 | url: url.to_owned(), | 43 | url: url.to_owned(), |
| 44 | user_agent_header: concat!("User-Agent: git/oxide-", env!("CARGO_PKG_VERSION")), | 44 | user_agent_header: concat!("User-Agent: git/oxide-", env!("CARGO_PKG_VERSION")), |
| @@ -62,10 +62,12 @@ impl<H: Http> Transport<H> { · 17 unmodified lines | |||
| 62 | .iter() | 62 | .iter() |
| 63 | .any(|l| l == &wanted_content_type) | 63 | .any(|l| l == &wanted_content_type) |
| 64 | { | 64 | { |
| 65 | return Err(client::Error::Http(Error::Detail(format!( | 65 | return Err(client::Error::Http(Error::Detail { |
| 66 | "Didn't find '{}' header to indicate 'smart' protocol, and 'dumb' protocol is not supported.", | 66 | description: format!( |
| 67 | wanted_content_type | 67 | "Didn't find '{}' header to indicate 'smart' protocol, and 'dumb' protocol is not supported.", |
| 68 | )))); | 68 | wanted_content_type |
| 69 | ), | ||
| 70 | })); | ||
| 69 | } | 71 | } |
| 70 | Ok(()) | 72 | Ok(()) |
| 71 | } | 73 | } |
| @@ -106,8 +108,8 @@ impl<H: Http> client::TransportWithoutIO for Transport<H> { · 34 unmodified lines | |||
| 106 | fn request( | 108 | fn request( |
| 107 | &mut self, | 109 | &mut self, |
| 108 | write_mode: client::WriteMode, | 110 | write_mode: client::WriteMode, |
| 109 | on_into_read: client::MessageKind, | 111 | on_into_read: MessageKind, |
| 110 | ) -> Result<client::RequestWriter<'_>, client::Error> { | 112 | ) -> Result<RequestWriter<'_>, client::Error> { |
| 111 | let service = self.service.expect("handshake() must have been called first"); | 113 | let service = self.service.expect("handshake() must have been called first"); |
| 112 | let url = append_url(self.url.as_ref(), service.as_str()); | 114 | let url = append_url(self.url.as_ref(), service.as_str()); |
| 113 | let static_headers = &[ | 115 | let static_headers = &[ |
| @@ -207,11 +209,13 @@ impl<H: Http> client::Transport for Transport<H> { · 93 unmodified lines | |||
| 207 | line_reader.as_read().read_to_string(&mut announced_service)?; | 209 | line_reader.as_read().read_to_string(&mut announced_service)?; |
| 208 | let expected_service_announcement = format!("# service={}", service.as_str()); | 210 | let expected_service_announcement = format!("# service={}", service.as_str()); |
| 209 | if announced_service.trim() != expected_service_announcement { | 211 | if announced_service.trim() != expected_service_announcement { |
| 210 | return Err(client::Error::Http(Error::Detail(format!( | 212 | return Err(client::Error::Http(Error::Detail { |
| 211 | "Expected to see {:?}, but got {:?}", | 213 | description: format!( |
| 212 | expected_service_announcement, | 214 | "Expected to see {:?}, but got {:?}", |
| 213 | announced_service.trim() | 215 | expected_service_announcement, |
| 214 | )))); | 216 | announced_service.trim() |
| 217 | ), | ||
| 218 | })); | ||
| 215 | } | 219 | } |
| 216 | 220 | ||
| 217 | let capabilities::recv::Outcome { | 221 | let capabilities::recv::Outcome { |
| @@ -236,24 +240,24 @@ struct HeadersThenBody<H: Http, B: Unpin> { · 18 unmodified lines | |||
| 236 | } | 240 | } |
| 237 | 241 | ||
| 238 | impl<H: Http, B: Unpin> HeadersThenBody<H, B> { | 242 | impl<H: Http, B: Unpin> HeadersThenBody<H, B> { |
| 239 | fn handle_headers(&mut self) -> io::Result<()> { | 243 | fn handle_headers(&mut self) -> std::io::Result<()> { |
| 240 | if let Some(headers) = self.headers.take() { | 244 | if let Some(headers) = self.headers.take() { |
| 241 | <Transport<H>>::check_content_type(self.service, "result", headers) | 245 | <Transport<H>>::check_content_type(self.service, "result", headers) |
| 242 | .map_err(|err| io::Error::new(io::ErrorKind::Other, err))? | 246 | .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))? |
| 243 | } | 247 | } |
| 244 | Ok(()) | 248 | Ok(()) |
| 245 | } | 249 | } |
| 246 | } | 250 | } |
| 247 | 251 | ||
| 248 | impl<H: Http, B: ExtendedBufRead + Unpin> io::Read for HeadersThenBody<H, B> { | 252 | impl<H: Http, B: ExtendedBufRead + Unpin> Read for HeadersThenBody<H, B> { |
| 249 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | 253 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
| 250 | self.handle_headers()?; | 254 | self.handle_headers()?; |
| 251 | self.body.read(buf) | 255 | self.body.read(buf) |
| 252 | } | 256 | } |
| 253 | } | 257 | } |
| 254 | 258 | ||
| 255 | impl<H: Http, B: ExtendedBufRead + Unpin> io::BufRead for HeadersThenBody<H, B> { | 259 | impl<H: Http, B: ExtendedBufRead + Unpin> BufRead for HeadersThenBody<H, B> { |
| 256 | fn fill_buf(&mut self) -> io::Result<&[u8]> { | 260 | fn fill_buf(&mut self) -> std::io::Result<&[u8]> { |
| 257 | self.handle_headers()?; | 261 | self.handle_headers()?; |
| 258 | self.body.fill_buf() | 262 | self.body.fill_buf() |
| 259 | } | 263 | } |
| @@ -268,7 +272,7 @@ impl<H: Http, B: ExtendedBufRead + Unpin> ExtendedBufRead for HeadersThenBody<H, · 8 unmodified lines | |||
| 268 | self.body.set_progress_handler(handle_progress) | 272 | self.body.set_progress_handler(handle_progress) |
| 269 | } | 273 | } |
| 270 | 274 | ||
| 271 | fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], client::Error>>> { | 275 | fn peek_data_line(&mut self) -> Option<std::io::Result<Result<&[u8], client::Error>>> { |
| 272 | if let Err(err) = self.handle_headers() { | 276 | if let Err(err) = self.handle_headers() { |
| 273 | return Some(Err(err)); | 277 | return Some(Err(err)); |
| 274 | } | 278 | } |
| @@ -285,6 +289,6 @@ impl<H: Http, B: ExtendedBufRead + Unpin> ExtendedBufRead for HeadersThenBody<H, · 10 unmodified lines | |||
| 285 | } | 289 | } |
| 286 | 290 | ||
| 287 | /// Connect to the given `url` via HTTP/S using the `desired_version` of the `git` protocol. | 291 | /// Connect to the given `url` via HTTP/S using the `desired_version` of the `git` protocol. |
| 288 | pub fn connect(url: &BStr, desired_version: crate::Protocol) -> Result<Transport<Impl>, Infallible> { | 292 | pub fn connect(url: &BStr, desired_version: Protocol) -> Result<Transport<Impl>, Infallible> { |
| 289 | Ok(Transport::new(url, desired_version)) | 293 | Ok(Transport::new(url, desired_version)) |
| 290 | } | 294 | } |
| @@ -1,22 +1,13 @@ | |||
| 1 | use bstr::BStr; | 1 | use bstr::BStr; |
| 2 | use std::io; | ||
| 3 | 2 | ||
| 4 | use quick_error::quick_error; | 3 | /// The error used by the [Http] trait. |
| 5 | 4 | #[derive(Debug, thiserror::Error)] | |
| 6 | quick_error! { | 5 | #[allow(missing_docs)] |
| 7 | /// The error used by the [Http] trait. | 6 | pub enum Error { |
| 8 | #[derive(Debug)] | 7 | #[error("{description}")] |
| 9 | #[allow(missing_docs)] | 8 | Detail { description: String }, |
| 10 | pub enum Error { | 9 | #[error("An IO error occurred while uploading the body of a POST request")] |
| 11 | Detail(description: String) { | 10 | PostBody(#[from] std::io::Error), |
| 12 | display("{}", description) | ||
| 13 | } | ||
| 14 | PostBody(err: io::Error) { | ||
| 15 | display("An IO error occurred while uploading the body of a POST request") | ||
| 16 | from() | ||
| 17 | source(err) | ||
| 18 | } | ||
| 19 | } | ||
| 20 | } | 11 | } |
| 21 | 12 | ||
| 22 | /// The return value of [Http::get()]. | 13 | /// The return value of [Http::get()]. |
| @@ -52,11 +43,11 @@ impl<A, B, C> From<PostResponse<A, B, C>> for GetResponse<A, B> { · 29 unmodified lines | |||
| 52 | #[allow(clippy::type_complexity)] | 43 | #[allow(clippy::type_complexity)] |
| 53 | pub trait Http { | 44 | pub trait Http { |
| 54 | /// A type providing headers line by line. | 45 | /// A type providing headers line by line. |
| 55 | type Headers: io::BufRead + Unpin; | 46 | type Headers: std::io::BufRead + Unpin; |
| 56 | /// A type providing the response. | 47 | /// A type providing the response. |
| 57 | type ResponseBody: io::BufRead; | 48 | type ResponseBody: std::io::BufRead; |
| 58 | /// A type allowing to write the content to post. | 49 | /// A type allowing to write the content to post. |
| 59 | type PostBody: io::Write; | 50 | type PostBody: std::io::Write; |
| 60 | 51 | ||
| 61 | /// Initiate a `GET` request to `url` provided the given `headers`. | 52 | /// Initiate a `GET` request to `url` provided the given `headers`. |
| 62 | /// | 53 | /// |
| @@ -1,19 +1,15 @@ | |||
| 1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
| 2 | 2 | ||
| 3 | use bstr::BString; | 3 | use bstr::BString; |
| 4 | use quick_error::quick_error; | ||
| 5 | 4 | ||
| 6 | use crate::{client::blocking_io, Protocol}; | 5 | use crate::{client::blocking_io, Protocol}; |
| 7 | 6 | ||
| 8 | quick_error! { | 7 | /// The error used in [`connect()`]. |
| 9 | /// The error used in [`connect()`]. | 8 | #[derive(Debug, thiserror::Error)] |
| 10 | #[derive(Debug)] | 9 | #[allow(missing_docs)] |
| 11 | #[allow(missing_docs)] | 10 | pub enum Error { |
| 12 | pub enum Error { | 11 | #[error("The ssh command {0:?} is not currently supported")] |
| 13 | UnsupportedSshCommand(command: String) { | 12 | UnsupportedSshCommand(String), |
| 14 | display("The ssh command '{}' is not currently supported", command) | ||
| 15 | } | ||
| 16 | } | ||
| 17 | } | 13 | } |
| 18 | 14 | ||
| 19 | /// Connect to `host` using the ssh program to obtain data from the repository at `path` on the remote. | 15 | /// Connect to `host` using the ssh program to obtain data from the repository at `path` on the remote. |
| @@ -1,38 +1,25 @@ | |||
| 1 | use std::io; | ||
| 2 | |||
| 3 | use bstr::{BStr, BString, ByteSlice}; | 1 | use bstr::{BStr, BString, ByteSlice}; |
| 4 | use quick_error::quick_error; | ||
| 5 | 2 | ||
| 6 | #[cfg(any(feature = "blocking-client", feature = "async-client"))] | 3 | #[cfg(any(feature = "blocking-client", feature = "async-client"))] |
| 7 | use crate::client; | 4 | use crate::client; |
| 8 | use crate::Protocol; | 5 | use crate::Protocol; |
| 9 | 6 | ||
| 10 | quick_error! { | 7 | /// The error used in [`Capabilities::from_bytes()`] and [`Capabilities::from_lines()`]. |
| 11 | /// The error used in [`Capabilities::from_bytes()`] and [`Capabilities::from_lines()`]. | 8 | #[derive(Debug, thiserror::Error)] |
| 12 | #[derive(Debug)] | 9 | #[allow(missing_docs)] |
| 13 | #[allow(missing_docs)] | 10 | pub enum Error { |
| 14 | pub enum Error { | 11 | #[error("Capabilities were missing entirely as there was no 0 byte")] |
| 15 | MissingDelimitingNullByte { | 12 | MissingDelimitingNullByte, |
| 16 | display("Capabilities were missing entirely as there was no 0 byte") | 13 | #[error("there was not a single capability behind the delimiter")] |
| 17 | } | 14 | NoCapabilities, |
| 18 | NoCapabilities { | 15 | #[error("a version line was expected, but none was retrieved")] |
| 19 | display("there was not a single capability behind the delimiter") | 16 | MissingVersionLine, |
| 20 | } | 17 | #[error("expected 'version X', got {0:?}")] |
| 21 | MissingVersionLine { | 18 | MalformattedVersionLine(String), |
| 22 | display("a version line was expected, but none was retrieved") | 19 | #[error("Got unsupported version '{}', expected {actual:?}", *desired as u8)] |
| 23 | } | 20 | UnsupportedVersion { desired: Protocol, actual: String }, |
| 24 | MalformattedVersionLine(actual: String) { | 21 | #[error("An IO error occurred while reading V2 lines")] |
| 25 | display("expected 'version X', got '{}'", actual) | 22 | Io(#[from] std::io::Error), |
| 26 | } | ||
| 27 | UnsupportedVersion(wanted: Protocol, got: String) { | ||
| 28 | display("Got unsupported version '{}', expected '{}'", got, *wanted as usize) | ||
| 29 | } | ||
| 30 | Io(err: io::Error) { | ||
| 31 | display("An IO error occurred while reading V2 lines") | ||
| 32 | from() | ||
| 33 | source(err) | ||
| 34 | } | ||
| 35 | } | ||
| 36 | } | 23 | } |
| 37 | 24 | ||
| 38 | /// A structure to represent multiple [capabilities][Capability] or features supported by the server. | 25 | /// A structure to represent multiple [capabilities][Capability] or features supported by the server. |
| @@ -113,7 +100,10 @@ impl Capabilities { · 74 unmodified lines | |||
| 113 | return Err(Error::MalformattedVersionLine(version_line)); | 100 | return Err(Error::MalformattedVersionLine(version_line)); |
| 114 | } | 101 | } |
| 115 | if value != " 2" { | 102 | if value != " 2" { |
| 116 | return Err(Error::UnsupportedVersion(Protocol::V2, value.to_owned())); | 103 | return Err(Error::UnsupportedVersion { |
| 104 | desired: Protocol::V2, | ||
| 105 | actual: value.to_owned(), | ||
| 106 | }); | ||
| 117 | } | 107 | } |
| 118 | Ok(Capabilities { | 108 | Ok(Capabilities { |
| 119 | value_sep: b'\n', | 109 | value_sep: b'\n', |
| @@ -181,7 +171,7 @@ pub mod recv { · 61 unmodified lines | |||
| 181 | /// | 171 | /// |
| 182 | /// This is `Some` only when protocol v1 is used. The [`io::BufRead`] must be exhausted by | 172 | /// This is `Some` only when protocol v1 is used. The [`io::BufRead`] must be exhausted by |
| 183 | /// the caller. | 173 | /// the caller. |
| 184 | pub refs: Option<Box<dyn io::BufRead + 'a>>, | 174 | pub refs: Option<Box<dyn std::io::BufRead + 'a>>, |
| 185 | /// The [`Protocol`] the remote advertised. | 175 | /// The [`Protocol`] the remote advertised. |
| 186 | pub protocol: Protocol, | 176 | pub protocol: Protocol, |
| 187 | } | 177 | } |
| @@ -1,4 +1,4 @@ | |||
| 1 | use std::{io, io::Write}; | 1 | use std::io::Write; |
| 2 | 2 | ||
| 3 | use bstr::BString; | 3 | use bstr::BString; |
| 4 | use git_packetline::PacketLineRef; | 4 | use git_packetline::PacketLineRef; |
| @@ -10,8 +10,8 @@ use crate::{ · 5 unmodified lines | |||
| 10 | 10 | ||
| 11 | impl<R, W> client::TransportWithoutIO for git::Connection<R, W> | 11 | impl<R, W> client::TransportWithoutIO for git::Connection<R, W> |
| 12 | where | 12 | where |
| 13 | R: io::Read, | 13 | R: std::io::Read, |
| 14 | W: io::Write, | 14 | W: std::io::Write, |
| 15 | { | 15 | { |
| 16 | fn request( | 16 | fn request( |
| 17 | &mut self, | 17 | &mut self, |
| @@ -57,8 +57,8 @@ where · 39 unmodified lines | |||
| 57 | 57 | ||
| 58 | impl<R, W> client::Transport for git::Connection<R, W> | 58 | impl<R, W> client::Transport for git::Connection<R, W> |
| 59 | where | 59 | where |
| 60 | R: io::Read, | 60 | R: std::io::Read, |
| 61 | W: io::Write, | 61 | W: std::io::Write, |
| 62 | { | 62 | { |
| 63 | fn handshake<'a>( | 63 | fn handshake<'a>( |
| 64 | &mut self, | 64 | &mut self, |
| @@ -92,8 +92,8 @@ where · 27 unmodified lines | |||
| 92 | 92 | ||
| 93 | impl<R, W> git::Connection<R, W> | 93 | impl<R, W> git::Connection<R, W> |
| 94 | where | 94 | where |
| 95 | R: io::Read, | 95 | R: std::io::Read, |
| 96 | W: io::Write, | 96 | W: std::io::Write, |
| 97 | { | 97 | { |
| 98 | /// Create a connection from the given `read` and `write`, asking for `desired_version` as preferred protocol | 98 | /// Create a connection from the given `read` and `write`, asking for `desired_version` as preferred protocol |
| 99 | /// and the transfer of the repository at `repository_path`. | 99 | /// and the transfer of the repository at `repository_path`. |
| @@ -137,29 +137,19 @@ where · 37 unmodified lines | |||
| 137 | 137 | ||
| 138 | /// | 138 | /// |
| 139 | pub mod connect { | 139 | pub mod connect { |
| 140 | use std::{ | 140 | use std::net::{TcpStream, ToSocketAddrs}; |
| 141 | io, | ||
| 142 | net::{TcpStream, ToSocketAddrs}, | ||
| 143 | }; | ||
| 144 | 141 | ||
| 145 | use bstr::BString; | 142 | use bstr::BString; |
| 146 | use quick_error::quick_error; | ||
| 147 | 143 | ||
| 148 | use crate::client::git; | 144 | use crate::client::git; |
| 149 | quick_error! { | 145 | /// The error used in [`connect()`]. |
| 150 | /// The error used in [`connect()`]. | 146 | #[derive(Debug, thiserror::Error)] |
| 151 | #[derive(Debug)] | 147 | #[allow(missing_docs)] |
| 152 | #[allow(missing_docs)] | 148 | pub enum Error { |
| 153 | pub enum Error { | 149 | #[error("An IO error occurred when connecting to the server")] |
| 154 | Io(err: io::Error){ | 150 | Io(#[from] std::io::Error), |
| 155 | display("An IO error occurred when connecting to the server") | 151 | #[error("Could not parse {host:?} as virtual host with format <host>[:port]")] |
| 156 | from() | 152 | VirtualHostInvalid { host: String }, |
| 157 | source(err) | ||
| 158 | } | ||
| 159 | VirtualHostInvalid(host: String) { | ||
| 160 | display("Could not parse '{}' as virtual host with format <host>[:port]", host) | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | 153 | } |
| 164 | 154 | ||
| 165 | fn parse_host(input: String) -> Result<(String, Option<u16>), Error> { | 155 | fn parse_host(input: String) -> Result<(String, Option<u16>), Error> { |
| @@ -168,7 +158,7 @@ pub mod connect { · 2 unmodified lines | |||
| 168 | (Some(host), None) => (host.to_owned(), None), | 158 | (Some(host), None) => (host.to_owned(), None), |
| 169 | (Some(host), Some(port)) => ( | 159 | (Some(host), Some(port)) => ( |
| 170 | host.to_owned(), | 160 | host.to_owned(), |
| 171 | Some(port.parse().map_err(|_| Error::VirtualHostInvalid(input))?), | 161 | Some(port.parse().map_err(|_| Error::VirtualHostInvalid { host: input })?), |
| 172 | ), | 162 | ), |
| 173 | _ => unreachable!("we expect at least one token, the original string"), | 163 | _ => unreachable!("we expect at least one token, the original string"), |
| 174 | }) | 164 | }) |
| @@ -18,7 +18,7 @@ pub use blocking_io::{ · 17 unmodified lines | |||
| 18 | }; | 18 | }; |
| 19 | #[cfg(feature = "blocking-client")] | 19 | #[cfg(feature = "blocking-client")] |
| 20 | #[doc(inline)] | 20 | #[doc(inline)] |
| 21 | pub use connect::connect; | 21 | pub use connect::function::connect; |
| 22 | 22 | ||
| 23 | /// | 23 | /// |
| 24 | pub mod capabilities; | 24 | pub mod capabilities; |
| @@ -29,39 +29,28 @@ pub enum MessageKind { · 28 unmodified lines | |||
| 29 | Text(&'static [u8]), | 29 | Text(&'static [u8]), |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | #[cfg(any(feature = "blocking-client", feature = "async-client"))] | ||
| 32 | pub(crate) mod connect { | 33 | pub(crate) mod connect { |
| 33 | use quick_error::quick_error; | 34 | /// The error used in [`connect()`][crate::connect()]. |
| 34 | quick_error! { | 35 | #[derive(Debug, thiserror::Error)] |
| 35 | /// The error used in [`connect()`][crate::connect()]. | 36 | #[allow(missing_docs)] |
| 36 | #[derive(Debug)] | 37 | pub enum Error { |
| 37 | #[allow(missing_docs)] | 38 | #[error(transparent)] |
| 38 | pub enum Error { | 39 | Url(#[from] git_url::parse::Error), |
| 39 | Url(err: git_url::parse::Error) { | 40 | #[error("The git repository path could not be converted to UTF8")] |
| 40 | display("The URL could not be parsed") | 41 | PathConversion(#[from] bstr::Utf8Error), |
| 41 | from() | 42 | #[error("connection failed")] |
| 42 | source(err) | 43 | Connection(#[from] Box<dyn std::error::Error + Send + Sync>), |
| 43 | } | 44 | #[error("The url {url:?} contains information that would not be used by the {scheme} protocol")] |
| 44 | PathConversion(err: bstr::Utf8Error) { | 45 | UnsupportedUrlTokens { |
| 45 | display("The git repository paths could not be converted to UTF8") | 46 | url: bstr::BString, |
| 46 | from() | 47 | scheme: git_url::Scheme, |
| 47 | source(err) | 48 | }, |
| 48 | } | 49 | #[error("The '{0}' protocol is currently unsupported")] |
| 49 | Connection(err: Box<dyn std::error::Error + Send + Sync>) { | 50 | UnsupportedScheme(git_url::Scheme), |
| 50 | display("connection failed") | 51 | #[cfg(not(feature = "http-client-curl"))] |
| 51 | from() | 52 | #[error("'{0}' is not compiled in. Compile with the 'http-client-curl' cargo feature")] |
| 52 | source(&**err) | 53 | CompiledWithoutHttp(git_url::Scheme), |
| 53 | } | ||
| 54 | UnsupportedUrlTokens(url: bstr::BString, scheme: git_url::Scheme) { | ||
| 55 | display("The url '{}' contains information that would not be used by the '{}' protocol", url, scheme) | ||
| 56 | } | ||
| 57 | UnsupportedScheme(scheme: git_url::Scheme) { | ||
| 58 | display("The '{}' protocol is currently unsupported", scheme) | ||
| 59 | } | ||
| 60 | #[cfg(not(feature = "http-client-curl"))] | ||
| 61 | CompiledWithoutHttp(scheme: git_url::Scheme) { | ||
| 62 | display("'{}' is not compiled in. Compile with the 'http-client-curl' cargo feature", scheme) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | 54 | } |
| 66 | } | 55 | } |
| 67 | 56 | ||