wse-server 2.3.1__tar.gz → 2.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {wse_server-2.3.1 → wse_server-2.4.0}/PKG-INFO +16 -11
  2. {wse_server-2.3.1 → wse_server-2.4.0}/README.md +15 -10
  3. {wse_server-2.3.1 → wse_server-2.4.0}/pyproject.toml +1 -1
  4. {wse_server-2.3.1 → wse_server-2.4.0}/rust/Cargo.lock +159 -36
  5. {wse_server-2.3.1 → wse_server-2.4.0}/rust/Cargo.toml +5 -3
  6. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/jwt.rs +62 -41
  7. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/cluster.rs +407 -399
  8. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/compression.rs +254 -31
  9. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/filters.rs +6 -8
  10. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/presence.rs +295 -37
  11. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/recovery.rs +61 -8
  12. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/security.rs +30 -21
  13. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/sequencer.rs +99 -13
  14. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/server.rs +740 -299
  15. {wse_server-2.3.1 → wse_server-2.4.0}/wse_server/__init__.py +1 -1
  16. {wse_server-2.3.1 → wse_server-2.4.0}/LICENSE +0 -0
  17. {wse_server-2.3.1 → wse_server-2.4.0}/rust/.cargo/config.toml +0 -0
  18. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/lib.rs +0 -0
  19. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/mod.rs +0 -0
  20. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/queue.rs +0 -0
  21. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/rate_limiter.rs +0 -0
  22. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/reliability.rs +0 -0
  23. {wse_server-2.3.1 → wse_server-2.4.0}/rust/src/wse/transformer.rs +0 -0
  24. {wse_server-2.3.1 → wse_server-2.4.0}/wse_server/_wse_accel.pyi +0 -0
  25. {wse_server-2.3.1 → wse_server-2.4.0}/wse_server/core/__init__.py +0 -0
  26. {wse_server-2.3.1 → wse_server-2.4.0}/wse_server/core/filters.py +0 -0
  27. {wse_server-2.3.1 → wse_server-2.4.0}/wse_server/core/types.py +0 -0
  28. {wse_server-2.3.1 → wse_server-2.4.0}/wse_server/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wse-server
3
- Version: 2.3.1
3
+ Version: 2.4.0
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -80,7 +80,7 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
80
80
  |---------|---------|
81
81
  | **Key exchange** | ECDH P-256 (per-connection keypair, automatic during handshake) |
82
82
  | **Encryption** | AES-GCM-256 with unique 12-byte IV per message |
83
- | **Key derivation** | HKDF-SHA256 (salt: `wse-encryption`, info: `aes-gcm-key`) |
83
+ | **Key derivation** | HKDF-SHA256 (no salt, info: `wse-encryption/aes-gcm-key`) |
84
84
  | **Wire format** | `E:` prefix + 12-byte IV + AES-GCM ciphertext + 16-byte auth tag |
85
85
  | **Key rotation** | Configurable rotation interval (default 1 hour), automatic renegotiation |
86
86
  | **Replay prevention** | Nonce cache (10K entries, 5-minute TTL) on the client side |
@@ -122,6 +122,7 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
122
122
  | **Memory management** | Global budget (default 256 MB), TTL eviction, LRU eviction when over budget |
123
123
  | **Zero-copy storage** | Recovery entries share `Bytes` (Arc) with the broadcast path |
124
124
  | **Recovery on reconnect** | `subscribe_with_recovery()` replays missed messages automatically |
125
+ | **Per-message stamp** | When recovery is enabled, every topic broadcast carries `tp`/`e`/`o` (topic, epoch, offset) so clients dedup idempotently and detect gaps from the messages themselves |
125
126
 
126
127
  ### Client SDKs (Python + TypeScript/React)
127
128
 
@@ -132,8 +133,8 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
132
133
  | **Circuit breaker** | CLOSED/OPEN/HALF_OPEN state machine, prevents connection storms |
133
134
  | **Rate limiting** | Client-side token bucket, coordinates with server feedback |
134
135
  | **E2E encryption** | Wire-compatible AES-GCM-256 + ECDH P-256 (both clients speak the same protocol) |
135
- | **Event sequencing** | Duplicate detection (sliding window) + out-of-order buffering |
136
- | **Network monitor** | Real-time latency, jitter, packet loss measurement, quality scoring |
136
+ | **Event sequencing** | Duplicate detection (sliding window) + per-topic `(epoch, offset)` idempotent dedup with automatic gap recovery |
137
+ | **Network monitor** | Real-time latency, jitter, and connection-quality scoring (Python client also estimates packet loss) |
137
138
  | **Priority queues** | 5 levels from CRITICAL to BACKGROUND |
138
139
  | **Offline queue** | IndexedDB persistence (TypeScript), replayed on reconnect |
139
140
  | **Compression** | Automatic zlib for messages above threshold |
@@ -146,8 +147,11 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
146
147
  |---------|---------|
147
148
  | **Origin validation** | Configure in reverse proxy (nginx/Caddy) to prevent CSWSH |
148
149
  | **Cookie auth** | `access_token` HTTP-only cookie with `Secure + SameSite=Lax` (OWASP recommended for browsers) |
149
- | **Frame protection** | 1 MB max frame size, serde_json parsing (no eval), escaped user IDs in server_ready |
150
+ | **Frame protection** | 1 MB max frame size, serde_json parsing (no eval), escaped user IDs in server_ready; inbound msgpack rejected beyond 64 nesting levels |
150
151
  | **Cluster frame protection** | zstd decompression output capped at 1 MB (MAX_FRAME_SIZE), protocol version validation |
152
+ | **Slowloris protection** | At most 512 concurrent pre-handshake connections; sockets that never complete the WS upgrade are bounded and dropped |
153
+ | **Bounded callback dispatch** | Callback mode caps in-flight `on_message` callbacks at 256 per connection; excess inbound work is shed and counted as rate-limited |
154
+ | **Fail-closed recovery ACL** | Recovery replay applies the connection's topic ACL — unauthorized topics are never replayed or position-probed |
151
155
 
152
156
  ---
153
157
 
@@ -226,6 +230,7 @@ token = rust_jwt_encode(
226
230
  | `presence_max_data_size` | 4096 | Max bytes for a user's presence metadata |
227
231
  | `presence_max_members` | 0 | Max tracked members per topic (0 = unlimited) |
228
232
  | `max_outbound_queue_bytes` | 16777216 | Per-connection outbound buffer limit (bytes, default 16 MB). Messages dropped when exceeded |
233
+ | `max_subscriptions_per_connection` | 0 | Max topics per connection (0 = unlimited). Prevents topic explosion DoS |
229
234
  | `rate_limit_capacity` | 100000.0 | Token bucket capacity per connection |
230
235
  | `rate_limit_refill` | 10000.0 | Token bucket refill rate per second |
231
236
  | `max_message_size` | 1048576 | Maximum WebSocket frame size in bytes (default 1 MB) |
@@ -358,10 +363,10 @@ result = server.subscribe_with_recovery(
358
363
  epoch=client_epoch, # From previous session
359
364
  offset=client_offset, # From previous session
360
365
  )
361
- # {"topics": {"prices": {"epoch": 123, "offset": 456, "recovered": True, "count": 12}}}
366
+ # {"topics": {"prices": {"epoch": "0000007b", "offset": 456, "recovered": True, "count": 12}}}
362
367
  ```
363
368
 
364
- The server maintains per-topic ring buffers (power-of-2 capacity, bitmask indexing). Clients store the `epoch` and `offset` from their last received message. On reconnect, the server replays missed messages from the ring buffer. If the gap is too large or the epoch has changed, the client receives a `NotRecovered` status and should re-subscribe from scratch.
369
+ The server maintains per-topic ring buffers (power-of-2 capacity, bitmask indexing). When recovery is enabled, every topic broadcast is stamped with `tp`/`e`/`o` (topic, epoch, offset) fields, so clients track their per-topic position from the messages themselves, drop duplicates idempotently by `(epoch, offset)`, and automatically request recovery when they detect an offset gap. On reconnect, the server replays missed messages from the ring buffer. If the gap is too large or the epoch has changed, the client receives a `NotRecovered` status and should re-subscribe from scratch.
365
370
 
366
371
  Memory is managed with a global budget (default 256 MB), TTL eviction for idle buffers, and LRU eviction when over budget.
367
372
 
@@ -542,7 +547,7 @@ def handle(event):
542
547
  client.run_forever()
543
548
  ```
544
549
 
545
- Key features: 4 reconnect strategies (exponential, linear, fibonacci, adaptive), connection pool with health scoring and 3 load balancing strategies, circuit breaker, token bucket rate limiter, event sequencer with dedup and reorder buffering, network quality monitoring (latency/jitter/packet loss).
550
+ Key features: 4 reconnect strategies (exponential, linear, fibonacci, adaptive), connection pool with health scoring and 3 load balancing strategies, circuit breaker, token bucket rate limiter, event sequencer with id dedup plus per-topic `(epoch, offset)` idempotent dedup and gap recovery, network quality monitoring (latency/jitter/packet loss).
546
551
 
547
552
  See [python-client/](python-client/) for full source and examples.
548
553
 
@@ -570,7 +575,7 @@ function App() {
570
575
 
571
576
  Key features: offline queue with IndexedDB persistence, adaptive quality management, connection pool with health scoring, E2E encryption (Web Crypto API), message batching, 5 priority levels, Zustand store for external state access.
572
577
 
573
- See [client/](client/) for full source and examples.
578
+ See [ts-client/](ts-client/) for full source and examples.
574
579
 
575
580
  ---
576
581
 
@@ -587,7 +592,7 @@ Benchmarked on AMD EPYC 7502P (32 cores / 64 threads, 128 GB RAM), Ubuntu 24.04.
587
592
 
588
593
  Sub-millisecond latency. Median 0.38ms with JWT authentication. Connection handshake: 0.53ms median (Rust JWT path).
589
594
 
590
- Detailed results: [Benchmarks](docs/BENCHMARKS.md) | [Fan-out](docs/BENCHMARKS_FANOUT.md) | [Rust Client](docs/BENCHMARKS_RUST_CLIENT.md) | [Python Client](docs/BENCHMARKS_PYTHON_CLIENT.md) | [TypeScript Client](docs/BENCHMARKS_TS_CLIENT.md)
595
+ Detailed results: [Benchmarks](docs/BENCHMARKS.md) | [Fan-out](docs/BENCHMARKS_FANOUT.md) | [Rust Client](docs/BENCHMARKS_RUST_CLIENT.md) | [Python Client](docs/BENCHMARKS_PYTHON_CLIENT.md) | [TS Client](docs/BENCHMARKS_TS_CLIENT.md)
591
596
 
592
597
  ---
593
598
 
@@ -613,7 +618,7 @@ Working examples in the [`examples/`](examples/) directory:
613
618
  - [Rust Utilities](docs/RUST_UTILITIES.md) - priority queues, rate limiters, sequencing, compression, crypto, JWT
614
619
  - [Security](docs/SECURITY.md) - JWT, encryption, mTLS, rate limiting, circuit breaker
615
620
  - [Deployment](docs/DEPLOYMENT.md) - production setup, Docker, Kubernetes, cluster configuration
616
- - [Migration Guide](docs/MIGRATION.md) - upgrading from v1.x to v2.0
621
+ - [Migration Guide](docs/MIGRATION.md) - upgrading from v1.x to v2.x
617
622
  - [Contributing](CONTRIBUTING.md) - development setup and coding standards
618
623
  - [Changelog](CHANGELOG.md) - version history
619
624
 
@@ -39,7 +39,7 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
39
39
  |---------|---------|
40
40
  | **Key exchange** | ECDH P-256 (per-connection keypair, automatic during handshake) |
41
41
  | **Encryption** | AES-GCM-256 with unique 12-byte IV per message |
42
- | **Key derivation** | HKDF-SHA256 (salt: `wse-encryption`, info: `aes-gcm-key`) |
42
+ | **Key derivation** | HKDF-SHA256 (no salt, info: `wse-encryption/aes-gcm-key`) |
43
43
  | **Wire format** | `E:` prefix + 12-byte IV + AES-GCM ciphertext + 16-byte auth tag |
44
44
  | **Key rotation** | Configurable rotation interval (default 1 hour), automatic renegotiation |
45
45
  | **Replay prevention** | Nonce cache (10K entries, 5-minute TTL) on the client side |
@@ -81,6 +81,7 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
81
81
  | **Memory management** | Global budget (default 256 MB), TTL eviction, LRU eviction when over budget |
82
82
  | **Zero-copy storage** | Recovery entries share `Bytes` (Arc) with the broadcast path |
83
83
  | **Recovery on reconnect** | `subscribe_with_recovery()` replays missed messages automatically |
84
+ | **Per-message stamp** | When recovery is enabled, every topic broadcast carries `tp`/`e`/`o` (topic, epoch, offset) so clients dedup idempotently and detect gaps from the messages themselves |
84
85
 
85
86
  ### Client SDKs (Python + TypeScript/React)
86
87
 
@@ -91,8 +92,8 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
91
92
  | **Circuit breaker** | CLOSED/OPEN/HALF_OPEN state machine, prevents connection storms |
92
93
  | **Rate limiting** | Client-side token bucket, coordinates with server feedback |
93
94
  | **E2E encryption** | Wire-compatible AES-GCM-256 + ECDH P-256 (both clients speak the same protocol) |
94
- | **Event sequencing** | Duplicate detection (sliding window) + out-of-order buffering |
95
- | **Network monitor** | Real-time latency, jitter, packet loss measurement, quality scoring |
95
+ | **Event sequencing** | Duplicate detection (sliding window) + per-topic `(epoch, offset)` idempotent dedup with automatic gap recovery |
96
+ | **Network monitor** | Real-time latency, jitter, and connection-quality scoring (Python client also estimates packet loss) |
96
97
  | **Priority queues** | 5 levels from CRITICAL to BACKGROUND |
97
98
  | **Offline queue** | IndexedDB persistence (TypeScript), replayed on reconnect |
98
99
  | **Compression** | Automatic zlib for messages above threshold |
@@ -105,8 +106,11 @@ High-performance WebSocket server built in Rust with native clustering, E2E encr
105
106
  |---------|---------|
106
107
  | **Origin validation** | Configure in reverse proxy (nginx/Caddy) to prevent CSWSH |
107
108
  | **Cookie auth** | `access_token` HTTP-only cookie with `Secure + SameSite=Lax` (OWASP recommended for browsers) |
108
- | **Frame protection** | 1 MB max frame size, serde_json parsing (no eval), escaped user IDs in server_ready |
109
+ | **Frame protection** | 1 MB max frame size, serde_json parsing (no eval), escaped user IDs in server_ready; inbound msgpack rejected beyond 64 nesting levels |
109
110
  | **Cluster frame protection** | zstd decompression output capped at 1 MB (MAX_FRAME_SIZE), protocol version validation |
111
+ | **Slowloris protection** | At most 512 concurrent pre-handshake connections; sockets that never complete the WS upgrade are bounded and dropped |
112
+ | **Bounded callback dispatch** | Callback mode caps in-flight `on_message` callbacks at 256 per connection; excess inbound work is shed and counted as rate-limited |
113
+ | **Fail-closed recovery ACL** | Recovery replay applies the connection's topic ACL — unauthorized topics are never replayed or position-probed |
110
114
 
111
115
  ---
112
116
 
@@ -185,6 +189,7 @@ token = rust_jwt_encode(
185
189
  | `presence_max_data_size` | 4096 | Max bytes for a user's presence metadata |
186
190
  | `presence_max_members` | 0 | Max tracked members per topic (0 = unlimited) |
187
191
  | `max_outbound_queue_bytes` | 16777216 | Per-connection outbound buffer limit (bytes, default 16 MB). Messages dropped when exceeded |
192
+ | `max_subscriptions_per_connection` | 0 | Max topics per connection (0 = unlimited). Prevents topic explosion DoS |
188
193
  | `rate_limit_capacity` | 100000.0 | Token bucket capacity per connection |
189
194
  | `rate_limit_refill` | 10000.0 | Token bucket refill rate per second |
190
195
  | `max_message_size` | 1048576 | Maximum WebSocket frame size in bytes (default 1 MB) |
@@ -317,10 +322,10 @@ result = server.subscribe_with_recovery(
317
322
  epoch=client_epoch, # From previous session
318
323
  offset=client_offset, # From previous session
319
324
  )
320
- # {"topics": {"prices": {"epoch": 123, "offset": 456, "recovered": True, "count": 12}}}
325
+ # {"topics": {"prices": {"epoch": "0000007b", "offset": 456, "recovered": True, "count": 12}}}
321
326
  ```
322
327
 
323
- The server maintains per-topic ring buffers (power-of-2 capacity, bitmask indexing). Clients store the `epoch` and `offset` from their last received message. On reconnect, the server replays missed messages from the ring buffer. If the gap is too large or the epoch has changed, the client receives a `NotRecovered` status and should re-subscribe from scratch.
328
+ The server maintains per-topic ring buffers (power-of-2 capacity, bitmask indexing). When recovery is enabled, every topic broadcast is stamped with `tp`/`e`/`o` (topic, epoch, offset) fields, so clients track their per-topic position from the messages themselves, drop duplicates idempotently by `(epoch, offset)`, and automatically request recovery when they detect an offset gap. On reconnect, the server replays missed messages from the ring buffer. If the gap is too large or the epoch has changed, the client receives a `NotRecovered` status and should re-subscribe from scratch.
324
329
 
325
330
  Memory is managed with a global budget (default 256 MB), TTL eviction for idle buffers, and LRU eviction when over budget.
326
331
 
@@ -501,7 +506,7 @@ def handle(event):
501
506
  client.run_forever()
502
507
  ```
503
508
 
504
- Key features: 4 reconnect strategies (exponential, linear, fibonacci, adaptive), connection pool with health scoring and 3 load balancing strategies, circuit breaker, token bucket rate limiter, event sequencer with dedup and reorder buffering, network quality monitoring (latency/jitter/packet loss).
509
+ Key features: 4 reconnect strategies (exponential, linear, fibonacci, adaptive), connection pool with health scoring and 3 load balancing strategies, circuit breaker, token bucket rate limiter, event sequencer with id dedup plus per-topic `(epoch, offset)` idempotent dedup and gap recovery, network quality monitoring (latency/jitter/packet loss).
505
510
 
506
511
  See [python-client/](python-client/) for full source and examples.
507
512
 
@@ -529,7 +534,7 @@ function App() {
529
534
 
530
535
  Key features: offline queue with IndexedDB persistence, adaptive quality management, connection pool with health scoring, E2E encryption (Web Crypto API), message batching, 5 priority levels, Zustand store for external state access.
531
536
 
532
- See [client/](client/) for full source and examples.
537
+ See [ts-client/](ts-client/) for full source and examples.
533
538
 
534
539
  ---
535
540
 
@@ -546,7 +551,7 @@ Benchmarked on AMD EPYC 7502P (32 cores / 64 threads, 128 GB RAM), Ubuntu 24.04.
546
551
 
547
552
  Sub-millisecond latency. Median 0.38ms with JWT authentication. Connection handshake: 0.53ms median (Rust JWT path).
548
553
 
549
- Detailed results: [Benchmarks](docs/BENCHMARKS.md) | [Fan-out](docs/BENCHMARKS_FANOUT.md) | [Rust Client](docs/BENCHMARKS_RUST_CLIENT.md) | [Python Client](docs/BENCHMARKS_PYTHON_CLIENT.md) | [TypeScript Client](docs/BENCHMARKS_TS_CLIENT.md)
554
+ Detailed results: [Benchmarks](docs/BENCHMARKS.md) | [Fan-out](docs/BENCHMARKS_FANOUT.md) | [Rust Client](docs/BENCHMARKS_RUST_CLIENT.md) | [Python Client](docs/BENCHMARKS_PYTHON_CLIENT.md) | [TS Client](docs/BENCHMARKS_TS_CLIENT.md)
550
555
 
551
556
  ---
552
557
 
@@ -572,7 +577,7 @@ Working examples in the [`examples/`](examples/) directory:
572
577
  - [Rust Utilities](docs/RUST_UTILITIES.md) - priority queues, rate limiters, sequencing, compression, crypto, JWT
573
578
  - [Security](docs/SECURITY.md) - JWT, encryption, mTLS, rate limiting, circuit breaker
574
579
  - [Deployment](docs/DEPLOYMENT.md) - production setup, Docker, Kubernetes, cluster configuration
575
- - [Migration Guide](docs/MIGRATION.md) - upgrading from v1.x to v2.0
580
+ - [Migration Guide](docs/MIGRATION.md) - upgrading from v1.x to v2.x
576
581
  - [Contributing](CONTRIBUTING.md) - development setup and coding standards
577
582
  - [Changelog](CHANGELOG.md) - version history
578
583
 
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "wse-server"
7
- version = "2.3.1"
7
+ version = "2.4.0"
8
8
  description = "Rust-powered WebSocket engine for Python. Up to 5M fan-out del/s. Zero-GIL JWT."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -82,13 +82,52 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
82
82
 
83
83
  [[package]]
84
84
  name = "arc-swap"
85
- version = "1.8.2"
85
+ version = "1.9.0"
86
86
  source = "registry+https://github.com/rust-lang/crates.io-index"
87
- checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5"
87
+ checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6"
88
88
  dependencies = [
89
89
  "rustversion",
90
90
  ]
91
91
 
92
+ [[package]]
93
+ name = "asn1-rs"
94
+ version = "0.7.1"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
97
+ dependencies = [
98
+ "asn1-rs-derive",
99
+ "asn1-rs-impl",
100
+ "displaydoc",
101
+ "nom",
102
+ "num-traits",
103
+ "rusticata-macros",
104
+ "thiserror",
105
+ "time",
106
+ ]
107
+
108
+ [[package]]
109
+ name = "asn1-rs-derive"
110
+ version = "0.6.0"
111
+ source = "registry+https://github.com/rust-lang/crates.io-index"
112
+ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
113
+ dependencies = [
114
+ "proc-macro2",
115
+ "quote",
116
+ "syn",
117
+ "synstructure",
118
+ ]
119
+
120
+ [[package]]
121
+ name = "asn1-rs-impl"
122
+ version = "0.2.0"
123
+ source = "registry+https://github.com/rust-lang/crates.io-index"
124
+ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
125
+ dependencies = [
126
+ "proc-macro2",
127
+ "quote",
128
+ "syn",
129
+ ]
130
+
92
131
  [[package]]
93
132
  name = "autocfg"
94
133
  version = "1.5.0"
@@ -97,9 +136,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
97
136
 
98
137
  [[package]]
99
138
  name = "aws-lc-rs"
100
- version = "1.16.1"
139
+ version = "1.16.2"
101
140
  source = "registry+https://github.com/rust-lang/crates.io-index"
102
- checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf"
141
+ checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
103
142
  dependencies = [
104
143
  "aws-lc-sys",
105
144
  "zeroize",
@@ -107,9 +146,9 @@ dependencies = [
107
146
 
108
147
  [[package]]
109
148
  name = "aws-lc-sys"
110
- version = "0.38.0"
149
+ version = "0.39.0"
111
150
  source = "registry+https://github.com/rust-lang/crates.io-index"
112
- checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e"
151
+ checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a"
113
152
  dependencies = [
114
153
  "cc",
115
154
  "cmake",
@@ -164,9 +203,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
164
203
 
165
204
  [[package]]
166
205
  name = "cc"
167
- version = "1.2.56"
206
+ version = "1.2.57"
168
207
  source = "registry+https://github.com/rust-lang/crates.io-index"
169
- checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
208
+ checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
170
209
  dependencies = [
171
210
  "find-msvc-tools",
172
211
  "jobserver",
@@ -339,6 +378,20 @@ dependencies = [
339
378
  "zeroize",
340
379
  ]
341
380
 
381
+ [[package]]
382
+ name = "der-parser"
383
+ version = "10.0.0"
384
+ source = "registry+https://github.com/rust-lang/crates.io-index"
385
+ checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
386
+ dependencies = [
387
+ "asn1-rs",
388
+ "displaydoc",
389
+ "nom",
390
+ "num-bigint",
391
+ "num-traits",
392
+ "rusticata-macros",
393
+ ]
394
+
342
395
  [[package]]
343
396
  name = "deranged"
344
397
  version = "0.5.8"
@@ -360,6 +413,17 @@ dependencies = [
360
413
  "subtle",
361
414
  ]
362
415
 
416
+ [[package]]
417
+ name = "displaydoc"
418
+ version = "0.2.5"
419
+ source = "registry+https://github.com/rust-lang/crates.io-index"
420
+ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
421
+ dependencies = [
422
+ "proc-macro2",
423
+ "quote",
424
+ "syn",
425
+ ]
426
+
363
427
  [[package]]
364
428
  name = "dunce"
365
429
  version = "1.0.5"
@@ -709,9 +773,9 @@ dependencies = [
709
773
 
710
774
  [[package]]
711
775
  name = "itoa"
712
- version = "1.0.17"
776
+ version = "1.0.18"
713
777
  source = "registry+https://github.com/rust-lang/crates.io-index"
714
- checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
778
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
715
779
 
716
780
  [[package]]
717
781
  name = "jobserver"
@@ -773,9 +837,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
773
837
 
774
838
  [[package]]
775
839
  name = "libc"
776
- version = "0.2.182"
840
+ version = "0.2.183"
777
841
  source = "registry+https://github.com/rust-lang/crates.io-index"
778
- checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
842
+ checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
779
843
 
780
844
  [[package]]
781
845
  name = "libm"
@@ -832,6 +896,12 @@ dependencies = [
832
896
  "libmimalloc-sys",
833
897
  ]
834
898
 
899
+ [[package]]
900
+ name = "minimal-lexical"
901
+ version = "0.2.1"
902
+ source = "registry+https://github.com/rust-lang/crates.io-index"
903
+ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
904
+
835
905
  [[package]]
836
906
  name = "miniz_oxide"
837
907
  version = "0.8.9"
@@ -853,6 +923,16 @@ dependencies = [
853
923
  "windows-sys 0.61.2",
854
924
  ]
855
925
 
926
+ [[package]]
927
+ name = "nom"
928
+ version = "7.1.3"
929
+ source = "registry+https://github.com/rust-lang/crates.io-index"
930
+ checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
931
+ dependencies = [
932
+ "memchr",
933
+ "minimal-lexical",
934
+ ]
935
+
856
936
  [[package]]
857
937
  name = "nu-ansi-term"
858
938
  version = "0.50.3"
@@ -924,11 +1004,20 @@ dependencies = [
924
1004
  "libm",
925
1005
  ]
926
1006
 
1007
+ [[package]]
1008
+ name = "oid-registry"
1009
+ version = "0.8.1"
1010
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1011
+ checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
1012
+ dependencies = [
1013
+ "asn1-rs",
1014
+ ]
1015
+
927
1016
  [[package]]
928
1017
  name = "once_cell"
929
- version = "1.21.3"
1018
+ version = "1.21.4"
930
1019
  source = "registry+https://github.com/rust-lang/crates.io-index"
931
- checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
1020
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
932
1021
 
933
1022
  [[package]]
934
1023
  name = "opaque-debug"
@@ -1236,14 +1325,15 @@ dependencies = [
1236
1325
 
1237
1326
  [[package]]
1238
1327
  name = "rcgen"
1239
- version = "0.13.2"
1328
+ version = "0.14.7"
1240
1329
  source = "registry+https://github.com/rust-lang/crates.io-index"
1241
- checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
1330
+ checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e"
1242
1331
  dependencies = [
1243
1332
  "pem",
1244
1333
  "ring",
1245
1334
  "rustls-pki-types",
1246
1335
  "time",
1336
+ "x509-parser",
1247
1337
  "yasna",
1248
1338
  ]
1249
1339
 
@@ -1366,6 +1456,15 @@ dependencies = [
1366
1456
  "semver",
1367
1457
  ]
1368
1458
 
1459
+ [[package]]
1460
+ name = "rusticata-macros"
1461
+ version = "4.1.0"
1462
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1463
+ checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
1464
+ dependencies = [
1465
+ "nom",
1466
+ ]
1467
+
1369
1468
  [[package]]
1370
1469
  name = "rustls"
1371
1470
  version = "0.23.37"
@@ -1392,9 +1491,9 @@ dependencies = [
1392
1491
 
1393
1492
  [[package]]
1394
1493
  name = "rustls-webpki"
1395
- version = "0.103.9"
1494
+ version = "0.103.13"
1396
1495
  source = "registry+https://github.com/rust-lang/crates.io-index"
1397
- checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
1496
+ checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
1398
1497
  dependencies = [
1399
1498
  "aws-lc-rs",
1400
1499
  "ring",
@@ -1608,6 +1707,17 @@ dependencies = [
1608
1707
  "unicode-ident",
1609
1708
  ]
1610
1709
 
1710
+ [[package]]
1711
+ name = "synstructure"
1712
+ version = "0.13.2"
1713
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1714
+ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
1715
+ dependencies = [
1716
+ "proc-macro2",
1717
+ "quote",
1718
+ "syn",
1719
+ ]
1720
+
1611
1721
  [[package]]
1612
1722
  name = "target-lexicon"
1613
1723
  version = "0.13.5"
@@ -1712,9 +1822,9 @@ dependencies = [
1712
1822
 
1713
1823
  [[package]]
1714
1824
  name = "tokio-tungstenite"
1715
- version = "0.28.0"
1825
+ version = "0.29.0"
1716
1826
  source = "registry+https://github.com/rust-lang/crates.io-index"
1717
- checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857"
1827
+ checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c"
1718
1828
  dependencies = [
1719
1829
  "futures-util",
1720
1830
  "log",
@@ -1791,9 +1901,9 @@ dependencies = [
1791
1901
 
1792
1902
  [[package]]
1793
1903
  name = "tracing-subscriber"
1794
- version = "0.3.22"
1904
+ version = "0.3.23"
1795
1905
  source = "registry+https://github.com/rust-lang/crates.io-index"
1796
- checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
1906
+ checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
1797
1907
  dependencies = [
1798
1908
  "matchers",
1799
1909
  "nu-ansi-term",
@@ -1812,9 +1922,9 @@ dependencies = [
1812
1922
 
1813
1923
  [[package]]
1814
1924
  name = "tungstenite"
1815
- version = "0.28.0"
1925
+ version = "0.29.0"
1816
1926
  source = "registry+https://github.com/rust-lang/crates.io-index"
1817
- checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
1927
+ checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
1818
1928
  dependencies = [
1819
1929
  "bytes",
1820
1930
  "data-encoding",
@@ -1824,7 +1934,6 @@ dependencies = [
1824
1934
  "rand 0.9.2",
1825
1935
  "sha1",
1826
1936
  "thiserror",
1827
- "utf-8",
1828
1937
  ]
1829
1938
 
1830
1939
  [[package]]
@@ -1861,12 +1970,6 @@ version = "0.9.0"
1861
1970
  source = "registry+https://github.com/rust-lang/crates.io-index"
1862
1971
  checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
1863
1972
 
1864
- [[package]]
1865
- name = "utf-8"
1866
- version = "0.7.6"
1867
- source = "registry+https://github.com/rust-lang/crates.io-index"
1868
- checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
1869
-
1870
1973
  [[package]]
1871
1974
  name = "uuid"
1872
1975
  version = "1.22.0"
@@ -2224,7 +2327,7 @@ dependencies = [
2224
2327
 
2225
2328
  [[package]]
2226
2329
  name = "wse-accel"
2227
- version = "2.3.1"
2330
+ version = "2.4.0"
2228
2331
  dependencies = [
2229
2332
  "aes-gcm",
2230
2333
  "ahash",
@@ -2239,6 +2342,7 @@ dependencies = [
2239
2342
  "hex",
2240
2343
  "hkdf",
2241
2344
  "hmac",
2345
+ "indexmap",
2242
2346
  "jsonwebtoken",
2243
2347
  "mimalloc",
2244
2348
  "p256",
@@ -2261,9 +2365,28 @@ dependencies = [
2261
2365
  "tracing",
2262
2366
  "tracing-subscriber",
2263
2367
  "uuid",
2368
+ "zeroize",
2264
2369
  "zstd",
2265
2370
  ]
2266
2371
 
2372
+ [[package]]
2373
+ name = "x509-parser"
2374
+ version = "0.18.1"
2375
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2376
+ checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
2377
+ dependencies = [
2378
+ "asn1-rs",
2379
+ "data-encoding",
2380
+ "der-parser",
2381
+ "lazy_static",
2382
+ "nom",
2383
+ "oid-registry",
2384
+ "ring",
2385
+ "rusticata-macros",
2386
+ "thiserror",
2387
+ "time",
2388
+ ]
2389
+
2267
2390
  [[package]]
2268
2391
  name = "yasna"
2269
2392
  version = "0.5.2"
@@ -2275,18 +2398,18 @@ dependencies = [
2275
2398
 
2276
2399
  [[package]]
2277
2400
  name = "zerocopy"
2278
- version = "0.8.40"
2401
+ version = "0.8.47"
2279
2402
  source = "registry+https://github.com/rust-lang/crates.io-index"
2280
- checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
2403
+ checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
2281
2404
  dependencies = [
2282
2405
  "zerocopy-derive",
2283
2406
  ]
2284
2407
 
2285
2408
  [[package]]
2286
2409
  name = "zerocopy-derive"
2287
- version = "0.8.40"
2410
+ version = "0.8.47"
2288
2411
  source = "registry+https://github.com/rust-lang/crates.io-index"
2289
- checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
2412
+ checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
2290
2413
  dependencies = [
2291
2414
  "proc-macro2",
2292
2415
  "quote",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "wse-accel"
3
- version = "2.3.1"
3
+ version = "2.4.0"
4
4
  edition = "2024"
5
5
 
6
6
  [lib]
@@ -28,7 +28,7 @@ base64 = "0.22"
28
28
  chrono = { version = "0.4.44", features = ["serde"] }
29
29
  regex = "1.12.3"
30
30
  tokio = { version = "1.50.0", features = ["rt-multi-thread", "macros", "sync", "net", "time", "io-util"] }
31
- tokio-tungstenite = "0.28.0"
31
+ tokio-tungstenite = "0.29.0"
32
32
  futures-util = "0.3.32"
33
33
  crossbeam-channel = "0.5"
34
34
  tokio-util = { version = "0.7", features = ["rt"] }
@@ -40,12 +40,14 @@ rustls = { version = "0.23", default-features = true }
40
40
  rustls-pki-types = "1"
41
41
  zstd = "0.13"
42
42
  parking_lot = "0.12"
43
+ indexmap = "2"
44
+ zeroize = "1"
43
45
  tracing = "0.1"
44
46
  tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
45
47
  arc-swap = "1"
46
48
 
47
49
  [dev-dependencies]
48
- rcgen = "0.13"
50
+ rcgen = "0.14"
49
51
 
50
52
  [profile.release]
51
53
  lto = true