# adas-core.module.ProxyDevices — Proxy Devices Module > A **functional module** of the ADAS Core platform. > Encapsulates all logic for proxying communication with external hardware devices over HTTP. This module acts as a configurable gateway layer that the Host registers at startup, translating internal requests into authenticated HTTP calls to remote endpoints. --- ## Table of Contents 1. [Overview](#overview) 2. [Responsibilities](#responsibilities) 3. [Project Structure](#project-structure) 4. [Dependencies](#dependencies) 5. [Device Proxy Architecture](#device-proxy-architecture) 6. [ProxyDeviceService Flow](#proxydeviceservice-flow) 7. [Design Rules](#design-rules) --- ## Overview `adas-core.module.ProxyDevices` is a modular satellite project that provides a **proxy gateway** for interacting with external hardware devices over HTTP. It abstracts the complexity of device authentication, connection management, and response handling behind a uniform `IProxyDevice` contract. Key characteristics: - **Modular Monolith Pattern** — Self-contained module with its own device drivers, service orchestration, and configuration model. No other module depends on it directly. - **Plugin Architecture** — New device types are added by creating a class that implements `IProxyDevice` and naming it `{Type}Device`. The service discovers and instantiates them at runtime via reflection. - **Digest Authentication Support** — The built-in `HttpDevice` supports HTTP Digest authentication via `NetworkCredential`. - **Instance Caching** — `ProxyDeviceService` maintains an in-memory cache of initialized device instances to avoid repeated construction and initialization overhead. - **Dual Operation Modes** — Each device supports both `Process()` (full response) and `Stream()` (response-headers-read) operations. - **Configuration-Driven** — All devices, their types, and parameters are defined in `ProxyDeviceSettings` bound from `appsettings.json`. --- ## Responsibilities | Concern | What this project does | |---------|----------------------| | **Device Proxying** | Acts as an intermediary between the Host and external HTTP-based devices, shielding the rest of the system from protocol specifics. | | **Device Lifecycle** | Manages device initialization, caching, and disposal through the `IProxyDevice` contract. | | **HTTP Communication** | `HttpDevice` performs authenticated and unauthenticated HTTP GET requests with timeout handling. | | **Digest Authentication** | Supports HTTP Digest auth via `username`, `password`, and optional `domain` parameters. | | **Reflection-Based Factory** | `ProxyDeviceService` dynamically instantiates device classes by type name at runtime (`{Type}Device`). | | **Instance Caching** | Caches initialized `IProxyDevice` instances per `deviceId` to minimize repeated setup costs. | | **Streaming vs Processing** | Offers `Process()` for complete responses and `Stream()` for streaming (headers-first) scenarios. | | **Error Translation** | Converts `HttpRequestException`, `TaskCanceledException`, and missing-device errors into standardized `HttpResponseMessage` responses. | | **Configuration Model** | Defines `ProxyDeviceSettings` and `ProxyDevice` as strongly-typed configuration objects bound from the Host's configuration system. | | **Audit Logging** | Emits audit events for device access and proxy operations via `AuditLogs`. | --- ## Project Structure ``` adas-core.module.ProxyDevices/ ├── ProxyDeviceSettings.cs # Strongly-typed settings model (list of configured proxy devices) │ ├── Devices/ │ ├── IProxyDevice.cs # Core contract: Initialize, Process, Stream │ └── HttpDevice.cs # Concrete HTTP proxy with digest-auth support │ └── Services/ ├── IProxyDeviceService.cs # Service contract: Process(deviceId), Stream(deviceId) └── ProxyDeviceService.cs # Orchestrator: reflection factory, caching, error handling ``` | File | Role | |------|------| | `ProxyDeviceSettings.cs` | Configuration root. A `List` bound from `appsettings.json`. Each entry defines `Id`, `Type`, `Enabled`, and `Parameters`. | | `IProxyDevice.cs` | Interface contract. All proxy devices implement `Initialize(Dictionary)`, `Process()`, and `Stream()`. | | `HttpDevice.cs` | Concrete proxy for HTTP endpoints. Validates `url`, optional digest auth, performs GET requests, handles timeouts. | | `IProxyDeviceService.cs` | Application-facing contract. Host controllers depend on this interface, not on module internals. | | `ProxyDeviceService.cs` | Implementation. Reads settings, looks up devices by `Id`, lazily instantiates and caches drivers, delegates to `Process()` or `Stream()`. | --- ## Dependencies ### Downstream References This project has **no direct project references** to other ADAS Core projects. It is a completely decoupled satellite module. | Package | Version | Purpose | |---------|---------|---------| | `Microsoft.Extensions.DependencyInjection` | 10.0.8 | DI registration helpers used during Host startup. | | `Microsoft.Extensions.Hosting` | 10.0.8 | Hosting abstractions for optional background proxy services. | ### Upstream References (projects that depend on this) | Project | Reason | |---------|--------| | `adas-core` (Host) | Registers `IProxyDeviceService` and `ProxyDeviceSettings` at startup. Host controllers invoke `Process()` and `Stream()` through the service interface. | ### NuGet Packages | Package | Version | Purpose | |---------|---------|---------| | `Microsoft.Extensions.DependencyInjection` | 10.0.8 | Service-collection registration helpers. | | `Microsoft.Extensions.Hosting` | 10.0.8 | Hosting abstractions for background services. | | `Serilog` | 4.3.1 | Structured logging for timeout and HTTP error events. | | `AuditLogs` | 1.0.59 | Audit trail emission for device access and proxy operations. | | `SharpCompress` | 0.49.0 | Available for potential compression pipelines in future device types. | | `Snappier` | 1.3.1 | Fast compression library for future streaming optimization. | --- ## Device Proxy Architecture ### `IProxyDevice` Contract ```csharp public interface IProxyDevice { void Initialize(Dictionary deviceParameters); Task Process(); Task Stream(); } ``` | Member | Purpose | |--------|---------| | `Initialize(parameters)` | One-time setup. Validates required keys (`url`), extracts optional keys (`name`, `username`, `password`, `domain`), and marks the device as ready. | | `Process()` | Performs a full HTTP GET request and returns the complete `HttpResponseMessage`. | | `Stream()` | Performs an HTTP GET with `HttpCompletionOption.ResponseHeadersRead`, enabling callers to stream the body without buffering. | ### `HttpDevice` — Built-in HTTP Proxy - **URI Validation** — Requires a non-empty, absolute `url` in parameters. Throws `ArgumentNullException` if missing or invalid. - **Single Initialization Guard** — Throws `InvalidOperationException` if `Initialize()` is called twice. - **Optional Digest Auth** — When `username` is provided, sets `HttpClientHandler.Credentials` with `NetworkCredential(username, password, domain)`. - **Timeout Translation** — Catches `TaskCanceledException` (timeout) and re-throws as `TimeoutException` with a descriptive message. - **Diagnostics** — Overrides `ToString()` to emit `[HttpDevice] (name uri username password domain)` for logging. ### Adding a New Device Type 1. Create a class in `Devices/` that implements `IProxyDevice`. 2. Name it `{Type}Device` (e.g., `TcpDevice`, `UdpDevice`). 3. Register the type string in `appsettings.json` under `ProxyDeviceSettings`. 4. `ProxyDeviceService` will resolve it automatically via reflection at first use. ## ProxyDeviceService Flow ### Device Resolution & Caching ``` [Host Controller] --> [ProxyDeviceService.Process(deviceId)] | v [Look up ProxyDevice by Id in settings] | +---------+---------+ | | v v [Enabled=true] [Enabled=false / Not Found] | | v v [Check _cache] [Return 404 Not Found] | | | +--> [Miss] --> [Reflect {Type}Device] | [Call Initialize(params)] | [Add to _cache] v | [Call device.Process()] <--+ | v [Return HttpResponseMessage] ``` ### Key Behaviors - **Thread-Safe Cache** — The `_cache` dictionary is guarded by a `lock` during read/write to prevent race conditions. - **Lazy Instantiation** — Devices are created only on first access, not at startup. - **Graceful Degradation** — If a device is not found, disabled, or throws `HttpRequestException`, the service returns an `HttpResponseMessage` with an appropriate status code rather than crashing. - **No Dispose Lifecycle** — Cached instances are reused for the lifetime of the service. The Host manages the service scope (typically Singleton). --- ## Design Rules 1. **True Module Independence** — This project must not reference any other ADAS Core project. It communicates with the Host exclusively through its own public interfaces and the .NET configuration/options system. 2. **Configuration-Driven Discovery** — Device types, endpoints, and credentials must come exclusively from `ProxyDeviceSettings` bound to `appsettings.json`. No hardcoded URLs or auth secrets. 3. **Reflection Factory** — New device types are added by convention (`{Type}Device` implementing `IProxyDevice`). No central switch statement needs modification (Open/Closed). 4. **Cache Safety** — The device instance cache is protected by a `lock` during writes. Reads inside the lock are safe because initialization is atomic. 5. **Fail Securely** — Missing or disabled devices return `404 Not Found`. HTTP failures are translated into `HttpResponseMessage` with the original status code. Timeouts become `TimeoutException`. 6. **No Business Logic** — This module handles transport only. Any transformation, validation, or business-rule enforcement belongs in `Application` or the Host. 7. **Lazy Initialization** — Devices are instantiated on first request, not at Host startup. This prevents delaying startup when a remote device is unreachable. 8. **Digest Auth Only** — `HttpDevice` supports HTTP Digest authentication. Basic auth, bearer tokens, or mTLS must be implemented in a separate `IProxyDevice` implementation if needed. 9. **Audit Every Access** — Every `Process()` and `Stream()` call emits an audit event via `AuditLogs` tagging the device ID and outcome. 10. **Singleton Service** — `ProxyDeviceService` is registered as a **Singleton** because it holds cached device instances and settings that are immutable after startup. ---

Back to adas-core Root README