Skip to content

Commit d59d362

Browse files
committed
feat: add and implement various new http options for the curl backend.
- `schannel_check_revoke` as `curl`-backend specific configuration. - `ssl_version` - `ssl_ca_info` - `http_version`
1 parent da9b2d5 commit d59d362

File tree

3 files changed

+118
-3
lines changed

3 files changed

+118
-3
lines changed

Diff for: git-transport/src/client/blocking_io/http/curl/mod.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,20 @@ use crate::client::http::traits::PostBodyDataKind;
1010

1111
mod remote;
1212

13+
/// Options to configure the `curl` HTTP handler.
14+
#[derive(Default)]
15+
pub struct Options {
16+
/// If `true` and runtime configuration is possible for `curl` backends, certificates revocation will be checked.
17+
///
18+
/// This only works on windows apparently. Ignored if `None`.
19+
pub schannel_check_revoke: Option<bool>,
20+
}
21+
22+
/// The error returned by the 'remote' helper, a purely internal construct to perform http requests.
23+
///
24+
/// It can be used for downcasting errors, which are boxed to hide the actual implementation.
1325
#[derive(Debug, thiserror::Error)]
26+
#[allow(missing_docs)]
1427
pub enum Error {
1528
#[error(transparent)]
1629
Curl(#[from] curl::Error),
@@ -31,7 +44,7 @@ impl crate::IsSpuriousError for Error {
3144
}
3245
}
3346

34-
pub fn curl_is_spurious(err: &curl::Error) -> bool {
47+
pub(crate) fn curl_is_spurious(err: &curl::Error) -> bool {
3548
err.is_couldnt_connect()
3649
|| err.is_couldnt_resolve_proxy()
3750
|| err.is_couldnt_resolve_host()
@@ -44,6 +57,7 @@ pub fn curl_is_spurious(err: &curl::Error) -> bool {
4457
|| err.is_partial_file()
4558
}
4659

60+
/// A utility to abstract interactions with curl handles.
4761
pub struct Curl {
4862
req: SyncSender<remote::Request>,
4963
res: Receiver<remote::Response>,

Diff for: git-transport/src/client/blocking_io/http/curl/remote.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use curl::easy::{Auth, Easy2};
1010
use git_features::io::pipe;
1111

1212
use crate::client::http::curl::curl_is_spurious;
13+
use crate::client::http::options::{HttpVersion, SslVersion};
1314
use crate::client::http::traits::PostBodyDataKind;
1415
use crate::client::{
1516
blocking_io::http::{self, curl::Error, redirect},
@@ -153,7 +154,10 @@ pub fn new() -> (
153154
user_agent,
154155
proxy_authenticate,
155156
verbose,
156-
backend: _,
157+
ssl_ca_info,
158+
ssl_version,
159+
http_version,
160+
backend,
157161
},
158162
} in req_recv
159163
{
@@ -168,6 +172,35 @@ pub fn new() -> (
168172
headers.append("Expect:")?;
169173
handle.verbose(verbose)?;
170174

175+
if let Some(ca_info) = ssl_ca_info {
176+
handle.cainfo(ca_info)?;
177+
}
178+
179+
if let Some(ref mut curl_options) = backend.as_ref().and_then(|backend| backend.lock().ok()) {
180+
if let Some(opts) = curl_options.downcast_mut::<super::Options>() {
181+
if let Some(enabled) = opts.schannel_check_revoke {
182+
handle.ssl_options(curl::easy::SslOpt::new().no_revoke(!enabled))?;
183+
}
184+
}
185+
}
186+
187+
if let Some(ssl_version) = ssl_version {
188+
let (min, max) = ssl_version.min_max();
189+
if min == max {
190+
handle.ssl_version(to_curl_ssl_version(min))?;
191+
} else {
192+
handle.ssl_min_max_version(to_curl_ssl_version(min), to_curl_ssl_version(max))?;
193+
}
194+
}
195+
196+
if let Some(http_version) = http_version {
197+
let version = match http_version {
198+
HttpVersion::V1_1 => curl::easy::HttpVersion::V11,
199+
HttpVersion::V2 => curl::easy::HttpVersion::V2,
200+
};
201+
handle.http_version(version)?;
202+
}
203+
171204
let mut proxy_auth_action = None;
172205
if let Some(proxy) = proxy {
173206
handle.proxy(&proxy)?;
@@ -321,6 +354,20 @@ pub fn new() -> (
321354
(handle, req_send, res_recv)
322355
}
323356

357+
fn to_curl_ssl_version(vers: SslVersion) -> curl::easy::SslVersion {
358+
use curl::easy::SslVersion::*;
359+
match vers {
360+
SslVersion::Default => Default,
361+
SslVersion::TlsV1 => Tlsv1,
362+
SslVersion::SslV2 => Sslv2,
363+
SslVersion::SslV3 => Sslv3,
364+
SslVersion::TlsV1_0 => Tlsv10,
365+
SslVersion::TlsV1_1 => Tlsv11,
366+
SslVersion::TlsV1_2 => Tlsv12,
367+
SslVersion::TlsV1_3 => Tlsv13,
368+
}
369+
}
370+
324371
impl From<Error> for http::Error {
325372
fn from(err: Error) -> Self {
326373
http::Error::Detail {

Diff for: git-transport/src/client/blocking_io/http/mod.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::path::PathBuf;
12
use std::{
23
any::Any,
34
borrow::Cow,
@@ -10,6 +11,7 @@ use git_packetline::PacketLineRef;
1011
pub use traits::{Error, GetResponse, Http, PostResponse};
1112

1213
use crate::client::blocking_io::bufread_ext::ReadlineBufRead;
14+
use crate::client::http::options::{HttpVersion, SslVersionRangeInclusive};
1315
use crate::{
1416
client::{self, capabilities, Capabilities, ExtendedBufRead, HandleProgress, MessageKind, RequestWriter},
1517
Protocol, Service,
@@ -19,7 +21,8 @@ use crate::{
1921
compile_error!("Cannot set both 'http-client-reqwest' and 'http-client-curl' features as they are mutually exclusive");
2022

2123
#[cfg(feature = "http-client-curl")]
22-
mod curl;
24+
///
25+
pub mod curl;
2326

2427
/// The experimental `reqwest` backend.
2528
///
@@ -73,6 +76,51 @@ pub mod options {
7376
ProxyAuthMethod::AnyAuth
7477
}
7578
}
79+
80+
/// Available SSL version numbers.
81+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
82+
#[allow(missing_docs)]
83+
pub enum SslVersion {
84+
/// The implementation default, which is unknown to this layer of abstraction.
85+
Default,
86+
TlsV1,
87+
SslV2,
88+
SslV3,
89+
TlsV1_0,
90+
TlsV1_1,
91+
TlsV1_2,
92+
TlsV1_3,
93+
}
94+
95+
/// Available HTTP version numbers.
96+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
97+
#[allow(missing_docs)]
98+
pub enum HttpVersion {
99+
/// Equivalent to HTTP/1.1
100+
V1_1,
101+
/// Equivalent to HTTP/2
102+
V2,
103+
}
104+
105+
/// The desired range of acceptable SSL versions, or the single version to allow if both are set to the same value.
106+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
107+
pub struct SslVersionRangeInclusive {
108+
/// The smallest allowed ssl version to use.
109+
pub min: SslVersion,
110+
/// The highest allowed ssl version to use.
111+
pub max: SslVersion,
112+
}
113+
114+
impl SslVersionRangeInclusive {
115+
/// Return `min` and `max` fields in the right order so `min` is smaller or equal to `max`.
116+
pub fn min_max(&self) -> (SslVersion, SslVersion) {
117+
if self.min > self.max {
118+
(self.max, self.min)
119+
} else {
120+
(self.min, self.max)
121+
}
122+
}
123+
}
76124
}
77125

78126
/// Options to configure http requests.
@@ -131,6 +179,12 @@ pub struct Options {
131179
pub connect_timeout: Option<std::time::Duration>,
132180
/// If enabled, emit additional information about connections and possibly the data received or written.
133181
pub verbose: bool,
182+
/// If set, use this path to point to a file with CA certificates to verify peers.
183+
pub ssl_ca_info: Option<PathBuf>,
184+
/// The SSL version or version range to use, or `None` to let the TLS backend determine which versions are acceptable.
185+
pub ssl_version: Option<SslVersionRangeInclusive>,
186+
/// The HTTP version to enforce. If unset, it is implementation defined.
187+
pub http_version: Option<HttpVersion>,
134188
/// Backend specific options, if available.
135189
pub backend: Option<Arc<Mutex<dyn Any + Send + Sync + 'static>>>,
136190
}

0 commit comments

Comments
 (0)