From 271e67a450557a393a9953885a2ce9a0e937f218 Mon Sep 17 00:00:00 2001 From: CarloTUM Date: Sun, 11 Oct 2026 00:16:11 +0200 Subject: [PATCH] Expose CURLOPT_XOAUTH2_BEARER and CURLOPT_NETRC_FILE oauth2_bearer(token) plus Auth::bearer let libcurl send "Authorization: Bearer" itself instead of callers composing the header; netrc_file(path) points netrc at a file other than ~/.netrc, which is what tests and tools with their own config directory need. Both were already in libcurl; the sys constants existed (one commented out). Tests check the header on the wire. --- curl-sys/lib.rs | 3 ++- src/easy/handle.rs | 10 ++++++++++ src/easy/handler.rs | 25 +++++++++++++++++++++++++ tests/easy.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 5e5b10c28..f5ab649cb 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -310,6 +310,7 @@ pub const CURLAUTH_GSSNEGOTIATE: c_ulong = 1 << 2; pub const CURLAUTH_NTLM: c_ulong = 1 << 3; pub const CURLAUTH_DIGEST_IE: c_ulong = 1 << 4; pub const CURLAUTH_NTLM_WB: c_ulong = 1 << 5; +pub const CURLAUTH_BEARER: c_ulong = 1 << 6; pub const CURLAUTH_AWS_SIGV4: c_ulong = 1 << 7; pub const CURLAUTH_HTTPSIG: c_ulong = 1 << 8; // pub const CURLAUTH_ONLY: c_ulong = 1 << 31; @@ -588,7 +589,7 @@ pub const CURLOPT_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 216; // pub const CURLOPT_MAIL_AUTH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 217; // pub const CURLOPT_SASL_IR: CURLoption = CURLOPTTYPE_LONG + 218; // pub const CURLOPT_XFERINFOFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 219; -// pub const CURLOPT_XOAUTH2_BEARER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 220; +pub const CURLOPT_XOAUTH2_BEARER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 220; // pub const CURLOPT_DNS_INTERFACE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 221; // pub const CURLOPT_DNS_LOCAL_IP4: CURLoption = CURLOPTTYPE_OBJECTPOINT + 222; // pub const CURLOPT_DNS_LOCAL_IP6: CURLoption = CURLOPTTYPE_OBJECTPOINT + 223; diff --git a/src/easy/handle.rs b/src/easy/handle.rs index 10bcce350..267806d18 100644 --- a/src/easy/handle.rs +++ b/src/easy/handle.rs @@ -768,6 +768,16 @@ impl Easy { self.inner.netrc(netrc) } + /// Same as [`Easy2::netrc_file`](struct.Easy2.html#method.netrc_file) + pub fn netrc_file>(&mut self, path: P) -> Result<(), Error> { + self.inner.netrc_file(path) + } + + /// Same as [`Easy2::oauth2_bearer`](struct.Easy2.html#method.oauth2_bearer) + pub fn oauth2_bearer(&mut self, token: &str) -> Result<(), Error> { + self.inner.oauth2_bearer(token) + } + // ========================================================================= // HTTP Options diff --git a/src/easy/handler.rs b/src/easy/handler.rs index d9aa7b6ff..e851eef01 100644 --- a/src/easy/handler.rs +++ b/src/easy/handler.rs @@ -1409,6 +1409,25 @@ impl Easy2 { self.setopt_long(curl_sys::CURLOPT_NETRC, netrc as c_long) } + /// The file to read netrc credentials from, instead of `~/.netrc`. + /// + /// Only used when [`netrc`](Easy2::netrc) is not `NetRc::Ignored`. By + /// default this value is not set and corresponds to `CURLOPT_NETRC_FILE`. + pub fn netrc_file>(&mut self, path: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_NETRC_FILE, path.as_ref()) + } + + /// The OAuth 2.0 bearer token to authenticate with. + /// + /// For HTTP, enable [`Auth::bearer`] on [`http_auth`](Easy2::http_auth) + /// as well; libcurl then sends it as `Authorization: Bearer `. + /// By default this value is not set and corresponds to + /// `CURLOPT_XOAUTH2_BEARER`. + pub fn oauth2_bearer(&mut self, token: &str) -> Result<(), Error> { + let token = CString::new(token)?; + self.setopt_str(curl_sys::CURLOPT_XOAUTH2_BEARER, &token) + } + // ========================================================================= // HTTP Options @@ -3780,6 +3799,12 @@ impl Auth { self.flag(curl_sys::CURLAUTH_NTLM, on) } + /// HTTP Bearer token authentication, with the token from + /// [`oauth2_bearer`](Easy2::oauth2_bearer). + pub fn bearer(&mut self, on: bool) -> &mut Auth { + self.flag(curl_sys::CURLAUTH_BEARER, on) + } + /// NTLM delegating to winbind helper. /// /// Authentication is performed by a separate binary application that is diff --git a/tests/easy.rs b/tests/easy.rs index b3fde1828..cef070258 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -1035,3 +1035,47 @@ fn test_timeout() { .unwrap() ); } + +#[test] +fn bearer_token() { + let s = Server::new(); + s.receive( + "\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:$PORT\r\n\ + Authorization: Bearer t0k\r\n\ + Accept: */*\r\n\ + \r\n", + ); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + t!(handle.url(&s.url("/"))); + t!(handle.oauth2_bearer("t0k")); + t!(handle.http_auth(curl::easy::Auth::new().bearer(true))); + t!(handle.perform()); +} + +#[test] +fn netrc_file() { + let netrc = std::env::temp_dir().join(format!("curl-rust-netrc-{}", std::process::id())); + std::fs::write(&netrc, "machine 127.0.0.1 login nu password np\n").unwrap(); + + let s = Server::new(); + s.receive( + "\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:$PORT\r\n\ + Authorization: Basic bnU6bnA=\r\n\ + Accept: */*\r\n\ + \r\n", + ); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + t!(handle.url(&s.url("/"))); + t!(handle.netrc(curl::easy::NetRc::Required)); + t!(handle.netrc_file(&netrc)); + t!(handle.perform()); + std::fs::remove_file(&netrc).unwrap(); +}