# Components ## Component Overview ```mermaid graph TB subgraph "main (Entry Point)" MAIN[main.go] end subgraph "internal/config" CFG[Config Loader] end subgraph "internal/hub" HUB[Hub Manager] WS[WebSocket Handler] end subgraph "internal/metrics" MET[Prometheus Metrics] end MAIN --> CFG MAIN --> HUB MAIN --> MET HUB --> WS HUB --> MET ``` ## Package: `main` **File:** `main.go` **Responsibility:** Application entry point and server initialization. **Behavior:** 1. Parses CLI flags (`--config-file`) 2. Loads YAML configuration 3. Creates and starts the Hub 4. Optionally starts the metrics HTTP server on a separate port 5. Starts the WebSocket HTTP/TLS server **Dependencies:** `internal/config`, `internal/hub`, `prometheus/client_golang` --- ## Package: `internal/hub` **File:** `internal/hub/hub.go` **Responsibility:** WebSocket connection lifecycle management and message broadcasting. ### Struct: `Hub` | Field | Type | Purpose | |-------|------|---------| | `clients` | `map[*websocket.Conn]bool` | Set of active connections | | `broadcast` | `chan []byte` | Channel for messages to relay | | `register` | `chan *websocket.Conn` | Channel for new connections | | `unregister` | `chan *websocket.Conn` | Channel for disconnections | | `mu` | `sync.RWMutex` | Protects the clients map | ### Methods | Method | Signature | Description | |--------|-----------|-------------| | `New` | `func New() *Hub` | Constructor, initializes all channels and map | | `Run` | `func (h *Hub) Run()` | Main event loop processing channels (blocking) | | `HandleWebSocket` | `func (h *Hub) HandleWebSocket(w, r)` | HTTP handler — upgrades connection and starts reader | | `ClientCount` | `func (h *Hub) ClientCount() int` | Returns current connected client count (thread-safe) | ### Connection Flow ```mermaid stateDiagram-v2 [*] --> HTTPRequest: Client connects HTTPRequest --> Upgraded: WebSocket upgrade Upgraded --> Registered: register channel Registered --> Reading: goroutine loop Reading --> Broadcasting: message received Broadcasting --> Reading: continue Reading --> Unregistered: error/close Unregistered --> [*]: connection cleaned up ``` --- ## Package: `internal/config` **File:** `internal/config/config.go` **Responsibility:** YAML configuration file loading and parsing. ### Struct: `Config` ```go type Config struct { Server struct { Port int TLS struct { Enabled bool CertFile string KeyFile string } } Metrics struct { Enabled bool Port int } } ``` ### Functions | Function | Signature | Description | |----------|-----------|-------------| | `Load` | `func Load(filename string) (*Config, error)` | Reads and parses YAML config file | --- ## Package: `internal/metrics` **File:** `internal/metrics/metrics.go` **Responsibility:** Prometheus metrics registration and exposure. ### Metrics Defined | Variable | Prometheus Type | Metric Name | Description | |----------|----------------|-------------|-------------| | `ConnectedClients` | Gauge | `websocket_connected_clients` | Current number of connected clients | | `MessagesTotal` | Gauge | `websocket_message` | Total messages processed | | `ConnectionsTotal` | Gauge | `websocket_connection` | Total connections established | | `DisconnectionsTotal` | Gauge | `websocket_disconnection` | Total disconnections | > **Note:** All metrics use `promauto.NewGauge` for auto-registration. The "total" metrics use Gauge instead of Counter, which means they track cumulative counts but will reset on restart. --- ## Test Coverage | Package | Test File | Tests | |---------|-----------|-------| | `internal/hub` | `hub_test.go` | `TestNew`, `TestClientCount`, `TestBroadcastChannel` | | `internal/config` | `config_test.go` | `TestLoad`, `TestLoadFileNotFound` | | `internal/metrics` | — | No dedicated tests |