iroh_blobs/
net_protocol.rs

1//! Adaptation of `iroh-blobs` as an [`iroh`] [`ProtocolHandler`].
2//!
3//! This is the easiest way to share data from a [`crate::api::Store`] over iroh connections.
4//!
5//! # Example
6//!
7//! ```rust
8//! # async fn example() -> anyhow::Result<()> {
9//! use iroh::{protocol::Router, Endpoint};
10//! use iroh_blobs::{store, BlobsProtocol};
11//!
12//! // create a store
13//! let store = store::fs::FsStore::load("blobs").await?;
14//!
15//! // add some data
16//! let t = store.add_slice(b"hello world").await?;
17//!
18//! // create an iroh endpoint
19//! let endpoint = Endpoint::builder().discovery_n0().bind().await?;
20//!
21//! // create a blobs protocol handler
22//! let blobs = BlobsProtocol::new(&store, endpoint.clone(), None);
23//!
24//! // create a router and add the blobs protocol handler
25//! let router = Router::builder(endpoint)
26//!     .accept(iroh_blobs::ALPN, blobs.clone())
27//!     .spawn();
28//!
29//! // this data is now globally available using the ticket
30//! let ticket = blobs.ticket(t).await?;
31//! println!("ticket: {}", ticket);
32//!
33//! // wait for control-c to exit
34//! tokio::signal::ctrl_c().await?;
35//! #   Ok(())
36//! # }
37//! ```
38
39use std::{fmt::Debug, ops::Deref, sync::Arc};
40
41use iroh::{
42    endpoint::Connection,
43    protocol::{AcceptError, ProtocolHandler},
44    Endpoint, Watcher,
45};
46use tracing::error;
47
48use crate::{api::Store, provider::events::EventSender, ticket::BlobTicket, HashAndFormat};
49
50#[derive(Debug)]
51pub(crate) struct BlobsInner {
52    pub(crate) store: Store,
53    pub(crate) endpoint: Endpoint,
54    pub(crate) events: EventSender,
55}
56
57/// A protocol handler for the blobs protocol.
58#[derive(Debug, Clone)]
59pub struct BlobsProtocol {
60    pub(crate) inner: Arc<BlobsInner>,
61}
62
63impl Deref for BlobsProtocol {
64    type Target = Store;
65
66    fn deref(&self) -> &Self::Target {
67        &self.inner.store
68    }
69}
70
71impl BlobsProtocol {
72    pub fn new(store: &Store, endpoint: Endpoint, events: Option<EventSender>) -> Self {
73        Self {
74            inner: Arc::new(BlobsInner {
75                store: store.clone(),
76                endpoint,
77                events: events.unwrap_or(EventSender::DEFAULT),
78            }),
79        }
80    }
81
82    pub fn store(&self) -> &Store {
83        &self.inner.store
84    }
85
86    pub fn endpoint(&self) -> &Endpoint {
87        &self.inner.endpoint
88    }
89
90    /// Create a ticket for content on this node.
91    ///
92    /// Note that this does not check whether the content is partially or fully available. It is
93    /// just a convenience method to create a ticket from content and the address of this node.
94    pub async fn ticket(&self, content: impl Into<HashAndFormat>) -> anyhow::Result<BlobTicket> {
95        let content = content.into();
96        let addr = self.inner.endpoint.node_addr().initialized().await;
97        let ticket = BlobTicket::new(addr, content.hash, content.format);
98        Ok(ticket)
99    }
100}
101
102impl ProtocolHandler for BlobsProtocol {
103    async fn accept(&self, conn: Connection) -> std::result::Result<(), AcceptError> {
104        let store = self.store().clone();
105        let events = self.inner.events.clone();
106        crate::provider::handle_connection(conn, store, events).await;
107        Ok(())
108    }
109
110    async fn shutdown(&self) {
111        if let Err(cause) = self.store().shutdown().await {
112            error!("error shutting down store: {:?}", cause);
113        }
114    }
115}