ureq_proto/client/
redirect.rs1use http::uri::Scheme;
2use http::{header, Method, StatusCode, Uri};
3
4use crate::ext::{MethodExt, StatusExt};
5use crate::Error;
6
7use super::state::{Cleanup, Prepare, Redirect};
8use super::{Call, RedirectAuthHeaders};
9
10impl Call<Redirect> {
11 pub fn as_new_call(
23 &mut self,
24 redirect_auth_headers: RedirectAuthHeaders,
25 ) -> Result<Option<Call<Prepare>>, Error> {
26 let header = match &self.inner.location {
27 Some(v) => v,
28 None => return Err(Error::NoLocationHeader),
29 };
30
31 let location = match header.to_str() {
32 Ok(v) => v,
33 Err(_) => {
34 return Err(Error::BadLocationHeader(
35 String::from_utf8_lossy(header.as_bytes()).to_string(),
36 ))
37 }
38 };
39
40 let previous = &mut self.inner.request;
42
43 let status = self.inner.status.unwrap();
45 let method = previous.method();
46
47 let uri = previous.new_uri_from_location(location)?;
49
50 let new_method = if status.is_redirect_retaining_status() {
52 if method.need_request_body() {
53 return Ok(None);
55 } else if method == Method::DELETE {
56 return Ok(None);
58 } else {
59 method.clone()
60 }
61 } else {
62 if matches!(*method, Method::GET | Method::HEAD) {
65 method.clone()
66 } else {
67 Method::GET
68 }
69 };
70
71 let mut request = previous.take_request();
72
73 *request.method_mut() = new_method;
75
76 let keep_auth_header = match redirect_auth_headers {
77 RedirectAuthHeaders::Never => false,
78 RedirectAuthHeaders::SameHost => can_redirect_auth_header(request.uri(), &uri),
79 };
80
81 *request.uri_mut() = uri;
83
84 let headers = request.headers_mut();
86 if !keep_auth_header {
87 headers.remove(header::AUTHORIZATION);
88 }
89 headers.remove(header::COOKIE);
90 headers.remove(header::CONTENT_LENGTH);
91
92 let next = Call::new(request)?;
94
95 Ok(Some(next))
96 }
97
98 pub fn status(&self) -> StatusCode {
100 self.inner.status.unwrap()
101 }
102
103 pub fn must_close_connection(&self) -> bool {
107 self.close_reason().is_some()
108 }
109
110 pub fn close_reason(&self) -> Option<&'static str> {
112 self.inner.close_reason.first().map(|s| s.explain())
113 }
114
115 pub fn proceed(self) -> Call<Cleanup> {
117 Call::wrap(self.inner)
118 }
119}
120
121fn can_redirect_auth_header(prev: &Uri, next: &Uri) -> bool {
122 let host_prev = prev.authority().map(|a| a.host());
123 let host_next = next.authority().map(|a| a.host());
124 let scheme_prev = prev.scheme();
125 let scheme_next = next.scheme();
126 host_prev == host_next && (scheme_prev == scheme_next || scheme_next == Some(&Scheme::HTTPS))
127}