Skip to content

Provide custom header callback in addition to progress #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/ffi/easy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ use http::{header, Response};

use curl_ffi as ffi;

pub type HeaderCb<'a> = FnMut(&str, &str) + 'a;
pub type ProgressCb<'a> = FnMut(usize, usize, usize, usize) + 'a;

pub struct Easy {
curl: *mut ffi::CURL
}

struct HeaderCbData {
resp_p: usize,
cb_p: usize
}

impl Easy {
pub fn new() -> Easy {
// Ensure that curl is globally initialized
Expand Down Expand Up @@ -47,6 +53,7 @@ impl Easy {

pub fn perform(&mut self,
body: Option<&mut Body>,
header: Option<Box<HeaderCb>>,
progress: Option<Box<ProgressCb>>)
-> Result<Response, err::ErrCode> {
let mut builder = ResponseBuilder::new();
Expand All @@ -58,11 +65,21 @@ impl Easy {
None => 0
};

let header_p: usize = match header.as_ref() {
Some(cb) => mem::transmute(cb),
None => 0
};
let progress_p: usize = match progress.as_ref() {
Some(cb) => mem::transmute(cb),
None => 0
};

let header_data = HeaderCbData {
resp_p: resp_p,
cb_p: header_p
};
let header_data_p: usize = mem::transmute(&header_data);

// Set callback options
//
// Use explicit `as` casts to work around rust-lang/rust#32201
Expand All @@ -76,7 +93,7 @@ impl Easy {

ffi::curl_easy_setopt(self.curl, opt::HEADERFUNCTION,
curl_header_fn as extern fn(_, _, _, _) -> _);
ffi::curl_easy_setopt(self.curl, opt::HEADERDATA, resp_p);
ffi::curl_easy_setopt(self.curl, opt::HEADERDATA, header_data_p);

ffi::curl_easy_setopt(self.curl, opt::PROGRESSFUNCTION,
curl_progress_fn as extern fn(_, _, _, _, _) -> _);
Expand Down Expand Up @@ -215,15 +232,22 @@ extern fn curl_write_fn(p: *mut u8, size: size_t, nmemb: size_t,
}

extern fn curl_header_fn(p: *mut u8, size: size_t, nmemb: size_t,
resp: &mut ResponseBuilder) -> size_t {
header_data: &mut HeaderCbData) -> size_t {
// TODO: Skip the first call (it seems to be the status line)

let vec = unsafe { slice::from_raw_parts(p as *const u8,
(size * nmemb) as usize) };
let resp: &mut ResponseBuilder = unsafe { mem::transmute(header_data.resp_p) };
let cb: *mut Box<HeaderCb> = unsafe { mem::transmute(header_data.cb_p) };

match header::parse(&vec) {
Some((name, val)) => {
resp.add_header(name, val);

if !cb.is_null() {
let cb: &mut HeaderCb = unsafe { &mut **cb };
(*cb)(name, val);
}
}
None => {}
}
Expand Down
14 changes: 12 additions & 2 deletions src/http/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ffi::opt;
use ffi;
use http::Response;
use http::body::{Body,ToBody};
use {ProgressCb,ErrCode};
use {HeaderCb,ProgressCb,ErrCode};

use self::Method::{Get, Head, Post, Put, Patch, Delete};
use self::BodyType::{Fixed, Chunked};
Expand Down Expand Up @@ -187,6 +187,7 @@ pub struct Request<'a, 'b> {
body_type: Option<BodyType>,
content_type: bool, // whether or not the content type was set
expect_continue: bool, // whether to expect a 100 continue from the server
header_cb: Option<Box<HeaderCb<'b>>>,
progress: Option<Box<ProgressCb<'b>>>,
follow: bool,
}
Expand All @@ -207,6 +208,7 @@ impl<'a, 'b> Request<'a, 'b> {
body_type: None,
content_type: false,
expect_continue: false,
header_cb: None,
progress: None,
follow: false,
}
Expand Down Expand Up @@ -269,6 +271,13 @@ impl<'a, 'b> Request<'a, 'b> {
self
}

pub fn header_cb<F>(mut self, cb: F) -> Request<'a, 'b>
where F: FnMut(&str, &str) + 'b
{
self.header_cb = Some(Box::new(cb) as Box<HeaderCb<'b>>);
self
}

pub fn progress<F>(mut self, cb: F) -> Request<'a, 'b>
where F: FnMut(usize, usize, usize, usize) + 'b
{
Expand All @@ -292,6 +301,7 @@ impl<'a, 'b> Request<'a, 'b> {
body_type,
content_type,
expect_continue,
header_cb,
progress,
follow,
..
Expand Down Expand Up @@ -399,7 +409,7 @@ impl<'a, 'b> Request<'a, 'b> {
// [1]: http://curl.haxx.se/libcurl/c/threadsafe.html
try!(handle.easy.setopt(opt::NOSIGNAL, 1));

handle.easy.perform(body.as_mut(), progress)
handle.easy.perform(body.as_mut(), header_cb, progress)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern crate curl_sys as curl_ffi;
#[cfg(all(unix, not(target_os = "macos")))]
extern crate openssl_sys as openssl;

pub use ffi::easy::HeaderCb;
pub use ffi::easy::ProgressCb;
pub use ffi::err::ErrCode;

Expand Down
35 changes: 35 additions & 0 deletions test/test_get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,41 @@ pub fn test_get_with_custom_headers() {
assert!(res.get_header("content-length") == ["5".to_string()]);
}

#[test]
pub fn test_get_header_callback() {
let srv = server!(
recv!(
b"GET / HTTP/1.1\r\n\
Host: localhost:{PORT}\r\n\
Accept: */*\r\n\r\n"),
send!(
b"HTTP/1.1 200 OK\r\n\
Server: test\r\n\
Content-Length: 5\r\n\r\n\
Hello\r\n"));

let mut headers: Vec<String> = vec![];
let mut values: Vec<String> = vec![];

let res = handle()
.get(server::url("/"))
.header_cb(|k, v| {
headers.push(String::from(k));
values.push(String::from(v));
})
.exec().unwrap();

srv.assert();

assert!(res.get_code() == 200);
assert!(headers.len() == 2);
assert!(values.len() == 2);
assert!(headers[0] == "Server");
assert!(values[0] == "test");
assert!(headers[1] == "Content-Length");
assert!(values[1] == "5");
}

#[test]
pub fn test_get_tracking_progress() {
let srv = server!(
Expand Down