nar_bridge/
lib.rs

1use axum::http::StatusCode;
2use axum::response::IntoResponse;
3use axum::routing::{head, put};
4use axum::{routing::get, Router};
5use lru::LruCache;
6use nix_compat::nix_http;
7use parking_lot::RwLock;
8use snix_castore::blobservice::BlobService;
9use snix_castore::directoryservice::DirectoryService;
10use snix_castore::Node;
11use snix_store::pathinfoservice::PathInfoService;
12use std::num::NonZeroUsize;
13use std::sync::Arc;
14
15mod nar;
16mod narinfo;
17
18#[derive(Clone)]
19pub struct AppState {
20    blob_service: Arc<dyn BlobService>,
21    directory_service: Arc<dyn DirectoryService>,
22    path_info_service: Arc<dyn PathInfoService>,
23
24    /// Lookup table from NarHash to [Node], necessary to populate the root_node
25    /// field of the PathInfo when processing the narinfo upload.
26    root_nodes: Arc<RwLock<LruCache<[u8; 32], Node>>>,
27}
28
29impl AppState {
30    pub fn new(
31        blob_service: Arc<dyn BlobService>,
32        directory_service: Arc<dyn DirectoryService>,
33        path_info_service: Arc<dyn PathInfoService>,
34        root_nodes_cache_capacity: NonZeroUsize,
35    ) -> Self {
36        Self {
37            blob_service,
38            directory_service,
39            path_info_service,
40            root_nodes: Arc::new(RwLock::new(LruCache::new(root_nodes_cache_capacity))),
41        }
42    }
43}
44
45pub fn gen_router(priority: u64) -> Router<AppState> {
46    #[cfg(feature = "otlp")]
47    let metrics_meter = opentelemetry::global::meter("nar-bridge");
48
49    #[cfg(feature = "otlp")]
50    let metrics_layer = tower_otel_http_metrics::HTTPMetricsLayerBuilder::new()
51        .with_meter(metrics_meter)
52        .build()
53        .unwrap();
54
55    let router = Router::new()
56        .route("/", get(root))
57        .route("/nar/:nar_str", get(four_o_four))
58        .route("/nar/:nar_str", head(nar::head_root_nodes))
59        .route("/nar/:nar_str", put(nar::put))
60        .route("/nar/snix-castore/:root_node_enc", get(nar::get_head))
61        .route("/nar/snix-castore/:root_node_enc", head(nar::get_head))
62        .route("/:narinfo_str", get(narinfo::get))
63        .route("/:narinfo_str", head(narinfo::head))
64        .route("/:narinfo_str", put(narinfo::put))
65        .route("/nix-cache-info", get(move || nix_cache_info(priority)));
66
67    let router = router.layer(tower_http::compression::CompressionLayer::new());
68
69    #[cfg(feature = "otlp")]
70    return router.layer(metrics_layer);
71    #[cfg(not(feature = "otlp"))]
72    return router;
73}
74
75async fn root() -> &'static str {
76    "Hello from nar-bridge"
77}
78
79async fn four_o_four() -> Result<(), StatusCode> {
80    Err(StatusCode::NOT_FOUND)
81}
82
83async fn nix_cache_info(priority: u64) -> impl IntoResponse {
84    (
85        [("Content-Type", nix_http::MIME_TYPE_CACHE_INFO)],
86        format!(
87            "StoreDir: /nix/store\nWantMassQuery: 1\nPriority: {}\n",
88            priority
89        ),
90    )
91}