axum_cometd/
lib.rs

1#![deny(unused_must_use)]
2#![warn(
3    rust_2018_idioms,
4    rust_2021_compatibility,
5    missing_docs,
6    missing_debug_implementations,
7    clippy::expect_used,
8    clippy::missing_panics_doc,
9    clippy::panic_in_result_fn,
10    clippy::panicking_unwrap,
11    clippy::unwrap_used,
12    clippy::if_let_mutex,
13    clippy::std_instead_of_core,
14    clippy::missing_const_for_fn,
15    clippy::str_to_string,
16    clippy::clone_on_ref_ptr,
17    clippy::panic,
18    clippy::explicit_iter_loop,
19    clippy::pattern_type_mismatch,
20    clippy::indexing_slicing,
21    clippy::use_debug,
22    clippy::unnested_or_patterns,
23    clippy::return_self_not_must_use,
24    clippy::map_unwrap_or,
25    clippy::items_after_statements,
26    clippy::needless_pass_by_value,
27    clippy::if_not_else,
28    clippy::option_if_let_else
29)]
30//! This crate aims to make ability to use CometD protocol in servers written in Rust.
31//!
32//! This project is in progress and might change a lot from version to version.
33//!
34//! # Table of contents
35//! - [Server endpoints](#server-endpoints)
36//! - [`clientId` and `BAYEUX_BROWSER` cookie](#clientId-bayeux-browser-cookie)
37//! - [How server works](#how-server-works)
38//! - [How get server events](#how-get-server-events)
39//!
40//! # Server endpoints
41//!
42//! Server have 4 endpoints:
43//! 1) `/handshake` -- to register and get `clientId`;
44//! 2) `/` -- to subscribe on channels;
45//! 3) `/connect` -- to receiving or publish messages;
46//! 4) `/disconnect` -- to say to server clean data for `clientId`;
47//!
48//! You can change base part of these endpoints through
49//! [`RouterBuilder::handshake_base_path`],
50//! [`RouterBuilder::subscribe_base_path`],
51//! [`RouterBuilder::connect_base_path`],
52//! [`RouterBuilder::disconnect_base_path`].
53//! For example, to make `/node/0/handshake` and `/node/1/connect` you can do this:
54//! ```rust,no_run
55//! use std::sync::Arc;
56//! use axum_cometd::{LongPollingServiceContextBuilder, RouterBuilder};
57//!
58//! let context = LongPollingServiceContextBuilder::new()
59//!     .build();
60//! # let context: Arc<axum_cometd::LongPollingServiceContext<(), ()>> = context;
61//!
62//! let router = RouterBuilder::new()
63//!     .handshake_base_path("/node/0")
64//!     .connect_base_path("/node/1")
65//!     .build(Arc::clone(&context));
66//!
67//! ```
68//!
69//! # `clientId` and `BAYEUX_BROWSER` cookie
70//!
71//! `clientId` and `BAYEUX_BROWSER` cookie is 40-character length hex string,
72//! with possibility of leading zeroes.
73//! Server will return '402::session_unknown' error if it will be not.
74//! To get some uniquity first 8 bytes is taken from Unix timestamp, and for randomness
75//! last part filled with random numbers.
76//!
77//! # How server works
78//!
79//! `BAYEUX_BROWSER` cookie will be generated and set at `/handshake` request,
80//! if there isn't one already.
81//!
82//! At others endpoints ([Server endpoints]) server check `clientId` and `BAYEUX_BROWSER` cookie
83//! (in case of publish messages to `/connect` it will be check each `clientId`).
84//! If `clientId` will be used with different `BAYEUX_BROWSER` cookie,
85//! server will return '402::session_unknown' error.
86//!
87//! # How get server events
88//!
89//! Server have 3 events:
90//! 1) [`Event::SessionAdded`]
91//! 2) [`Event::Subscribe`]
92//! 3) [`Event::SessionRemoved`]
93//! 4) [`Event::CustomData`]
94//!
95//! `SessionAdded` and `Subscribe` can contain additional data, which will be attached through
96//! [`axum::Extension`].
97//! To get those events, you must use get receive channel [`LongPollingServiceContext::rx`].
98//! Server do not use [`Event::CustomData`], it user custom message which can be received in
99//! receiver.
100//! ```rust,no_run
101//! use std::sync::Arc;
102//! use axum::Extension;
103//! use axum_cometd::{LongPollingServiceContextBuilder, RouterBuilder};
104//!
105//! #[derive(Debug, Clone)]
106//! struct ContextData {
107//!     server_name: Arc<str>,
108//! }
109//!
110//! # #[tokio::main(flavor = "current_thread")]
111//! # async fn main() {
112//! use std::time::Duration;
113//! use axum_cometd::Event;
114//! let context = LongPollingServiceContextBuilder::new()
115//!     .build::<ContextData, &'static str>();
116//!
117//! let app = RouterBuilder::new()
118//!     .build_with_additional_data(Arc::clone(&context))
119//!     .layer(Extension(ContextData {
120//!         server_name: std::env::var("SERVER_NAME")
121//!             .map(Arc::from)
122//!             .unwrap_or_else(|_| Arc::from("Skalica")),
123//!     }));
124//!
125//! let tx = context.tx();
126//! let mut rx = context.rx();
127//!
128//! tokio::task::spawn(async move {
129//!     loop {
130//!         tx.send("CUSTOM_DATA").await;
131//!         tokio::time::sleep(Duration::from_secs(1)).await;
132//!     }
133//! });
134//!
135//! while let Some(event) = rx.recv().await {
136//!     match *event {
137//!         Event::SessionAdded{
138//!             client_id,
139//!             ref headers,
140//!             ref data,
141//!         } => {
142//!             println!("sessionAdded with clientId({client_id}), headers({headers:?}), data({data:?})");
143//!         }
144//!         Event::Subscribe{
145//!             client_id,
146//!             ref headers,
147//!             ref channels,
148//!             ref data,
149//!         } => {
150//!             println!("subscribed on channels({channels:?}) with clientId({client_id}), headers({headers:?}), data({data:?})");
151//!         }
152//!         Event::SessionRemoved{
153//!             client_id,
154//!         } => println!("clientId({client_id}) session removed"),
155//!         Event::CustomData(msg) => println!("got CustomData({msg})"),
156//!     }
157//! }
158//! # }
159//! ```
160
161mod consts;
162mod context;
163mod ext;
164mod handlers;
165mod types;
166mod utils;
167
168pub(crate) use ext::*;
169pub use {context::*, types::error::*, types::*};