diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/curl/tests | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/curl/tests')
-rw-r--r-- | vendor/curl/tests/atexit.rs | 17 | ||||
-rw-r--r-- | vendor/curl/tests/easy.rs | 972 | ||||
-rw-r--r-- | vendor/curl/tests/formdata | 1 | ||||
-rw-r--r-- | vendor/curl/tests/multi.rs | 297 | ||||
-rw-r--r-- | vendor/curl/tests/post.rs | 118 | ||||
-rw-r--r-- | vendor/curl/tests/protocols.rs | 19 | ||||
-rw-r--r-- | vendor/curl/tests/server/mod.rs | 223 |
7 files changed, 1647 insertions, 0 deletions
diff --git a/vendor/curl/tests/atexit.rs b/vendor/curl/tests/atexit.rs new file mode 100644 index 000000000..0872a4e97 --- /dev/null +++ b/vendor/curl/tests/atexit.rs @@ -0,0 +1,17 @@ +use curl::easy::Easy; + +pub extern "C" fn hook() { + let mut easy = Easy::new(); + easy.url("google.com").unwrap(); + easy.write_function(|data| Ok(data.len())).unwrap(); + easy.perform().unwrap(); +} + +fn main() { + curl::init(); + hook(); + unsafe { + libc::atexit(hook); + } + println!("Finishing...") +} diff --git a/vendor/curl/tests/easy.rs b/vendor/curl/tests/easy.rs new file mode 100644 index 000000000..942922933 --- /dev/null +++ b/vendor/curl/tests/easy.rs @@ -0,0 +1,972 @@ +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, List, ReadError, Transfer, WriteError}; +use curl::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<usize, WriteError> { + 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::<u8>::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::<Vec<_>>(); + // 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(); + 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", + ); + + 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<bool>, + unpaused: Cell<bool>, + transfer: RefCell<Transfer<'a, 'b>>, + } + + 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[..])); +} diff --git a/vendor/curl/tests/formdata b/vendor/curl/tests/formdata new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/vendor/curl/tests/formdata @@ -0,0 +1 @@ +hello diff --git a/vendor/curl/tests/multi.rs b/vendor/curl/tests/multi.rs new file mode 100644 index 000000000..0c5e70150 --- /dev/null +++ b/vendor/curl/tests/multi.rs @@ -0,0 +1,297 @@ +#![cfg(unix)] + +use std::collections::HashMap; +use std::io::{Cursor, Read}; +use std::time::Duration; + +use curl::easy::{Easy, List}; +use curl::multi::Multi; + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {:?}", stringify!($e), e), + } + }; +} + +use crate::server::Server; +mod server; + +#[test] +fn smoke() { + let m = Multi::new(); + let mut e = Easy::new(); + + 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"); + + t!(e.url(&s.url("/"))); + let _e = t!(m.add(e)); + while t!(m.perform()) > 0 { + t!(m.wait(&mut [], Duration::from_secs(1))); + } +} + +#[test] +fn smoke2() { + let m = Multi::new(); + + let s1 = 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("HTTP/1.1 200 OK\r\n\r\n"); + + let s2 = Server::new(); + s2.receive( + "\ + GET / 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 e1 = Easy::new(); + t!(e1.url(&s1.url("/"))); + let _e1 = t!(m.add(e1)); + let mut e2 = Easy::new(); + t!(e2.url(&s2.url("/"))); + let _e2 = t!(m.add(e2)); + + while t!(m.perform()) > 0 { + t!(m.wait(&mut [], Duration::from_secs(1))); + } + + let mut done = 0; + m.messages(|msg| { + msg.result().unwrap().unwrap(); + done += 1; + }); + assert_eq!(done, 2); +} + +#[test] +fn upload_lots() { + use curl::multi::{Events, Socket, SocketEvents}; + + #[derive(Debug)] + enum Message { + Timeout(Option<Duration>), + Wait(Socket, SocketEvents, usize), + } + + let mut m = Multi::new(); + let poll = t!(mio::Poll::new()); + let (tx, rx) = mio_extras::channel::channel(); + let tx2 = tx.clone(); + t!(m.socket_function(move |socket, events, token| { + t!(tx2.send(Message::Wait(socket, events, token))); + })); + t!(m.timer_function(move |dur| { + t!(tx.send(Message::Timeout(dur))); + true + })); + + let s = Server::new(); + s.receive(&format!( + "\ + PUT / HTTP/1.1\r\n\ + Host: 127.0.0.1:$PORT\r\n\ + Accept: */*\r\n\ + Content-Length: 131072\r\n\ + \r\n\ + {}\n", + vec!["a"; 128 * 1024 - 1].join("") + )); + s.send( + "\ + HTTP/1.1 200 OK\r\n\ + \r\n", + ); + + let mut data = vec![b'a'; 128 * 1024 - 1]; + data.push(b'\n'); + let mut data = Cursor::new(data); + let mut list = List::new(); + t!(list.append("Expect:")); + let mut h = Easy::new(); + t!(h.url(&s.url("/"))); + t!(h.put(true)); + t!(h.read_function(move |buf| Ok(data.read(buf).unwrap()))); + t!(h.in_filesize(128 * 1024)); + t!(h.upload(true)); + t!(h.http_headers(list)); + + t!(poll.register(&rx, mio::Token(0), mio::Ready::all(), mio::PollOpt::level())); + + let e = t!(m.add(h)); + + assert!(t!(m.perform()) > 0); + let mut next_token = 1; + let mut token_map = HashMap::new(); + let mut cur_timeout = None; + let mut events = mio::Events::with_capacity(128); + let mut running = true; + + while running { + let n = t!(poll.poll(&mut events, cur_timeout)); + + if n == 0 && t!(m.timeout()) == 0 { + running = false; + } + + for event in events.iter() { + while event.token() == mio::Token(0) { + match rx.try_recv() { + Ok(Message::Timeout(dur)) => cur_timeout = dur, + Ok(Message::Wait(socket, events, token)) => { + let evented = mio::unix::EventedFd(&socket); + if events.remove() { + token_map.remove(&token).unwrap(); + } else { + let mut e = mio::Ready::empty(); + if events.input() { + e |= mio::Ready::readable(); + } + if events.output() { + e |= mio::Ready::writable(); + } + if token == 0 { + let token = next_token; + next_token += 1; + t!(m.assign(socket, token)); + token_map.insert(token, socket); + t!(poll.register( + &evented, + mio::Token(token), + e, + mio::PollOpt::level() + )); + } else { + t!(poll.reregister( + &evented, + mio::Token(token), + e, + mio::PollOpt::level() + )); + } + } + } + Err(_) => break, + } + } + + if event.token() == mio::Token(0) { + continue; + } + + let token = event.token(); + let socket = token_map[&token.into()]; + let mut e = Events::new(); + if event.readiness().is_readable() { + e.input(true); + } + if event.readiness().is_writable() { + e.output(true); + } + if mio::unix::UnixReady::from(event.readiness()).is_error() { + e.error(true); + } + let remaining = t!(m.action(socket, &e)); + if remaining == 0 { + running = false; + } + } + } + + let mut done = 0; + m.messages(|m| { + m.result().unwrap().unwrap(); + done += 1; + }); + assert_eq!(done, 1); + + let mut e = t!(m.remove(e)); + assert_eq!(t!(e.response_code()), 200); +} + +// Tests passing raw file descriptors to Multi::wait. The test is limited to Linux only as the +// semantics of the underlying poll(2) system call used by curl apparently differ on other +// platforms, making the test fail. +#[cfg(target_os = "linux")] +#[test] +fn waitfds() { + use curl::multi::WaitFd; + use std::fs::File; + use std::os::unix::io::AsRawFd; + + let filenames = ["/dev/null", "/dev/zero", "/dev/urandom"]; + let files: Vec<File> = filenames + .iter() + .map(|filename| File::open(filename).unwrap()) + .collect(); + let mut waitfds: Vec<WaitFd> = files + .iter() + .map(|f| { + let mut waitfd = WaitFd::new(); + waitfd.set_fd(f.as_raw_fd()); + waitfd.poll_on_read(true); + waitfd + }) + .collect(); + + let m = Multi::new(); + let events = t!(m.wait(&mut waitfds, Duration::from_secs(1))); + assert_eq!(events, 3); + for waitfd in waitfds { + assert!(waitfd.received_read()); + } +} + +// Tests passing raw file descriptors to Multi::wait. The test is limited to Linux only as the +// semantics of the underlying poll(2) system call used by curl apparently differ on other +// platforms, making the test fail. +#[cfg(feature = "poll_7_68_0")] +#[cfg(target_os = "linux")] +#[test] +fn pollfds() { + use curl::multi::WaitFd; + use std::fs::File; + use std::os::unix::io::AsRawFd; + + let filenames = ["/dev/null", "/dev/zero", "/dev/urandom"]; + let files: Vec<File> = filenames + .iter() + .map(|filename| File::open(filename).unwrap()) + .collect(); + let mut waitfds: Vec<WaitFd> = files + .iter() + .map(|f| { + let mut waitfd = WaitFd::new(); + waitfd.set_fd(f.as_raw_fd()); + waitfd.poll_on_read(true); + waitfd + }) + .collect(); + + let m = Multi::new(); + let events = t!(m.poll(&mut waitfds, Duration::from_secs(1))); + assert_eq!(events, 3); + for waitfd in waitfds { + assert!(waitfd.received_read()); + } +} diff --git a/vendor/curl/tests/post.rs b/vendor/curl/tests/post.rs new file mode 100644 index 000000000..15e2df7d3 --- /dev/null +++ b/vendor/curl/tests/post.rs @@ -0,0 +1,118 @@ +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, Form, List}; + +use crate::server::Server; +mod server; + +fn handle() -> Easy { + let mut e = Easy::new(); + t!(e.timeout(Duration::new(20, 0))); + let mut list = List::new(); + t!(list.append("Expect:")); + t!(e.http_headers(list)); + e +} + +#[test] +fn custom() { + 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: 142\r\n\ + Content-Type: multipart/form-data; boundary=--[..]\r\n\ + \r\n\ + --[..]\r\n\ + Content-Disposition: form-data; name=\"foo\"\r\n\ + \r\n\ + 1234\r\n\ + --[..]\r\n", + ); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + let mut form = Form::new(); + t!(form.part("foo").contents(b"1234").add()); + t!(handle.url(&s.url("/"))); + t!(handle.httppost(form)); + t!(handle.perform()); +} + +#[test] +fn buffer() { + 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: 181\r\n\ + Content-Type: multipart/form-data; boundary=--[..]\r\n\ + \r\n\ + --[..]\r\n\ + Content-Disposition: form-data; name=\"foo\"; filename=\"bar\"\r\n\ + Content-Type: foo/bar\r\n\ + \r\n\ + 1234\r\n\ + --[..]\r\n", + ); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + let mut form = Form::new(); + t!(form + .part("foo") + .buffer("bar", b"1234".to_vec()) + .content_type("foo/bar") + .add()); + t!(handle.url(&s.url("/"))); + t!(handle.httppost(form)); + t!(handle.perform()); +} + +#[test] +fn file() { + let s = Server::new(); + let formdata = include_str!("formdata"); + s.receive( + format!( + "\ + POST / HTTP/1.1\r\n\ + Host: 127.0.0.1:$PORT\r\n\ + Accept: */*\r\n\ + Content-Length: {}\r\n\ + Content-Type: multipart/form-data; boundary=--[..]\r\n\ + \r\n\ + --[..]\r\n\ + Content-Disposition: form-data; name=\"foo\"; filename=\"formdata\"\r\n\ + Content-Type: application/octet-stream\r\n\ + \r\n\ + {}\ + \r\n\ + --[..]\r\n", + 199 + formdata.len(), + formdata + ) + .as_str(), + ); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + let mut form = Form::new(); + t!(form.part("foo").file("tests/formdata").add()); + t!(handle.url(&s.url("/"))); + t!(handle.httppost(form)); + t!(handle.perform()); +} diff --git a/vendor/curl/tests/protocols.rs b/vendor/curl/tests/protocols.rs new file mode 100644 index 000000000..7215fe972 --- /dev/null +++ b/vendor/curl/tests/protocols.rs @@ -0,0 +1,19 @@ +#[cfg(all(feature = "static-curl", not(feature = "protocol-ftp")))] +#[test] +fn static_with_ftp_disabled() { + assert!(curl::Version::get() + .protocols() + .filter(|&p| p == "ftp") + .next() + .is_none()); +} + +#[cfg(all(feature = "static-curl", feature = "protocol-ftp"))] +#[test] +fn static_with_ftp_enabled() { + assert!(curl::Version::get() + .protocols() + .filter(|&p| p == "ftp") + .next() + .is_some()); +} diff --git a/vendor/curl/tests/server/mod.rs b/vendor/curl/tests/server/mod.rs new file mode 100644 index 000000000..a474a1aa8 --- /dev/null +++ b/vendor/curl/tests/server/mod.rs @@ -0,0 +1,223 @@ +#![allow(dead_code)] + +use std::collections::HashSet; +use std::io::prelude::*; +use std::io::BufReader; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::path::PathBuf; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::thread; + +pub struct Server { + messages: Option<Sender<Message>>, + addr: Addr, + thread: Option<thread::JoinHandle<()>>, +} + +enum Message { + Read(String), + Write(String), +} + +enum Addr { + Tcp(SocketAddr), + Unix(PathBuf), +} + +fn run(stream: impl Read + Write, rx: &Receiver<Message>) { + let mut socket = BufReader::new(stream); + for msg in rx.iter() { + match msg { + Message::Read(ref expected) => { + let mut expected = &expected[..]; + let mut expected_headers = HashSet::new(); + while let Some(i) = expected.find('\n') { + let line = &expected[..i + 1]; + expected = &expected[i + 1..]; + expected_headers.insert(line); + if line == "\r\n" { + break; + } + } + + let mut expected_len = None; + while !expected_headers.is_empty() { + let mut actual = String::new(); + t!(socket.read_line(&mut actual)); + if actual.starts_with("Content-Length") { + let len = actual.split(": ").nth(1).unwrap(); + expected_len = len.trim().parse().ok(); + } + // various versions of libcurl do different things here + if actual == "Proxy-Connection: Keep-Alive\r\n" { + continue; + } + if expected_headers.remove(&actual[..]) { + continue; + } + + let mut found = None; + for header in expected_headers.iter() { + if lines_match(header, &actual) { + found = Some(header.clone()); + break; + } + } + if let Some(found) = found { + expected_headers.remove(&found); + continue; + } + panic!( + "unexpected header: {:?} (remaining headers {:?})", + actual, expected_headers + ); + } + for header in expected_headers { + panic!("expected header but not found: {:?}", header); + } + + let mut line = String::new(); + let mut socket = match expected_len { + Some(amt) => socket.by_ref().take(amt), + None => socket.by_ref().take(expected.len() as u64), + }; + while socket.limit() > 0 { + line.truncate(0); + t!(socket.read_line(&mut line)); + if line.is_empty() { + break; + } + if expected.is_empty() { + panic!("unexpected line: {:?}", line); + } + let i = expected.find('\n').unwrap_or(expected.len() - 1); + let expected_line = &expected[..i + 1]; + expected = &expected[i + 1..]; + if lines_match(expected_line, &line) { + continue; + } + panic!( + "lines didn't match:\n\ + expected: {:?}\n\ + actual: {:?}\n", + expected_line, line + ) + } + if !expected.is_empty() { + println!("didn't get expected data: {:?}", expected); + } + } + Message::Write(ref to_write) => { + t!(socket.get_mut().write_all(to_write.as_bytes())); + return; + } + } + } + + let mut dst = Vec::new(); + t!(socket.read_to_end(&mut dst)); + assert_eq!(dst.len(), 0); +} + +fn lines_match(expected: &str, mut actual: &str) -> bool { + for (i, part) in expected.split("[..]").enumerate() { + match actual.find(part) { + Some(j) => { + if i == 0 && j != 0 { + return false; + } + actual = &actual[j + part.len()..]; + } + None => return false, + } + } + actual.is_empty() || expected.ends_with("[..]") +} + +impl Server { + pub fn new() -> Server { + let listener = t!(TcpListener::bind("127.0.0.1:0")); + let addr = t!(listener.local_addr()); + let (tx, rx) = channel(); + let thread = thread::spawn(move || run(listener.accept().unwrap().0, &rx)); + Server { + messages: Some(tx), + addr: Addr::Tcp(addr), + thread: Some(thread), + } + } + + #[cfg(not(windows))] + pub fn new_unix() -> Server { + use std::os::unix::net::UnixListener; + + let path = "/tmp/easy_server.sock"; + std::fs::remove_file(path).ok(); + let listener = t!(UnixListener::bind(path)); + let (tx, rx) = channel(); + let thread = thread::spawn(move || run(listener.incoming().next().unwrap().unwrap(), &rx)); + Server { + messages: Some(tx), + addr: Addr::Unix(path.into()), + thread: Some(thread), + } + } + + pub fn receive(&self, msg: &str) { + self.msg(Message::Read(self.replace_port(msg))); + } + + fn replace_port(&self, msg: &str) -> String { + match &self.addr { + Addr::Tcp(addr) => msg.replace("$PORT", &addr.port().to_string()), + Addr::Unix(_) => msg.to_string(), + } + } + + pub fn send(&self, msg: &str) { + self.msg(Message::Write(self.replace_port(msg))); + } + + fn msg(&self, msg: Message) { + t!(self.messages.as_ref().unwrap().send(msg)); + } + + pub fn addr(&self) -> &SocketAddr { + match &self.addr { + Addr::Tcp(addr) => addr, + Addr::Unix(_) => panic!("server is a UnixListener"), + } + } + + #[cfg(not(windows))] + pub fn path(&self) -> &str { + match &self.addr { + Addr::Tcp(_) => panic!("server is a TcpListener"), + Addr::Unix(p) => p.as_os_str().to_str().unwrap(), + } + } + + pub fn url(&self, path: &str) -> String { + match &self.addr { + Addr::Tcp(addr) => format!("http://{}{}", addr, path), + Addr::Unix(_) => format!("http://localhost{}", path), + } + } +} + +impl Drop for Server { + fn drop(&mut self) { + match &self.addr { + Addr::Tcp(addr) => drop(TcpStream::connect(addr)), + Addr::Unix(p) => t!(std::fs::remove_file(p)), + } + + drop(self.messages.take()); + let res = self.thread.take().unwrap().join(); + if !thread::panicking() { + t!(res); + } else if let Err(e) = res { + println!("child server thread also failed: {:?}", e); + } + } +} |