use std::sync::Arc;
use std::time::Duration;
use sentry_types::protocol::v7::client_report::{
Category as ClientReportCategory, LossSource, Reason as ClientReportReason,
};
use sentry_types::protocol::v7::EnvelopeItem;
use self::slot::TransportSlot;
use super::client_reports::{ClientReportAggregator, Recorder};
use crate::{Envelope, Transport};
#[derive(Clone, Default)]
pub(crate) struct EnvelopeSender {
transport_slot: TransportSlot<dyn Transport>,
client_report_aggregator: ClientReportAggregator,
}
impl EnvelopeSender {
pub(crate) fn send_envelope(&self, envelope: Envelope) {
self.send_envelope_with(|| Some(envelope));
}
pub(super) fn send_envelope_with<F>(&self, builder: F)
where
F: FnOnce() -> Option<Envelope>,
{
self.transport_slot.send_envelope_with(|| {
builder().map(
|envelope| match self.client_report_aggregator.take_pending_report() {
Some(client_report) => with_item(envelope, client_report),
None => envelope,
},
)
})
}
pub(super) fn new<F>(transport_builder: F) -> Self
where
F: FnOnce(Recorder) -> Arc<dyn Transport>,
{
let client_report_aggregator = ClientReportAggregator::new();
let recorder = client_report_aggregator.recorder();
let transport_slot = TransportSlot::new(transport_builder(recorder));
Self {
transport_slot,
client_report_aggregator,
}
}
pub(super) fn record_lost_data<L>(&self, data: &L, reason: ClientReportReason)
where
L: LossSource + ?Sized,
{
self.client_report_aggregator.record_lost_data(data, reason);
}
pub(super) fn record_loss(
&self,
category: ClientReportCategory,
reason: ClientReportReason,
quantity: u64,
) {
self.client_report_aggregator
.record_loss(category, reason, quantity);
}
pub(super) fn flush(&self, timeout: Duration) -> bool {
self.transport_slot.flush(timeout)
}
pub(super) fn shutdown(&self, timeout: Duration) -> bool {
self.transport_slot.shutdown(timeout)
}
pub(super) fn clone_with_new_transport_slot(&self) -> Self {
let transport_slot = self.transport_slot.clone_into_new_slot();
Self {
transport_slot,
..self.clone()
}
}
pub(super) fn is_enabled(&self) -> bool {
self.transport_slot.is_occupied()
}
}
mod slot {
use std::sync::{Arc, RwLock};
use std::time::Duration;
use sentry_types::protocol::v7::Envelope;
use crate::Transport;
const READ_EXPECT_MSG: &str = "could not acquire transport read lock";
const WRITE_EXPECT_MSG: &str = "could not acquire transport write lock";
#[derive(Debug)]
pub(super) struct TransportSlot<T: ?Sized> {
inner: Arc<RwLock<Option<Arc<T>>>>,
}
impl<T: ?Sized> TransportSlot<T> {
pub(super) fn new(transport: Arc<T>) -> Self {
let inner = Arc::new(RwLock::new(Some(transport)));
Self { inner }
}
pub(super) fn is_occupied(&self) -> bool {
self.inner.read().expect(READ_EXPECT_MSG).is_some()
}
pub(super) fn clone_into_new_slot(&self) -> Self {
self.inner
.read()
.expect(READ_EXPECT_MSG)
.as_ref()
.map(|transport| Self::new(transport.clone()))
.unwrap_or_else(|| self.clone())
}
}
impl<T> TransportSlot<T>
where
T: Transport + ?Sized,
{
pub(super) fn send_envelope_with<F>(&self, builder: F)
where
F: FnOnce() -> Option<Envelope>,
{
if let Some((transport, envelope)) = self
.inner
.read()
.expect(READ_EXPECT_MSG)
.as_deref()
.and_then(|transport| Some((transport, builder()?)))
{
transport.send_envelope(envelope);
}
}
pub(super) fn flush(&self, timeout: Duration) -> bool {
self.inner
.read()
.expect(READ_EXPECT_MSG)
.as_deref()
.map(|transport| transport.flush(timeout))
.unwrap_or(true)
}
pub(super) fn shutdown(&self, timeout: Duration) -> bool {
let transport_opt = self.inner.write().expect(WRITE_EXPECT_MSG).take();
if let Some(transport) = transport_opt {
sentry_debug!("client close; request transport to shut down");
transport.shutdown(timeout)
} else {
sentry_debug!("client close; no transport to shut down");
true
}
}
}
impl<T: ?Sized> Clone for TransportSlot<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T: ?Sized> Default for TransportSlot<T> {
fn default() -> Self {
Self {
inner: Default::default(),
}
}
}
}
fn with_item<I>(mut envelope: Envelope, item: I) -> Envelope
where
I: Into<EnvelopeItem>,
{
envelope.add_item(item);
envelope
}