mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-23 11:02:43 +03:00
Fix relative redirect
This commit is contained in:
parent
1c2e2ffc72
commit
1ae68efd82
@ -1,2 +1,5 @@
|
|||||||
curl 'http://localhost:8000/redirect'
|
|
||||||
curl 'http://localhost:8000/redirected'
|
curl 'http://localhost:8000/redirected'
|
||||||
|
curl 'http://localhost:8000/redirect-absolute'
|
||||||
|
curl 'http://localhost:8000/redirect-absolute' -L
|
||||||
|
curl 'http://localhost:8000/redirect-relative'
|
||||||
|
curl 'http://localhost:8000/redirect-relative' -L
|
||||||
|
@ -1,13 +1,37 @@
|
|||||||
<pre><code class="language-hurl"><span class="hurl-entry"><span class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirect</span></span>
|
<pre><code class="language-hurl"><span class="hurl-entry"><span class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirected</span></span>
|
||||||
|
</span><span class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="number">200</span></span>
|
||||||
|
<span class="raw"><span class="line">```Redirected```</span></span>
|
||||||
|
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="line"></span><span class="comment"># Absolute redirects</span>
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirect-absolute</span></span>
|
||||||
</span><span class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="number">302</span></span>
|
</span><span class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="number">302</span></span>
|
||||||
<span class="line"><span class="string">Location</span><span>:</span> <span class="string">http://localhost:8000/redirected</span></span>
|
<span class="line"><span class="string">Location</span><span>:</span> <span class="string">http://localhost:8000/redirected</span></span>
|
||||||
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||||
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirected</span></span>
|
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirect-absolute</span></span>
|
||||||
</span><span class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="number">200</span></span>
|
<span class="line section-header">[Options]</span>
|
||||||
|
<span class="line"><span class="string">location</span><span>:</span> <span class="boolean">true</span></span>
|
||||||
|
</span><span class="response"><span class="line"></span>
|
||||||
|
<span class="line"><span class="version">HTTP/1.0</span> <span class="number">200</span></span>
|
||||||
|
<span class="raw"><span class="line">```Redirected```</span></span>
|
||||||
|
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="line"></span><span class="comment"># Relative redirects</span>
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirect-relative</span></span>
|
||||||
|
</span><span class="response"><span class="line"><span class="version">HTTP/1.0</span> <span class="number">302</span></span>
|
||||||
|
<span class="line"><span class="string">Location</span><span>:</span> <span class="string">/redirected</span></span>
|
||||||
|
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||||
|
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/redirect-relative</span></span>
|
||||||
|
<span class="line section-header">[Options]</span>
|
||||||
|
<span class="line"><span class="string">location</span><span>:</span> <span class="boolean">true</span></span>
|
||||||
|
</span><span class="response"><span class="line"></span>
|
||||||
|
<span class="line"><span class="version">HTTP/1.0</span> <span class="number">200</span></span>
|
||||||
|
<span class="raw"><span class="line">```Redirected```</span></span>
|
||||||
</span></span><span class="line"></span>
|
</span></span><span class="line"></span>
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
<span class="line"></span>
|
|
||||||
</code></pre>
|
</code></pre>
|
@ -1,10 +1,34 @@
|
|||||||
GET http://localhost:8000/redirect
|
GET http://localhost:8000/redirected
|
||||||
|
HTTP/1.0 200
|
||||||
|
```Redirected```
|
||||||
|
|
||||||
|
|
||||||
|
# Absolute redirects
|
||||||
|
|
||||||
|
GET http://localhost:8000/redirect-absolute
|
||||||
HTTP/1.0 302
|
HTTP/1.0 302
|
||||||
Location: http://localhost:8000/redirected
|
Location: http://localhost:8000/redirected
|
||||||
|
|
||||||
GET http://localhost:8000/redirected
|
GET http://localhost:8000/redirect-absolute
|
||||||
|
[Options]
|
||||||
|
location: true
|
||||||
|
|
||||||
HTTP/1.0 200
|
HTTP/1.0 200
|
||||||
|
```Redirected```
|
||||||
|
|
||||||
|
|
||||||
|
# Relative redirects
|
||||||
|
|
||||||
|
GET http://localhost:8000/redirect-relative
|
||||||
|
HTTP/1.0 302
|
||||||
|
Location: /redirected
|
||||||
|
|
||||||
|
GET http://localhost:8000/redirect-relative
|
||||||
|
[Options]
|
||||||
|
location: true
|
||||||
|
|
||||||
|
HTTP/1.0 200
|
||||||
|
```Redirected```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/redirect"},"response":{"version":"HTTP/1.0","status":302,"headers":[{"name":"Location","value":"http://localhost:8000/redirected"}]}},{"request":{"method":"GET","url":"http://localhost:8000/redirected"},"response":{"version":"HTTP/1.0","status":200}}]}
|
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/redirected"},"response":{"version":"HTTP/1.0","status":200,"body":{"type":"raw-string","value":"Redirected"}}},{"request":{"method":"GET","url":"http://localhost:8000/redirect-absolute"},"response":{"version":"HTTP/1.0","status":302,"headers":[{"name":"Location","value":"http://localhost:8000/redirected"}]}},{"request":{"method":"GET","url":"http://localhost:8000/redirect-absolute"},"response":{"version":"HTTP/1.0","status":200,"body":{"type":"raw-string","value":"Redirected"}}},{"request":{"method":"GET","url":"http://localhost:8000/redirect-relative"},"response":{"version":"HTTP/1.0","status":302,"headers":[{"name":"Location","value":"/redirected"}]}},{"request":{"method":"GET","url":"http://localhost:8000/redirect-relative"},"response":{"version":"HTTP/1.0","status":200,"body":{"type":"raw-string","value":"Redirected"}}}]}
|
@ -1,12 +1,20 @@
|
|||||||
from app import app
|
from app import app
|
||||||
from flask import redirect
|
from flask import redirect, Response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/redirect")
|
@app.route("/redirect-absolute")
|
||||||
def redirectme():
|
def redirect_absolute():
|
||||||
return redirect("http://localhost:8000/redirected")
|
return redirect("http://localhost:8000/redirected")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/redirect-relative")
|
||||||
|
def redirect_relative():
|
||||||
|
response = Response(status=302)
|
||||||
|
response.headers["Location"] = "/redirected"
|
||||||
|
response.autocorrect_location_header = False
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/redirected")
|
@app.route("/redirected")
|
||||||
def redirected():
|
def redirected():
|
||||||
return ""
|
return "Redirected"
|
||||||
|
@ -82,12 +82,13 @@ impl Client {
|
|||||||
self.redirect_count = 0;
|
self.redirect_count = 0;
|
||||||
loop {
|
loop {
|
||||||
let (request, response) = self.execute(&request_spec, options, logger)?;
|
let (request, response) = self.execute(&request_spec, options, logger)?;
|
||||||
calls.push((request, response.clone()));
|
calls.push((request.clone(), response.clone()));
|
||||||
if !options.follow_location {
|
if !options.follow_location {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(url) = self.get_follow_location(&response) {
|
let base_url = request.base_url()?;
|
||||||
|
if let Some(url) = self.get_follow_location(&response, &base_url) {
|
||||||
logger.debug("");
|
logger.debug("");
|
||||||
logger.debug(format!("=> Redirect to {}", url).as_str());
|
logger.debug(format!("=> Redirect to {}", url).as_str());
|
||||||
logger.debug("");
|
logger.debug("");
|
||||||
@ -513,14 +514,14 @@ impl Client {
|
|||||||
/// 1. the option follow_location set to true
|
/// 1. the option follow_location set to true
|
||||||
/// 2. a 3xx response code
|
/// 2. a 3xx response code
|
||||||
/// 3. a header Location
|
/// 3. a header Location
|
||||||
fn get_follow_location(&mut self, response: &Response) -> Option<String> {
|
fn get_follow_location(&mut self, response: &Response, base_url: &str) -> Option<String> {
|
||||||
let response_code = response.status;
|
let response_code = response.status;
|
||||||
if !(300..400).contains(&response_code) {
|
if !(300..400).contains(&response_code) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let location = match response.get_header_values("Location").get(0) {
|
let location = match response.get_header_values("Location").get(0) {
|
||||||
None => return None,
|
None => return None,
|
||||||
Some(value) => value.clone(),
|
Some(value) => get_redirect_url(value, base_url),
|
||||||
};
|
};
|
||||||
|
|
||||||
if location.is_empty() {
|
if location.is_empty() {
|
||||||
@ -590,6 +591,15 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the redirect url.
|
||||||
|
fn get_redirect_url(location: &str, base_url: &str) -> String {
|
||||||
|
if location.starts_with('/') {
|
||||||
|
format!("{}{}", base_url, location)
|
||||||
|
} else {
|
||||||
|
location.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns cookies from both cookies from the cookie storage and the request.
|
/// Returns cookies from both cookies from the cookie storage and the request.
|
||||||
pub fn all_cookies(cookie_storage: &[Cookie], request_spec: &RequestSpec) -> Vec<RequestCookie> {
|
pub fn all_cookies(cookie_storage: &[Cookie], request_spec: &RequestSpec) -> Vec<RequestCookie> {
|
||||||
let mut cookies = request_spec.cookies.clone();
|
let mut cookies = request_spec.cookies.clone();
|
||||||
@ -739,4 +749,16 @@ mod tests {
|
|||||||
assert!(match_cookie(&cookie, "http://sub.example.com/toto"));
|
assert!(match_cookie(&cookie, "http://sub.example.com/toto"));
|
||||||
assert!(!match_cookie(&cookie, "http://example.com/tata"));
|
assert!(!match_cookie(&cookie, "http://example.com/tata"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_redirect_url() {
|
||||||
|
assert_eq!(
|
||||||
|
get_redirect_url("http://localhost:8000/redirected", "http://localhost:8000"),
|
||||||
|
"http://localhost:8000/redirected".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_redirect_url("/redirected", "http://localhost:8000"),
|
||||||
|
"http://localhost:8000/redirected".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,5 @@ pub enum HttpError {
|
|||||||
UnsupportedContentEncoding {
|
UnsupportedContentEncoding {
|
||||||
description: String,
|
description: String,
|
||||||
},
|
},
|
||||||
|
InvalidUrl(String),
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
use super::core::*;
|
use super::core::*;
|
||||||
use super::Header;
|
use super::Header;
|
||||||
use crate::http::header;
|
use crate::http::{header, HttpError};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@ -61,6 +61,26 @@ impl Request {
|
|||||||
.get(0)
|
.get(0)
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the base url http(s)://host(:port)
|
||||||
|
pub fn base_url(&self) -> Result<String, HttpError> {
|
||||||
|
// FIXME: is it possible to do it with libcurl?
|
||||||
|
let url = match Url::parse(&self.url) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(_) => return Err(HttpError::InvalidUrl(self.url.clone())),
|
||||||
|
};
|
||||||
|
let base_url = format!(
|
||||||
|
"{}://{}{}",
|
||||||
|
url.scheme(),
|
||||||
|
url.host().unwrap(),
|
||||||
|
if let Some(port) = url.port() {
|
||||||
|
format!(":{}", port)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(base_url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cookies(s: &str) -> Vec<RequestCookie> {
|
fn parse_cookies(s: &str) -> Vec<RequestCookie> {
|
||||||
@ -136,20 +156,20 @@ pub mod tests {
|
|||||||
vec![
|
vec![
|
||||||
Param {
|
Param {
|
||||||
name: "param1".to_string(),
|
name: "param1".to_string(),
|
||||||
value: "value1".to_string()
|
value: "value1".to_string(),
|
||||||
},
|
},
|
||||||
Param {
|
Param {
|
||||||
name: "param2".to_string(),
|
name: "param2".to_string(),
|
||||||
value: "".to_string()
|
value: "".to_string(),
|
||||||
},
|
},
|
||||||
Param {
|
Param {
|
||||||
name: "param3".to_string(),
|
name: "param3".to_string(),
|
||||||
value: "a=b".to_string()
|
value: "a=b".to_string(),
|
||||||
},
|
},
|
||||||
Param {
|
Param {
|
||||||
name: "param4".to_string(),
|
name: "param4".to_string(),
|
||||||
value: "1,2,3".to_string()
|
value: "1,2,3".to_string(),
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -162,12 +182,12 @@ pub mod tests {
|
|||||||
vec![
|
vec![
|
||||||
RequestCookie {
|
RequestCookie {
|
||||||
name: "cookie1".to_string(),
|
name: "cookie1".to_string(),
|
||||||
value: "value1".to_string()
|
value: "value1".to_string(),
|
||||||
},
|
},
|
||||||
RequestCookie {
|
RequestCookie {
|
||||||
name: "cookie2".to_string(),
|
name: "cookie2".to_string(),
|
||||||
value: "value2".to_string()
|
value: "value2".to_string(),
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -179,12 +199,12 @@ pub mod tests {
|
|||||||
vec![
|
vec![
|
||||||
RequestCookie {
|
RequestCookie {
|
||||||
name: "cookie1".to_string(),
|
name: "cookie1".to_string(),
|
||||||
value: "value1".to_string()
|
value: "value1".to_string(),
|
||||||
},
|
},
|
||||||
RequestCookie {
|
RequestCookie {
|
||||||
name: "cookie2".to_string(),
|
name: "cookie2".to_string(),
|
||||||
value: "value2".to_string()
|
value: "value2".to_string(),
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -195,8 +215,45 @@ pub mod tests {
|
|||||||
parse_cookie("cookie1=value1"),
|
parse_cookie("cookie1=value1"),
|
||||||
RequestCookie {
|
RequestCookie {
|
||||||
name: "cookie1".to_string(),
|
name: "cookie1".to_string(),
|
||||||
value: "value1".to_string()
|
value: "value1".to_string(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_base_url() {
|
||||||
|
assert_eq!(
|
||||||
|
Request {
|
||||||
|
url: "http://localhost".to_string(),
|
||||||
|
method: "".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
body: vec![],
|
||||||
|
}
|
||||||
|
.base_url()
|
||||||
|
.unwrap(),
|
||||||
|
"http://localhost".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Request {
|
||||||
|
url: "http://localhost:8000/redirect-relative".to_string(),
|
||||||
|
method: "".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
body: vec![],
|
||||||
|
}
|
||||||
|
.base_url()
|
||||||
|
.unwrap(),
|
||||||
|
"http://localhost:8000".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Request {
|
||||||
|
url: "https://localhost:8000".to_string(),
|
||||||
|
method: "".to_string(),
|
||||||
|
headers: vec![],
|
||||||
|
body: vec![],
|
||||||
|
}
|
||||||
|
.base_url()
|
||||||
|
.unwrap(),
|
||||||
|
"https://localhost:8000".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,6 +172,7 @@ impl From<HttpError> for RunnerError {
|
|||||||
HttpError::UnsupportedContentEncoding { description } => {
|
HttpError::UnsupportedContentEncoding { description } => {
|
||||||
RunnerError::UnsupportedContentEncoding(description)
|
RunnerError::UnsupportedContentEncoding(description)
|
||||||
}
|
}
|
||||||
|
HttpError::InvalidUrl(url) => RunnerError::InvalidUrl(url),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,13 +358,16 @@ fn test_form_params() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_redirect() {
|
fn test_redirect() {
|
||||||
let request_spec = default_get_request("http://localhost:8000/redirect");
|
let request_spec = default_get_request("http://localhost:8000/redirect-absolute");
|
||||||
let logger = Logger::new(false, false, "", "");
|
let logger = Logger::new(false, false, "", "");
|
||||||
let options = ClientOptions::default();
|
let options = ClientOptions::default();
|
||||||
let mut client = Client::new(None);
|
let mut client = Client::new(None);
|
||||||
let (request, response) = client.execute(&request_spec, &options, &logger).unwrap();
|
let (request, response) = client.execute(&request_spec, &options, &logger).unwrap();
|
||||||
assert_eq!(request.method, "GET".to_string());
|
assert_eq!(request.method, "GET".to_string());
|
||||||
assert_eq!(request.url, "http://localhost:8000/redirect".to_string());
|
assert_eq!(
|
||||||
|
request.url,
|
||||||
|
"http://localhost:8000/redirect-absolute".to_string()
|
||||||
|
);
|
||||||
assert_eq!(request.headers.len(), 3);
|
assert_eq!(request.headers.len(), 3);
|
||||||
|
|
||||||
assert_eq!(response.status, 302);
|
assert_eq!(response.status, 302);
|
||||||
@ -377,7 +380,7 @@ fn test_redirect() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_follow_location() {
|
fn test_follow_location() {
|
||||||
let request_spec = default_get_request("http://localhost:8000/redirect");
|
let request_spec = default_get_request("http://localhost:8000/redirect-absolute");
|
||||||
let logger = Logger::new(false, false, "", "");
|
let logger = Logger::new(false, false, "", "");
|
||||||
let options = ClientOptions {
|
let options = ClientOptions {
|
||||||
follow_location: true,
|
follow_location: true,
|
||||||
@ -388,7 +391,7 @@ fn test_follow_location() {
|
|||||||
assert_eq!(options.curl_args(), vec!["-L".to_string()]);
|
assert_eq!(options.curl_args(), vec!["-L".to_string()]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.curl_command_line(&request_spec, &context_dir, &options),
|
client.curl_command_line(&request_spec, &context_dir, &options),
|
||||||
"curl 'http://localhost:8000/redirect' -L".to_string()
|
"curl 'http://localhost:8000/redirect-absolute' -L".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
let calls = client
|
let calls = client
|
||||||
@ -398,7 +401,10 @@ fn test_follow_location() {
|
|||||||
|
|
||||||
let (request1, response1) = calls.get(0).unwrap();
|
let (request1, response1) = calls.get(0).unwrap();
|
||||||
assert_eq!(request1.method, "GET".to_string());
|
assert_eq!(request1.method, "GET".to_string());
|
||||||
assert_eq!(request1.url, "http://localhost:8000/redirect".to_string());
|
assert_eq!(
|
||||||
|
request1.url,
|
||||||
|
"http://localhost:8000/redirect-absolute".to_string()
|
||||||
|
);
|
||||||
assert_eq!(request1.headers.len(), 3);
|
assert_eq!(request1.headers.len(), 3);
|
||||||
assert_eq!(response1.status, 302);
|
assert_eq!(response1.status, 302);
|
||||||
assert!(response1.headers.contains(&Header {
|
assert!(response1.headers.contains(&Header {
|
||||||
|
Loading…
Reference in New Issue
Block a user