197 lines
11 KiB
Markdown
197 lines
11 KiB
Markdown
# 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<ProxyDevice>` 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<string, object?> deviceParameters);
|
|
Task<HttpResponseMessage> Process();
|
|
Task<HttpResponseMessage> 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.
|
|
|
|
---
|
|
|
|
<p align="center">
|
|
Back to <a href="../README.md">adas-core Root README</a>
|
|
</p>
|