Skip to content

Commit e642690

Browse files
committed
Fix git-transport reqwest client: Support redirect
Fixed #974 Signed-off-by: Jiahao XU <[email protected]>
1 parent f584d76 commit e642690

File tree

2 files changed

+58
-8
lines changed

2 files changed

+58
-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: 57 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,51 @@ 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+
46+
// This initial value is just a place holder, it will get modified
47+
// before `client.execute` is called.
48+
let current_follow_policy = Arc::new(atomic::AtomicBool::new(false));
49+
50+
let follow_policy = Arc::clone(&current_follow_policy);
51+
4352
// We may error while configuring, which is expected as part of the internal protocol. The error will be
4453
// received and the sender of the request might restart us.
4554
let client = reqwest::blocking::ClientBuilder::new()
4655
.connect_timeout(std::time::Duration::from_secs(20))
4756
.http1_title_case_headers()
57+
.redirect(reqwest::redirect::Policy::custom(move |attempt| {
58+
if follow_policy.load(atomic::Ordering::Relaxed) {
59+
let curr_url = attempt.url();
60+
let prev_urls = attempt.previous();
61+
62+
match prev_urls.first() {
63+
Some(prev_url) if prev_url.host_str() != curr_url.host_str() => {
64+
// gix does not want to be redirected to a different host.
65+
attempt.stop()
66+
}
67+
_ => attempt.follow(),
68+
}
69+
} else {
70+
attempt.stop()
71+
}
72+
}))
4873
.build()?;
74+
4975
for Request {
5076
url,
77+
base_url,
5178
headers,
5279
upload_body_kind,
5380
config,
5481
} in req_recv
5582
{
83+
let effective_url = redirect::swap_tails(redirected_base_url.as_deref(), &base_url, url.clone());
5684
let mut req_builder = if upload_body_kind.is_some() {
57-
client.post(url)
85+
client.post(&effective_url)
5886
} else {
59-
client.get(url)
87+
client.get(&effective_url)
6088
}
6189
.headers(headers);
6290
let (post_body_tx, mut post_body_rx) = pipe::unidirectional(0);
@@ -91,6 +119,21 @@ impl Default for Remote {
91119
}
92120
}
93121
}
122+
123+
let follow = follow.get_or_insert(config.follow_redirects);
124+
125+
current_follow_policy.store(
126+
match *follow {
127+
FollowRedirects::None => false,
128+
FollowRedirects::Initial | FollowRedirects::All => true,
129+
},
130+
atomic::Ordering::Relaxed,
131+
);
132+
133+
if *follow == FollowRedirects::Initial {
134+
*follow = FollowRedirects::None;
135+
}
136+
94137
let mut res = match client
95138
.execute(req)
96139
.and_then(reqwest::blocking::Response::error_for_status)
@@ -116,6 +159,11 @@ impl Default for Remote {
116159
}
117160
};
118161

162+
let actual_url = res.url().as_str();
163+
if actual_url != effective_url.as_str() {
164+
redirected_base_url = redirect::base_url(actual_url, &base_url, url)?.into();
165+
}
166+
119167
let send_headers = {
120168
let headers = res.headers();
121169
move || -> std::io::Result<()> {
@@ -172,7 +220,7 @@ impl Remote {
172220
fn make_request(
173221
&mut self,
174222
url: &str,
175-
_base_url: &str,
223+
base_url: &str,
176224
headers: impl IntoIterator<Item = impl AsRef<str>>,
177225
upload_body_kind: Option<PostBodyDataKind>,
178226
) -> Result<http::PostResponse<pipe::Reader, pipe::Reader, pipe::Writer>, http::Error> {
@@ -197,6 +245,7 @@ impl Remote {
197245
.request
198246
.send(Request {
199247
url: url.to_owned(),
248+
base_url: base_url.to_owned(),
200249
headers: header_map,
201250
upload_body_kind,
202251
config: self.config.clone(),
@@ -259,6 +308,7 @@ impl http::Http for Remote {
259308

260309
pub(crate) struct Request {
261310
pub url: String,
311+
pub base_url: String,
262312
pub headers: reqwest::header::HeaderMap,
263313
pub upload_body_kind: Option<PostBodyDataKind>,
264314
pub config: http::Options,

0 commit comments

Comments
 (0)