mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 04:34:38 +08:00
Refresh client ownership developer docs
This commit is contained in:
parent
cbaf02c769
commit
2b43ce6a3e
3 changed files with 48 additions and 26 deletions
|
|
@ -83,6 +83,9 @@
|
|||
- Refreshed contributor and development guidance so new commands are added
|
||||
through `command_catalog`, `exec_catalog`, and `i18n_text` instead of stale
|
||||
`ssh_server.c` / inline-`strcmp` instructions.
|
||||
- Refreshed developer ownership guidance to match the current update-sequence
|
||||
model: room broadcasts update shared state only, while each interactive
|
||||
client renders and flushes its own SSH channel.
|
||||
- `exec_catalog` now owns SSH exec command matching as well as help metadata,
|
||||
reducing duplicate command knowledge in `src/exec.c`.
|
||||
- Replaced hard-coded `chat.m1ng.space` examples with `chat.example.com` so
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ make release-check # release preflight
|
|||
make test # unit + integration tests
|
||||
make ci-test # local CI-equivalent checks
|
||||
make stress-test # concurrent-client stress test
|
||||
make soak-test # idle/reconnect/control-plane soak
|
||||
make slow-client-test # slow interactive-client backpressure
|
||||
make user-lifecycle-test # two-user TUI lifecycle
|
||||
```
|
||||
|
||||
## Debug
|
||||
|
|
@ -78,7 +81,8 @@ utf8.c → UTF-8 string handling
|
|||
## Common Bugs to Avoid
|
||||
|
||||
1. Don't use `strtok()` on client data - use `strtok_r()` or copy first
|
||||
2. Always increment ref_count before using client outside lock
|
||||
2. Always use `client_addref()` / `client_release()` before using a client
|
||||
outside `g_room->lock`; never modify `ref_count` directly
|
||||
3. Check SSH API return values (can be SSH_ERROR, SSH_AGAIN, or negative)
|
||||
4. UTF-8 chars are multi-byte - use utf8_* functions
|
||||
|
||||
|
|
|
|||
|
|
@ -55,10 +55,13 @@ TNT uses a multi-threaded architecture with a main accept loop and per-client th
|
|||
|
||||
### Key Design Principles
|
||||
|
||||
1. **Fixed-size buffers** - No dynamic allocation in hot paths
|
||||
2. **Reader-writer locks** - Multiple readers, single writer
|
||||
3. **Reference counting** - Prevent use-after-free
|
||||
4. **Ring buffer** - Fixed-size message history (last 100 messages)
|
||||
1. **Fixed-size buffers** - Keep message, command, and UI buffers bounded
|
||||
2. **Reader-writer locks** - Multiple readers, single writer for room state
|
||||
3. **Per-client output ownership** - Each interactive session writes only to
|
||||
its own SSH channel
|
||||
4. **Reference counting** - Keep client objects alive across callbacks and
|
||||
cross-thread lookups
|
||||
5. **Ring buffer** - Fixed-size in-memory message history (last 100 messages)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -76,7 +79,7 @@ src/
|
|||
├── command_catalog.c - COMMAND-mode names, aliases, and help summaries
|
||||
├── exec_catalog.c - SSH exec command matching and help metadata
|
||||
├── exec.c - SSH exec command dispatch
|
||||
├── chat_room.c - Chat room logic and message broadcasting
|
||||
├── chat_room.c - Chat room state, message ring, and update sequence
|
||||
├── message.c - Message persistence (RFC3339 format)
|
||||
├── history_view.c - NORMAL-mode scroll window rules
|
||||
├── tui.c - Terminal UI rendering (ANSI escape codes)
|
||||
|
|
@ -119,12 +122,15 @@ typedef struct client {
|
|||
ssh_session session;
|
||||
ssh_channel channel;
|
||||
char username[MAX_USERNAME_LEN];
|
||||
int width, height; // Terminal dimensions
|
||||
_Atomic int width, height; // Terminal dimensions
|
||||
client_mode_t mode; // INSERT/NORMAL/COMMAND
|
||||
int scroll_pos;
|
||||
bool connected;
|
||||
atomic_bool connected;
|
||||
char *outbox; // Bounded queued interactive output
|
||||
size_t outbox_len, outbox_pos;
|
||||
int ref_count; // Reference counting
|
||||
pthread_mutex_t ref_lock;
|
||||
pthread_mutex_t io_lock; // Own SSH channel writes only
|
||||
} client_t;
|
||||
```
|
||||
|
||||
|
|
@ -134,6 +140,7 @@ typedef struct {
|
|||
pthread_rwlock_t lock; // Reader-writer lock
|
||||
struct client **clients; // Dynamic array
|
||||
int client_count;
|
||||
uint64_t update_seq; // Bumped when message history changes
|
||||
message_t *messages; // Ring buffer
|
||||
int message_count;
|
||||
} chat_room_t;
|
||||
|
|
@ -189,6 +196,9 @@ make anonymous-access-test # Verify default anonymous login behavior
|
|||
make connection-limit-test # Verify per-IP concurrency and rate limits
|
||||
make security-test # Run security feature checks
|
||||
make stress-test # Run configurable concurrent-client stress test
|
||||
make soak-test # Run idle/reconnect/control-plane soak test
|
||||
make slow-client-test # Run slow interactive-client backpressure test
|
||||
make user-lifecycle-test # Run a two-user TUI lifecycle test
|
||||
make ci-test # Run the same checks as GitHub Actions
|
||||
|
||||
# Individual tests
|
||||
|
|
@ -197,6 +207,9 @@ cd tests
|
|||
./test_security_features.sh # Security checks
|
||||
./test_anonymous_access.sh # Anonymous access
|
||||
./test_stress.sh # Concurrent connections
|
||||
./test_soak.sh # Idle/reconnect soak
|
||||
./test_slow_client.sh # Slow-client backpressure
|
||||
./test_user_lifecycle.sh # Two-user TUI lifecycle
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
|
@ -205,6 +218,10 @@ cd tests
|
|||
- **Security**: RSA keys, env vars, UTF-8 validation, buffer overflow protection
|
||||
- **Anonymous**: Passwordless access, any username
|
||||
- **Stress**: 10 concurrent clients for 30 seconds
|
||||
- **Soak**: idle session, reconnect churn, health/stats/users/post/tail
|
||||
- **Slow client**: unread interactive SSH client cannot block control paths
|
||||
- **Lifecycle**: two-user TUI story covering help, history, search, private
|
||||
messages, nickname, action messages, and persistence boundaries
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -244,33 +261,29 @@ while ((!ctx->auth_success || ctx->channel == NULL || !ctx->channel_ready) && !t
|
|||
|
||||
### 2. Chat Room (chat_room.c)
|
||||
|
||||
**Thread-safe broadcasting:**
|
||||
**Thread-safe message publication:**
|
||||
```c
|
||||
void room_broadcast(chat_room_t *room, const message_t *msg) {
|
||||
pthread_rwlock_wrlock(&room->lock);
|
||||
|
||||
/* Copy client list with ref counting */
|
||||
client_t **clients_copy = calloc(...);
|
||||
for (int i = 0; i < count; i++) {
|
||||
clients_copy[i]->ref_count++;
|
||||
}
|
||||
room_add_message(room, msg);
|
||||
room->update_seq++;
|
||||
|
||||
pthread_rwlock_unlock(&room->lock); // Release lock early
|
||||
|
||||
/* Render outside lock (avoid deadlock) */
|
||||
for (int i = 0; i < count; i++) {
|
||||
tui_render_screen(clients_copy[i]);
|
||||
client_release(clients_copy[i]);
|
||||
}
|
||||
pthread_rwlock_unlock(&room->lock);
|
||||
}
|
||||
```
|
||||
|
||||
**Why this works:**
|
||||
- Copy client list while holding write lock
|
||||
- Increment reference counts
|
||||
- Release lock BEFORE rendering
|
||||
- Render to all clients outside lock
|
||||
- Decrement reference counts (may free clients)
|
||||
- Broadcast updates shared room state only; it does not render or write to
|
||||
any SSH channel.
|
||||
- Each interactive session tracks `room_get_update_seq()` in its own
|
||||
`input_run_session()` loop.
|
||||
- When the sequence changes, the client renders and flushes its own output.
|
||||
- This keeps slow SSH windows local to that client and prevents one recipient
|
||||
from blocking a sender or the whole room.
|
||||
- Cross-client lookups, such as mentions and private messages, must call
|
||||
`client_addref()` before using a client pointer outside `g_room->lock`, then
|
||||
`client_release()` when done. Do not increment `ref_count` directly.
|
||||
|
||||
### 3. Message Persistence (message.c)
|
||||
|
||||
|
|
@ -380,6 +393,8 @@ void utf8_remove_last_word(char *str) {
|
|||
```sh
|
||||
tests/test_exec_mode.sh # exec command behavior
|
||||
tests/test_interactive_input.sh # COMMAND-mode/TUI behavior
|
||||
tests/test_user_lifecycle.sh # end-to-end two-user TUI behavior
|
||||
tests/test_slow_client.sh # slow SSH reader/backpressure behavior
|
||||
tests/unit/test_i18n.c # localized shared text
|
||||
tests/unit/test_command_catalog.c # interactive command metadata
|
||||
tests/unit/test_exec_catalog.c # exec command help metadata
|
||||
|
|
|
|||
Loading…
Reference in a new issue