refactor(cache): redesign cache system with structured keys and improved performance
- Add repo_path parameter to cached_response and cached_vec_response functions - Implement structured cache key format with namespace, repo_path, and request proto - Replace global cache with Moka in-memory cache using weight-based eviction - Set 256MB memory cap with 10-minute TTL and 2-minute TTI policy - Add metrics collection for cache operations and evictions - Implement efficient repo-scoped invalidation using key structure - Add detailed documentation comments explaining cache architecture - Remove outdated dependencies and update dependency versions - Add error handling for encoding failures in cache operations - Optimize Vec responses with length-delimited encoding and pre-allocation
This commit is contained in:
+33
-5
@@ -11,9 +11,7 @@ use crate::error::GitResult;
|
||||
/// Git disallows: space, `~`, `^`, `:`, `?`, `*`, `[`, `\`, and all ASCII
|
||||
/// control characters (bytes 0–31 and 127). The control characters are
|
||||
/// checked separately via `is_ascii_control()`.
|
||||
const FORBIDDEN_REF_CHARS: &[char] = &[
|
||||
'~', '^', ':', '?', '*', '[', '\\', ' ',
|
||||
];
|
||||
const FORBIDDEN_REF_CHARS: &[char] = &['~', '^', ':', '?', '*', '[', '\\', ' '];
|
||||
|
||||
/// Returns true if `c` is an ASCII control character (bytes 0–31, 127).
|
||||
fn is_ascii_control(c: char) -> bool {
|
||||
@@ -30,6 +28,24 @@ fn is_ascii_control(c: char) -> bool {
|
||||
/// - Cannot contain '..'
|
||||
/// - Cannot contain '@{'
|
||||
/// - Cannot be empty
|
||||
pub fn validate_oid_hex(hex: &str) -> GitResult<()> {
|
||||
if hex.is_empty() {
|
||||
return Err(GitError::InvalidArgument("oid hex cannot be empty".into()));
|
||||
}
|
||||
if !(4..=64).contains(&hex.len()) {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"oid hex length must be 4..=64 chars: {}",
|
||||
hex.len()
|
||||
)));
|
||||
}
|
||||
if !hex.chars().all(|c| c.is_ascii_hexdigit()) {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"oid hex contains non-hex character: {hex}"
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_ref_name(name: &str) -> GitResult<()> {
|
||||
if name.is_empty() {
|
||||
return Err(GitError::InvalidArgument("ref name cannot be empty".into()));
|
||||
@@ -253,8 +269,10 @@ pub fn validate_config_key(key: &str) -> GitResult<()> {
|
||||
for pattern in DANGEROUS_CONFIG_KEYS {
|
||||
if pattern.contains('*') {
|
||||
// e.g. "remote.*.url" — match any "remote.<something>.url"
|
||||
let (prefix, suffix) = pattern.split_once('*').unwrap();
|
||||
if key.starts_with(prefix) && key.ends_with(suffix) {
|
||||
if let Some((prefix, suffix)) = pattern.split_once('*')
|
||||
&& key.starts_with(prefix)
|
||||
&& key.ends_with(suffix)
|
||||
{
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"config key '{key}' matches dangerous pattern '{pattern}'"
|
||||
)));
|
||||
@@ -347,6 +365,16 @@ pub fn validate_relative_path(path: &str) -> GitResult<()> {
|
||||
"relative_path must be relative, not absolute".into(),
|
||||
));
|
||||
}
|
||||
if path.contains('\0') {
|
||||
return Err(GitError::InvalidArgument(
|
||||
"relative_path cannot contain null byte".into(),
|
||||
));
|
||||
}
|
||||
if path.len() > 4096 {
|
||||
return Err(GitError::InvalidArgument(
|
||||
"relative_path too long (max 4096 chars)".into(),
|
||||
));
|
||||
}
|
||||
if path.contains("..") {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"path traversal detected: relative_path contains '..': {path}"
|
||||
|
||||
Reference in New Issue
Block a user