source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "chardetng"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
+dependencies = [
+ "cfg-if",
+ "encoding_rs",
+ "memchr",
+]
+
[[package]]
name = "chrono"
version = "0.4.23"
"async-stream",
"async_zip",
"base64 0.21.0",
+ "chardetng",
"chrono",
"clap",
"clap_complete",
alphanumeric-sort = "1.4"
content_inspector = "0.2"
anyhow = "1.0"
+chardetng = "0.1"
[features]
default = ["tls"]
None
};
- if let Some(mime) = mime_guess::from_path(path).first() {
- res.headers_mut().typed_insert(ContentType::from(mime));
- } else {
- res.headers_mut().insert(
- CONTENT_TYPE,
- HeaderValue::from_static("application/octet-stream"),
- );
- }
+ res.headers_mut().insert(
+ CONTENT_TYPE,
+ HeaderValue::from_str(&get_content_type(path).await?)?,
+ );
let filename = try_get_file_name(path)?;
res.headers_mut().insert(
res.headers_mut()
.insert("DAV", HeaderValue::from_static("1,2"));
}
+
+async fn get_content_type(path: &Path) -> Result<String> {
+ let mut buffer: Vec<u8> = vec![];
+ fs::File::open(path)
+ .await?
+ .take(1024)
+ .read_to_end(&mut buffer)
+ .await?;
+ let mime = mime_guess::from_path(path).first();
+ let is_text = content_inspector::inspect(&buffer).is_text();
+ let content_type = if is_text {
+ let mut detector = chardetng::EncodingDetector::new();
+ detector.feed(&buffer, buffer.len() < 1024);
+ let (enc, confident) = detector.guess_assess(None, true);
+ let charset = if confident {
+ format!("; charset={}", enc.name())
+ } else {
+ "".into()
+ };
+ match mime {
+ Some(m) => format!("{m}{charset}"),
+ None => format!("text/plain{charset}"),
+ }
+ } else {
+ match mime {
+ Some(m) => m.to_string(),
+ None => "application/octet-stream".into(),
+ }
+ };
+ Ok(content_type)
+}
let tmpdir = assert_fs::TempDir::new().expect("Couldn't create a temp dir for tests");
for file in FILES {
if *file == BIN_FILE {
- tmpdir
- .child(file)
- .write_binary(b"bin\0\0123")
- .expect("Couldn't write to file");
+ tmpdir.child(file).write_binary(b"bin\0\0123").unwrap();
} else {
tmpdir
.child(file)
.write_str(&format!("This is {file}"))
- .expect("Couldn't write to file");
+ .unwrap();
}
}
for directory in DIRECTORIES {
tmpdir
.child(format!("{}{}", directory, "index.html"))
.write_str("__ASSERTS_PREFIX__index.js;DATA = __INDEX_DATA__")
- .expect("Couldn't write to file");
+ .unwrap();
} else {
for file in FILES {
if *directory == DIR_NO_INDEX && *file == "index.html" {
tmpdir
.child(format!("{directory}{file}"))
.write_binary(b"bin\0\0123")
- .expect("Couldn't write to file");
+ .unwrap();
} else {
tmpdir
.child(format!("{directory}{file}"))
.write_str(&format!("This is {directory}{file}"))
- .expect("Couldn't write to file");
+ .unwrap();
}
}
}
}
tmpdir.child("dir4/hidden").touch().unwrap();
+ tmpdir
+ .child("content-types/bin.tar")
+ .write_binary(b"\x7f\x45\x4c\x46\x02\x01\x00\x00")
+ .unwrap();
+ tmpdir
+ .child("content-types/bin")
+ .write_binary(b"\x7f\x45\x4c\x46\x02\x01\x00\x00")
+ .unwrap();
+ tmpdir
+ .child("content-types/file-utf8.txt")
+ .write_str("世界")
+ .unwrap();
+ tmpdir
+ .child("content-types/file-gbk.txt")
+ .write_binary(b"\xca\xc0\xbd\xe7")
+ .unwrap();
+ tmpdir
+ .child("content-types/file")
+ .write_str("世界")
+ .unwrap();
tmpdir
}
fn get_file(server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}index.html", server.url()))?;
assert_eq!(resp.status(), 200);
- assert_eq!(resp.headers().get("content-type").unwrap(), "text/html");
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "text/html; charset=UTF-8"
+ );
assert_eq!(resp.headers().get("accept-ranges").unwrap(), "bytes");
assert!(resp.headers().contains_key("etag"));
assert!(resp.headers().contains_key("last-modified"));
fn head_file(server: TestServer) -> Result<(), Error> {
let resp = fetch!(b"HEAD", format!("{}index.html", server.url())).send()?;
assert_eq!(resp.status(), 200);
- assert_eq!(resp.headers().get("content-type").unwrap(), "text/html");
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "text/html; charset=UTF-8"
+ );
assert_eq!(resp.headers().get("accept-ranges").unwrap(), "bytes");
assert!(resp.headers().contains_key("content-disposition"));
assert!(resp.headers().contains_key("etag"));
assert_eq!(resp.status(), 404);
Ok(())
}
+
+#[rstest]
+fn get_file_content_type(server: TestServer) -> Result<(), Error> {
+ let resp = reqwest::blocking::get(format!("{}content-types/bin.tar", server.url()))?;
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "application/x-tar"
+ );
+ let resp = reqwest::blocking::get(format!("{}content-types/bin", server.url()))?;
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "application/octet-stream"
+ );
+ let resp = reqwest::blocking::get(format!("{}content-types/file-utf8.txt", server.url()))?;
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "text/plain; charset=UTF-8"
+ );
+ let resp = reqwest::blocking::get(format!("{}content-types/file-gbk.txt", server.url()))?;
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "text/plain; charset=GBK"
+ );
+ let resp = reqwest::blocking::get(format!("{}content-types/file", server.url()))?;
+ assert_eq!(
+ resp.headers().get("content-type").unwrap(),
+ "text/plain; charset=UTF-8"
+ );
+ Ok(())
+}