[MIRROR ONLY] A correct and efficient ATProto blob proxy for secure content delivery. codeberg.org/Blooym/porxie
36
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: move server into module

Lyna b66567f6 17c0fa96

+250 -175
+2 -1
crates/porxie/src/blob_service.rs
··· 6 6 }; 7 7 use bytes::Bytes; 8 8 use cid::Cid; 9 + use core::{num::NonZeroU64, time::Duration}; 9 10 use jacquard_common::types::did::Did; 10 11 use mime::Mime; 11 12 use moka::{future::Cache as MokaCache, policy::EvictionPolicy}; 12 13 use multihash_codetable::{Code, MultihashDigest}; 13 14 use reqwest::{StatusCode, header, header::HeaderValue}; 14 - use std::{num::NonZeroU64, sync::Arc, time::Duration}; 15 + use std::sync::Arc; 15 16 use thiserror::Error; 16 17 use tracing::instrument; 17 18
+1 -1
crates/porxie/src/cache.rs
··· 1 - use std::{cmp, num::NonZeroU64}; 1 + use core::{cmp, num::NonZeroU64}; 2 2 use thiserror::Error; 3 3 4 4 #[derive(Debug, Error)]
+3 -3
crates/porxie/src/extractors/admin_xrpc_auth.rs crates/porxie/src/server/extractors/admin_xrpc_auth.rs
··· 1 - use crate::AppState; 1 + use crate::server::ServerState; 2 2 use axum::{ 3 3 extract::FromRequestParts, 4 4 http::{StatusCode, request::Parts}, ··· 17 17 /// Specification: <https://atproto.com/specs/xrpc#admin-token-temporary-specification>. 18 18 pub struct AdminXrpcAuth; 19 19 20 - impl FromRequestParts<Arc<AppState>> for AdminXrpcAuth { 20 + impl FromRequestParts<Arc<ServerState>> for AdminXrpcAuth { 21 21 type Rejection = StatusCode; 22 22 23 23 async fn from_request_parts( 24 24 parts: &mut Parts, 25 - state: &Arc<AppState>, 25 + state: &Arc<ServerState>, 26 26 ) -> Result<Self, Self::Rejection> { 27 27 let Ok(basic_auth) = 28 28 TypedHeader::<Authorization<Basic>>::from_request_parts(parts, state).await
crates/porxie/src/extractors/mod.rs crates/porxie/src/server/extractors/mod.rs
+1 -1
crates/porxie/src/http.rs
··· 1 1 use bytes::Bytes; 2 + use core::num::NonZeroU64; 2 3 use futures_util::StreamExt; 3 - use std::num::NonZeroU64; 4 4 use thiserror::Error; 5 5 6 6 pub const PORXIE_USER_AGENT: &str = concat!(
+2 -1
crates/porxie/src/identity_service.rs
··· 1 1 use crate::http::PORXIE_USER_AGENT; 2 + use core::{str::FromStr, time::Duration}; 2 3 use jacquard_common::types::did::Did; 3 4 use jacquard_identity::{ 4 5 JacquardResolver, ··· 6 7 }; 7 8 use moka::{future::Cache as MokaCache, policy::EvictionPolicy}; 8 9 use reqwest::Url; 9 - use std::{str::FromStr, sync::Arc, time::Duration}; 10 + use std::sync::Arc; 10 11 use thiserror::Error; 11 12 use tracing::instrument; 12 13
+9 -154
crates/porxie/src/main.rs
··· 2 2 3 3 mod blob_service; 4 4 mod cache; 5 - mod extractors; 6 5 mod http; 7 6 mod identity_service; 8 7 mod mime; 9 8 mod policy_client; 10 - mod routes; 9 + mod server; 11 10 mod types; 12 11 13 12 use crate::{ ··· 15 14 cache::compute_cache_sizes, 16 15 identity_service::{IdentityService, IdentityServiceOptions}, 17 16 policy_client::{PolicyClient, PolicyClientOptions}, 18 - routes::{ 19 - get_blob_handler, get_index_handler, 20 - xrpc::{ 21 - dev_blooym::porxie::{ 22 - cache::{xrpc_cache_purge_actor_handler, xrpc_cache_purge_blob_handler}, 23 - xrpc_compat_get_blob_handler, 24 - }, 25 - xrpc_fallback_handler, xrpc_get_health_handler, 26 - }, 27 - }, 17 + server::{PorxieServer, PorxieServerOptions, address::Address}, 28 18 }; 29 19 use ::mime::Mime; 30 - use anyhow::{Context, bail}; 31 - use axum::{ 32 - Router, 33 - extract::Request, 34 - http::{HeaderName, HeaderValue, StatusCode, header}, 35 - middleware::{self as axum_middleware, Next}, 36 - response::Response, 37 - routing::{any, get, post}, 38 - }; 20 + use axum::http::{HeaderName, HeaderValue}; 39 21 use bytesize::ByteSize; 40 22 use clap::{Args, Parser}; 23 + use core::num::NonZeroU64; 41 24 use dotenvy::dotenv; 42 25 use reqwest::Url; 43 - use std::{net::SocketAddr, num::NonZeroU64, path::PathBuf, str::FromStr, sync::Arc}; 44 - use tower_http::{ 45 - catch_panic::CatchPanicLayer, 46 - normalize_path::NormalizePathLayer, 47 - timeout::TimeoutLayer, 48 - trace::{self, DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer}, 49 - }; 50 - use tracing::Level; 51 26 use tracing_subscriber::EnvFilter; 52 27 53 28 // Jemalloc seems to perform better compared to most system allocators, ··· 57 32 // It especially performs a lot better on MUSL (when last benchmarked). 58 33 #[global_allocator] 59 34 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 60 - 61 - #[derive(Debug, Clone)] 62 - enum AddressType { 63 - /// An IP socket address. 64 - Ip(SocketAddr), 65 - /// A UNIX socket path. 66 - #[cfg(unix)] 67 - Unix(PathBuf), 68 - } 69 - 70 - impl FromStr for AddressType { 71 - type Err = anyhow::Error; 72 - 73 - fn from_str(s: &str) -> Result<Self, Self::Err> { 74 - #[cfg(unix)] 75 - if let Some(path) = s.strip_prefix("unix:") { 76 - return Ok(AddressType::Unix(PathBuf::from(path))); 77 - } 78 - if let Some(ip) = s.strip_prefix("ip:") { 79 - return Ok(ip.parse::<SocketAddr>().map(AddressType::Ip)?); 80 - } 81 - 82 - #[cfg(unix)] 83 - bail!("unknown address binding type, expected 'ip:<addr>' or 'unix:<path>'".to_string(),); 84 - #[cfg(not(unix))] 85 - bail!("unknown address binding type, expected 'ip:<addr>'".to_string()); 86 - } 87 - } 88 35 89 36 #[derive(Parser)] 90 37 #[clap( ··· 124 71 env = "PORXIE_SERVER_ADDRESS", 125 72 default_value = "ip:127.0.0.1:6314" 126 73 )] 127 - address: AddressType, 74 + address: Address, 128 75 129 76 /// Admin password for authenticating priviledged requests. 130 77 /// ··· 417 364 http_connect_timeout: humantime::Duration, 418 365 } 419 366 420 - struct AppState { 421 - // Authentication. 422 - admin_password: Option<String>, 423 - // Blob handling. 424 - allowed_mimetypes: Vec<Mime>, 425 - blob_service: BlobService, 426 - cache_control_header: HeaderValue, 427 - max_blob_size: NonZeroU64, 428 - // Policy. 429 - policy_client: Option<PolicyClient>, 430 - policy_fail_open: bool, 431 - // Identity. 432 - identity_service: IdentityService, 433 - } 434 - 435 367 #[tokio::main(flavor = "multi_thread")] 436 368 async fn main() -> anyhow::Result<()> { 437 369 dotenv().ok(); ··· 440 372 .init(); 441 373 let args = AppArgs::parse(); 442 374 443 - // Setup state. 444 375 let cache_sizes = compute_cache_sizes(args.cache.size)?; 445 - let app_state = Arc::new(AppState { 376 + let server = PorxieServer::new(PorxieServerOptions { 377 + blob_processing_timeout: args.blob.processing_timeout.into(), 446 378 identity_service: IdentityService::new(IdentityServiceOptions { 447 379 cache_memory_allocation: cache_sizes.identity, 448 380 cache_ttl: args.cache.identity_ttl.into(), ··· 481 413 policy_fail_open: args.policy.fail_open, 482 414 }); 483 415 484 - // Setup router. 485 - let router = Router::new() 486 - .route("/", get(get_index_handler)) 487 - .route( 488 - "/{did}/{cid}", 489 - get(get_blob_handler).layer(TimeoutLayer::with_status_code( 490 - StatusCode::REQUEST_TIMEOUT, 491 - args.blob.processing_timeout.into(), 492 - )), 493 - ) 494 - .nest( 495 - "/xrpc", 496 - Router::new() 497 - .route("/_health", get(xrpc_get_health_handler)) 498 - .route( 499 - "/dev.blooym.porxie.getBlob", 500 - get(xrpc_compat_get_blob_handler).layer(TimeoutLayer::with_status_code( 501 - StatusCode::REQUEST_TIMEOUT, 502 - args.blob.processing_timeout.into(), 503 - )), 504 - ) 505 - .route( 506 - "/dev.blooym.porxie.cache.purgeActor", 507 - post(xrpc_cache_purge_actor_handler), 508 - ) 509 - .route( 510 - "/dev.blooym.porxie.cache.purgeBlob", 511 - post(xrpc_cache_purge_blob_handler), 512 - ) 513 - // Ensure /xrpc/... routes don't fall through elsewhere. 514 - .route("/{rest}", any(xrpc_fallback_handler)), 515 - ) 516 - .layer( 517 - TraceLayer::new_for_http() 518 - .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) 519 - .on_request(DefaultOnRequest::default().level(Level::DEBUG)) 520 - .on_response(DefaultOnResponse::default().level(Level::INFO)) 521 - .on_failure(DefaultOnFailure::default().level(Level::ERROR)), 522 - ) 523 - .layer(NormalizePathLayer::trim_trailing_slash()) 524 - .layer(CatchPanicLayer::new()) 525 - .layer(axum_middleware::from_fn(additional_headers_middleware)) 526 - .with_state(app_state); 527 - 528 - // Start server listener on specified address. 529 - match args.server.address { 530 - AddressType::Ip(ip) => { 531 - let listener = tokio::net::TcpListener::bind(ip) 532 - .await 533 - .context("failed to bind tcp listener")?; 534 - tracing::info!("server listening on http://{ip}"); 535 - axum::serve(listener, router) 536 - .with_graceful_shutdown(shutdown_signal()) 537 - .await?; 538 - } 539 - #[cfg(unix)] 540 - AddressType::Unix(path) => { 541 - let _ = std::fs::remove_file(&path); 542 - let listener = 543 - tokio::net::UnixListener::bind(&path).context("failed to bind unix listener")?; 544 - tracing::info!("server listening on unix:{}", path.display()); 545 - axum::serve(listener, router) 546 - .with_graceful_shutdown(shutdown_signal()) 547 - .await?; 548 - let _ = std::fs::remove_file(&path); 549 - } 550 - } 416 + server.start(args.server.address, shutdown_signal()).await?; 551 417 552 418 Ok(()) 553 419 } 554 420 555 - async fn additional_headers_middleware(req: Request, next: Next) -> Response { 556 - let mut res = next.run(req).await; 557 - let res_headers = res.headers_mut(); 558 - res_headers.insert( 559 - header::SERVER, 560 - const { HeaderValue::from_static(env!("CARGO_PKG_NAME")) }, 561 - ); 562 - res_headers.insert("X-Robots-Tag", const { HeaderValue::from_static("none") }); 563 - res 564 - } 565 - 566 421 // https://github.com/tokio-rs/axum/blob/15917c6dbcb4a48707a20e9cfd021992a279a662/examples/graceful-shutdown/src/main.rs#L55 567 422 async fn shutdown_signal() { 568 423 let ctrl_c = async { ··· 580 435 }; 581 436 582 437 #[cfg(not(unix))] 583 - let terminate = std::future::pending::<()>(); 438 + let terminate = core::future::pending::<()>(); 584 439 585 440 tokio::select! { 586 441 _ = ctrl_c => {},
+1 -1
crates/porxie/src/mime.rs
··· 66 66 67 67 #[cfg(test)] 68 68 mod tests { 69 + use core::str::FromStr; 69 70 use mime::Mime; 70 - use std::str::FromStr; 71 71 72 72 #[test] 73 73 fn no_match() {
+5 -3
crates/porxie/src/routes/blob.rs crates/porxie/src/server/routes/blob.rs
··· 1 1 use crate::{ 2 - AppState, 3 2 blob_service::{BlobDownloadError, BlobOwnershipError, BlobUrlResolver}, 4 3 policy_client::PolicyDecision, 5 - routes::{CACHE_CONTROL_NOCACHE_VALUE, XrpcErrorResponse}, 4 + server::{ 5 + ServerState, 6 + routes::{CACHE_CONTROL_NOCACHE_VALUE, XrpcErrorResponse}, 7 + }, 6 8 types::blob_cid::BlobCid, 7 9 }; 8 10 use axum::{ ··· 18 20 /// Fetch a blob from a given upstream and return it. 19 21 pub async fn get_blob_handler( 20 22 Path((raw_did, raw_cid)): Path<(String, String)>, 21 - State(state): State<Arc<AppState>>, 23 + State(state): State<Arc<ServerState>>, 22 24 ) -> Result< 23 25 Response, 24 26 (
crates/porxie/src/routes/index.rs crates/porxie/src/server/routes/index.rs
crates/porxie/src/routes/mod.rs crates/porxie/src/server/routes/mod.rs
crates/porxie/src/routes/xrpc/dev_blooym/mod.rs crates/porxie/src/server/routes/xrpc/dev_blooym/mod.rs
crates/porxie/src/routes/xrpc/dev_blooym/porxie/cache/mod.rs crates/porxie/src/server/routes/xrpc/dev_blooym/porxie/cache/mod.rs
+2 -2
crates/porxie/src/routes/xrpc/dev_blooym/porxie/cache/purge_actor.rs crates/porxie/src/server/routes/xrpc/dev_blooym/porxie/cache/purge_actor.rs
··· 1 - use crate::{AppState, extractors::AdminXrpcAuth}; 1 + use crate::server::{ServerState, extractors::AdminXrpcAuth}; 2 2 use axum::extract::State; 3 3 use jacquard_axum::ExtractXrpc; 4 4 use lexgen::dev_blooym::porxie::cache::purge_actor::PurgeActorRequest; ··· 7 7 8 8 pub async fn xrpc_cache_purge_actor_handler( 9 9 _auth: AdminXrpcAuth, 10 - State(state): State<Arc<AppState>>, 10 + State(state): State<Arc<ServerState>>, 11 11 ExtractXrpc(request): ExtractXrpc<PurgeActorRequest>, 12 12 ) -> StatusCode { 13 13 if let Some(ref policy_client) = state.policy_client {
+6 -4
crates/porxie/src/routes/xrpc/dev_blooym/porxie/cache/purge_blob.rs crates/porxie/src/server/routes/xrpc/dev_blooym/porxie/cache/purge_blob.rs
··· 1 1 use crate::{ 2 - AppState, 3 - extractors::AdminXrpcAuth, 4 - routes::{CACHE_CONTROL_NOCACHE_VALUE, XrpcErrorResponse}, 2 + server::{ 3 + ServerState, 4 + extractors::AdminXrpcAuth, 5 + routes::{CACHE_CONTROL_NOCACHE_VALUE, XrpcErrorResponse}, 6 + }, 5 7 types::blob_cid::BlobCid, 6 8 }; 7 9 use axum::{ ··· 16 18 17 19 pub async fn xrpc_cache_purge_blob_handler( 18 20 _auth: AdminXrpcAuth, 19 - State(state): State<Arc<AppState>>, 21 + State(state): State<Arc<ServerState>>, 20 22 ExtractXrpc(request): ExtractXrpc<PurgeBlobRequest>, 21 23 ) -> Result< 22 24 StatusCode,
+2 -2
crates/porxie/src/routes/xrpc/dev_blooym/porxie/get_blob.rs crates/porxie/src/server/routes/xrpc/dev_blooym/porxie/get_blob.rs
··· 1 - use crate::{AppState, routes::get_blob_handler}; 1 + use crate::server::{ServerState, routes::get_blob_handler}; 2 2 use axum::{ 3 3 extract::{Path, State}, 4 4 response::IntoResponse, ··· 11 11 /// regular get blob request. May become the primary method 12 12 /// in the future. 13 13 pub async fn xrpc_compat_get_blob_handler( 14 - state: State<Arc<AppState>>, 14 + state: State<Arc<ServerState>>, 15 15 ExtractXrpc(request): ExtractXrpc<GetBlobRequest>, 16 16 ) -> impl IntoResponse { 17 17 get_blob_handler(
crates/porxie/src/routes/xrpc/dev_blooym/porxie/mod.rs crates/porxie/src/server/routes/xrpc/dev_blooym/porxie/mod.rs
+2 -1
crates/porxie/src/routes/xrpc/health.rs crates/porxie/src/server/routes/xrpc/health.rs
··· 1 - use crate::routes::CACHE_CONTROL_NOCACHE_VALUE; 2 1 use axum::{ 3 2 Json, 4 3 http::{StatusCode, header}, 5 4 response::IntoResponse, 6 5 }; 7 6 use serde::Serialize; 7 + 8 + use crate::server::routes::CACHE_CONTROL_NOCACHE_VALUE; 8 9 9 10 #[derive(Serialize)] 10 11 struct GetHealthResponse {
crates/porxie/src/routes/xrpc/mod.rs crates/porxie/src/server/routes/xrpc/mod.rs
+34
crates/porxie/src/server/address.rs
··· 1 + use anyhow::bail; 2 + use core::{net::SocketAddr, str::FromStr}; 3 + 4 + #[cfg(unix)] 5 + use std::path::PathBuf; 6 + 7 + #[derive(Debug, Clone)] 8 + pub enum Address { 9 + /// An IP socket address. 10 + Ip(SocketAddr), 11 + 12 + /// A UNIX socket path. 13 + #[cfg(unix)] 14 + Unix(PathBuf), 15 + } 16 + 17 + impl FromStr for Address { 18 + type Err = anyhow::Error; 19 + 20 + fn from_str(s: &str) -> Result<Self, Self::Err> { 21 + #[cfg(unix)] 22 + if let Some(path) = s.strip_prefix("unix:") { 23 + return Ok(Address::Unix(PathBuf::from(path))); 24 + } 25 + if let Some(ip) = s.strip_prefix("ip:") { 26 + return Ok(ip.parse::<SocketAddr>().map(Address::Ip)?); 27 + } 28 + 29 + #[cfg(unix)] 30 + bail!("unknown address binding type, expected 'ip:<addr>' or 'unix:<path>'".to_string(),); 31 + #[cfg(not(unix))] 32 + bail!("unknown address binding type, expected 'ip:<addr>'".to_string()); 33 + } 34 + }
+17
crates/porxie/src/server/middlewares/mod.rs
··· 1 + use axum::{ 2 + extract::Request, 3 + http::{HeaderValue, header}, 4 + middleware::Next, 5 + response::Response, 6 + }; 7 + 8 + pub async fn server_headers_middleware(req: Request, next: Next) -> Response { 9 + let mut res = next.run(req).await; 10 + let res_headers = res.headers_mut(); 11 + res_headers.insert( 12 + header::SERVER, 13 + const { HeaderValue::from_static(env!("CARGO_PKG_NAME")) }, 14 + ); 15 + res_headers.insert("X-Robots-Tag", const { HeaderValue::from_static("none") }); 16 + res 17 + }
+162
crates/porxie/src/server/mod.rs
··· 1 + pub mod address; 2 + mod extractors; 3 + mod middlewares; 4 + mod routes; 5 + 6 + use crate::{ 7 + blob_service::BlobService, 8 + identity_service::IdentityService, 9 + policy_client::PolicyClient, 10 + server::{ 11 + address::Address, 12 + middlewares::server_headers_middleware, 13 + routes::{ 14 + get_index_handler, 15 + xrpc::{ 16 + dev_blooym::porxie::{ 17 + cache::{xrpc_cache_purge_actor_handler, xrpc_cache_purge_blob_handler}, 18 + xrpc_compat_get_blob_handler, 19 + }, 20 + xrpc_fallback_handler, xrpc_get_health_handler, 21 + }, 22 + }, 23 + }, 24 + }; 25 + use anyhow::Context; 26 + use axum::{ 27 + Router, 28 + http::HeaderValue, 29 + middleware::{self as axum_middleware}, 30 + routing::{any, get, post}, 31 + }; 32 + use core::{num::NonZeroU64, time::Duration}; 33 + use mime::Mime; 34 + use reqwest::StatusCode; 35 + use std::sync::Arc; 36 + use tower_http::{ 37 + catch_panic::CatchPanicLayer, 38 + normalize_path::NormalizePathLayer, 39 + timeout::TimeoutLayer, 40 + trace::{self, DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer}, 41 + }; 42 + use tracing::Level; 43 + 44 + struct ServerState { 45 + admin_password: Option<String>, 46 + allowed_mimetypes: Vec<Mime>, 47 + blob_service: BlobService, 48 + cache_control_header: HeaderValue, 49 + identity_service: IdentityService, 50 + max_blob_size: NonZeroU64, 51 + policy_client: Option<PolicyClient>, 52 + policy_fail_open: bool, 53 + } 54 + 55 + pub struct PorxieServerOptions { 56 + pub admin_password: Option<String>, 57 + pub allowed_mimetypes: Vec<Mime>, 58 + pub blob_processing_timeout: Duration, 59 + pub blob_service: BlobService, 60 + pub cache_control_header: HeaderValue, 61 + pub identity_service: IdentityService, 62 + pub max_blob_size: NonZeroU64, 63 + pub policy_client: Option<PolicyClient>, 64 + pub policy_fail_open: bool, 65 + } 66 + 67 + pub struct PorxieServer { 68 + router: Router, 69 + } 70 + 71 + impl PorxieServer { 72 + /// Create a new Porxie server. 73 + pub fn new(options: PorxieServerOptions) -> Self { 74 + let router = Router::new() 75 + .route("/", get(get_index_handler)) 76 + .route( 77 + "/{did}/{cid}", 78 + get(get_index_handler).layer(TimeoutLayer::with_status_code( 79 + StatusCode::REQUEST_TIMEOUT, 80 + options.blob_processing_timeout, 81 + )), 82 + ) 83 + .nest( 84 + "/xrpc", 85 + Router::new() 86 + .route("/_health", get(xrpc_get_health_handler)) 87 + .route( 88 + "/dev.blooym.porxie.getBlob", 89 + get(xrpc_compat_get_blob_handler).layer(TimeoutLayer::with_status_code( 90 + StatusCode::REQUEST_TIMEOUT, 91 + options.blob_processing_timeout, 92 + )), 93 + ) 94 + .route( 95 + "/dev.blooym.porxie.cache.purgeActor", 96 + post(xrpc_cache_purge_actor_handler), 97 + ) 98 + .route( 99 + "/dev.blooym.porxie.cache.purgeBlob", 100 + post(xrpc_cache_purge_blob_handler), 101 + ) 102 + // Ensure /xrpc/... routes don't fall through elsewhere. 103 + .route("/{rest}", any(xrpc_fallback_handler)), 104 + ) 105 + .layer( 106 + TraceLayer::new_for_http() 107 + .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) 108 + .on_request(DefaultOnRequest::default().level(Level::DEBUG)) 109 + .on_response(DefaultOnResponse::default().level(Level::INFO)) 110 + .on_failure(DefaultOnFailure::default().level(Level::ERROR)), 111 + ) 112 + .layer(NormalizePathLayer::trim_trailing_slash()) 113 + .layer(CatchPanicLayer::new()) 114 + .layer(axum_middleware::from_fn(server_headers_middleware)) 115 + .with_state(Arc::new(ServerState { 116 + admin_password: options.admin_password, 117 + allowed_mimetypes: options.allowed_mimetypes, 118 + blob_service: options.blob_service, 119 + cache_control_header: options.cache_control_header, 120 + identity_service: options.identity_service, 121 + max_blob_size: options.max_blob_size, 122 + policy_client: options.policy_client, 123 + policy_fail_open: options.policy_fail_open, 124 + })); 125 + 126 + Self { router } 127 + } 128 + 129 + /// Start server listener on specified address. 130 + pub async fn start<F: Future<Output = ()> + Send + 'static>( 131 + self, 132 + address: Address, 133 + shutdown_signal: F, 134 + ) -> anyhow::Result<()> { 135 + match address { 136 + Address::Ip(ip) => { 137 + let listener = tokio::net::TcpListener::bind(ip) 138 + .await 139 + .context("failed to bind tcp listener")?; 140 + tracing::info!("server listening on http://{ip}"); 141 + axum::serve(listener, self.router) 142 + .with_graceful_shutdown(shutdown_signal) 143 + .await?; 144 + Ok(()) 145 + } 146 + #[cfg(unix)] 147 + Address::Unix(path) => { 148 + use anyhow::Context; 149 + 150 + let _ = std::fs::remove_file(&path); 151 + let listener = tokio::net::UnixListener::bind(&path) 152 + .context("failed to bind unix listener")?; 153 + tracing::info!("server listening on unix:{}", path.display()); 154 + axum::serve(listener, self.router) 155 + .with_graceful_shutdown(shutdown_signal) 156 + .await?; 157 + let _ = std::fs::remove_file(&path); 158 + Ok(()) 159 + } 160 + } 161 + } 162 + }
+1 -1
crates/porxie/src/types/blob_cid.rs
··· 92 92 } 93 93 94 94 impl core::fmt::Display for BlobCid { 95 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 95 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 96 96 self.0.fmt(f) 97 97 } 98 98 }