add a few more clients for testing

This commit is contained in:
Brandon Thomas committed 2026-06-09 20:04:53 -04:00
1 parent 671fe5892b
commit 6c053aba7c
4 files changed
+93 -8

No files matched your search

@@ -208,7 +208,8 @@ CREATE TABLE generic_inference_jobs (
-- Multiple debug_logs rows can share the same event token.
maybe_debug_log_event_token VARCHAR(32) DEFAULT NULL,
-- The platform the enqueuing request came from ('web', 'desktop_app', 'curl'),
-- The platform the enqueuing request came from ('web', 'desktop_app',
-- 'curl', 'python_requests', 'postman'),
-- inferred from the request's User-Agent header.
platform_type VARCHAR(16) DEFAULT NULL,
@@ -65,7 +65,8 @@ CREATE TABLE media_files (
-- What service provided the generation (if any).
maybe_generation_provider VARCHAR(16) DEFAULT NULL,
-- The platform the creating request came from ('web', 'desktop_app', 'curl'),
-- The platform the creating request came from ('web', 'desktop_app',
-- 'curl', 'python_requests', 'postman'),
-- inferred from the request's User-Agent header. Copied from the originating
-- generic_inference_jobs row for inference results.
platform_type VARCHAR(16) DEFAULT NULL,
@@ -21,6 +21,8 @@ pub enum PlatformType {
Web,
DesktopApp,
Curl,
PythonRequests,
Postman,
}
impl_enum_display_and_debug_using_to_str!(PlatformType);
@@ -33,6 +35,8 @@ impl PlatformType {
Self::Web => "web",
Self::DesktopApp => "desktop_app",
Self::Curl => "curl",
Self::PythonRequests => "python_requests",
Self::Postman => "postman",
}
}
@@ -41,6 +45,8 @@ impl PlatformType {
"web" => Ok(Self::Web),
"desktop_app" => Ok(Self::DesktopApp),
"curl" => Ok(Self::Curl),
"python_requests" => Ok(Self::PythonRequests),
"postman" => Ok(Self::Postman),
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
}
}
@@ -51,6 +57,8 @@ impl PlatformType {
Self::Web,
Self::DesktopApp,
Self::Curl,
Self::PythonRequests,
Self::Postman,
])
}
}
@@ -69,6 +77,8 @@ mod tests {
assert_serialization(PlatformType::Web, "web");
assert_serialization(PlatformType::DesktopApp, "desktop_app");
assert_serialization(PlatformType::Curl, "curl");
assert_serialization(PlatformType::PythonRequests, "python_requests");
assert_serialization(PlatformType::Postman, "postman");
}
#[test]
@@ -76,6 +86,8 @@ mod tests {
assert_eq!(PlatformType::Web.to_str(), "web");
assert_eq!(PlatformType::DesktopApp.to_str(), "desktop_app");
assert_eq!(PlatformType::Curl.to_str(), "curl");
assert_eq!(PlatformType::PythonRequests.to_str(), "python_requests");
assert_eq!(PlatformType::Postman.to_str(), "postman");
}
#[test]
@@ -83,6 +95,8 @@ mod tests {
assert_eq!(PlatformType::from_str("web").unwrap(), PlatformType::Web);
assert_eq!(PlatformType::from_str("desktop_app").unwrap(), PlatformType::DesktopApp);
assert_eq!(PlatformType::from_str("curl").unwrap(), PlatformType::Curl);
assert_eq!(PlatformType::from_str("python_requests").unwrap(), PlatformType::PythonRequests);
assert_eq!(PlatformType::from_str("postman").unwrap(), PlatformType::Postman);
}
#[test]
@@ -99,10 +113,12 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = PlatformType::all_variants();
assert_eq!(variants.len(), 3);
assert_eq!(variants.len(), 5);
assert_eq!(variants.pop_first(), Some(PlatformType::Web));
assert_eq!(variants.pop_first(), Some(PlatformType::DesktopApp));
assert_eq!(variants.pop_first(), Some(PlatformType::Curl));
assert_eq!(variants.pop_first(), Some(PlatformType::PythonRequests));
assert_eq!(variants.pop_first(), Some(PlatformType::Postman));
assert_eq!(variants.pop_first(), None);
}
}
@@ -9,11 +9,24 @@ const DESKTOP_CLIENT_USER_AGENT_PREFIX : &str = "storyteller-client";
/// User-Agent prefix sent by the curl CLI tool, eg. "curl/8.7.1".
const CURL_USER_AGENT_PREFIX : &str = "curl";
/// User-Agent prefix sent by the Python `requests` library.
/// Modern releases send "python-requests/2.31.0" (optionally followed by
/// "CPython/x.y.z OS/release" in older releases, eg.
/// "python-requests/0.14.2 CPython/2.7.3 Linux/3.2.0"). Some early releases
/// sent the bare "python-requests.org", which shares this prefix.
const PYTHON_REQUESTS_USER_AGENT_PREFIX : &str = "python-requests";
/// User-Agent prefix sent by Postman, eg. "PostmanRuntime/7.36.0".
/// All known Postman Runtime versions (2.x through 8.x) use this form.
/// (Compared case-insensitively.)
const POSTMAN_USER_AGENT_PREFIX : &str = "postmanruntime";
/// Infer the calling platform from the request's User-Agent header.
///
/// Returns `None` if the header is absent or unreadable. Otherwise, the ArtCraft
/// desktop client identifies itself with a "storyteller-client" prefix, curl-like
/// CLI callers are flagged as such, and anything else is assumed to be a browser.
/// desktop client identifies itself with a "storyteller-client" prefix, known
/// CLI/API tools (curl, python-requests, Postman) are flagged as such, and
/// anything else is assumed to be a browser.
pub fn get_request_platform_type(http_request: &HttpRequest) -> Option<PlatformType> {
let user_agent_header_name = HeaderName::from_static("user-agent");
let header_map : &HeaderMap = http_request.headers();
@@ -32,9 +45,16 @@ fn platform_type_from_user_agent(user_agent: &str) -> Option<PlatformType> {
if user_agent.starts_with(DESKTOP_CLIENT_USER_AGENT_PREFIX) {
return Some(PlatformType::DesktopApp);
}
if user_agent.to_ascii_lowercase().starts_with(CURL_USER_AGENT_PREFIX) {
let lowercase_user_agent = user_agent.to_ascii_lowercase();
if lowercase_user_agent.starts_with(CURL_USER_AGENT_PREFIX) {
return Some(PlatformType::Curl);
}
if lowercase_user_agent.starts_with(PYTHON_REQUESTS_USER_AGENT_PREFIX) {
return Some(PlatformType::PythonRequests);
}
if lowercase_user_agent.starts_with(POSTMAN_USER_AGENT_PREFIX) {
return Some(PlatformType::Postman);
}
Some(PlatformType::Web)
}
@@ -69,6 +89,20 @@ mod tests {
Some(PlatformType::Curl));
}
#[test]
fn python_requests_user_agent() {
assert_eq!(
platform_type_for_user_agent_header("python-requests/2.31.0"),
Some(PlatformType::PythonRequests));
}
#[test]
fn postman_user_agent() {
assert_eq!(
platform_type_for_user_agent_header("PostmanRuntime/7.36.0"),
Some(PlatformType::Postman));
}
#[test]
fn browser_user_agent() {
assert_eq!(
@@ -105,10 +139,43 @@ mod tests {
assert_eq!(platform_type_from_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)"), Some(PlatformType::Web));
}
#[test]
fn python_requests_versions() {
// Modern releases: bare "python-requests/<version>".
assert_eq!(platform_type_from_user_agent("python-requests/2.31.0"), Some(PlatformType::PythonRequests));
assert_eq!(platform_type_from_user_agent("python-requests/2.32.3"), Some(PlatformType::PythonRequests));
// Older releases appended runtime and OS details.
assert_eq!(
platform_type_from_user_agent("python-requests/0.14.2 CPython/2.7.3 Linux/3.2.0-armv7l"),
Some(PlatformType::PythonRequests));
assert_eq!(
platform_type_from_user_agent("python-requests/1.1.0 CPython/2.6.6 Linux/2.6.32-431.3.1.el6.x86_64"),
Some(PlatformType::PythonRequests));
// Some early releases sent the bare project domain.
assert_eq!(platform_type_from_user_agent("python-requests.org"), Some(PlatformType::PythonRequests));
// Case-insensitive.
assert_eq!(platform_type_from_user_agent("Python-Requests/2.25.1"), Some(PlatformType::PythonRequests));
}
#[test]
fn postman_versions() {
// Modern Postman Runtime 7.x/8.x.
assert_eq!(platform_type_from_user_agent("PostmanRuntime/7.36.0"), Some(PlatformType::Postman));
assert_eq!(platform_type_from_user_agent("PostmanRuntime/8.0.3"), Some(PlatformType::Postman));
// Historical Postman Runtime versions.
assert_eq!(platform_type_from_user_agent("PostmanRuntime/2.4.5"), Some(PlatformType::Postman));
assert_eq!(platform_type_from_user_agent("PostmanRuntime/7.6.0"), Some(PlatformType::Postman));
// Case-insensitive, with surrounding whitespace.
assert_eq!(platform_type_from_user_agent(" postmanruntime/7.26.8 "), Some(PlatformType::Postman));
}
#[test]
fn unknown_tools_are_web() {
assert_eq!(platform_type_from_user_agent("python-requests/2.31.0"), Some(PlatformType::Web));
assert_eq!(platform_type_from_user_agent("PostmanRuntime/7.36.0"), Some(PlatformType::Web));
assert_eq!(platform_type_from_user_agent("Go-http-client/2.0"), Some(PlatformType::Web));
assert_eq!(platform_type_from_user_agent("insomnia/8.6.1"), Some(PlatformType::Web));
// Other Python HTTP clients are not the `requests` library.
assert_eq!(platform_type_from_user_agent("python-httpx/0.27.0"), Some(PlatformType::Web));
assert_eq!(platform_type_from_user_agent("Python-urllib/3.11"), Some(PlatformType::Web));
}
#[test]