///|
/// State of a managed transport endpoint.
pub enum ManagedTransportState {
TransportCreated
TransportConnecting
TransportStarted
TransportStopping
TransportStopped
TransportFailed
TransportClosed
} derive(Eq, Debug)
///|
pub enum TransportEventKind {
TransportOpened
TransportStartedEvent
TransportFrameSent
TransportFrameReceived
TransportAcknowledged
TransportTimerExpired
TransportFault
TransportClosedEvent
} derive(Eq, Debug)
///|
pub struct TransportEvent {
sequence : Int
timestamp : Int
kind : TransportEventKind
endpoint : Int
frame_size : Int
detail : String
} derive(Eq, Debug)
///|
pub fn TransportEvent::sequence(self : TransportEvent) -> Int {
self.sequence
}
///|
pub fn TransportEvent::timestamp(self : TransportEvent) -> Int {
self.timestamp
}
///|
pub fn TransportEvent::kind(self : TransportEvent) -> TransportEventKind {
self.kind
}
///|
pub fn TransportEvent::endpoint(self : TransportEvent) -> Int {
self.endpoint
}
///|
pub fn TransportEvent::frame_size(self : TransportEvent) -> Int {
self.frame_size
}
///|
pub fn TransportEvent::detail(self : TransportEvent) -> String {
self.detail
}
///|
pub struct TransportEndpoint {
id : Int
station : CommonAddress
parameters : ConnectionParameters
window : SequenceWindow
decoder : ApduStreamDecoder
mut state : ManagedTransportState
mut last_activity : Int
mut last_send : Int
mut last_receive : Int
mut sent_frames : Int
mut received_frames : Int
mut malformed_frames : Int
} derive(Debug)
///|
pub fn TransportEndpoint::new(
id : Int,
station : CommonAddress,
parameters : ConnectionParameters,
window_capacity : Int,
max_apdu? : Int = 255,
) -> Result[TransportEndpoint, String] {
match SequenceWindow::new(window_capacity) {
Err(error) => Err(error)
Ok(window) =>
match ApduStreamDecoder::new(max_apdu~) {
Err(error) => Err(error)
Ok(decoder) =>
Ok({
id,
station,
parameters,
window,
decoder,
state: TransportCreated,
last_activity: 0,
last_send: 0,
last_receive: 0,
sent_frames: 0,
received_frames: 0,
malformed_frames: 0,
})
}
}
}
///|
pub fn TransportEndpoint::id(self : TransportEndpoint) -> Int {
self.id
}
///|
pub fn TransportEndpoint::station(self : TransportEndpoint) -> CommonAddress {
self.station
}
///|
pub fn TransportEndpoint::parameters(
self : TransportEndpoint,
) -> ConnectionParameters {
self.parameters
}
///|
pub fn TransportEndpoint::state(
self : TransportEndpoint,
) -> ManagedTransportState {
self.state
}
///|
pub fn TransportEndpoint::last_activity(self : TransportEndpoint) -> Int {
self.last_activity
}
///|
pub fn TransportEndpoint::last_send(self : TransportEndpoint) -> Int {
self.last_send
}
///|
pub fn TransportEndpoint::last_receive(self : TransportEndpoint) -> Int {
self.last_receive
}
///|
pub fn TransportEndpoint::sent_frames(self : TransportEndpoint) -> Int {
self.sent_frames
}
///|
pub fn TransportEndpoint::received_frames(self : TransportEndpoint) -> Int {
self.received_frames
}
///|
pub fn TransportEndpoint::malformed_frames(self : TransportEndpoint) -> Int {
self.malformed_frames
}
///|
pub fn TransportEndpoint::open(
self : TransportEndpoint,
timestamp : Int,
) -> Result[Frame, String] {
if self.state != TransportCreated && self.state != TransportStopped {
Err("transport endpoint is not openable")
} else {
self.state = TransportConnecting
self.last_activity = timestamp
Ok(unnumbered_frame(UCommand::control(StartDataTransfer)))
}
}
///|
pub fn TransportEndpoint::start(
self : TransportEndpoint,
timestamp : Int,
) -> Result[Frame, String] {
if self.state != TransportConnecting {
Err("transport endpoint is not connecting")
} else {
self.state = TransportStarted
self.last_activity = timestamp
Ok(unnumbered_frame(UCommand::control(StartDataTransfer)))
}
}
///|
pub fn TransportEndpoint::stop(
self : TransportEndpoint,
timestamp : Int,
) -> Result[Frame, String] {
if self.state != TransportStarted {
Err("transport endpoint is not started")
} else {
self.state = TransportStopping
self.last_activity = timestamp
Ok(unnumbered_frame(UCommand::control(StopDataTransfer)))
}
}
///|
pub fn TransportEndpoint::close(
self : TransportEndpoint,
timestamp : Int,
) -> Unit {
self.state = TransportClosed
self.last_activity = timestamp
}
///|
pub fn TransportEndpoint::send(
self : TransportEndpoint,
payload : Bytes,
timestamp : Int,
) -> Result[Bytes, Diagnostic] {
if self.state != TransportStarted {
Err(Diagnostic::new(StateViolation, "transport endpoint is not started"))
} else {
match self.window.reserve_send() {
Err(_) =>
Err(Diagnostic::new(WindowExhausted, "transport send window is full"))
Ok(sequence) => {
let frame = information_frame(
sequence,
self.window.next_receive(),
payload,
)
let encoded = encode_frame(frame)
self.last_send = timestamp
self.last_activity = timestamp
self.sent_frames += 1
Ok(encoded)
}
}
}
}
///|
pub fn TransportEndpoint::receive(
self : TransportEndpoint,
data : Bytes,
timestamp : Int,
) -> Result[Array[Frame], Diagnostic] {
if self.state != TransportStarted && self.state != TransportConnecting {
Err(
Diagnostic::new(
StateViolation,
"transport endpoint cannot receive in current state",
),
)
} else {
match self.decoder.push(data) {
Err(error) => Err(error)
Ok(_) => {
let frames : Array[Frame] = []
while self.decoder.available_frames() > 0 {
match self.decoder.next() {
ApduParseResult::Complete(frame, _) => {
self.received_frames += 1
self.last_receive = timestamp
self.last_activity = timestamp
match frame.kind {
Information => ignore(self.window.receive(frame.send_sequence))
Supervisory =>
ignore(self.window.acknowledge(frame.receive_sequence))
Unnumbered =>
if u_command(frame.control) == StartDataTransfer {
self.state = TransportStarted
}
}
frames.push(frame)
}
ApduParseResult::Invalid(error) => {
self.malformed_frames += 1
return Err(error)
}
ApduParseResult::NeedMore(_) => break
}
}
Ok(frames)
}
}
}
}
///|
pub fn TransportEndpoint::poll(
self : TransportEndpoint,
timestamp : Int,
) -> Result[Unit, Diagnostic] {
if self.state == TransportStarted &&
timestamp - self.last_activity > self.parameters.t3() {
self.state = TransportFailed
Err(Diagnostic::new(TimerExpired, "transport idle timer expired"))
} else {
Ok(())
}
}
///|
pub struct TransportManagerMetrics {
mut opened : Int
mut sent : Int
mut received : Int
mut failures : Int
mut closed : Int
} derive(Eq, Debug)
///|
pub fn TransportManagerMetrics::new() -> TransportManagerMetrics {
{ opened: 0, sent: 0, received: 0, failures: 0, closed: 0 }
}
///|
pub fn TransportManagerMetrics::opened(self : TransportManagerMetrics) -> Int {
self.opened
}
///|
pub fn TransportManagerMetrics::sent(self : TransportManagerMetrics) -> Int {
self.sent
}
///|
pub fn TransportManagerMetrics::received(self : TransportManagerMetrics) -> Int {
self.received
}
///|
pub fn TransportManagerMetrics::failures(self : TransportManagerMetrics) -> Int {
self.failures
}
///|
pub fn TransportManagerMetrics::closed(self : TransportManagerMetrics) -> Int {
self.closed
}
///|
pub struct TransportManager {
endpoints : Map[Int, TransportEndpoint]
events : Array[TransportEvent]
event_limit : Int
mut next_event : Int
metrics : TransportManagerMetrics
} derive(Debug)
///|
pub fn TransportManager::new(
event_limit? : Int = 4096,
) -> Result[TransportManager, String] {
if event_limit < 1 {
Err("transport event limit must be positive")
} else {
Ok({
endpoints: {},
events: [],
event_limit,
next_event: 1,
metrics: TransportManagerMetrics::new(),
})
}
}
///|
pub fn TransportManager::metrics(
self : TransportManager,
) -> TransportManagerMetrics {
self.metrics
}
///|
pub fn TransportManager::events(
self : TransportManager,
) -> Array[TransportEvent] {
self.events.copy()
}
///|
pub fn TransportManager::endpoint_count(self : TransportManager) -> Int {
self.endpoints.length()
}
///|
pub fn TransportManager::endpoint(
self : TransportManager,
id : Int,
) -> TransportEndpoint? {
self.endpoints.get(id)
}
///|
fn TransportManager::record(
self : TransportManager,
timestamp : Int,
kind : TransportEventKind,
endpoint : Int,
frame_size : Int,
detail : String,
) -> Unit {
self.events.push({
sequence: self.next_event,
timestamp,
kind,
endpoint,
frame_size,
detail,
})
self.next_event += 1
while self.events.length() > self.event_limit {
ignore(self.events.remove(0))
}
}
///|
pub fn TransportManager::add(
self : TransportManager,
endpoint : TransportEndpoint,
) -> Result[Unit, String] {
if self.endpoints.contains(endpoint.id()) {
Err("transport endpoint id already exists")
} else {
self.endpoints[endpoint.id()] = endpoint
Ok(())
}
}
///|
pub fn TransportManager::open(
self : TransportManager,
id : Int,
timestamp : Int,
) -> Result[Bytes, String] {
match self.endpoints.get(id) {
None => Err("unknown transport endpoint")
Some(endpoint) =>
match endpoint.open(timestamp) {
Err(error) => Err(error)
Ok(frame) => {
self.endpoints[id] = endpoint
self.metrics.opened += 1
self.record(
timestamp,
TransportOpened,
id,
encoded_frame_size(frame),
"transport opened",
)
Ok(encode_frame(frame))
}
}
}
}
///|
pub fn TransportManager::send(
self : TransportManager,
id : Int,
payload : Bytes,
timestamp : Int,
) -> Result[Bytes, Diagnostic] {
match self.endpoints.get(id) {
None => Err(Diagnostic::new(InvalidAddress, "unknown transport endpoint"))
Some(endpoint) =>
match endpoint.send(payload, timestamp) {
Err(error) => {
self.metrics.failures += 1
Err(error)
}
Ok(data) => {
self.endpoints[id] = endpoint
self.metrics.sent += 1
self.record(
timestamp,
TransportFrameSent,
id,
data.length(),
"APDU sent",
)
Ok(data)
}
}
}
}
///|
pub fn TransportManager::receive(
self : TransportManager,
id : Int,
data : Bytes,
timestamp : Int,
) -> Result[Array[Frame], Diagnostic] {
match self.endpoints.get(id) {
None => Err(Diagnostic::new(InvalidAddress, "unknown transport endpoint"))
Some(endpoint) =>
match endpoint.receive(data, timestamp) {
Err(error) => {
self.metrics.failures += 1
Err(error)
}
Ok(frames) => {
self.endpoints[id] = endpoint
self.metrics.received += frames.length()
self.record(
timestamp,
TransportFrameReceived,
id,
data.length(),
"APDU received",
)
Ok(frames)
}
}
}
}
///|
pub fn TransportManager::close(
self : TransportManager,
id : Int,
timestamp : Int,
) -> Result[Unit, String] {
match self.endpoints.get(id) {
None => Err("unknown transport endpoint")
Some(endpoint) => {
endpoint.close(timestamp)
self.endpoints[id] = endpoint
self.metrics.closed += 1
self.record(timestamp, TransportClosedEvent, id, 0, "transport closed")
Ok(())
}
}
}
///|
pub fn TransportManager::poll(
self : TransportManager,
timestamp : Int,
) -> Array[Diagnostic] {
let errors : Array[Diagnostic] = []
for id, endpoint in self.endpoints {
match endpoint.poll(timestamp) {
Ok(_) => ()
Err(error) => {
self.metrics.failures += 1
errors.push(error)
self.record(timestamp, TransportTimerExpired, id, 0, error.to_line())
}
}
}
errors
}
///|
pub fn managed_transport_state_examples() -> Array[ManagedTransportState] {
[
TransportCreated,
TransportConnecting,
TransportStarted,
TransportStopping,
TransportStopped,
TransportFailed,
TransportClosed,
]
}
///|
pub fn transport_event_kind_examples() -> Array[TransportEventKind] {
[
TransportOpened,
TransportStartedEvent,
TransportFrameSent,
TransportFrameReceived,
TransportAcknowledged,
TransportTimerExpired,
TransportFault,
TransportClosedEvent,
]
}