fix: send http requests in origin not absolute form

Absolute form is meant for proxies and for example
nginx rejects it with 400 if the host contains an underscore, breaking autoconfig discovery.
This commit is contained in:
holger krekel
2026-08-14 12:09:24 +02:00
parent 0f8c91570b
commit ce7c8dbca1
3 changed files with 28 additions and 8 deletions
@@ -363,10 +363,7 @@ def remote_bob_loop(channel):
# ACFactory would configure from a "dcaccount" QR, # ACFactory would configure from a "dcaccount" QR,
# which old cores cannot use on underscore domains # which old cores cannot use on underscore domains
bob = dc.add_account() bob = dc.add_account()
bob.set_config_from_qr(dclogin_qr) bob.add_transport_from_qr(dclogin_qr)
if not bob.is_configured():
# cores <=2.22 only store login values from a "dclogin" QR
bob.configure()
bob.bring_online() bob.bring_online()
alice_vcard = channel.receive() alice_vcard = channel.receive()
@@ -45,7 +45,7 @@ def test_qr_setup_contact(acfactory, alice_and_remote_bob, version) -> None:
def test_send_and_receive_message(alice_and_remote_bob) -> None: def test_send_and_receive_message(alice_and_remote_bob) -> None:
"""Test other-core Bob profile can send a message to Alice on current core.""" """Test other-core Bob profile can send a message to Alice on current core."""
alice, alice_contact_bob, remote_eval = alice_and_remote_bob("2.20.0") alice, alice_contact_bob, remote_eval = alice_and_remote_bob("2.23.0")
remote_eval("bob_contact_alice.create_chat().send_text('hello')") remote_eval("bob_contact_alice.create_chat().send_text('hello')")
@@ -55,7 +55,7 @@ def test_send_and_receive_message(alice_and_remote_bob) -> None:
def test_second_device(acfactory, alice_and_remote_bob) -> None: def test_second_device(acfactory, alice_and_remote_bob) -> None:
"""Test setting up current version as a second device for old version.""" """Test setting up current version as a second device for old version."""
_alice, alice_contact_bob, remote_eval = alice_and_remote_bob("2.20.0") _alice, alice_contact_bob, remote_eval = alice_and_remote_bob("2.23.0")
remote_eval("locals().setdefault('future', bob._rpc.provide_backup.future(bob.id))") remote_eval("locals().setdefault('future', bob._rpc.provide_backup.future(bob.id))")
qr = remote_eval("bob._rpc.get_backup_qr(bob.id)") qr = remote_eval("bob._rpc.get_backup_qr(bob.id)")
+25 -2
View File
@@ -258,6 +258,15 @@ pub(crate) async fn http_cache_cleanup(context: &Context) -> Result<()> {
Ok(()) Ok(())
} }
/// Returns the request target in origin form, i.e. the path and query of `url`.
///
/// The absolute form is only for proxy requests and
/// nginx rejects it if the host starts contains an underscore.
fn origin_form(url: &hyper::Uri) -> &str {
url.path_and_query()
.map_or("/", |path_and_query| path_and_query.as_str())
}
/// Fetches URL and updates the cache. /// Fetches URL and updates the cache.
/// ///
/// URL is fetched regardless of whether there is an existing result in the cache. /// URL is fetched regardless of whether there is an existing result in the cache.
@@ -276,7 +285,7 @@ async fn fetch_url(context: &Context, original_url: &str, strict_tls: bool) -> R
.context("URL has no authority")? .context("URL has no authority")?
.clone(); .clone();
let req = hyper::Request::builder().uri(parsed_url); let req = hyper::Request::builder().uri(origin_form(&parsed_url));
// OSM usage policy requires // OSM usage policy requires
// that User-Agent is set for HTTP GET requests // that User-Agent is set for HTTP GET requests
@@ -409,7 +418,7 @@ pub(crate) async fn post_empty(context: &Context, url: &str) -> Result<(String,
.authority() .authority()
.context("URL has no authority")? .context("URL has no authority")?
.clone(); .clone();
let req = hyper::Request::post(parsed_url) let req = hyper::Request::post(origin_form(&parsed_url))
.header(hyper::header::HOST, authority.as_str()) .header(hyper::header::HOST, authority.as_str())
.body(http_body_util::Empty::<Bytes>::new())?; .body(http_body_util::Empty::<Bytes>::new())?;
@@ -432,6 +441,20 @@ mod tests {
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
use crate::tools::SystemTime; use crate::tools::SystemTime;
#[test]
fn test_origin_form() {
let url = "https://_cm0.localchat/autoconfig?emailaddress=x%40_cm0.localchat"
.parse()
.unwrap();
assert_eq!(
origin_form(&url),
"/autoconfig?emailaddress=x%40_cm0.localchat"
);
let url = "https://example.org".parse().unwrap();
assert_eq!(origin_form(&url), "/");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_http_cache() -> Result<()> { async fn test_http_cache() -> Result<()> {
let t = &TestContext::new().await; let t = &TestContext::new().await;