Repository navigation
go-mms v1.2.0
Date: 2026-10-28
Previous release: v1.1.0
Summary
Operability and quality release. Errors are classified so errors.Is /
errors.As work across every failure path; a pluggable Metrics interface,
configurable retry with backoff and jitter, Server.Shutdown, association and
handshake timeouts and Client.Done / Client.Err are added. A cancelled
request no longer tears down its association, and a silent peer can no longer
block iso.Listener.Accept. Fuzzing found and this release fixes several
codec bugs (Cancel PDU tags, BinaryTime, UTCTime fractions, long-form tags).
Every package now has ≥90% test coverage, enforced in CI. All API changes are
additive.
Interop runs against three MMS peers from the mms-interop
v0.3.0 images,
pinned by digest: libiec61850 (120 pass), dscsystems/go-iec61850 (112 pass,
8 skipped peer limitations) and iec61850-rust (120 pass).
Changes
Added
- Errors —
ErrServerClosed,ErrInvalidArgument;PhaseSession,PhasePresentation,PhaseACSE,PhaseMMS;RejectType(withString) andRejectErrorfor MMS Reject PDUs (it unwraps to the previous*ProtocolError, soerrors.Is(err, ErrProtocol)still holds);ServiceError.CodeName()(ISO 9506-2 code names). Failures carry sentinel kinds without changing their messages: association →ErrAssociationFailed, negotiation →ErrNegotiationFailed, connection loss →ErrClosed(+ErrDecodeFailed/ErrInvalidPDU), request deadline →ErrInvokeTimeout+context.DeadlineExceeded, argument validation →ErrInvalidArgument. - Metrics —
Metricsinterface (ObserveRequest,ObserveConnection),NopMetrics,Role,Service,RequestEvent,ConnectionState,ConnectionEvent; set viaDialOptions.MetricsandServerOptions.Metrics. OBSERVABILITY.md has a Prometheus adapter example. - Retry —
RetryPolicy(attempts, exponential backoff, jitter,Retryable,OnRetry),DefaultRetryPolicy,IsRetryable,Retry;iso.WithRetryretriesDialTCPandDial(connect, TLS, COTP and association), honouring the context. - Lifecycle —
Client.Done,Client.Err,DialOptions.RequestTimeout,Server.Shutdown(ctx)(closes listeners, waits for in-flight handlers, aborts associations;Serve/ListenAndServethen returnErrServerClosed),ServerOptions.AssociationTimeout,iso.WithHandshakeTimeout,iso.WithDialer. RawHookSend/RawHookRecvconstants for the raw PDU hook direction.
Changed
- Request cancellation — a request whose context ends mid-write returns at once; the write completes in the background (an unstarted one is skipped) instead of closing the association.
- Association timeout —
Serveaborts an association that has not completed its handshake within 30s by default (ServerOptions.AssociationTimeout; negative disables).Servealso closes the transport when its context is cancelled. - iso.Listener — TLS and COTP handshakes run concurrently (up to 64 pending) with a 10s default timeout, so a peer that connects and stays silent no longer blocks other connections. Temporary accept errors (such as EMFILE) are retried with backoff.
ServiceError.Error()includes the code name, e.g.(object-non-existent). Compare witherrors.Asand theClass/Codefields, never the string.- Server error mapping — a
*DataAccessErrorreturned by a Read/Write callback is sent with its code (other errors: temporarily-unavailable, as before); a*ServiceErrorfrom a handler is sent as returned; a malformed Status request gets the invalid-request service error. Value.Equalcompares floats bitwise (all NaNs equal,+0≠-0).- Logs carry
roleandremote_addr; requests are logged at debug level withservice,invoke_id,durationanderror. Messages are prefixedmms:/iso:.
Fixed
- Cancel PDUs — cancel-Request/Response/Error use tags
[5],[6],[7](the tag constants were wrong). - BinaryTime — values below one day encode in the 4-octet TimeOfDay form (previously not re-encodable after decoding); the decoder rejects a millisecond-of-day ≥ 86 400 000.
- UTCTime — the fraction of a second round-trips losslessly.
- GeneralizedTime — a fractional second accepted on decode is kept on encode (it was dropped); whole seconds encode as before.
- Long-form tags — MMS PDUs with a long-form outer tag are rejected; tag numbers above 30 encode correctly.
- Deadline classification — an I/O timeout caused by a context deadline is reported as
context.DeadlineExceededeven when the connection deadline fires just before the context's timer (association timeout, ISO transport). - Full-duplex deadlines — go-cotp v1.0.4 arms a both-direction deadline around each read and write; a write deadline could cut a concurrent read (
read tpkt header: i/o timeout). transport/iso now uses per-direction deadlines. Client.Aborthonours its context; closing a transport whose deadline already fired no longer returns a timeout error.- Server callback errors that are not wire errors no longer reach the error-code encoder (possible panic path).
Tests
- Coverage ≥90% in every package and in total, enforced by
scripts/coverage-gate.sh(make coverage, CI quality job). internal/chaos: fault-injectingnet.Conn/net.Listener(delays, chunked I/O, bandwidth limit, drops, resets, stalls).e2echaos tests (lossy, drops with reconnect viaRetry, slow peer) and a concurrent stress test under-race;make chaos.- 57 fuzz targets, including
FuzzValueCodec,FuzzServerDispatch,FuzzServerSessionand new session/presentation/ACSE, codec and BER targets;scripts/fuzz.shdiscovers them (make fuzz,make fuzz-shortinmake checkand CI). - Interop is stack-parametrized (
MMS_INTEROP_STACK,make interop-all): one test body per scenario, run in CI against libiec61850, dscsystems/go-iec61850 and iec61850-rust from mms-interop v0.3.0. Peer limitations are explicit per-stack quirks or skips with reasons (INTEROP.md). NewTestClient_PeerDisconnect(Done/Err) andTestServer_Shutdown.
Docs
- ERRORS.md, OBSERVABILITY.md, TIMEOUT_AND_CLOSE.md, API.md, README.md, LIMITS.md, KNOWN_LIMITATIONS.md, COMPLIANCE.md, INTEROP.md, RACE_NOTES.md and RELEASE_CHECKLIST.md updated; PROGRESS.md replaced by a current-status summary. Removed stale PLAN.md, REQUIREMENTS.md, extract_c_functions.py and mms_functions.csv.