diff --git a/src/easy/handler.rs b/src/easy/handler.rs index 3a5517ba9..61b3b5bc8 100644 --- a/src/easy/handler.rs +++ b/src/easy/handler.rs @@ -16,7 +16,7 @@ use socket2::Socket; use crate::easy::form; use crate::easy::list; #[cfg(feature = "mime")] -use crate::easy::mime::Mime; +use crate::easy::mime::{Mime, MimeHandle}; use crate::easy::windows; use crate::easy::{Form, List}; use crate::panic; @@ -380,9 +380,6 @@ pub fn ssl_ctx(cx: *mut c_void) -> Result<(), Error> { /// ``` pub struct Easy2 { inner: Box>, - #[cfg(feature = "mime")] - /// Mime handles to free upon drop - mimes: Vec<*mut curl_sys::curl_mime>, } struct Inner { @@ -393,6 +390,9 @@ struct Inner { form: Option
, error_buf: RefCell>, handler: H, + #[cfg(feature = "mime")] + /// [MimeHandle] object to drop when it's safe + mime: Option, } unsafe impl Send for Inner {} @@ -603,9 +603,9 @@ impl Easy2 { form: None, error_buf: RefCell::new(vec![0; curl_sys::CURL_ERROR_SIZE]), handler, + #[cfg(feature = "mime")] + mime: Default::default(), }), - #[cfg(feature = "mime")] - mimes: vec![], }; ret.default_configure(); ret @@ -3525,12 +3525,16 @@ impl Easy2 { Mime::new(self) } - pub(crate) fn mimepost(&mut self, mime_handle: *mut curl_sys::curl_mime) -> Result<(), Error> { - self.mimes.push(mime_handle); + pub(super) fn mimepost(&mut self, mime: MimeHandle) -> Result<(), Error> { + assert!(self.inner.mime.is_none()); + + let rc = + unsafe { curl_sys::curl_easy_setopt(self.raw(), curl_sys::CURLOPT_MIMEPOST, mime.0) }; + + if rc == curl_sys::CURLE_OK { + self.inner.mime = Some(mime); + } - let rc = unsafe { - curl_sys::curl_easy_setopt(self.raw(), curl_sys::CURLOPT_MIMEPOST, mime_handle) - }; self.cvt(rc) } } @@ -3548,11 +3552,6 @@ impl Drop for Easy2 { fn drop(&mut self) { unsafe { curl_sys::curl_easy_cleanup(self.inner.handle); - - #[cfg(feature = "mime")] - for &mime_handle in self.mimes.iter() { - curl_sys::curl_mime_free(mime_handle); - } } } } diff --git a/src/easy/mime.rs b/src/easy/mime.rs index b038977f2..c1f49a484 100644 --- a/src/easy/mime.rs +++ b/src/easy/mime.rs @@ -1,35 +1,49 @@ use crate::easy::Easy2; use crate::error::Error; use curl_sys::{ - curl_mime_addpart, curl_mime_data, curl_mime_filename, curl_mime_free, curl_mime_init, - curl_mime_name, curl_mime_type, curl_mimepart, CURLcode, CURLE_OK, + curl_mime, curl_mime_addpart, curl_mime_data, curl_mime_filename, curl_mime_free, + curl_mime_init, curl_mime_name, curl_mime_type, curl_mimepart, CURLcode, CURL, CURLE_OK, }; use std::ffi::CString; use std::marker::PhantomData; -use std::ptr::null_mut; + +#[derive(Debug)] +pub(super) struct MimeHandle(pub *mut curl_mime); + +impl MimeHandle { + fn new(easy: *mut CURL) -> Self { + let handle = unsafe { curl_mime_init(easy) }; + assert!(!handle.is_null()); + + Self(handle) + } +} + +impl Drop for MimeHandle { + fn drop(&mut self) { + unsafe { curl_mime_free(self.0) } + } +} #[derive(Debug)] pub struct Mime<'e, E> { - handle: *mut curl_sys::curl_mime, + pub(super) handle: MimeHandle, easy: &'e mut Easy2, } impl<'a, T> Mime<'a, T> { /// Create a mime handle - pub(crate) fn new(easy: &'a mut Easy2) -> Self { - let handle = unsafe { curl_mime_init(easy.raw()) }; - assert!(!handle.is_null()); + pub(super) fn new(easy: &'a mut Easy2) -> Self { + let handle = MimeHandle::new(easy.raw()); Self { handle, easy } } /// Finalize creation of a mime post. - pub fn post(mut self) -> Result<(), Error> { - // once giving the mime handle to `Easy2` it is now their responsibility to free the handle. - // so we need to make sure `Drop` below won't try to free it. - let mime_handle = self.handle; - self.handle = null_mut(); - self.easy.mimepost(mime_handle) + pub fn post(self) -> Result<(), Error> { + // We give ownership on `MimeHandle` to `Easy2`. `Easy2` will keep record of this object + // until it is safe to free (drop) it. + self.easy.mimepost(self.handle) } /// Append a new empty part to a mime structure @@ -38,15 +52,6 @@ impl<'a, T> Mime<'a, T> { } } -impl Drop for Mime<'_, E> { - fn drop(&mut self) { - // we only need to free mime handles which hadn't been given to the ownership of `Easy2`. - if !self.handle.is_null() { - unsafe { curl_mime_free(self.handle) } - } - } -} - #[derive(Debug)] pub struct MimePart<'a> { handle: *mut curl_mimepart, @@ -56,7 +61,7 @@ pub struct MimePart<'a> { impl<'a> MimePart<'a> { fn new(mime: &mut Mime) -> Self { - let handle = unsafe { curl_mime_addpart(mime.handle) }; + let handle = unsafe { curl_mime_addpart(mime.handle.0) }; assert!(!handle.is_null()); Self { @@ -110,3 +115,33 @@ fn code_ok(code: CURLcode) -> Result<(), Error> { Err(Error::new(code)) } } + +#[cfg(test)] +mod tests { + use crate::easy::Easy; + + /// Trivial test which checks that objects can be used as planned. + #[test] + fn test_ownership() { + let mut easy = Easy::new(); + let mut mime = easy.add_mime(); + + for i in 1..5 { + let name = format!("name{i}"); + let data = format!("data{i}"); + let fname = format!("fname{i}"); + + mime.add_part() + .set_data(name) + .unwrap() + .set_data(data) + .unwrap() + .set_filename(&fname) + .unwrap() + .set_content_type("plain/text") + .unwrap(); + } + + mime.post().unwrap(); + } +}