#axum #http #cookies #cometd

axum-cometd

CometD服务器创建框架

28个版本 (8个破坏性)

0.10.0 2023年2月14日
0.9.4 2023年5月2日
0.9.3 2023年3月13日
0.7.0-beta.32022年12月21日
0.3.0 2022年11月30日

#42 in #cookies

Download history 1/week @ 2024-03-26 8/week @ 2024-04-02

每月82次下载

Apache-2.0

81KB
2K SLoC

Rust 2K SLoC // 0.0% comments JavaScript 127 SLoC TypeScript 63 SLoC // 0.3% comments Shell 63 SLoC // 0.2% comments

axum-cometd


lib.rs:

此crate旨在使在用Rust编写的服务器中使用CometD协议成为可能。

此项目正在进行中,版本之间可能会有很大变化。

目录

服务器端点

服务器有4个端点

  1. /handshake -- 注册并获取 clientId
  2. / -- 订阅频道;
  3. /connect -- 接收或发布消息;
  4. /disconnect -- 告诉服务器清理 clientId 的数据;

您可以通过 RouterBuilder::handshake_base_pathRouterBuilder::subscribe_base_pathRouterBuilder::connect_base_pathRouterBuilder::disconnect_base_path 改变这些端点的基准部分。例如,要使 /node/0/handshake/node/1/connect,您可以这样做

use std::sync::Arc;
use axum_cometd::{LongPollingServiceContextBuilder, RouterBuilder};

let context = LongPollingServiceContextBuilder::new()
    .build();

let router = RouterBuilder::new()
    .handshake_base_path("/node/0")
    .connect_base_path("/node/1")
    .build(Arc::clone(&context));

clientIdBAYEUX_BROWSER cookie

clientIdBAYEUX_BROWSER cookie 是长度为40的十六进制字符串,可能以零开头。如果它不是这样,服务器将返回 '402::session_unknown' 错误。为了获得一些唯一性,首先从Unix时间戳中取出前8个字节,然后使用随机数填充最后部分。

服务器如何工作

BAYEUX_BROWSER cookie 将在 /handshake 请求中生成和设置,如果还没有的话。

在其他端点([服务器端点])中,服务器检查 clientIdBAYEUX_BROWSER 糖果(如果向 /connect 发送消息,则将检查每个 clientId)。如果 clientId 与不同的 BAYEUX_BROWSER 糖果一起使用,则服务器将返回 '402::session_unknown' 错误。

如何获取服务器事件

服务器有 3 个事件

  1. 事件::SessionAdded
  2. 事件::Subscribe
  3. 事件::SessionRemoved
  4. 事件::CustomData

SessionAddedSubscribe 可以包含附加数据,这些数据将通过 axum::Extension 附加。要获取这些事件,您必须使用获取接收通道 LongPollingServiceContext::rx。服务器不使用 Event::CustomData,它使用自定义消息,该消息可以在接收器中接收。

use std::sync::Arc;
use axum::Extension;
use axum_cometd::{LongPollingServiceContextBuilder, RouterBuilder};

#[derive(Debug, Clone)]
struct ContextData {
    server_name: Arc<str>,
}

use std::time::Duration;
use axum_cometd::Event;
let context = LongPollingServiceContextBuilder::new()
    .build::<ContextData, &'static str>();

let app = RouterBuilder::new()
    .build_with_additional_data(Arc::clone(&context))
    .layer(Extension(ContextData {
        server_name: std::env::var("SERVER_NAME")
            .map(Arc::from)
            .unwrap_or_else(|_| Arc::from("Skalica")),
    }));

let tx = context.tx();
let mut rx = context.rx();

tokio::task::spawn(async move {
    loop {
        tx.send("CUSTOM_DATA").await;
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
});

while let Some(event) = rx.recv().await {
    match *event {
        Event::SessionAdded{
            client_id,
            ref headers,
            ref data,
        } => {
            println!("sessionAdded with clientId({client_id}), headers({headers:?}), data({data:?})");
        }
        Event::Subscribe{
            client_id,
            ref headers,
            ref channels,
            ref data,
        } => {
            println!("subscribed on channels({channels:?}) with clientId({client_id}), headers({headers:?}), data({data:?})");
        }
        Event::SessionRemoved{
            client_id,
        } => println!("clientId({client_id}) session removed"),
        Event::CustomData(msg) => println!("got CustomData({msg})"),
    }
}

依赖关系

~14MB
~263K SLoC