Skip to content

Commit 9da634e

Browse files
committed
Add support for post_fields without buffer copy
1 parent 33b7a0b commit 9da634e

File tree

3 files changed

+121
-12
lines changed

3 files changed

+121
-12
lines changed

src/easy/handle.rs

+47-11
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,14 @@ pub struct Easy {
9090
/// The callbacks attached to a `Transfer` are only active for that one transfer
9191
/// object, and they allow to elide both the `Send` and `'static` bounds to
9292
/// close over stack-local information.
93+
///
94+
/// Likewise, the post_data attached to a `Transfer` are only active for that one
95+
/// transfer object, and they allow to elide both the `Send` and `'static` bounds
96+
/// to close over stack-local information.
9397
pub struct Transfer<'easy, 'data> {
9498
easy: &'easy mut Easy,
95-
data: Box<Callbacks<'data>>,
99+
callbacks: Box<Callbacks<'data>>,
100+
is_postfields: Cell<bool>,
96101
}
97102

98103
pub struct EasyData {
@@ -719,6 +724,11 @@ impl Easy {
719724
self.inner.post_fields_copy(data)
720725
}
721726

727+
/// Same as [`Easy2::post_field`](struct.Easy2.html#method.post_field)
728+
pub fn post_fields(&mut self, data: &'static [u8]) -> Result<(), Error> {
729+
self.inner.post_fields(data)
730+
}
731+
722732
/// Same as [`Easy2::post_field_size`](struct.Easy2.html#method.post_field_size)
723733
pub fn post_field_size(&mut self, size: u64) -> Result<(), Error> {
724734
self.inner.post_field_size(size)
@@ -1216,8 +1226,9 @@ impl Easy {
12161226
pub fn transfer<'data, 'easy>(&'easy mut self) -> Transfer<'easy, 'data> {
12171227
assert!(!self.inner.get_ref().running.get());
12181228
Transfer {
1219-
data: Box::new(Callbacks::default()),
1229+
callbacks: Box::new(Callbacks::default()),
12201230
easy: self,
1231+
is_postfields: Cell::new(false),
12211232
}
12221233
}
12231234

@@ -1379,7 +1390,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
13791390
where
13801391
F: FnMut(&[u8]) -> Result<usize, WriteError> + 'data,
13811392
{
1382-
self.data.write = Some(Box::new(f));
1393+
self.callbacks.write = Some(Box::new(f));
13831394
Ok(())
13841395
}
13851396

@@ -1389,7 +1400,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
13891400
where
13901401
F: FnMut(&mut [u8]) -> Result<usize, ReadError> + 'data,
13911402
{
1392-
self.data.read = Some(Box::new(f));
1403+
self.callbacks.read = Some(Box::new(f));
13931404
Ok(())
13941405
}
13951406

@@ -1399,7 +1410,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
13991410
where
14001411
F: FnMut(SeekFrom) -> SeekResult + 'data,
14011412
{
1402-
self.data.seek = Some(Box::new(f));
1413+
self.callbacks.seek = Some(Box::new(f));
14031414
Ok(())
14041415
}
14051416

@@ -1409,7 +1420,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14091420
where
14101421
F: FnMut(f64, f64, f64, f64) -> bool + 'data,
14111422
{
1412-
self.data.progress = Some(Box::new(f));
1423+
self.callbacks.progress = Some(Box::new(f));
14131424
Ok(())
14141425
}
14151426

@@ -1419,7 +1430,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14191430
where
14201431
F: FnMut(*mut c_void) -> Result<(), Error> + Send + 'data,
14211432
{
1422-
self.data.ssl_ctx = Some(Box::new(f));
1433+
self.callbacks.ssl_ctx = Some(Box::new(f));
14231434
Ok(())
14241435
}
14251436

@@ -1429,7 +1440,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14291440
where
14301441
F: FnMut(InfoType, &[u8]) + 'data,
14311442
{
1432-
self.data.debug = Some(Box::new(f));
1443+
self.callbacks.debug = Some(Box::new(f));
14331444
Ok(())
14341445
}
14351446

@@ -1439,7 +1450,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14391450
where
14401451
F: FnMut(&[u8]) -> bool + 'data,
14411452
{
1442-
self.data.header = Some(Box::new(f));
1453+
self.callbacks.header = Some(Box::new(f));
14431454
Ok(())
14441455
}
14451456

@@ -1454,7 +1465,7 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14541465
// This should be ok, however, because `do_perform` checks for recursive
14551466
// invocations of `perform` and disallows them. Our type also isn't
14561467
// `Sync`.
1457-
inner.borrowed.set(&*self.data as *const _ as *mut _);
1468+
inner.borrowed.set(&*self.callbacks as *const _ as *mut _);
14581469

14591470
// Make sure to reset everything back to the way it was before when
14601471
// we're done.
@@ -1466,7 +1477,21 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14661477
}
14671478
let _reset = Reset(&inner.borrowed);
14681479

1469-
self.easy.do_perform()
1480+
let res = self.easy.do_perform();
1481+
1482+
// restore configuration
1483+
if self.is_postfields.get() {
1484+
self.easy
1485+
.inner
1486+
.setopt_ptr(curl_sys::CURLOPT_POSTFIELDS, 0 as *const _)
1487+
.expect("Failed to reset post_field_size");
1488+
self.easy
1489+
.inner
1490+
.setopt_ptr(curl_sys::CURLOPT_POSTFIELDS, ptr::null() as *const _)
1491+
.expect("Failed to set postfields to null");
1492+
self.is_postfields.set(false);
1493+
}
1494+
res
14701495
}
14711496

14721497
/// Same as `Easy::unpause_read`.
@@ -1478,6 +1503,17 @@ impl<'easy, 'data> Transfer<'easy, 'data> {
14781503
pub fn unpause_write(&self) -> Result<(), Error> {
14791504
self.easy.unpause_write()
14801505
}
1506+
1507+
/// Similar to [`Easy2::post_field`](struct.Easy2.html#method.post_field) just
1508+
/// takes a non `'static` lifetime corresponding to the lifetime of this transfer.
1509+
pub fn post_fields(&mut self, data: &'data [u8]) -> Result<(), Error> {
1510+
// Set the length before the pointer so libcurl knows how much to read
1511+
self.is_postfields.set(true);
1512+
self.easy.inner.post_field_size(data.len() as u64)?;
1513+
self.easy
1514+
.inner
1515+
.setopt_ptr(curl_sys::CURLOPT_POSTFIELDS, data.as_ptr() as *const _)
1516+
}
14811517
}
14821518

14831519
impl<'easy, 'data> fmt::Debug for Transfer<'easy, 'data> {

src/easy/handler.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1209,6 +1209,16 @@ impl<H> Easy2<H> {
12091209
self.setopt_ptr(curl_sys::CURLOPT_COPYPOSTFIELDS, data.as_ptr() as *const _)
12101210
}
12111211

1212+
/// Configures the data that will be uploaded as part of a POST.
1213+
///
1214+
/// By default this option is not set and corresponds to
1215+
/// `CURLOPT_POSTFIELDS`.
1216+
pub fn post_fields(&mut self, data: &'static [u8]) -> Result<(), Error> {
1217+
// Set the length before the pointer so libcurl knows how much to read
1218+
self.post_field_size(data.len() as u64)?;
1219+
self.setopt_ptr(curl_sys::CURLOPT_POSTFIELDS, data.as_ptr() as *const _)
1220+
}
1221+
12121222
/// Configures the size of data that's going to be uploaded as part of a
12131223
/// POST operation.
12141224
///
@@ -2827,7 +2837,11 @@ impl<H> Easy2<H> {
28272837
self.setopt_ptr(opt, val.as_ptr())
28282838
}
28292839

2830-
fn setopt_ptr(&self, opt: curl_sys::CURLoption, val: *const c_char) -> Result<(), Error> {
2840+
pub(crate) fn setopt_ptr(
2841+
&self,
2842+
opt: curl_sys::CURLoption,
2843+
val: *const c_char,
2844+
) -> Result<(), Error> {
28312845
unsafe { self.cvt(curl_sys::curl_easy_setopt(self.inner.handle, opt, val)) }
28322846
}
28332847

tests/easy.rs

+59
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,32 @@ fn post3() {
515515
t!(h.perform());
516516
}
517517

518+
#[test]
519+
fn post4() {
520+
let s = Server::new();
521+
s.receive(
522+
"\
523+
POST / HTTP/1.1\r\n\
524+
Host: 127.0.0.1:$PORT\r\n\
525+
Accept: */*\r\n\
526+
Content-Length: 5\r\n\
527+
Content-Type: application/x-www-form-urlencoded\r\n\
528+
\r\n\
529+
data\n",
530+
);
531+
s.send(
532+
"\
533+
HTTP/1.1 200 OK\r\n\
534+
\r\n",
535+
);
536+
537+
let mut h = handle();
538+
t!(h.url(&s.url("/")));
539+
t!(h.post(true));
540+
t!(h.post_fields(b"data\n"));
541+
t!(h.perform());
542+
}
543+
518544
#[test]
519545
fn referer() {
520546
let s = Server::new();
@@ -769,6 +795,39 @@ b",
769795
t!(h.transfer.borrow().perform());
770796
}
771797

798+
#[test]
799+
fn transfer_post_fields() {
800+
let s = Server::new();
801+
s.receive(
802+
"\
803+
POST / HTTP/1.1\r\n\
804+
Host: 127.0.0.1:$PORT\r\n\
805+
Accept: */*\r\n\
806+
Content-Length: 5\r\n\
807+
Content-Type: application/x-www-form-urlencoded\r\n\
808+
\r\n\
809+
data\n",
810+
);
811+
s.send(
812+
"\
813+
HTTP/1.1 200 OK\r\n\
814+
\r\n",
815+
);
816+
817+
fn do_transfer_post(e: &mut Easy, data: &[u8]) {
818+
let mut transfer = e.transfer();
819+
t!(transfer.post_fields(data));
820+
t!(transfer.perform());
821+
}
822+
823+
let mut h = handle();
824+
t!(h.url(&s.url("/")));
825+
t!(h.post(true));
826+
let mut data = Vec::new();
827+
data.extend_from_slice(b"data\n");
828+
do_transfer_post(&mut h, &data);
829+
}
830+
772831
#[test]
773832
fn perform_in_perform_is_bad() {
774833
let s = Server::new();

0 commit comments

Comments
 (0)