br-web-server 0.5.17

This is an WEB SERVER
Documentation
# AGENTS.md - br-web-server

> AI agent guidelines for this Rust HTTP/WebSocket server library.

**Generated:** 2025-02-07 | **Commit:** 88e6b41 | **Branch:** main

## Overview

Rust library crate for HTTP/1.0, HTTP/1.1, HTTP/2, WebSocket with TLS via rustls. Published on crates.io as `br-web-server` v0.5.15.

## Structure

```
br-web-server/
├── src/
│   ├── lib.rs          # Entry + WebServer, Handler trait, enums (858 lines)
│   ├── config.rs       # Config, TlsConfig (181 lines)
│   ├── request.rs      # Request parsing (535 lines)
│   ├── response.rs     # Response builder (1109 lines)
│   ├── websocket.rs    # WebSocket impl (1183 lines)
│   ├── stream.rs       # HTTP/HTTPS stream abstractions
│   ├── client.rs       # HTTP client (feature-gated)
│   ├── client_response.rs  # Client response (private)
│   └── url.rs          # URL parsing
├── tests/              # 8 test files
├── examples/
│   ├── br_web_service.rs  # Server example (primary)
│   ├── br_clinet.rs       # Client example (typo)
│   └── br_service.rs      # Duplicate server example
└── Cargo.toml
```

## Commands

```bash
# Build
cargo build                          # Debug
cargo build --release                # Release
cargo build --features client        # Client feature only

# Lint & Format (CI policy)
cargo clippy -- -D warnings          # Strict lint - treats warnings as errors
cargo fmt --check                    # Check format
cargo fmt                            # Auto-format

# Test
cargo test                           # All tests
cargo test test_name                 # Single test by name
cargo test -- --nocapture            # Show println! output
cargo test --test unit_tests         # Run specific test file

# Run Examples
cargo run --example br_web_service   # Server
cargo run --example br_clinet        # Client

# Docs
cargo doc --open
```

## Public API

| Module | Key Types | Notes |
|--------|-----------|-------|
| `lib.rs` | `WebServer`, `Handler`, `HttpError`, `Method`, `Protocol`, `Uri`, `Status` | Core |
| `config` | `Config`, `TlsConfig` | Server config |
| `request` | `Request` | Incoming request |
| `response` | `Response`, `CookieOptions`, `SameSite` | Response builder |
| `websocket` | `Websocket`, `Message`, `CloseCode`, `USERS`, `SUBSCRIPTIONS` | WS support |
| `client` | `Client` | HTTP client (feature: `client`) |

## Code Conventions

### Imports (grouped, blank-line separated)
```rust
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};

use json::{object, JsonValue};
use log::{error, info};

use crate::config::Config;
```

### Naming
| Item | Style | Example |
|------|-------|---------|
| Structs/Enums | PascalCase | `WebServer`, `HttpError` |
| Functions/vars | snake_case | `handle_request` |
| Constants | SCREAMING_SNAKE | `FLAG_END_STREAM`, `MAX_FRAME_SIZE` |

### Enum Pattern (from/str conversion)
```rust
impl Method {
    pub fn from(name: &str) -> Self {
        match name.to_lowercase().as_str() {
            "post" => Self::POST,
            _ => Self::Other(name.to_string()),
        }
    }
    pub fn str(&self) -> &str { ... }
}
```

### Error Handling
- Custom type: `HttpError { code: u16, body: String }`
- Use `Result<T, HttpError>` or `io::Result<T>`
- Propagate with `?` operator

### Thread Safety
- `Arc<Mutex<T>>` for shared state
- `std::sync::LazyLock` for statics (`USERS`, `SUBSCRIPTIONS`, `WS_NOTICE`)
- `DashMap` for concurrent maps

### Comments
- Chinese comments acceptable (project convention)
- Use `///` for public API docs

## Features

```toml
[features]
client = ["infer"]    # HTTP client
server = []           # Server (no extra deps)
default = ["client", "server"]
```

## Dependencies (non-obvious)

| Crate | Usage |
|-------|-------|
| `json` | JSON (NOT serde_json) - use `object!{}` macro |
| `br-crypto` | base64, sha1, sha256, md5, urlencoding |
| `rustls` | TLS (not openssl) |
| `hpack` | HTTP/2 header compression |
| `dashmap` | Concurrent HashMap |
| `flate2` | Gzip compression |
| `brotli` | Brotli decompression |

## Anti-Patterns (THIS PROJECT)

1. **No serde_json** - Use `json` crate with `object!{}` macro
2. **Monolithic lib.rs** - Contains runtime logic, not just re-exports
3. **Thread-per-connection** - Each request spawns a thread (no async runtime)
4. **Private module leak** - `ClientResponse` in private module but returned by public API

## Safety Warnings

| Location | Warning |
|----------|---------|
| `response.rs:250` | Path traversal protection - sanitizes request paths |
| `response.rs:410,539` | Panic conditions documented for file handling |
| `websocket.rs:978,980` | Close codes 1005/1006 reserved, must not send |
| `request.rs:311` | Fallback for non-standard content-type to avoid panic |

## Known Issues

- `examples/br_clinet.rs` - Typo (should be `br_client`)
- Duplicate examples: `br_service.rs` vs `br_web_service.rs`
- `examples/ssl/` contains committed key material
- HTTP/2 support is partial
- HTTP/3 not implemented

## Config Schema

```toml
[br_web_server]
debug = true
host = "0.0.0.0:3535"
https = false
log = true
public = "public"
webpage = "spa"
runtime = "runtime"
charset = "utf-8"
max_body_size = 10485760  # 10MB default
read_timeout = 30
write_timeout = 30

[br_web_server.tls]
certs = "ssl/server.pem"
key = "ssl/server.key"
```

## Handler Pattern

```rust
#[derive(Clone, Debug)]
struct MyHandler { out: Websocket }

impl Handler for MyHandler {
    fn on_request(&mut self, request: Request, response: &mut Response) {
        response.status(200).json(object! { "ok": true });
    }
    fn on_options(&mut self, response: &mut Response) { /* CORS */ }
    fn on_open(&mut self) -> Result<(), HttpError> { Ok(()) }  // WS
    fn on_message(&mut self, msg: Message) -> Result<(), HttpError> { Ok(()) }
    fn on_close(&mut self, code: CloseCode, reason: &str) {}
}

// Start server
WebServer::new_service(config, |out| Box::new(MyHandler { out }));
```

## WebSocket Globals

```rust
// Connected users map
pub static USERS: LazyLock<DashMap<String, Websocket>>

// Channel subscriptions  
pub static SUBSCRIPTIONS: LazyLock<DashMap<String, HashSet<String>>>

// Notice queue
pub static WS_NOTICE: LazyLock<Mutex<Vec<NoticeMsg>>>
```

## Timeouts

| Context | Read | Write |
|---------|------|-------|
| Server | 30s | 30s |
| Client | 10s | 10s |