detached 2bd81b9

fix(dgram): erase late readiness keys

commit
2bd81b93b4c770b28c51b634f42bb2e1d00e7787
author
brice · · 2026-07-23 20:36 UTC
parent
4175002
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +36 -6
M internal/adapters/dgram/ssh_dialer.go +15 -6
@@ -198,15 +198,24 @@ func (d RemoteDialer) Dial(ctx context.Context) (ports.Transport, error) {
198198 return t, nil
199199 }
200200
201-func readUDPReadyContext(ctx context.Context, r io.Reader) (udpReady, error) {
202- type result struct {
203- ready udpReady
204- err error
201+type udpReadyResult struct {
202+ ready udpReady
203+ err error
204+}
205+
206+func deliverUDPReady(ctx context.Context, ch chan<- udpReadyResult, res udpReadyResult) {
207+ select {
208+ case ch <- res:
209+ case <-ctx.Done():
210+ pdgram.Erase(res.ready.key)
205211 }
206- ch := make(chan result, 1)
212+}
213+
214+func readUDPReadyContext(ctx context.Context, r io.Reader) (udpReady, error) {
215+ ch := make(chan udpReadyResult)
207216 go func() {
208217 ready, err := readUDPReady(r)
209- ch <- result{ready: ready, err: err}
218+ deliverUDPReady(ctx, ch, udpReadyResult{ready: ready, err: err})
210219 }()
211220 select {
212221 case res := <-ch:
@@ -198,15 +198,24 @@ func (d RemoteDialer) Dial(ctx context.Context) (ports.Transport, error) {
198 return t, nil198 return t, nil
199 }199 }
200 200
201-func readUDPReadyContext(ctx context.Context, r io.Reader) (udpReady, error) {201+type udpReadyResult struct {
202- type result struct {202+ ready udpReady
203- ready udpReady203+ err error
204- err error204+}
205+
206+func deliverUDPReady(ctx context.Context, ch chan<- udpReadyResult, res udpReadyResult) {
207+ select {
208+ case ch <- res:
209+ case <-ctx.Done():
210+ pdgram.Erase(res.ready.key)
205 }211 }
206- ch := make(chan result, 1)212+}
213+
214+func readUDPReadyContext(ctx context.Context, r io.Reader) (udpReady, error) {
215+ ch := make(chan udpReadyResult)
207 go func() {216 go func() {
208 ready, err := readUDPReady(r)217 ready, err := readUDPReady(r)
209- ch <- result{ready: ready, err: err}218+ deliverUDPReady(ctx, ch, udpReadyResult{ready: ready, err: err})
210 }()219 }()
211 select {220 select {
212 case res := <-ch:221 case res := <-ch:
M internal/adapters/dgram/ssh_dialer_test.go +21 -0
@@ -174,6 +174,27 @@ type closeTrackPacketConn struct {
174174
175175 func (c closeTrackPacketConn) Close() error { *c.closed = true; return c.PacketConn.Close() }
176176
177+func TestDeliverUDPReadyErasesParsedKeyAfterCancellation(t *testing.T) {
178+ ctx, cancel := context.WithCancel(context.Background())
179+ cancel()
180+ key := make([]byte, pdgram.KeySize)
181+ for i := range key {
182+ key[i] = byte(i + 1)
183+ }
184+ ready, err := readUDPReady(strings.NewReader("VEV-UDP 1234 " + base64.StdEncoding.EncodeToString(key) + "\n"))
185+ if err != nil {
186+ t.Fatal(err)
187+ }
188+
189+ deliverUDPReady(ctx, make(chan udpReadyResult), udpReadyResult{ready: ready})
190+
191+ for i, b := range ready.key {
192+ if b != 0 {
193+ t.Fatalf("key[%d] = %d, want erased", i, b)
194+ }
195+ }
196+}
197+
177198 func TestReadUDPReady(t *testing.T) {
178199 key := base64.StdEncoding.EncodeToString(make([]byte, pdgram.KeySize))
179200 tests := []struct {
@@ -174,6 +174,27 @@ type closeTrackPacketConn struct {
174 174
175 func (c closeTrackPacketConn) Close() error { *c.closed = true; return c.PacketConn.Close() }175 func (c closeTrackPacketConn) Close() error { *c.closed = true; return c.PacketConn.Close() }
176 176
177+func TestDeliverUDPReadyErasesParsedKeyAfterCancellation(t *testing.T) {
178+ ctx, cancel := context.WithCancel(context.Background())
179+ cancel()
180+ key := make([]byte, pdgram.KeySize)
181+ for i := range key {
182+ key[i] = byte(i + 1)
183+ }
184+ ready, err := readUDPReady(strings.NewReader("VEV-UDP 1234 " + base64.StdEncoding.EncodeToString(key) + "\n"))
185+ if err != nil {
186+ t.Fatal(err)
187+ }
188+
189+ deliverUDPReady(ctx, make(chan udpReadyResult), udpReadyResult{ready: ready})
190+
191+ for i, b := range ready.key {
192+ if b != 0 {
193+ t.Fatalf("key[%d] = %d, want erased", i, b)
194+ }
195+ }
196+}
197+
177 func TestReadUDPReady(t *testing.T) {198 func TestReadUDPReady(t *testing.T) {
178 key := base64.StdEncoding.EncodeToString(make([]byte, pdgram.KeySize))199 key := base64.StdEncoding.EncodeToString(make([]byte, pdgram.KeySize))
179 tests := []struct {200 tests := []struct {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close