feat(nix-compat/nix_http): init parse_nar[info]_str
This moves the URL component parsing code we had in nar-bridge to nix-compat. We change the function signature to return an Option, not a Result<_, StatusCode>. This allows returning more appropriate error codes, as we can ok_or(…) at the callsite, which we now do: on an upload to an invalid path, we now return "unauthorized", while on a GET/HEAD, we return "not found". This also adds support to parse compression suffixes. While not supported in nar-bridge, other users of nix-compat might very well want to parse these paths. Also fix the error message when parsing NAR urls, it mentioned 32, not 52, which is a copypasta error from the narinfo URL parsing code. Change-Id: Id1be9a8044814b54ce68b125c52dfe933c9c4f74 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12260 Reviewed-by: raitobezarius <tvl@lahfa.xyz> Autosubmit: flokli <flokli@flokli.de> Tested-by: BuildkiteCI
This commit is contained in:
parent
2357079891
commit
e03ea11bad
15 changed files with 634 additions and 178 deletions
|
|
@ -2,6 +2,7 @@ pub(crate) mod aterm;
|
|||
pub mod derivation;
|
||||
pub mod nar;
|
||||
pub mod narinfo;
|
||||
pub mod nix_http;
|
||||
pub mod nixbase32;
|
||||
pub mod nixcpp;
|
||||
pub mod nixhash;
|
||||
|
|
|
|||
108
tvix/nix-compat/src/nix_http/mod.rs
Normal file
108
tvix/nix-compat/src/nix_http/mod.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use tracing::trace;
|
||||
|
||||
use crate::nixbase32;
|
||||
|
||||
/// Parses a `14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar`
|
||||
/// string and returns the nixbase32-decoded digest, as well as the compression
|
||||
/// suffix (which might be empty).
|
||||
pub fn parse_nar_str(s: &str) -> Option<([u8; 32], &str)> {
|
||||
if !s.is_char_boundary(52) {
|
||||
trace!("invalid string, no char boundary at 52");
|
||||
return None;
|
||||
}
|
||||
|
||||
let (hash_str, suffix) = s.split_at(52);
|
||||
|
||||
// we know hash_str is 52 bytes, so it's ok to unwrap here.
|
||||
let hash_str_fixed: [u8; 52] = hash_str.as_bytes().try_into().unwrap();
|
||||
|
||||
match suffix.strip_prefix(".nar") {
|
||||
Some(compression_suffix) => match nixbase32::decode_fixed(hash_str_fixed) {
|
||||
Err(e) => {
|
||||
trace!(err=%e, "invalid nixbase32 encoding");
|
||||
None
|
||||
}
|
||||
Ok(digest) => Some((digest, compression_suffix)),
|
||||
},
|
||||
None => {
|
||||
trace!("no .nar suffix");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a `3mzh8lvgbynm9daj7c82k2sfsfhrsfsy.narinfo` string and returns the
|
||||
/// nixbase32-decoded digest.
|
||||
pub fn parse_narinfo_str(s: &str) -> Option<[u8; 20]> {
|
||||
if !s.is_char_boundary(32) {
|
||||
trace!("invalid string, no char boundary at 32");
|
||||
return None;
|
||||
}
|
||||
|
||||
match s.split_at(32) {
|
||||
(hash_str, ".narinfo") => {
|
||||
// we know this is 32 bytes, so it's ok to unwrap here.
|
||||
let hash_str_fixed: [u8; 32] = hash_str.as_bytes().try_into().unwrap();
|
||||
|
||||
match nixbase32::decode_fixed(hash_str_fixed) {
|
||||
Err(e) => {
|
||||
trace!(err=%e, "invalid nixbase32 encoding");
|
||||
None
|
||||
}
|
||||
Ok(digest) => Some(digest),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
trace!("invalid string, no .narinfo suffix");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{parse_nar_str, parse_narinfo_str};
|
||||
use hex_literal::hex;
|
||||
|
||||
#[test]
|
||||
fn parse_nar_str_success() {
|
||||
assert_eq!(
|
||||
(
|
||||
hex!("13a8cf7ca57f68a9f1752acee36a72a55187d3a954443c112818926f26109d91"),
|
||||
""
|
||||
),
|
||||
parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar").unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
(
|
||||
hex!("13a8cf7ca57f68a9f1752acee36a72a55187d3a954443c112818926f26109d91"),
|
||||
".xz"
|
||||
),
|
||||
parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar.xz").unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_nar_str_failure() {
|
||||
assert!(parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0").is_none());
|
||||
assert!(
|
||||
parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0🦊.nar").is_none()
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn parse_narinfo_str_success() {
|
||||
assert_eq!(
|
||||
hex!("8a12321522fd91efbd60ebb2481af88580f61600"),
|
||||
parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44la.narinfo").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_narinfo_str_failure() {
|
||||
assert!(parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44la").is_none());
|
||||
assert!(parse_narinfo_str("/00bgd045z0d4icpbc2yyz4gx48ak44la").is_none());
|
||||
assert!(parse_narinfo_str("000000").is_none());
|
||||
assert!(parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44l🦊.narinfo").is_none());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue