Repository navigation
Releases: getlantern/soar
Release list
Soar v0.2.4
Fixes
- An unused round tripper no longer holds a stream for good.
- The bug:
NewRoundTripperconfirms its connection before returning, and that connection holds one of the session's streams until a request takes it. A round tripper discarded unused, as a race does when another transport wins, kept its stream open. Open streams kept the session from looking idle, so it was never retired. OnceMaxStreams(64 by default) had leaked, every dial failed withsoar: too many streamsuntil the process restarted. - Impact: one Windows client on a Lantern beta failed every Soar attempt about 1,800 times over two days. It was losing short races to Unbounded's signaling polls, which run every ~14 s.
- The fix: the returned transport implements
io.Closer.Closereleases an untaken connection and closes idle connections. An untaken connection also closes itself after 10 s, for callers that never callClose. (#15)
- The bug:
- getlantern/kindling#61 makes kindling's race close the round trippers it doesn't use. Either fix alone stops this leak.
Compatibility
There are no API changes: NewRoundTripper still returns an http.RoundTripper, which can now also be asserted to io.Closer. The wire protocol is unchanged: v0.2.4 interoperates with v0.2.x, v0.1.0 and soar-rs clients and servers. soar-rs needs no matching change, since its connections close their streams when dropped.
Full changelog: v0.2.3...v0.2.4
Soar v0.2.3
Fixes
- A dial no longer fails on a session left behind by an abandoned handshake.
- The bug: when a dial's context ended mid-handshake, typically because another transport won a race, the handshake still completed. That left a session that never carried a stream. The server ends such a session 15 s after its hello, but the client kept it for
SessionIdle(90 s), so the next dial in that window failed withsoar: server lost the session. - Impact: with kindling's 8 s start delay this was the common case. Soar joins the race, domain fronting wins half a second later, and the next Soar attempt fails at once.
- The fix: a session that has never carried a stream is retired 10 s after its first hello, timed from the hello rather than its reply, since retried hellos get the same session back. A session that came up less than a second ago is kept, because a dial waiting on it is about to use it. (#14)
- The bug: when a dial's context ended mid-handshake, typically because another transport won a race, the handshake still completed. That left a session that never carried a stream. The server ends such a session 15 s after its hello, but the client kept it for
SPEC.md§7.4 notes the behavior.
Compatibility
There are no API changes, and the wire protocol is unchanged: v0.2.3 interoperates with v0.2.x, v0.1.0 and soar-rs clients and servers.
Full changelog: v0.2.2...v0.2.3
Soar v0.2.2
Fixes
- The first dial after a pause no longer fails with
soar: server lost the session.- The bug: an idle client sends nothing, so the server ends its session after its idle timeout (2 minutes) without the client knowing. The client kept the session, and the next stream failed as soon as the server's signed unknown-session answer arrived.
- Impact: Lantern calls Soar minutes apart, so this cost its first Soar attempt after nearly every pause.
- The fix: a session with no streams and no authenticated answer for
Config.SessionIdle(default 90 s) is retired, and the next dial handshakes again. The check and the close happen under one lock, and a dial that loses that race handshakes again instead of failing. (#13)
Additions
Config.SessionIdlesets that limit. Keep it below the server'sIdleTimeout.SPEC.md§7.4 now recommends this behavior for clients.
Compatibility
There are no breaking API changes; the new field is optional.
The wire protocol is unchanged: v0.2.2 interoperates with v0.2.x, v0.1.0, and soar-rs clients and servers.
Also in this range
- The website moved to getlantern/soar-site and is still served at soar.lantern.io. (#11)
Full changelog: v0.2.1...v0.2.2
Soar v0.2.1
Fixes
- Mixed upload/download no longer shrinks query names for good. The client judged a held poll lost once three later data queries were answered, but the server holds polls and answers data at once by design. Two such false losses demoted names from 253 to 101 characters, cutting upload capacity per query about 2.5×. Loss is now judged by later polls only, and the server releases held polls oldest first. (#8)
Conn.Closecould lose the request. RESET went out once the data was acked, before the FIN was, so the server aborted the stream before dialing. It now waits for the FIN's ack. (#10)- Discovery resumes when working resolvers get blocked.
Wantnow counts only healthy paths (verified, usable, not on a run of losses), and discovery wakes at its configured pace instead of about once a second. (#10) - Liveness, hello retries and caps: the liveness clock restarts instead of stopping while queries are outstanding; hello retries rotate through all resolvers, not the first 16; the tail copy of an upload respects
MaxInflight; and a full DNS ID space can no longer spinnewTxnLockedholding the client lock. (#10) - Session and server: a full control queue ends the session instead of silently dropping frames (a dropped
MAX_STREAM_DATAstalled a stream for good); a malformed destination getsRESET_STREAMcode 1 instead of hangingwaitOpened;PublicOnlyrejects192.88.99.0/24,2001:20::/28and3fff::/20. (#10)
Behavior changes
ClientConfig.MaxInflightis capped at 1,024, andMaxStreamsat 256 on both client and server. Larger values are clamped.
Compatibility
No API changes, and the wire protocol is unchanged: v0.2.1 interoperates with v0.2.0 and v0.1.0 clients and servers.
Also in this range
SPEC.mdspecifies the wire format (version 1), andtestdata/vectors.jsonholds test vectors generated from the real encoders; CI fails on any unregenerated wire change. (#7)dnsbenchadds soar-rs, the Rust implementation, as a contender. (#9)
Full changelog: v0.2.0...v0.2.1
Soar v0.2.0
What's new
Client.PathStats()returns a read-only snapshot of every resolver path: transport (udp/tcp), verified, suspended, candidate, RTT, loss, inflight, window, query-name cap, the answer-size limit sizing has accepted, record type (TXT/AAAA), successes and last success. Use it to tell which resolvers actually carry the tunnel on a given network. (#5, thanks @mrgluek)Client.Established()reports whether a session with the server is up. (#5)Config.StreamOnly func(resolver string) boolmarks resolvers that only speak a stream transport (plain TCP/53, or DNS over TLS through a customDialTCP). They get a TCP path from the start and no UDP path, whateverTCPis set to. Nil keeps the previous behavior. (#5)golang.org/x/crypto0.49.0 → 0.52.0.
Compatibility
All changes are additive and the wire protocol is unchanged: v0.2.0 clients work with v0.1.0 servers and the reverse.
Also in this range
dnsbench(bench/, a separate Go module) is the benchmark harness behind the README's comparison table. The README and site figures are now medians of three runs on incompressible data. (#6)
Full changelog: v0.1.0...v0.2.0