Design Notes
Table of Contents
1. Overview
2. Architectural Model
3. Execution Model
4. State Model
5. Error Handling Strategy
6. Threading Model
7. Memory Model
8. Identity & RTTI
9. Subsystem Responsibilities
10. Design Principles
1. Overview
The SSH package is a layered, RAII-driven wrapper around libssh2, designed to integrate naturally with the U++ ecosystem.
Its goals:
Provide a unified object-oriented SSH abstraction
Preserve libssh2’s flexibility without exposing its procedural complexity
Support both GUI and console applications
Be cross-platform (POSIX + Windows)
Support multithreading
Follow U++ idioms (pick semantics, Event, RTTI, memory manager)
At the center of the architecture is the Ssh base class (the “core” abstraction).
2. Architectural Model
2.1 Layered Design
Ssh (Core)
├── SshSession
│ ├── SshChannel
│ │ ├── Scp
│ │ ├── SshExec
│ │ ├── SshShell
│ │ └── SshTunnel
│ └── SFtp
│ └── SFtpStream
└── SshHosts
2.2 Responsibilities by Layer
The Ssh class provides:
State machine
Error handling
Timeout handling
Abort support
Thread synchronization
Event-driven wait loop
Common diagnostics/tracing
Polymorphic base (RTTI-enabled)
It encapsulates:
LIBSSH2_SESSION*
Socket state
Timing state
Error state
Status flags (IDLE, WORKING, FAILED, ABORTED)
It does not represent a concrete SSH entity. It represents the execution model.
3. Execution Model
3.1 Cooperative Run Loop
Core execution is handled by:
bool Ssh::Run(Gate<>&& fn, bool abortable)
Design intent:
Wrap libssh2’s non-blocking behavior
Centralize timeout and abort checks
Serialize critical SSH operations
Ensure proper socket direction handling
Flow:
Set status = WORKING
Record start time
Repeatedly:
Validate timeout
Validate abort
Validate socket state
Lazily initialize session
Execute user-provided operation
Call Wait() if libssh2 indicates blocking
Transition to IDLE or FAILED
This abstracts libssh2’s EAGAIN-style state machine into a higher-level loop.
3.2 Wait Strategy
void Ssh::Wait()
Uses:
libssh2_session_block_directions()
SocketWaitEvent
WAIT_READ / WAIT_WRITE mask
Configurable wait step
Design goals:
Respect non-blocking semantics
Avoid busy looping
Integrate cleanly with U++ event loop
Support both console and GUI contexts
4. State Model
Core states:
IDLE
WORKING
FAILED
ABORTED
Transitions:
IDLE → WORKING (Run begins)
WORKING → IDLE (Success)
WORKING → FAILED (Error)
WORKING → ABORTED (User abort)
Abort is cooperative:
Sets status flag
Next Run iteration throws
5. Error Handling Strategy
Errors are captured via:
struct Error : Exc
Properties:
Numeric error code
Descriptive message
Propagated via exceptions
Normalized into internal state via SetError()
This ensures:
Consistent error propagation
Clean user-facing API
libssh2 error translation
All public operations return status via operator bool(), IsError(), GetError(), GetErrorDesc()
6. Threading Model
Key aspects:
Static global mutex protects critical operations
Designed for multithreaded usage at object level
Internal operations are serialized
This design ensures:
No race conditions inside libssh2 calls
Controlled concurrency model
Predictable behavior under load
7. Memory Model
RAII-based lifecycle
Pick semantics supported
Compatible with U++ memory manager
Optional native malloc mode
Object lifetime is deterministic.
Resources (session, channel, socket) are released in destructors.
8. Identity & RTTI
Supports:
Polymorphic storage
Runtime casting via To<T>()
Type checking via Is<T>()
Design goal: safe polymorphism without losing performance.
9. Subsystem Responsibilities
SshSession
Authentication
Handshake
Agent support
Known hosts verification
Proxy support
SshChannel
Abstract channel handling
Shared channel lifecycle logic
Derived specializations:
Scp → file transfer
SshExec → command execution
SshShell → interactive shell + X11
SshTunnel → port forwarding
SFtp
Subsystem initialization
Directory traversal
File operations
Stream interface (SFtpStream)
SshHosts
Known host verification
Host fingerprint management
10. Design Principles
Non-blocking core wrapped in controlled blocking loop
RAII discipline
Clear separation of session vs channel vs subsystem
Cross-platform behavior abstraction
Debug traceability at object granularity
Avoid leaking libssh2 procedural complexity to users
|