Skip to content

Commit 54a5e96

Browse files
committed
Expose CURL handle to write function via Handler
1 parent 742fe88 commit 54a5e96

File tree

4 files changed

+103
-2
lines changed

4 files changed

+103
-2
lines changed

src/easy/context.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use std::fmt;
2+
3+
use super::handle::EasyData;
4+
use super::handler::Inner;
5+
#[cfg(doc)]
6+
use super::{Easy, Handler};
7+
8+
/// Provides access to the handle inside [`Handler::write`] callback.
9+
pub struct WriteContext2<H: ?Sized> {
10+
inner: *mut Inner<H>,
11+
}
12+
13+
/// Provides access to the handle inside [`Easy::write_function`] callback.
14+
#[repr(transparent)]
15+
pub struct WriteContext(WriteContext2<EasyData>);
16+
17+
impl<H: ?Sized> WriteContext2<H> {
18+
/// Returns the raw Easy pointer.
19+
#[inline]
20+
pub fn raw(&self) -> *mut curl_sys::CURL {
21+
// Safety: make sure not to borrow `inner` that would be an alias to the inner handle
22+
// activated in a Handler callback.
23+
unsafe { *std::ptr::addr_of!((*self.inner).handle) }
24+
}
25+
}
26+
27+
impl WriteContext {
28+
/// Returns the raw Easy pointer.
29+
#[inline]
30+
pub fn raw(&self) -> *mut curl_sys::CURL {
31+
self.0.raw()
32+
}
33+
34+
pub(super) fn from_mut(inner: &mut WriteContext2<EasyData>) -> &mut Self {
35+
// Safety: `inner` has repr transparent over WriteContext2<EasyData>.
36+
unsafe { std::mem::transmute::<&mut WriteContext2<EasyData>, &mut Self>(inner) }
37+
}
38+
}
39+
40+
impl<H: ?Sized> fmt::Debug for WriteContext2<H> {
41+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42+
f.debug_struct("WriteContext2").finish()
43+
}
44+
}
45+
46+
impl fmt::Debug for WriteContext {
47+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48+
f.debug_struct("WriteContext").finish()
49+
}
50+
}

src/easy/handle.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::easy::handler::{Auth, NetRc, PostRedirections, ProxyType, SslOpt};
1313
use crate::easy::handler::{HttpVersion, IpResolve, SslVersion, TimeCondition};
1414
use crate::easy::{Easy2, Handler};
1515
use crate::easy::{Form, List};
16+
use crate::easy::{WriteContext, WriteContext2};
1617
use crate::Error;
1718

1819
/// Raw bindings to a libcurl "easy session".
@@ -105,6 +106,8 @@ unsafe impl Send for EasyData {}
105106
#[derive(Default)]
106107
struct Callbacks<'a> {
107108
write: Option<Box<dyn FnMut(&[u8]) -> Result<usize, WriteError> + 'a>>,
109+
write_context:
110+
Option<Box<dyn FnMut(&[u8], &mut WriteContext) -> Result<usize, WriteError> + 'a>>,
108111
read: Option<Box<dyn FnMut(&mut [u8]) -> Result<usize, ReadError> + 'a>>,
109112
seek: Option<Box<dyn FnMut(SeekFrom) -> SeekResult + 'a>>,
110113
debug: Option<Box<dyn FnMut(InfoType, &[u8]) + 'a>>,
@@ -251,6 +254,15 @@ impl Easy {
251254
Ok(())
252255
}
253256

257+
/// Same as [`Easy::write_function`] but with access to the [`WriteContext`].
258+
pub fn write_function_with_context<F>(&mut self, f: F) -> Result<(), Error>
259+
where
260+
F: FnMut(&[u8], &mut WriteContext) -> Result<usize, WriteError> + Send + 'static,
261+
{
262+
self.inner.get_mut().owned.write_context = Some(Box::new(f));
263+
Ok(())
264+
}
265+
254266
/// Read callback for data uploads.
255267
///
256268
/// This callback function gets called by libcurl as soon as it needs to
@@ -1515,6 +1527,22 @@ impl Handler for EasyData {
15151527
}
15161528
}
15171529

1530+
fn write_context(
1531+
&mut self,
1532+
data: &[u8],
1533+
ctx: &mut WriteContext2<Self>,
1534+
) -> Result<usize, WriteError> {
1535+
unsafe {
1536+
match self.callback(|s| &mut s.write_context) {
1537+
Some(write) => write(data, WriteContext::from_mut(ctx)),
1538+
None => match self.callback(|s| &mut s.write) {
1539+
Some(write) => write(data),
1540+
None => Ok(data.len()),
1541+
},
1542+
}
1543+
}
1544+
}
1545+
15181546
fn read(&mut self, data: &mut [u8]) -> Result<usize, ReadError> {
15191547
unsafe {
15201548
match self.callback(|s| &mut s.read) {
@@ -1587,6 +1615,16 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
15871615
Ok(())
15881616
}
15891617

1618+
/// Same as `Easy::write_context_function`, just takes a non `'static`
1619+
/// lifetime corresponding to the lifetime of this transfer.
1620+
pub fn write_context_function<F>(&mut self, f: F) -> Result<(), Error>
1621+
where
1622+
F: FnMut(&[u8], &mut WriteContext) -> Result<usize, WriteError> + 'data,
1623+
{
1624+
self.data.write_context = Some(Box::new(f));
1625+
Ok(())
1626+
}
1627+
15901628
/// Same as `Easy::read_function`, just takes a non `'static` lifetime
15911629
/// corresponding to the lifetime of this transfer.
15921630
pub fn read_function<F>(&mut self, f: F) -> Result<(), Error>

src/easy/handler.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use socket2::Socket;
1515
use crate::easy::form;
1616
use crate::easy::list;
1717
use crate::easy::windows;
18+
use crate::easy::WriteContext2;
1819
use crate::easy::{Form, List};
1920
use crate::panic;
2021
use crate::Error;
@@ -81,6 +82,16 @@ pub trait Handler {
8182
Ok(data.len())
8283
}
8384

85+
/// Same as [`write`], but with a context allowing access to some functionalities within
86+
/// the callback and the raw handle.
87+
fn write_context(
88+
&mut self,
89+
data: &[u8],
90+
_ctx: &mut WriteContext2<Self>,
91+
) -> Result<usize, WriteError> {
92+
self.write(data)
93+
}
94+
8495
/// Read callback for data uploads.
8596
///
8697
/// This callback function gets called by libcurl as soon as it needs to
@@ -379,8 +390,8 @@ pub struct Easy2<H> {
379390
inner: Box<Inner<H>>,
380391
}
381392

382-
struct Inner<H> {
383-
handle: *mut curl_sys::CURL,
393+
pub(super) struct Inner<H: ?Sized> {
394+
pub(super) handle: *mut curl_sys::CURL,
384395
header_list: Option<List>,
385396
resolve_list: Option<List>,
386397
connect_to_list: Option<List>,

src/easy/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
//! Most simple usage of libcurl will likely use the `Easy` structure here, and
88
//! you can find more docs about its usage on that struct.
99
10+
mod context;
1011
mod form;
1112
mod handle;
1213
mod handler;
1314
mod list;
1415
mod windows;
1516

17+
pub use self::context::{WriteContext, WriteContext2};
1618
pub use self::form::{Form, Part};
1719
pub use self::handle::{Easy, Transfer};
1820
pub use self::handler::{Auth, NetRc, PostRedirections, ProxyType, SslOpt};

0 commit comments

Comments
 (0)