From 76202e772aa264100e4f666fbdf1ffffd30c2bee Mon Sep 17 00:00:00 2001 From: bdbai Date: Sun, 15 Jun 2025 15:40:47 +0800 Subject: [PATCH] Expose open_socket callback to Easy --- src/easy/handle.rs | 37 +++++++++++++++++++++++++++++++++++++ src/easy/handler.rs | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/easy/handle.rs b/src/easy/handle.rs index 6d074be1f..77107b803 100644 --- a/src/easy/handle.rs +++ b/src/easy/handle.rs @@ -6,6 +6,7 @@ use std::ptr; use std::str; use std::time::Duration; +use libc::c_int; use libc::c_void; use crate::easy::handler::{self, InfoType, ReadError, SeekResult, WriteError}; @@ -111,6 +112,7 @@ struct Callbacks<'a> { header: Option bool + 'a>>, progress: Option bool + 'a>>, ssl_ctx: Option Result<(), Error> + 'a>>, + open_socket: Option Option>>, } impl Easy { @@ -542,6 +544,27 @@ impl Easy { Ok(()) } + /// Callback to open sockets for libcurl. + /// + /// This callback function gets called by libcurl instead of the socket(2) + /// call. The callback function should return the newly created socket + /// or `None` in case no connection could be established or another + /// error was detected. Any additional `setsockopt(2)` calls can of course + /// be done on the socket at the user's discretion. A `None` return + /// value from the callback function will signal an unrecoverable error to + /// libcurl and it will return `is_couldnt_connect` from the function that + /// triggered this callback. + /// + /// By default this function opens a standard socket and + /// corresponds to `CURLOPT_OPENSOCKETFUNCTION `. + pub fn open_socket_function(&mut self, f: F) -> Result<(), Error> + where + F: FnMut(c_int, c_int, c_int) -> Option + Send + 'static, + { + self.inner.get_mut().owned.open_socket = Some(Box::new(f)); + Ok(()) + } + // ========================================================================= // Error options @@ -1568,6 +1591,20 @@ impl Handler for EasyData { } } } + + fn open_socket( + &mut self, + family: c_int, + socktype: c_int, + protocol: c_int, + ) -> Option { + unsafe { + match self.callback(|s| &mut s.open_socket) { + Some(open_socket) => open_socket(family, socktype, protocol), + None => handler::open_socket(family, socktype, protocol), + } + } + } } impl fmt::Debug for EasyData { diff --git a/src/easy/handler.rs b/src/easy/handler.rs index 66185cc55..85c08f134 100644 --- a/src/easy/handler.rs +++ b/src/easy/handler.rs @@ -278,24 +278,7 @@ pub trait Handler { socktype: c_int, protocol: c_int, ) -> Option { - // Note that we override this to calling a function in `socket2` to - // ensure that we open all sockets with CLOEXEC. Otherwise if we rely on - // libcurl to open sockets it won't use CLOEXEC. - return Socket::new(family.into(), socktype.into(), Some(protocol.into())) - .ok() - .map(cvt); - - #[cfg(unix)] - fn cvt(socket: Socket) -> curl_sys::curl_socket_t { - use std::os::unix::prelude::*; - socket.into_raw_fd() - } - - #[cfg(windows)] - fn cvt(socket: Socket) -> curl_sys::curl_socket_t { - use std::os::windows::prelude::*; - socket.into_raw_socket() - } + open_socket(family, socktype, protocol) } } @@ -321,6 +304,31 @@ pub fn ssl_ctx(cx: *mut c_void) -> Result<(), Error> { Ok(()) } +pub fn open_socket( + family: c_int, + socktype: c_int, + protocol: c_int, +) -> Option { + // Note that we override this to calling a function in `socket2` to + // ensure that we open all sockets with CLOEXEC. Otherwise if we rely on + // libcurl to open sockets it won't use CLOEXEC. + return Socket::new(family.into(), socktype.into(), Some(protocol.into())) + .ok() + .map(cvt); + + #[cfg(unix)] + fn cvt(socket: Socket) -> curl_sys::curl_socket_t { + use std::os::unix::prelude::*; + socket.into_raw_fd() + } + + #[cfg(windows)] + fn cvt(socket: Socket) -> curl_sys::curl_socket_t { + use std::os::windows::prelude::*; + socket.into_raw_socket() + } +} + /// Raw bindings to a libcurl "easy session". /// /// This type corresponds to the `CURL` type in libcurl, and is probably what