mirror of
https://github.com/ducaale/xh.git
synced 2025-05-05 15:32:50 +00:00
431 lines
11 KiB
Rust
431 lines
11 KiB
Rust
use std::{
|
|
fs::{self, OpenOptions},
|
|
io::Write,
|
|
};
|
|
|
|
use predicates::str::contains;
|
|
use tempfile::tempdir;
|
|
|
|
use crate::prelude::*;
|
|
|
|
#[test]
|
|
fn download() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.body("file contents\n".into())
|
|
.unwrap()
|
|
});
|
|
|
|
let outfile = dir.path().join("outfile");
|
|
get_command()
|
|
.arg("--download")
|
|
.arg("--output")
|
|
.arg(&outfile)
|
|
.arg(server.base_url())
|
|
.assert()
|
|
.success();
|
|
assert_eq!(fs::read_to_string(&outfile).unwrap(), "file contents\n");
|
|
}
|
|
|
|
#[test]
|
|
fn accept_encoding_not_modifiable_in_download_mode() {
|
|
let server = server::http(|req| async move {
|
|
assert_eq!(req.headers()["accept-encoding"], "identity");
|
|
hyper::Response::builder()
|
|
.body(r#"{"ids":[1,2,3]}"#.into())
|
|
.unwrap()
|
|
});
|
|
|
|
let dir = tempdir().unwrap();
|
|
get_command()
|
|
.current_dir(&dir)
|
|
.args([&server.base_url(), "--download", "accept-encoding:gzip"])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn download_generated_filename() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header("Content-Type", "application/json")
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.url("/foo/bar/")])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
|
|
get_command()
|
|
.args(["--download", &server.url("/foo/bar/")])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("bar.json")).unwrap(),
|
|
"file"
|
|
);
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("bar.json-1")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_supplied_filename() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header("Content-Disposition", r#"attachment; filename="foo.bar""#)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("foo.bar")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_supplied_unicode_filename() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header("Content-Disposition", r#"attachment; filename="😀.bar""#)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("😀.bar")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_support_filename_rfc_5987() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header(
|
|
"Content-Disposition",
|
|
r#"attachment; filename*=UTF-8''abcd1234.txt"#,
|
|
)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("abcd1234.txt")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
#[test]
|
|
fn download_support_filename_rfc_5987_percent_encoded() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header(
|
|
"Content-Disposition",
|
|
r#"attachment; filename*=UTF-8''%E6%B5%8B%E8%AF%95.txt"#,
|
|
)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("测试.txt")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_support_filename_rfc_5987_percent_encoded_with_iso_8859_1() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header(
|
|
"Content-Disposition",
|
|
r#"attachment; filename*=iso-8859-1'en'%A3%20rates.txt"#,
|
|
)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("£ rates.txt")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_filename_star_with_high_priority() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header(
|
|
"Content-Disposition",
|
|
r#"attachment; filename="fallback.txt"; filename*=UTF-8''%E6%B5%8B%E8%AF%95.txt"#,
|
|
)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("测试.txt")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_supplied_unquoted_filename() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header("Content-Disposition", r#"attachment; filename=foo bar baz"#)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("foo bar baz")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn download_filename_with_directory_traversal() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header(
|
|
"Content-Disposition",
|
|
r#"attachment; filename="foo/baz/bar""#,
|
|
)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("foo_baz_bar")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn download_filename_with_windows_directory_traversal() {
|
|
let dir = tempdir().unwrap();
|
|
let server = server::http(|_req| async move {
|
|
hyper::Response::builder()
|
|
.header(
|
|
"Content-Disposition",
|
|
r#"attachment; filename="foo\baz\bar""#,
|
|
)
|
|
.body("file".into())
|
|
.unwrap()
|
|
});
|
|
|
|
get_command()
|
|
.args(["--download", &server.base_url()])
|
|
.current_dir(&dir)
|
|
.assert()
|
|
.success();
|
|
assert_eq!(
|
|
fs::read_to_string(dir.path().join("foo_baz_bar")).unwrap(),
|
|
"file"
|
|
);
|
|
}
|
|
|
|
// TODO: test implicit download filenames
|
|
// For this we have to pretend the output is a tty
|
|
// This intersects with both #41 and #59
|
|
|
|
#[test]
|
|
fn it_can_resume_a_download() {
|
|
let server = server::http(|req| async move {
|
|
assert_eq!(req.headers()[hyper::header::RANGE], "bytes=5-");
|
|
|
|
hyper::Response::builder()
|
|
.status(206)
|
|
.header(hyper::header::CONTENT_RANGE, "bytes 5-11/12")
|
|
.body(" world\n".into())
|
|
.unwrap()
|
|
});
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let filename = dir.path().join("input.txt");
|
|
OpenOptions::new()
|
|
.create(true)
|
|
.truncate(true)
|
|
.write(true)
|
|
.open(&filename)
|
|
.unwrap()
|
|
.write_all(b"Hello")
|
|
.unwrap();
|
|
|
|
get_command()
|
|
.arg("--download")
|
|
.arg("--continue")
|
|
.arg("--output")
|
|
.arg(&filename)
|
|
.arg(server.base_url())
|
|
.assert()
|
|
.success();
|
|
|
|
assert_eq!(fs::read_to_string(&filename).unwrap(), "Hello world\n");
|
|
}
|
|
|
|
#[test]
|
|
fn it_can_resume_a_download_with_one_byte() {
|
|
let server = server::http(|req| async move {
|
|
assert_eq!(req.headers()[hyper::header::RANGE], "bytes=5-");
|
|
|
|
hyper::Response::builder()
|
|
.status(206)
|
|
.header(hyper::header::CONTENT_RANGE, "bytes 5-5/6")
|
|
.body("!".into())
|
|
.unwrap()
|
|
});
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let filename = dir.path().join("input.txt");
|
|
OpenOptions::new()
|
|
.create(true)
|
|
.truncate(true)
|
|
.write(true)
|
|
.open(&filename)
|
|
.unwrap()
|
|
.write_all(b"Hello")
|
|
.unwrap();
|
|
|
|
get_command()
|
|
.arg("--download")
|
|
.arg("--continue")
|
|
.arg("--output")
|
|
.arg(&filename)
|
|
.arg(server.base_url())
|
|
.assert()
|
|
.success();
|
|
|
|
assert_eq!(fs::read_to_string(&filename).unwrap(), "Hello!");
|
|
}
|
|
|
|
#[test]
|
|
fn it_rejects_incorrect_content_range_headers() {
|
|
let server = server::http(|req| async move {
|
|
assert_eq!(req.headers()[hyper::header::RANGE], "bytes=5-");
|
|
|
|
hyper::Response::builder()
|
|
.status(206)
|
|
.header(hyper::header::CONTENT_RANGE, "bytes 6-10/11")
|
|
.body("world\n".into())
|
|
.unwrap()
|
|
});
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let filename = dir.path().join("input.txt");
|
|
OpenOptions::new()
|
|
.create(true)
|
|
.truncate(true)
|
|
.write(true)
|
|
.open(&filename)
|
|
.unwrap()
|
|
.write_all(b"Hello")
|
|
.unwrap();
|
|
|
|
get_command()
|
|
.arg("--download")
|
|
.arg("--continue")
|
|
.arg("--output")
|
|
.arg(&filename)
|
|
.arg(server.base_url())
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains("Content-Range has wrong start"));
|
|
}
|
|
|
|
#[test]
|
|
fn it_refuses_to_combine_continue_and_range() {
|
|
let server = server::http(|req| async move {
|
|
assert_eq!(req.headers()[hyper::header::RANGE], "bytes=20-30");
|
|
|
|
hyper::Response::builder()
|
|
.status(206)
|
|
.header(hyper::header::CONTENT_RANGE, "bytes 20-30/100")
|
|
.body("lorem ipsum".into())
|
|
.unwrap()
|
|
});
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let filename = dir.path().join("input.txt");
|
|
OpenOptions::new()
|
|
.create(true)
|
|
.truncate(true)
|
|
.write(true)
|
|
.open(&filename)
|
|
.unwrap()
|
|
.write_all(b"Hello")
|
|
.unwrap();
|
|
|
|
get_command()
|
|
.arg("--download")
|
|
.arg("--continue")
|
|
.arg("--output")
|
|
.arg(&filename)
|
|
.arg(server.base_url())
|
|
.arg("Range:bytes=20-30")
|
|
.assert()
|
|
.success()
|
|
.stderr(contains("warning: --continue can't be used with"));
|
|
|
|
assert_eq!(fs::read_to_string(&filename).unwrap(), "lorem ipsum");
|
|
}
|