summaryrefslogtreecommitdiff
path: root/ext/fetch
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2024-07-24 22:22:43 +0100
committerGitHub <noreply@github.com>2024-07-24 23:22:43 +0200
commit1fad6eb2acc993714fad9d333f409495f5b3d6db (patch)
tree2b00c02b7306c7ad0577cd92e71fceba0a475475 /ext/fetch
parentc7f468d33b5d0814b56036639eb2a8226d4bfbbf (diff)
fix(ext/fetch): respect authority from URL (#24705)
This commit fixes handling of "authority" in the URL by properly sending "Authorization Basic..." header in `fetch` API. This is a regression from https://github.com/denoland/deno/pull/24593 Fixes https://github.com/denoland/deno/issues/24697 CC @seanmonstar
Diffstat (limited to 'ext/fetch')
-rw-r--r--ext/fetch/Cargo.toml1
-rw-r--r--ext/fetch/lib.rs42
-rw-r--r--ext/fetch/proxy.rs13
3 files changed, 50 insertions, 6 deletions
diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml
index c28d88b24..616956e65 100644
--- a/ext/fetch/Cargo.toml
+++ b/ext/fetch/Cargo.toml
@@ -27,6 +27,7 @@ hyper.workspace = true
hyper-rustls.workspace = true
hyper-util.workspace = true
ipnet.workspace = true
+percent-encoding.workspace = true
rustls-webpki.workspace = true
serde.workspace = true
serde_json.workspace = true
diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs
index 9912ff307..7c717ccec 100644
--- a/ext/fetch/lib.rs
+++ b/ext/fetch/lib.rs
@@ -55,6 +55,7 @@ use http::header::HeaderName;
use http::header::HeaderValue;
use http::header::ACCEPT;
use http::header::ACCEPT_ENCODING;
+use http::header::AUTHORIZATION;
use http::header::CONTENT_LENGTH;
use http::header::HOST;
use http::header::PROXY_AUTHORIZATION;
@@ -77,6 +78,7 @@ use tower_http::decompression::Decompression;
// Re-export data_url
pub use data_url;
+pub use proxy::basic_auth;
pub use fs_fetch_handler::FsFetchHandler;
@@ -349,7 +351,7 @@ where
};
let method = Method::from_bytes(&method)?;
- let url = Url::parse(&url)?;
+ let mut url = Url::parse(&url)?;
// Check scheme before asking for net permission
let scheme = url.scheme();
@@ -385,6 +387,7 @@ where
let permissions = state.borrow_mut::<FP>();
permissions.check_net_url(&url, "fetch()")?;
+ let maybe_authority = extract_authority(&mut url);
let uri = url
.as_str()
.parse::<Uri>()
@@ -428,6 +431,12 @@ where
*request.method_mut() = method.clone();
*request.uri_mut() = uri;
+ if let Some((username, password)) = maybe_authority {
+ request.headers_mut().insert(
+ AUTHORIZATION,
+ proxy::basic_auth(&username, password.as_deref()),
+ );
+ }
if let Some(len) = con_len {
request.headers_mut().insert(CONTENT_LENGTH, len.into());
}
@@ -1096,3 +1105,34 @@ impl Client {
pub type ReqBody = http_body_util::combinators::BoxBody<Bytes, Error>;
pub type ResBody = http_body_util::combinators::BoxBody<Bytes, Error>;
+
+/// Copied from https://github.com/seanmonstar/reqwest/blob/b9d62a0323d96f11672a61a17bf8849baec00275/src/async_impl/request.rs#L572
+/// Check the request URL for a "username:password" type authority, and if
+/// found, remove it from the URL and return it.
+pub fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
+ use percent_encoding::percent_decode;
+
+ if url.has_authority() {
+ let username: String = percent_decode(url.username().as_bytes())
+ .decode_utf8()
+ .ok()?
+ .into();
+ let password = url.password().and_then(|pass| {
+ percent_decode(pass.as_bytes())
+ .decode_utf8()
+ .ok()
+ .map(String::from)
+ });
+ if !username.is_empty() || password.is_some() {
+ url
+ .set_username("")
+ .expect("has_authority means set_username shouldn't fail");
+ url
+ .set_password(None)
+ .expect("has_authority means set_password shouldn't fail");
+ return Some((username, password));
+ }
+ }
+
+ None
+}
diff --git a/ext/fetch/proxy.rs b/ext/fetch/proxy.rs
index c8e54d5ec..bbb40e2f1 100644
--- a/ext/fetch/proxy.rs
+++ b/ext/fetch/proxy.rs
@@ -106,7 +106,7 @@ pub(crate) fn from_env() -> Proxies {
Proxies { intercepts, no }
}
-pub(crate) fn basic_auth(user: &str, pass: &str) -> HeaderValue {
+pub fn basic_auth(user: &str, pass: Option<&str>) -> HeaderValue {
use base64::prelude::BASE64_STANDARD;
use base64::write::EncoderWriter;
use std::io::Write;
@@ -114,7 +114,10 @@ pub(crate) fn basic_auth(user: &str, pass: &str) -> HeaderValue {
let mut buf = b"Basic ".to_vec();
{
let mut encoder = EncoderWriter::new(&mut buf, &BASE64_STANDARD);
- let _ = write!(encoder, "{user}:{pass}");
+ let _ = write!(encoder, "{user}:");
+ if let Some(password) = pass {
+ let _ = write!(encoder, "{password}");
+ }
}
let mut header =
HeaderValue::from_bytes(&buf).expect("base64 is always valid HeaderValue");
@@ -140,10 +143,10 @@ impl Intercept {
pub(crate) fn set_auth(&mut self, user: &str, pass: &str) {
match self.target {
Target::Http { ref mut auth, .. } => {
- *auth = Some(basic_auth(user, pass));
+ *auth = Some(basic_auth(user, Some(pass)));
}
Target::Https { ref mut auth, .. } => {
- *auth = Some(basic_auth(user, pass));
+ *auth = Some(basic_auth(user, Some(pass)));
}
Target::Socks { ref mut auth, .. } => {
*auth = Some((user.into(), pass.into()));
@@ -192,7 +195,7 @@ impl Target {
if is_socks {
socks_auth = Some((user.into(), pass.into()));
} else {
- http_auth = Some(basic_auth(user, pass));
+ http_auth = Some(basic_auth(user, Some(pass)));
}
builder = builder.authority(host_port);
} else {