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::*};