Skip to content

Commit e83c38f

Browse files
committed
Merge branch 'fix-redirect'
2 parents f584d76 + 7663a48 commit e83c38f

File tree

2 files changed

+60
-8
lines changed

2 files changed

+60
-8
lines changed

gix-transport/src/client/blocking_io/http/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,5 +517,5 @@ pub fn connect(url: gix_url::Url, desired_version: Protocol) -> Transport<Impl>
517517
}
518518

519519
///
520-
#[cfg(feature = "http-client-curl")]
520+
#[cfg(any(feature = "http-client-curl", feature = "http-client-reqwest"))]
521521
pub mod redirect;

gix-transport/src/client/blocking_io/http/reqwest/remote.rs

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ use std::{
33
convert::TryFrom,
44
io::{Read, Write},
55
str::FromStr,
6+
sync::{atomic, Arc},
67
};
78

89
use gix_features::io::pipe;
910

10-
use crate::client::{
11-
http,
12-
http::{reqwest::Remote, traits::PostBodyDataKind},
13-
};
11+
use crate::client::http::{self, options::FollowRedirects, redirect, reqwest::Remote, traits::PostBodyDataKind};
1412

1513
/// The error returned by the 'remote' helper, a purely internal construct to perform http requests.
1614
#[derive(Debug, thiserror::Error)]
@@ -22,6 +20,8 @@ pub enum Error {
2220
ReadPostBody(#[from] std::io::Error),
2321
#[error("Request configuration failed")]
2422
ConfigureRequest(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
23+
#[error(transparent)]
24+
Redirect(#[from] redirect::Error),
2525
}
2626

2727
impl crate::IsSpuriousError for Error {
@@ -40,23 +40,57 @@ impl Default for Remote {
4040
let (req_send, req_recv) = std::sync::mpsc::sync_channel(0);
4141
let (res_send, res_recv) = std::sync::mpsc::sync_channel(0);
4242
let handle = std::thread::spawn(move || -> Result<(), Error> {
43+
let mut follow = None;
44+
let mut redirected_base_url = None::<String>;
45+
let allow_redirects = Arc::new(atomic::AtomicBool::new(false));
46+
4347
// We may error while configuring, which is expected as part of the internal protocol. The error will be
4448
// received and the sender of the request might restart us.
4549
let client = reqwest::blocking::ClientBuilder::new()
4650
.connect_timeout(std::time::Duration::from_secs(20))
4751
.http1_title_case_headers()
52+
.redirect(reqwest::redirect::Policy::custom({
53+
let allow_redirects = allow_redirects.clone();
54+
move |attempt| {
55+
if allow_redirects.load(atomic::Ordering::Relaxed) {
56+
let curr_url = attempt.url();
57+
let prev_urls = attempt.previous();
58+
59+
match prev_urls.first() {
60+
Some(prev_url) if prev_url.host_str() != curr_url.host_str() => {
61+
// git does not want to be redirected to a different host.
62+
attempt.stop()
63+
}
64+
_ => {
65+
// emulate default git behaviour which relies on curl default behaviour apparently.
66+
const CURL_DEFAULT_REDIRS: usize = 50;
67+
if prev_urls.len() >= CURL_DEFAULT_REDIRS {
68+
attempt.error("too many redirects")
69+
} else {
70+
attempt.follow()
71+
}
72+
}
73+
}
74+
} else {
75+
attempt.stop()
76+
}
77+
}
78+
}))
4879
.build()?;
80+
4981
for Request {
5082
url,
83+
base_url,
5184
headers,
5285
upload_body_kind,
5386
config,
5487
} in req_recv
5588
{
89+
let effective_url = redirect::swap_tails(redirected_base_url.as_deref(), &base_url, url.clone());
5690
let mut req_builder = if upload_body_kind.is_some() {
57-
client.post(url)
91+
client.post(&effective_url)
5892
} else {
59-
client.get(url)
93+
client.get(&effective_url)
6094
}
6195
.headers(headers);
6296
let (post_body_tx, mut post_body_rx) = pipe::unidirectional(0);
@@ -91,6 +125,17 @@ impl Default for Remote {
91125
}
92126
}
93127
}
128+
129+
let follow = follow.get_or_insert(config.follow_redirects);
130+
allow_redirects.store(
131+
matches!(follow, FollowRedirects::Initial | FollowRedirects::All),
132+
atomic::Ordering::Relaxed,
133+
);
134+
135+
if *follow == FollowRedirects::Initial {
136+
*follow = FollowRedirects::None;
137+
}
138+
94139
let mut res = match client
95140
.execute(req)
96141
.and_then(reqwest::blocking::Response::error_for_status)
@@ -116,6 +161,11 @@ impl Default for Remote {
116161
}
117162
};
118163

164+
let actual_url = res.url().as_str();
165+
if actual_url != effective_url.as_str() {
166+
redirected_base_url = redirect::base_url(actual_url, &base_url, url)?.into();
167+
}
168+
119169
let send_headers = {
120170
let headers = res.headers();
121171
move || -> std::io::Result<()> {
@@ -172,7 +222,7 @@ impl Remote {
172222
fn make_request(
173223
&mut self,
174224
url: &str,
175-
_base_url: &str,
225+
base_url: &str,
176226
headers: impl IntoIterator<Item = impl AsRef<str>>,
177227
upload_body_kind: Option<PostBodyDataKind>,
178228
) -> Result<http::PostResponse<pipe::Reader, pipe::Reader, pipe::Writer>, http::Error> {
@@ -197,6 +247,7 @@ impl Remote {
197247
.request
198248
.send(Request {
199249
url: url.to_owned(),
250+
base_url: base_url.to_owned(),
200251
headers: header_map,
201252
upload_body_kind,
202253
config: self.config.clone(),
@@ -259,6 +310,7 @@ impl http::Http for Remote {
259310

260311
pub(crate) struct Request {
261312
pub url: String,
313+
pub base_url: String,
262314
pub headers: reqwest::header::HeaderMap,
263315
pub upload_body_kind: Option<PostBodyDataKind>,
264316
pub config: http::Options,

0 commit comments

Comments
 (0)