Three things in register_transaction.go, all on the refresh path.
The granted expiry is never read. QualifyLoop uses t.expiry, the value we requested, and carries the TODO saying so. A registrar granting 120s against a requested hour means the binding lapses long before the refresh fires. The grant also arrives in the per-Contact expires param as often as the top-level Expires header (RFC 3261 10.3), and neither is consulted.
calcRetry returns RetryInterval verbatim when set, so the 0.75 ratio never runs. A caller who sets RetryInterval at all loses the derivation entirely, including the part that would bring the refresh forward when the grant is short.
reregisterLoop returns on the first failed refresh. One 503 from the registrar tears the registration down while the binding it granted is still valid.
Fix: read the grant from the per-Contact expires param as well as the top-level header and derive the refresh from it, keeping RetryInterval as something that can only bring the refresh forward, never push it past the derived point, with a 30s floor. On a failed refresh back off exponentially (1s doubling to a 5min cap) and escalate only once the granted lifetime has provably run out, latching so one outage escalates once.
Also there: the registration state is read by the refresh loop while Diago.Register writes the expiry from its own goroutine, so it goes behind an RWMutex. An unparseable or non-positive Expires value falls back to the requested expiry rather than returning an error, and digest auth is gated on the challenge header actually being present.
No new exported API.
Three things in register_transaction.go, all on the refresh path.
The granted expiry is never read. QualifyLoop uses t.expiry, the value we requested, and carries the TODO saying so. A registrar granting 120s against a requested hour means the binding lapses long before the refresh fires. The grant also arrives in the per-Contact expires param as often as the top-level Expires header (RFC 3261 10.3), and neither is consulted.
calcRetry returns RetryInterval verbatim when set, so the 0.75 ratio never runs. A caller who sets RetryInterval at all loses the derivation entirely, including the part that would bring the refresh forward when the grant is short.
reregisterLoop returns on the first failed refresh. One 503 from the registrar tears the registration down while the binding it granted is still valid.
Fix: read the grant from the per-Contact expires param as well as the top-level header and derive the refresh from it, keeping RetryInterval as something that can only bring the refresh forward, never push it past the derived point, with a 30s floor. On a failed refresh back off exponentially (1s doubling to a 5min cap) and escalate only once the granted lifetime has provably run out, latching so one outage escalates once.
Also there: the registration state is read by the refresh loop while Diago.Register writes the expiry from its own goroutine, so it goes behind an RWMutex. An unparseable or non-positive Expires value falls back to the requested expiry rather than returning an error, and digest auth is gated on the challenge header actually being present.
No new exported API.