дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
fix(http): add http:// to a URL whose query holds another URL (#988)
A request URL without a scheme defaults to http://, but the check was `contains("://")`, so `GET localhost:8080/login?next=http://…` was sent as is and failed to parse. Only a `://` after a leading RFC 3986 scheme counts now, both for HTTP Client requests and the proxy connection check.
Co-authored-by: fhrmrk <[email protected]>
Цей коміт міститься в:
1 джерело
ebf2c2f8e3
коміт
839bd0cdb0
2 files changed
+27
-5
No files matched your search
@@ -57,6 +57,16 @@ pub fn shared() -> IdeResult<reqwest::Client> {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
/// `url`, with `http://` in front unless it starts with a scheme: a `://` later
|
||||
/// on (`localhost:8080/login?next=http://…`) does not count.
|
||||
pub(crate) fn with_scheme(url: &str) -> String {
|
||||
let has_scheme = url.split_once("://").is_some_and(|(scheme, _)| {
|
||||
scheme.starts_with(|c: char| c.is_ascii_alphabetic())
|
||||
&& scheme.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '+' | '-' | '.'))
|
||||
});
|
||||
if has_scheme { url.to_owned() } else { format!("http://{url}") }
|
||||
}
|
||||
|
||||
/// HTTP Proxy › Check connection: GETs `url` through `proxy` (the settings as
|
||||
/// edited, not yet applied). Any response but `407` counts as connected.
|
||||
pub async fn check_connection(proxy: &ProxySettings, url: &str, cancel: &CancellationToken) -> IdeResult<String> {
|
||||
@@ -64,7 +74,7 @@ pub async fn check_connection(proxy: &ProxySettings, url: &str, cancel: &Cancell
|
||||
return Err(IdeError::invalid(problem));
|
||||
}
|
||||
let url = url.trim();
|
||||
let url = if url.contains("://") { url.to_owned() } else { format!("http://{url}") };
|
||||
let url = with_scheme(url);
|
||||
let client = build(proxy)?;
|
||||
let request = client.get(&url).timeout(Duration::from_secs(30));
|
||||
let response = tokio::select! {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Preparing (substitution, body includes) and executing requests.
|
||||
|
||||
use crate::client::describe;
|
||||
use crate::client::{describe, with_scheme};
|
||||
use crate::env::{Environments, Resolver};
|
||||
use crate::parse::{BodyPart, RawRequest};
|
||||
use ide_core::{CancellationToken, IdeError, IdeResult};
|
||||
@@ -84,10 +84,8 @@ pub fn prepare(
|
||||
Some((_, host)) => format!("http://{host}{url}"),
|
||||
None => return Err(IdeError::invalid(format!("relative URL {url} needs a Host header"))),
|
||||
}
|
||||
} else if !url.contains("://") {
|
||||
format!("http://{url}")
|
||||
} else {
|
||||
url
|
||||
with_scheme(&url)
|
||||
};
|
||||
Ok(PreparedRequest {
|
||||
name: raw.block.name.clone(),
|
||||
@@ -214,6 +212,20 @@ mod tests {
|
||||
assert_eq!(p.url, "http://localhost:9000/status");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn urls_without_a_scheme_default_to_http() {
|
||||
let url = |text: &str| {
|
||||
let f = parse(text);
|
||||
prepare(&f.requests[0], &f.variables, &Environments::default(), None, None).unwrap().url
|
||||
};
|
||||
assert_eq!(url("GET localhost:8080/health\n"), "http://localhost:8080/health");
|
||||
assert_eq!(
|
||||
url("GET localhost:8080/login?next=http://localhost:3000/\n"),
|
||||
"http://localhost:8080/login?next=http://localhost:3000/"
|
||||
);
|
||||
assert_eq!(url("GET https://api.test/cb?u=http://x\n"), "https://api.test/cb?u=http://x");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn executes_against_a_local_server() {
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
Reference in new issue
Заблокувати користувача