use std::cell::{Cell, RefCell}; use std::io::Read; use std::rc::Rc; use std::str; use std::time::Duration; macro_rules! t { ($e:expr) => { match $e { Ok(e) => e, Err(e) => panic!("{} failed with {:?}", stringify!($e), e), } }; } use curl::easy::{Easy, Easy2, List, ReadError, Transfer, WriteError}; use curl::{Error, Version}; use crate::server::Server; mod server; fn handle() -> Easy { let mut e = Easy::new(); t!(e.timeout(Duration::new(20, 0))); e } fn sink(data: &[u8]) -> Result { Ok(data.len()) } #[test] fn get_smoke() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\n"); let mut handle = handle(); t!(handle.url(&s.url("/"))); t!(handle.perform()); } #[test] fn download_zero_size() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\n"); let mut handle = handle(); t!(handle.url(&s.url("/"))); t!(handle.perform()); assert_eq!(handle.download_size().unwrap(), 0_f64); } #[test] fn download_nonzero_size() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\nHello!"); let mut handle = handle(); t!(handle.url(&s.url("/"))); t!(handle.perform()); assert_eq!(handle.download_size().unwrap(), 6_f64); } #[test] fn upload_zero_size() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\n"); let mut handle = handle(); t!(handle.url(&s.url("/"))); t!(handle.perform()); assert_eq!(handle.upload_size().unwrap(), 0_f64); } #[test] fn upload_nonzero_size() { let s = Server::new(); s.receive( "\ PUT / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Content-Length: 5\r\n\ \r\n\ data\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut data = "data\n".as_bytes(); let mut list = List::new(); t!(list.append("Expect:")); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.put(true)); t!(h.in_filesize(5)); t!(h.upload(true)); t!(h.http_headers(list)); { let mut h = h.transfer(); t!(h.read_function(|buf| Ok(data.read(buf).unwrap()))); t!(h.perform()); } assert_eq!(h.upload_size().unwrap(), 5_f64); } #[test] fn get_path() { let s = Server::new(); s.receive( "\ GET /foo HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\n"); let mut handle = handle(); t!(handle.url(&s.url("/foo"))); t!(handle.perform()); } #[test] fn write_callback() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\nhello!"); let mut all = Vec::::new(); { let mut handle = handle(); t!(handle.url(&s.url("/"))); let mut handle = handle.transfer(); t!(handle.write_function(|data| { all.extend(data); Ok(data.len()) })); t!(handle.perform()); } assert_eq!(all, b"hello!"); } #[test] fn resolve() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: example.com:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\n"); let mut list = List::new(); t!(list.append(&format!("example.com:{}:127.0.0.1", s.addr().port()))); let mut handle = handle(); t!(handle.url(&format!("http://example.com:{}/", s.addr().port()))); t!(handle.resolve(list)); t!(handle.perform()); } #[test] fn progress() { let s = Server::new(); s.receive( "\ GET /foo HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\nHello!"); let mut hits = 0; let mut dl = 0.0; { let mut handle = handle(); t!(handle.url(&s.url("/foo"))); t!(handle.progress(true)); t!(handle.write_function(sink)); let mut handle = handle.transfer(); t!(handle.progress_function(|_, a, _, _| { hits += 1; dl = a; true })); t!(handle.perform()); } assert!(hits > 0); assert_eq!(dl, 6.0); } #[test] fn headers() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ Foo: bar\r\n\ Bar: baz\r\n\ \r\n Hello!", ); let mut headers = Vec::new(); { let mut handle = handle(); t!(handle.url(&s.url("/"))); let mut handle = handle.transfer(); t!(handle.header_function(|h| { headers.push(str::from_utf8(h).unwrap().to_string()); true })); t!(handle.write_function(sink)); t!(handle.perform()); } assert_eq!( headers, vec![ "HTTP/1.1 200 OK\r\n".to_string(), "Foo: bar\r\n".to_string(), "Bar: baz\r\n".to_string(), "\r\n".to_string(), ] ); } #[test] fn fail_on_error() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 401 Not so good\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.fail_on_error(true)); assert!(h.perform().is_err()); let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 401 Not so good\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.fail_on_error(false)); t!(h.perform()); } #[test] fn port() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: localhost:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url("http://localhost/")); t!(h.port(s.addr().port())); t!(h.perform()); } #[test] fn proxy() { let s = Server::new(); s.receive( "\ GET http://example.com/ HTTP/1.1\r\n\ Host: example.com\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url("http://example.com/")); t!(h.proxy(&s.url("/"))); t!(h.perform()); } #[test] #[ignore] // fails on newer curl versions? seems benign fn noproxy() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.proxy(&s.url("/"))); t!(h.noproxy("127.0.0.1")); t!(h.perform()); } #[test] fn misc() { let mut h = handle(); t!(h.tcp_nodelay(true)); // t!(h.tcp_keepalive(true)); // t!(h.tcp_keepidle(Duration::new(3, 0))); // t!(h.tcp_keepintvl(Duration::new(3, 0))); t!(h.buffer_size(10)); if Version::get().version_num() >= 0x073e00 { // only available on curl 7.62.0 or later: t!(h.upload_buffer_size(10)); } t!(h.dns_cache_timeout(Duration::new(1, 0))); } #[test] fn dns_servers() { let mut h = handle(); // Tests are not using a libcurl with c-ares, so this // always fails. Test anyway to make sure it returns // an error instead of panicing. assert!(h.dns_servers("").is_err()); assert!(h.dns_servers("nonsense").is_err()); assert!(h.dns_servers("8.8.8.8,8.8.4.4").is_err()); } #[test] fn userpass() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Authorization: Basic YmFyOg==\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.username("foo")); t!(h.username("bar")); t!(h.perform()); } #[test] fn accept_encoding() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Accept-Encoding: gzip\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.accept_encoding("gzip")); t!(h.perform()); } #[test] fn follow_location() { let s1 = Server::new(); let s2 = Server::new(); s1.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s1.send(&format!( "\ HTTP/1.1 301 Moved Permanently\r\n\ Location: http://{}/foo\r\n\ \r\n", s2.addr() )); s2.receive( "\ GET /foo HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s2.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s1.url("/"))); t!(h.follow_location(true)); t!(h.perform()); } #[test] fn put() { let s = Server::new(); s.receive( "\ PUT / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Content-Length: 5\r\n\ \r\n\ data\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut data = "data\n".as_bytes(); let mut list = List::new(); t!(list.append("Expect:")); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.put(true)); t!(h.in_filesize(5)); t!(h.upload(true)); t!(h.http_headers(list)); let mut h = h.transfer(); t!(h.read_function(|buf| Ok(data.read(buf).unwrap()))); t!(h.perform()); } #[test] fn post1() { let s = Server::new(); s.receive( "\ POST / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Content-Length: 5\r\n\ Content-Type: application/x-www-form-urlencoded\r\n\ \r\n\ data\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.post(true)); t!(h.post_fields_copy(b"data\n")); t!(h.perform()); } #[test] fn post2() { let s = Server::new(); s.receive( "\ POST / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Content-Length: 5\r\n\ Content-Type: application/x-www-form-urlencoded\r\n\ \r\n\ data\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.post(true)); t!(h.post_fields_copy(b"data\n")); t!(h.write_function(sink)); t!(h.perform()); } #[test] fn post3() { let s = Server::new(); s.receive( "\ POST / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Content-Length: 5\r\n\ Content-Type: application/x-www-form-urlencoded\r\n\ \r\n\ data\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut data = "data\n".as_bytes(); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.post(true)); t!(h.post_field_size(5)); let mut h = h.transfer(); t!(h.read_function(|buf| Ok(data.read(buf).unwrap()))); t!(h.perform()); } #[test] fn referer() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Referer: foo\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.referer("foo")); t!(h.perform()); } #[test] fn useragent() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ User-Agent: foo\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.useragent("foo")); t!(h.perform()); } #[test] fn custom_headers() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Foo: bar\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut custom = List::new(); t!(custom.append("Foo: bar")); t!(custom.append("Accept:")); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.http_headers(custom)); t!(h.perform()); } #[test] fn cookie() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Cookie: foo\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.cookie("foo")); t!(h.perform()); } #[test] fn url_encoding() { let mut h = handle(); assert_eq!(h.url_encode(b"foo"), "foo"); assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); assert_eq!(h.url_encode(b""), ""); assert_eq!(h.url_decode("foo"), b"foo"); assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); assert_eq!(h.url_decode("foo%2"), b"foo%2"); assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); assert_eq!(h.url_decode(""), b""); } #[test] fn getters() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.cookie_file("/dev/null")); t!(h.perform()); assert_eq!(t!(h.response_code()), 200); assert_eq!(t!(h.redirect_count()), 0); assert_eq!(t!(h.redirect_url()), None); assert_eq!(t!(h.content_type()), None); let addr = format!("http://{}/", s.addr()); assert_eq!(t!(h.effective_url()), Some(&addr[..])); // TODO: test this // let cookies = t!(h.cookies()).iter() // .map(|s| s.to_vec()) // .collect::>(); // assert_eq!(cookies.len(), 1); } #[test] #[should_panic] fn panic_in_callback() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.header_function(|_| panic!())); t!(h.perform()); } #[test] fn abort_read() { let s = Server::new(); // 8.7.0 seems to have changed the behavior that curl will call the read // function before sending headers (I'm guessing so that it batches // everything up into a single write). if Version::get().version_num() < 0x080700 { s.receive( "\ PUT / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ Content-Length: 2\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); } else { s.receive(""); } let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.read_function(|_| Err(ReadError::Abort))); t!(h.put(true)); t!(h.in_filesize(2)); let mut list = List::new(); t!(list.append("Expect:")); t!(h.http_headers(list)); let err = h.perform().unwrap_err(); assert!(err.is_aborted_by_callback()); } #[test] fn pause_write_then_resume() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n a\n b", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.progress(true)); struct State<'a, 'b> { paused: Cell, unpaused: Cell, transfer: RefCell>, } let h = Rc::new(State { paused: Cell::new(false), unpaused: Cell::new(false), transfer: RefCell::new(h.transfer()), }); let h2 = h.clone(); t!(h.transfer .borrow_mut() .write_function(move |data| if h2.unpaused.get() { h2.unpaused.set(false); Ok(data.len()) } else { h2.paused.set(true); Err(WriteError::Pause) })); let h2 = h.clone(); t!(h.transfer .borrow_mut() .progress_function(move |_, _, _, _| { if h2.paused.get() { h2.paused.set(false); h2.unpaused.set(true); t!(h2.transfer.borrow().unpause_write()); } true })); t!(h.transfer.borrow().perform()); } #[test] fn perform_in_perform_is_bad() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n a\n b", ); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.progress(true)); let h = Rc::new(RefCell::new(h.transfer())); let h2 = h.clone(); t!(h.borrow_mut().write_function(move |data| { assert!(h2.borrow().perform().is_err()); Ok(data.len()) })); t!(h.borrow().perform()); } #[cfg(not(windows))] #[test] fn check_unix_socket() { let s = Server::new_unix(); s.receive( "\ POST / HTTP/1.1\r\n\ Host: localhost\r\n\ Accept: */*\r\n\ Content-Length: 5\r\n\ Content-Type: application/x-www-form-urlencoded\r\n\ \r\n\ data\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.unix_socket(s.path())); t!(h.url(&s.url("/"))); t!(h.post(true)); t!(h.post_fields_copy(b"data\n")); t!(h.perform()); } #[cfg(feature = "upkeep_7_62_0")] #[test] fn test_upkeep() { let s = Server::new(); s.receive( "\ GET / HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send("HTTP/1.1 200 OK\r\n\r\n"); let mut handle = handle(); t!(handle.url(&s.url("/"))); t!(handle.perform()); // Ensure that upkeep can be called on the handle without problem. t!(handle.upkeep()); } #[test] fn path_as_is() { let s = Server::new(); s.receive( "\ GET /test/../ HTTP/1.1\r\n\ Host: 127.0.0.1:$PORT\r\n\ Accept: */*\r\n\ \r\n", ); s.send( "\ HTTP/1.1 200 OK\r\n\ \r\n", ); let mut h = handle(); t!(h.url(&s.url("/test/../"))); t!(h.path_as_is(true)); t!(h.perform()); let addr = format!("http://{}/test/../", s.addr()); assert_eq!(t!(h.response_code()), 200); assert_eq!(t!(h.effective_url()), Some(&addr[..])); } #[test] fn test_connect_timeout() { use curl::easy::Handler; struct Collector(Vec); impl Handler for Collector { fn write(&mut self, data: &[u8]) -> Result { self.0.extend_from_slice(data); Ok(data.len()) } } let mut easy2 = Easy2::new(Collector(Vec::new())); // Overflow value test must return an Error assert_eq!( Error::new(curl_sys::CURLE_BAD_FUNCTION_ARGUMENT), easy2 .connect_timeout(Duration::from_secs(std::u64::MAX)) .unwrap_err() ); // Valid value assert_eq!( (), easy2 .connect_timeout(Duration::from_millis(i32::MAX as u64)) .unwrap() ); } #[test] fn test_timeout() { use curl::easy::Handler; struct Collector(Vec); impl Handler for Collector { fn write(&mut self, data: &[u8]) -> Result { self.0.extend_from_slice(data); Ok(data.len()) } } let mut easy2 = Easy2::new(Collector(Vec::new())); // Overflow value test must return an Error assert_eq!( Error::new(curl_sys::CURLE_BAD_FUNCTION_ARGUMENT), easy2 .timeout(Duration::from_secs(std::u64::MAX)) .unwrap_err() ); // Valid value assert_eq!( (), easy2 .timeout(Duration::from_millis(i32::MAX as u64)) .unwrap() ); }