From 1cbb59d0fa57dd6c8323712b9167cab6d0f86d52 Mon Sep 17 00:00:00 2001 From: Quick104 <31828688+Quick104@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:45:33 -0400 Subject: [PATCH] fix(playback): finish segment acknowledgements --- internal/transcodeproxy/completion.go | 6 +++- internal/transcodeproxy/completion_test.go | 42 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 internal/transcodeproxy/completion_test.go diff --git a/internal/transcodeproxy/completion.go b/internal/transcodeproxy/completion.go index 8662f3d7..df771d70 100644 --- a/internal/transcodeproxy/completion.go +++ b/internal/transcodeproxy/completion.go @@ -85,7 +85,11 @@ func Acknowledge(ctx context.Context, client *http.Client, targetURL, jwtSecret, if client == nil { client = http.DefaultClient } - ackCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + // The downstream request is commonly canceled as soon as its response body + // reaches the client, while the handler is still issuing this acknowledgement. + // Preserve request-scoped values, but give the post-response hop its own + // bounded lifetime so a successful delivery is not mistaken for a disconnect. + ackCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) defer cancel() req, err := http.NewRequestWithContext(ackCtx, http.MethodPost, targetURL+"/downloaded", nil) if err != nil { diff --git a/internal/transcodeproxy/completion_test.go b/internal/transcodeproxy/completion_test.go new file mode 100644 index 00000000..505e3a01 --- /dev/null +++ b/internal/transcodeproxy/completion_test.go @@ -0,0 +1,42 @@ +package transcodeproxy + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" +) + +func TestAcknowledgeSurvivesCompletedDownstreamRequest(t *testing.T) { + const ( + jwtSecret = "test-secret" + generation = "incarnation:generation" + ) + + acknowledged := make(chan struct{}, 1) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + t.Errorf("method = %q, want POST", r.Method) + } + if got := r.Header.Get("Authorization"); got != "Bearer "+jwtSecret { + t.Errorf("Authorization = %q", got) + } + if got := r.Header.Get(GenerationHeader); got != generation { + t.Errorf("generation = %q, want %q", got, generation) + } + acknowledged <- struct{}{} + w.WriteHeader(http.StatusNoContent) + })) + t.Cleanup(server.Close) + + requestCtx, cancelRequest := context.WithCancel(context.Background()) + cancelRequest() + if err := Acknowledge(requestCtx, server.Client(), server.URL+"/segment", jwtSecret, generation); err != nil { + t.Fatalf("Acknowledge() error = %v", err) + } + select { + case <-acknowledged: + default: + t.Fatal("acknowledgement request was not delivered") + } +}