diff --git a/src/ffi/easy.rs b/src/ffi/easy.rs index 1a70ac4f8e..055dc77c25 100644 --- a/src/ffi/easy.rs +++ b/src/ffi/easy.rs @@ -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 @@ -47,6 +53,7 @@ impl Easy { pub fn perform(&mut self, body: Option<&mut Body>, + header: Option>, progress: Option>) -> Result { let mut builder = ResponseBuilder::new(); @@ -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 @@ -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(_, _, _, _, _) -> _); @@ -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 = 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 => {} } diff --git a/src/http/handle.rs b/src/http/handle.rs index ac3b8b0bd0..1c2e955800 100644 --- a/src/http/handle.rs +++ b/src/http/handle.rs @@ -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}; @@ -187,6 +187,7 @@ pub struct Request<'a, 'b> { body_type: Option, 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>>, progress: Option>>, follow: bool, } @@ -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, } @@ -269,6 +271,13 @@ impl<'a, 'b> Request<'a, 'b> { self } + pub fn header_cb(mut self, cb: F) -> Request<'a, 'b> + where F: FnMut(&str, &str) + 'b + { + self.header_cb = Some(Box::new(cb) as Box>); + self + } + pub fn progress(mut self, cb: F) -> Request<'a, 'b> where F: FnMut(usize, usize, usize, usize) + 'b { @@ -292,6 +301,7 @@ impl<'a, 'b> Request<'a, 'b> { body_type, content_type, expect_continue, + header_cb, progress, follow, .. @@ -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) } } diff --git a/src/lib.rs b/src/lib.rs index 99089cfb20..7d45ddd262 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/test/test_get.rs b/test/test_get.rs index 6c4ef291c0..bbdf7bc029 100644 --- a/test/test_get.rs +++ b/test/test_get.rs @@ -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 = vec![]; + let mut values: Vec = 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!(