From 1275fb7016d52af7c2360248ec46afee4ef6c08d Mon Sep 17 00:00:00 2001 From: ldm0 Date: Mon, 19 Oct 2026 02:44:35 +0800 Subject: [PATCH] chore(deps): upgrade curl bindings and bundle libcurl 8.23 Pin the fork rebased onto upstream curl-rust 0.4.51, including the native pre-request API and libcurl 8.23 with the upstream connect-only eviction fix. Enable the upstream apple-sectrust feature explicitly in moli-curl. Keep Moli proxy endpoints explicit while adapting transport comparisons to libcurl's implicit HTTP port and hostname validation. Update form wire request assertions to preserve the empty query already in navigation URLs. Validated workspace formatting, warnings-as-errors Clippy, and all 20,342 nextest cases (18 skipped). The curl fork passed Linux/macOS/Windows CI. --- Cargo.lock | 12 ++--- Cargo.toml | 4 +- moli-curl/Cargo.toml | 4 +- moli-curl/src/proxy.rs | 48 ++++++++++++------- moli-fetch/Cargo.toml | 4 +- moli-renderer-v8/Cargo.toml | 2 +- .../src/script_vm/tests/dom_xhr/forms.rs | 4 +- 7 files changed, 46 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e24d69a60d..a45559008f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -923,8 +923,8 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.49" -source = "git+https://github.com/lexmount/curl-rust?rev=dc8c170e4370772c0085c08a67dda0fa86efca70#dc8c170e4370772c0085c08a67dda0fa86efca70" +version = "0.4.51" +source = "git+https://github.com/lexmount/curl-rust?rev=9d40ca90d8f19ccfd11afe7dd49bc32aab79435c#9d40ca90d8f19ccfd11afe7dd49bc32aab79435c" dependencies = [ "curl-sys", "libc", @@ -932,13 +932,13 @@ dependencies = [ "openssl-sys", "schannel", "socket2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "curl-sys" -version = "0.4.87+curl-8.19.0" -source = "git+https://github.com/lexmount/curl-rust?rev=dc8c170e4370772c0085c08a67dda0fa86efca70#dc8c170e4370772c0085c08a67dda0fa86efca70" +version = "0.4.91+curl-8.23.0" +source = "git+https://github.com/lexmount/curl-rust?rev=9d40ca90d8f19ccfd11afe7dd49bc32aab79435c#9d40ca90d8f19ccfd11afe7dd49bc32aab79435c" dependencies = [ "brotlic-sys", "cc", @@ -948,7 +948,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d870d4d9d9..d44af54335 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,8 +104,8 @@ fontique = { git = "https://github.com/lexmount/parley", rev = "a18cfc3ba6638d44 subsetter = { git = "https://github.com/lexmount/subsetter", rev = "8d3815245835af309559c3b4a872d6a0a54913a9" } url = { git = "https://github.com/lexmount/rust-url", rev = "ca9fcad2b99f20c30c28a618ad78410f6fd31161" } xml5ever = { path = "vendor/xml5ever-0.39.0" } -curl = { git = "https://github.com/lexmount/curl-rust", rev = "dc8c170e4370772c0085c08a67dda0fa86efca70" } -curl-sys = { git = "https://github.com/lexmount/curl-rust", rev = "dc8c170e4370772c0085c08a67dda0fa86efca70" } +curl = { git = "https://github.com/lexmount/curl-rust", rev = "9d40ca90d8f19ccfd11afe7dd49bc32aab79435c" } +curl-sys = { git = "https://github.com/lexmount/curl-rust", rev = "9d40ca90d8f19ccfd11afe7dd49bc32aab79435c" } cookie = { git = "https://github.com/ldm0/cookie-rs", branch = "priority" } v8 = { path = "vendor/v8-152.2.0" } deno_v8 = { path = "vendor/deno_v8-0.3.0" } diff --git a/moli-curl/Cargo.toml b/moli-curl/Cargo.toml index 3f02f95d01..c8bb97b3fd 100644 --- a/moli-curl/Cargo.toml +++ b/moli-curl/Cargo.toml @@ -8,8 +8,8 @@ edition = "2024" anyhow = "1.0.100" cidr = "0.3.2" crossbeam-channel = "0.5.15" -curl = { version = "0.4.49", default-features = false, features = ["brotli", "http2", "poll_7_68_0", "ssl", "static-curl", "websocket"] } -curl-sys = { version = "0.4.87", default-features = false } +curl = { version = "0.4.51", default-features = false, features = ["apple-sectrust", "brotli", "http2", "poll_7_68_0", "ssl", "static-curl", "websocket"] } +curl-sys = { version = "0.4.91", default-features = false } moli-header-field = { path = "../moli-header-field" } moli-dns-resolver = { path = "../moli-dns-resolver" } openssl-sys = { version = "0.9", features = ["aws-lc"] } diff --git a/moli-curl/src/proxy.rs b/moli-curl/src/proxy.rs index 0288698d27..b2bf697ada 100644 --- a/moli-curl/src/proxy.rs +++ b/moli-curl/src/proxy.rs @@ -88,9 +88,10 @@ impl SelectedProxy { if url.host().is_none() { bail!("proxy URL `{raw}` is missing a host"); } - // libcurl's parse_proxy() defaults to 443 for HTTPS and 1080 for - // HTTP/SOCKS (see CURLOPT_PROXYPORT). Read the explicit port from the - // original authority because `url::Url` deliberately drops explicit + // Keep Moli's default ports: 443 for HTTPS and 1080 for HTTP/SOCKS. + // Newer libcurl uses scheme defaults for implicit ports (80 for HTTP), + // so pass our chosen endpoint explicitly below. Read explicit ports + // from the original authority because `url::Url` deliberately drops // default ports such as `:80` and `:443`. let default_port = match scheme { ProxyScheme::Https => 443, @@ -574,7 +575,16 @@ mod tests { .append(&format!("{host}:{origin_port}:127.0.0.1")) .unwrap(); easy.resolve(resolve).unwrap(); - easy.perform().unwrap(); + let result = easy.perform(); + // Newer libcurl rejects a hostname with two trailing dots + // before applying proxy routing. Keep that case in the matrix + // without comparing NO_PROXY decisions for an invalid URL. + if host == "api.example.test.." { + let error = result.expect_err("double trailing dots are rejected"); + assert!(error.is_url_malformed(), "{error}"); + continue; + } + result.unwrap_or_else(|error| panic!("{host}, NO_PROXY={no_proxy:?}: {error}")); let connected_port = easy.primary_port().unwrap(); assert!(connected_port == origin_port || connected_port == proxy_port); let curl_uses_proxy = connected_port == proxy_port; @@ -727,32 +737,36 @@ mod tests { } } - for raw in [ - "http://proxy.invalid", - "https://proxy.invalid", - "http://proxy.invalid:80", - "https://proxy.invalid:443", - "http://proxy.invalid:8080", - "https://proxy.invalid:8443", - "socks5h://proxy.invalid", - "socks4a://proxy.invalid", + for (raw, curl_port) in [ + ("http://proxy.invalid", 80), + ("https://proxy.invalid", 443), + ("http://proxy.invalid:80", 80), + ("https://proxy.invalid:443", 443), + ("http://proxy.invalid:8080", 8080), + ("https://proxy.invalid:8443", 8443), + ("socks5h://proxy.invalid", 1080), + ("socks4a://proxy.invalid", 1080), ] { let proxy = SelectedProxy::parse(raw).unwrap(); - for curl_proxy in [raw, proxy.curl_url()] { + // libcurl's implicit HTTP port is 80; Moli deliberately preserves + // 1080 by normalizing the proxy URL to an explicit port. Check the + // raw transport behavior and the normalized Moli endpoint. + for (curl_proxy, port) in [(raw, curl_port), (proxy.curl_url(), proxy.endpoint_port())] + { let mut easy = Easy2::new(RefuseSockets::default()); easy.url("http://origin.invalid/proxy-port").unwrap(); easy.proxy(curl_proxy).unwrap(); easy.noproxy("").unwrap(); easy.timeout(Duration::from_secs(1)).unwrap(); let mut dns = crate::CurlDnsResolution::resolve_endpoint( - DnsTarget::new(proxy.endpoint_host(), proxy.endpoint_port()), + DnsTarget::new(proxy.endpoint_host(), port), Vec::new(), ); dns.install(&mut easy, &[IpAddr::from([127, 0, 0, 1])]) .unwrap(); - // Let the linked libcurl interpret both URLs. Only Moli's - // chosen port has a DNS pin for this reserved hostname, so + // Let the linked libcurl interpret both URLs. Only the + // expected port has a DNS pin for this reserved hostname, so // reaching open_socket proves that libcurl selected it too. // Refuse the socket before any connection or TLS handshake. let error = easy.perform().expect_err("test refuses every socket"); diff --git a/moli-fetch/Cargo.toml b/moli-fetch/Cargo.toml index 654074764b..a048d19183 100644 --- a/moli-fetch/Cargo.toml +++ b/moli-fetch/Cargo.toml @@ -13,8 +13,8 @@ moli-header-field = { path = "../moli-header-field" } anyhow = "1.0.100" cidr = "0.3.2" crossbeam-channel = "0.5.15" -curl = { version = "0.4.49", default-features = false, features = ["brotli", "http2", "poll_7_68_0", "ssl", "static-curl"] } -curl-sys = { version = "0.4.87", default-features = false } +curl = { version = "0.4.51", default-features = false, features = ["brotli", "http2", "poll_7_68_0", "ssl", "static-curl"] } +curl-sys = { version = "0.4.91", default-features = false } http = "1" moli-browser-profile = { path = "../moli-browser-profile" } moli-cookie-jar = { path = "../moli-cookie-jar" } diff --git a/moli-renderer-v8/Cargo.toml b/moli-renderer-v8/Cargo.toml index 7e194383ba..da507adf0d 100644 --- a/moli-renderer-v8/Cargo.toml +++ b/moli-renderer-v8/Cargo.toml @@ -93,7 +93,7 @@ moli-webmcp = { path = "../moli-webmcp" } moli-window-features = { path = "../moli-window-features" } moli-websocket = { path = "../moli-websocket" } moli-xpath = { path = "../moli-xpath" } -curl = { version = "0.4.49", default-features = false, features = ["brotli", "http2", "poll_7_68_0", "ssl", "static-curl"] } +curl = { version = "0.4.51", default-features = false, features = ["brotli", "http2", "poll_7_68_0", "ssl", "static-curl"] } http = "1" httpdate = "1.0.3" html-escape = "0.2.13" diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/forms.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/forms.rs index 02645fd6df..7ef1ba92b7 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/forms.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/forms.rs @@ -2559,7 +2559,7 @@ async fn child_click_uses_pre_dispatch_button_activation_target_after_dom_remova ); assert_eq!( server.finish_targets().await, - vec!["/path/does_not_exist.html"] + vec!["/path/does_not_exist.html?"] ); } @@ -2818,7 +2818,7 @@ async fn programmatic_form_submit_keeps_successive_distinct_child_targets() { assert!( requests .iter() - .all(|request| request == "GET /path/formaction.html HTTP/1.1"), + .all(|request| request == "GET /path/formaction.html? HTTP/1.1"), "unexpected multi-target form requests: {requests:?}" ); }