use bytesize::ByteSize;
use dragonfly_api::common::v2::Priority;
use reqwest::header::HeaderMap;
use std::{fmt, str::FromStr};
use tracing::error;
pub const DRAGONFLY_TAG_HEADER: &str = "X-Dragonfly-Tag";
pub const DRAGONFLY_APPLICATION_HEADER: &str = "X-Dragonfly-Application";
pub const DRAGONFLY_PRIORITY_HEADER: &str = "X-Dragonfly-Priority";
pub const DRAGONFLY_REGISTRY_HEADER: &str = "X-Dragonfly-Registry";
pub const DRAGONFLY_FILTERED_QUERY_PARAMS_HEADER: &str = "X-Dragonfly-Filtered-Query-Params";
pub const DRAGONFLY_USE_P2P_HEADER: &str = "X-Dragonfly-Use-P2P";
pub const DRAGONFLY_PREFETCH_HEADER: &str = "X-Dragonfly-Prefetch";
pub const DRAGONFLY_OUTPUT_PATH_HEADER: &str = "X-Dragonfly-Output-Path";
pub const DRAGONFLY_FORCE_HARD_LINK_HEADER: &str = "X-Dragonfly-Force-Hard-Link";
pub const DRAGONFLY_PIECE_LENGTH_HEADER: &str = "X-Dragonfly-Piece-Length";
pub const DRAGONFLY_CONTENT_FOR_CALCULATING_TASK_ID_HEADER: &str =
"X-Dragonfly-Content-For-Calculating-Task-ID";
pub const DRAGONFLY_TASK_DOWNLOAD_FINISHED_HEADER: &str = "X-Dragonfly-Task-Download-Finished";
pub const DRAGONFLY_TASK_ID_HEADER: &str = "X-Dragonfly-Task-ID";
pub const DRAGONFLY_SERVER_IP_HEADER: &str = "X-Dragonfly-Server-IP";
pub const DRAGONFLY_ERROR_TYPE_HEADER: &str = "X-Dragonfly-Error-Type";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorType {
Backend,
Proxy,
Dfdaemon,
}
impl ErrorType {
pub fn as_str(&self) -> &'static str {
match self {
ErrorType::Backend => "backend",
ErrorType::Proxy => "proxy",
ErrorType::Dfdaemon => "dfdaemon",
}
}
}
impl fmt::Display for ErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl FromStr for ErrorType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"backend" => Ok(ErrorType::Backend),
"proxy" => Ok(ErrorType::Proxy),
"dfdaemon" => Ok(ErrorType::Dfdaemon),
_ => Err(format!("invalid error type: {}", s)),
}
}
}
pub fn get_tag(header: &HeaderMap) -> Option<String> {
header
.get(DRAGONFLY_TAG_HEADER)
.and_then(|tag| tag.to_str().ok())
.map(|tag| tag.to_string())
}
pub fn get_application(header: &HeaderMap) -> Option<String> {
header
.get(DRAGONFLY_APPLICATION_HEADER)
.and_then(|application| application.to_str().ok())
.map(|application| application.to_string())
}
pub fn get_priority(header: &HeaderMap) -> i32 {
let default_priority = Priority::Level6 as i32;
match header.get(DRAGONFLY_PRIORITY_HEADER) {
Some(priority) => match priority.to_str() {
Ok(priority) => match priority.parse::<i32>() {
Ok(priority) => priority,
Err(err) => {
error!("parse priority from header failed: {}", err);
default_priority
}
},
Err(err) => {
error!("get priority from header failed: {}", err);
default_priority
}
},
None => default_priority,
}
}
pub fn get_registry(header: &HeaderMap) -> Option<String> {
header
.get(DRAGONFLY_REGISTRY_HEADER)
.and_then(|registry| registry.to_str().ok())
.map(|registry| registry.to_string())
}
pub fn get_filtered_query_params(
header: &HeaderMap,
default_filtered_query_params: Vec<String>,
) -> Vec<String> {
match header.get(DRAGONFLY_FILTERED_QUERY_PARAMS_HEADER) {
Some(filters) => match filters.to_str() {
Ok(filters) => filters.split(',').map(|s| s.trim().to_string()).collect(),
Err(err) => {
error!("get filters from header failed: {}", err);
default_filtered_query_params
}
},
None => default_filtered_query_params,
}
}
pub fn get_use_p2p(header: &HeaderMap) -> bool {
match header.get(DRAGONFLY_USE_P2P_HEADER) {
Some(value) => match value.to_str() {
Ok(value) => value.eq_ignore_ascii_case("true"),
Err(err) => {
error!("get use p2p from header failed: {}", err);
false
}
},
None => false,
}
}
pub fn get_prefetch(header: &HeaderMap) -> Option<bool> {
match header.get(DRAGONFLY_PREFETCH_HEADER) {
Some(value) => match value.to_str() {
Ok(value) => Some(value.eq_ignore_ascii_case("true")),
Err(err) => {
error!("get use p2p from header failed: {}", err);
None
}
},
None => None,
}
}
pub fn get_output_path(header: &HeaderMap) -> Option<String> {
header
.get(DRAGONFLY_OUTPUT_PATH_HEADER)
.and_then(|output_path| output_path.to_str().ok())
.map(|output_path| output_path.to_string())
}
pub fn get_force_hard_link(header: &HeaderMap) -> bool {
match header.get(DRAGONFLY_FORCE_HARD_LINK_HEADER) {
Some(value) => match value.to_str() {
Ok(value) => value.eq_ignore_ascii_case("true"),
Err(err) => {
error!("get force hard link from header failed: {}", err);
false
}
},
None => false,
}
}
pub fn get_piece_length(header: &HeaderMap) -> Option<ByteSize> {
match header.get(DRAGONFLY_PIECE_LENGTH_HEADER) {
Some(piece_length) => match piece_length.to_str() {
Ok(piece_length) => match piece_length.parse::<ByteSize>() {
Ok(piece_length) => Some(piece_length),
Err(err) => {
error!("parse piece length from header failed: {}", err);
None
}
},
Err(err) => {
error!("get piece length from header failed: {}", err);
None
}
},
None => None,
}
}
pub fn get_content_for_calculating_task_id(header: &HeaderMap) -> Option<String> {
header
.get(DRAGONFLY_CONTENT_FOR_CALCULATING_TASK_ID_HEADER)
.and_then(|content| content.to_str().ok())
.map(|content| content.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use reqwest::header::{HeaderMap, HeaderValue};
#[test]
fn test_get_tag() {
let mut headers = HeaderMap::new();
headers.insert(DRAGONFLY_TAG_HEADER, HeaderValue::from_static("test-tag"));
assert_eq!(get_tag(&headers), Some("test-tag".to_string()));
let empty_headers = HeaderMap::new();
assert_eq!(get_tag(&empty_headers), None);
}
#[test]
fn test_get_application() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_APPLICATION_HEADER,
HeaderValue::from_static("test-app"),
);
assert_eq!(get_application(&headers), Some("test-app".to_string()));
let empty_headers = HeaderMap::new();
assert_eq!(get_application(&empty_headers), None);
}
#[test]
fn test_get_priority() {
let mut headers = HeaderMap::new();
headers.insert(DRAGONFLY_PRIORITY_HEADER, HeaderValue::from_static("5"));
assert_eq!(get_priority(&headers), 5);
let empty_headers = HeaderMap::new();
assert_eq!(get_priority(&empty_headers), Priority::Level6 as i32);
headers.insert(
DRAGONFLY_PRIORITY_HEADER,
HeaderValue::from_static("invalid"),
);
assert_eq!(get_priority(&headers), Priority::Level6 as i32);
}
#[test]
fn test_get_registry() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_REGISTRY_HEADER,
HeaderValue::from_static("test-registry"),
);
assert_eq!(get_registry(&headers), Some("test-registry".to_string()));
let empty_headers = HeaderMap::new();
assert_eq!(get_registry(&empty_headers), None);
}
#[test]
fn test_get_filtered_query_params() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_FILTERED_QUERY_PARAMS_HEADER,
HeaderValue::from_static("param1,param2"),
);
assert_eq!(
get_filtered_query_params(&headers, vec!["default".to_string()]),
vec!["param1".to_string(), "param2".to_string()]
);
let empty_headers = HeaderMap::new();
assert_eq!(
get_filtered_query_params(&empty_headers, vec!["default".to_string()]),
vec!["default".to_string()]
);
}
#[test]
fn test_get_use_p2p() {
let mut headers = HeaderMap::new();
headers.insert(DRAGONFLY_USE_P2P_HEADER, HeaderValue::from_static("true"));
assert!(get_use_p2p(&headers));
headers.insert(DRAGONFLY_USE_P2P_HEADER, HeaderValue::from_static("false"));
assert!(!get_use_p2p(&headers));
let empty_headers = HeaderMap::new();
assert!(!get_use_p2p(&empty_headers));
}
#[test]
fn test_get_prefetch() {
let mut headers = HeaderMap::new();
headers.insert(DRAGONFLY_PREFETCH_HEADER, HeaderValue::from_static("true"));
assert_eq!(get_prefetch(&headers), Some(true));
headers.insert(DRAGONFLY_PREFETCH_HEADER, HeaderValue::from_static("false"));
assert_eq!(get_prefetch(&headers), Some(false));
let empty_headers = HeaderMap::new();
assert_eq!(get_prefetch(&empty_headers), None);
}
#[test]
fn test_get_output_path() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_OUTPUT_PATH_HEADER,
HeaderValue::from_static("/path/to/output"),
);
assert_eq!(
get_output_path(&headers),
Some("/path/to/output".to_string())
);
let empty_headers = HeaderMap::new();
assert_eq!(get_output_path(&empty_headers), None);
}
#[test]
fn test_get_force_hard_link() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_FORCE_HARD_LINK_HEADER,
HeaderValue::from_static("true"),
);
assert!(get_force_hard_link(&headers));
headers.insert(
DRAGONFLY_FORCE_HARD_LINK_HEADER,
HeaderValue::from_static("false"),
);
assert!(!get_force_hard_link(&headers));
let empty_headers = HeaderMap::new();
assert!(!get_force_hard_link(&empty_headers));
}
#[test]
fn test_get_piece_length() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_PIECE_LENGTH_HEADER,
HeaderValue::from_static("4mib"),
);
assert_eq!(get_piece_length(&headers), Some(ByteSize::mib(4)));
let empty_headers = HeaderMap::new();
assert_eq!(get_piece_length(&empty_headers), None);
headers.insert(
DRAGONFLY_PIECE_LENGTH_HEADER,
HeaderValue::from_static("invalid"),
);
assert_eq!(get_piece_length(&headers), None);
headers.insert(DRAGONFLY_PIECE_LENGTH_HEADER, HeaderValue::from_static("0"));
assert_eq!(get_piece_length(&headers), Some(ByteSize::b(0)));
}
#[test]
fn test_get_content_for_calculating_task_id() {
let mut headers = HeaderMap::new();
headers.insert(
DRAGONFLY_CONTENT_FOR_CALCULATING_TASK_ID_HEADER,
HeaderValue::from_static("test-content"),
);
assert_eq!(
get_content_for_calculating_task_id(&headers),
Some("test-content".to_string())
);
let empty_headers = HeaderMap::new();
assert_eq!(get_registry(&empty_headers), None);
}
}