History

Merge branch 'fix-redirect'

Sebastian Thiel committed 3 years ago

Commit e83c38f2 parents f584d76 7663a482 files · +60 −8Browse files

Unified · Split

2 files
  1. gix-transport/src/client/blocking_io/http/mod.rs +1 −1
  2. gix-transport/src/client/blocking_io/http/reqwest/remote.rs +59 −7
gix-transport/src/client/blocking_io/http/mod.rs+1−1
@@ -517,5 +517,5 @@ pub fn connect(url: gix_url::Url, desired_version: Protocol) -> Transport<Impl> · 516 unmodified lines
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+59−7
@@ -3,14 +3,12 @@ use std::{ · 2 unmodified lines
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 { · 5 unmodified lines
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 { · 12 unmodified lines
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 { · 28 unmodified lines
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 { · 19 unmodified lines
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 { · 50 unmodified lines
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 { · 18 unmodified lines
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 { · 56 unmodified lines
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,