feat(tvix/nar-bridge): send content-type headers

This prevents browsers from treating NARInfo and nix-cache-info paths as
a separate "Download", but just show it in plaintext.

Change-Id: If99abe20ef1d24e4fa86c055160861ca47aa81ce
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12267
Tested-by: BuildkiteCI
Autosubmit: flokli <flokli@flokli.de>
Reviewed-by: Connor Brewster <cbrewster@hey.com>
This commit is contained in:
Florian Klink 2024-08-23 10:34:53 +03:00 committed by clbot
parent 35d5811eec
commit a4ebc8da7c
3 changed files with 18 additions and 9 deletions

View file

@ -1,7 +1,9 @@
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::{head, put}; use axum::routing::{head, put};
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use lru::LruCache; use lru::LruCache;
use nix_compat::nix_http;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::sync::Arc; use std::sync::Arc;
@ -71,9 +73,12 @@ async fn four_o_four() -> Result<(), StatusCode> {
Err(StatusCode::NOT_FOUND) Err(StatusCode::NOT_FOUND)
} }
async fn nix_cache_info(priority: u64) -> String { async fn nix_cache_info(priority: u64) -> impl IntoResponse {
format!( (
"StoreDir: /nix/store\nWantMassQuery: 1\nPriority: {}\n", [("Content-Type", nix_http::MIME_TYPE_CACHE_INFO)],
priority format!(
"StoreDir: /nix/store\nWantMassQuery: 1\nPriority: {}\n",
priority
),
) )
} }

View file

@ -77,6 +77,7 @@ pub async fn get(
.status(StatusCode::OK) .status(StatusCode::OK)
.header("cache-control", "max-age=31536000, immutable") .header("cache-control", "max-age=31536000, immutable")
.header("content-length", nar_size) .header("content-length", nar_size)
.header("content-type", nix_http::MIME_TYPE_NAR)
.body(Body::from_stream(ReaderStream::new(r))) .body(Body::from_stream(ReaderStream::new(r)))
.unwrap()) .unwrap())
} }

View file

@ -1,4 +1,4 @@
use axum::http::StatusCode; use axum::{http::StatusCode, response::IntoResponse};
use bytes::Bytes; use bytes::Bytes;
use nix_compat::{narinfo::NarInfo, nix_http, nixbase32}; use nix_compat::{narinfo::NarInfo, nix_http, nixbase32};
use prost::Message; use prost::Message;
@ -17,7 +17,7 @@ pub async fn head(
axum::extract::State(AppState { axum::extract::State(AppState {
path_info_service, .. path_info_service, ..
}): axum::extract::State<AppState>, }): axum::extract::State<AppState>,
) -> Result<&'static str, StatusCode> { ) -> Result<impl IntoResponse, StatusCode> {
let digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::NOT_FOUND)?; let digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::NOT_FOUND)?;
Span::current().record("path_info.digest", &narinfo_str[0..32]); Span::current().record("path_info.digest", &narinfo_str[0..32]);
@ -30,7 +30,7 @@ pub async fn head(
})? })?
.is_some() .is_some()
{ {
Ok("") Ok(([("content-type", nix_http::MIME_TYPE_NARINFO)], ""))
} else { } else {
warn!("PathInfo not found"); warn!("PathInfo not found");
Err(StatusCode::NOT_FOUND) Err(StatusCode::NOT_FOUND)
@ -43,7 +43,7 @@ pub async fn get(
axum::extract::State(AppState { axum::extract::State(AppState {
path_info_service, .. path_info_service, ..
}): axum::extract::State<AppState>, }): axum::extract::State<AppState>,
) -> Result<String, StatusCode> { ) -> Result<impl IntoResponse, StatusCode> {
let digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::NOT_FOUND)?; let digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::NOT_FOUND)?;
Span::current().record("path_info.digest", &narinfo_str[0..32]); Span::current().record("path_info.digest", &narinfo_str[0..32]);
@ -88,7 +88,10 @@ pub async fn get(
narinfo.url = &url; narinfo.url = &url;
Ok(narinfo.to_string()) Ok((
[("content-type", nix_http::MIME_TYPE_NARINFO)],
narinfo.to_string(),
))
} }
#[instrument(skip(path_info_service, root_nodes, request))] #[instrument(skip(path_info_service, root_nodes, request))]