Expand description
Internal implementation detail of sonos-sdk. Not intended for direct use.
Generic UPnP callback server for receiving event notifications.
This crate provides a lightweight HTTP server for handling UPnP NOTIFY requests. It is designed to be generic and has no knowledge of device-specific protocols.
§Overview
The callback server consists of three main components:
CallbackServer: HTTP server that binds to a local port and receives incoming UPnP event notifications via HTTP POST requests.EventRouter: Routes incoming events based on subscription IDs to registered handlers via channels.NotificationPayload: Generic data structure containing subscription ID and raw XML event body.
§Architecture
The callback server is designed to be a thin HTTP layer that:
- Binds to an available port in a specified range
- Validates incoming UPnP NOTIFY requests
- Extracts subscription IDs and event XML
- Routes events to registered handlers via channels
All device-specific logic (speaker IDs, service types, event parsing) should be handled by the consuming crate through an adapter layer.
§Example: Basic Usage
use callback_server::{CallbackServer, NotificationPayload};
use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> Result<(), String> {
// Create a channel for receiving notifications
let (tx, mut rx) = mpsc::unbounded_channel::<NotificationPayload>();
// Create and start the callback server
let server = CallbackServer::new((3400, 3500), tx).await?;
println!("Callback server listening at: {}", server.base_url());
// Register a subscription
server.router().register("uuid:subscription-123".to_string()).await;
// Handle incoming notifications
tokio::spawn(async move {
while let Some(notification) = rx.recv().await {
println!("Received event for subscription: {}", notification.subscription_id);
println!("Event XML: {}", notification.event_xml);
}
});
// Server runs until shutdown is called
// server.shutdown().await?;
Ok(())
}§Example: With Adapter Layer
Device-specific crates should create an adapter layer that wraps the generic types and adds domain-specific context:
use callback_server::{CallbackServer, NotificationPayload};
use tokio::sync::mpsc;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
// Device-specific event with additional context
#[derive(Debug, Clone)]
struct DeviceEvent {
subscription_id: String,
device_id: String,
service_type: String,
event_xml: String,
}
#[tokio::main]
async fn main() -> Result<(), String> {
// Create channels
let (notification_tx, mut notification_rx) = mpsc::unbounded_channel::<NotificationPayload>();
let (device_event_tx, mut device_event_rx) = mpsc::unbounded_channel::<DeviceEvent>();
// Create callback server
let server = CallbackServer::new((3400, 3500), notification_tx).await?;
// Maintain mapping from subscription ID to device context
let subscription_map: Arc<RwLock<HashMap<String, (String, String)>>> =
Arc::new(RwLock::new(HashMap::new()));
// Spawn adapter task to add device-specific context
let map_clone = subscription_map.clone();
tokio::spawn(async move {
while let Some(notification) = notification_rx.recv().await {
let map = map_clone.read().await;
if let Some((device_id, service_type)) = map.get(¬ification.subscription_id) {
let device_event = DeviceEvent {
subscription_id: notification.subscription_id,
device_id: device_id.clone(),
service_type: service_type.clone(),
event_xml: notification.event_xml,
};
let _ = device_event_tx.send(device_event);
}
}
});
// Register subscription with device context
let sub_id = "uuid:subscription-123".to_string();
server.router().register(sub_id.clone()).await;
subscription_map.write().await.insert(
sub_id,
("device-001".to_string(), "AVTransport".to_string())
);
// Process device-specific events
tokio::spawn(async move {
while let Some(event) = device_event_rx.recv().await {
println!("Device {} service {} event", event.device_id, event.service_type);
}
});
Ok(())
}§Private Workspace Crate
This crate is intended for internal use within the workspace and is not published to crates.io. It provides the foundation for device-specific event handling layers.
Re-exports§
pub use firewall_detection::CoordinatorStats;pub use firewall_detection::DetectionReason;pub use firewall_detection::DetectionResult;pub use firewall_detection::DeviceFirewallState;pub use firewall_detection::FirewallDetectionConfig;pub use firewall_detection::FirewallDetectionCoordinator;pub use firewall_detection::FirewallStatus;pub use router::EventRouter;pub use router::NotificationPayload;
Modules§
- firewall_
detection - Firewall detection coordinator for callback server.
- router
- Event routing for HTTP callback notifications.
Structs§
- Callback
Server - HTTP callback server for receiving UPnP event notifications.