fix(observability-proxy): open live tails without waiting for a line

Go's transport asks for gzip on its own when a request sets no
Accept-Encoding. vlselect answers a gzip-negotiated tail through a gzip
writer that holds even the response headers until the first line, so the
proxy could not forward its 200 and the console kept showing a
connecting state for quiet applications; buffered lines were also
delayed.

The tail request now sends Accept-Encoding: identity, which also turns
off the transport's transparent gzip. Other endpoints keep it: they are
bounded responses read to the end, compression saves bandwidth between
the proxy and the store, and the transport decompresses before the body
is copied to the client.

Assisted-by: LLM
Signed-off-by: ZverGuy <maximbel2003@gmail.com>
This commit is contained in:
ZverGuy committed 2026-11-10 00:45:06 +07:00
1 parent f19363ec62
commit e6e3466b7d
2 files changed
+54

No files matched your search

+5
View File
@@ -199,6 +199,11 @@ func (s *Server) serve(w http.ResponseWriter, r *http.Request, route Route) *HTT
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("AccountID", "0")
req.Header.Set("ProjectID", "0")
if tail {
// vlselect holds even the headers of a gzip-encoded tail until the
// first line; an explicit value stops the transport from asking for gzip.
req.Header.Set("Accept-Encoding", "identity")
}
resp, err := s.client.Do(req)
switch {
case err == nil:
@@ -326,6 +326,55 @@ func TestServerTailFlushesEveryLine(t *testing.T) {
}
}
// TestServerTailHeadersBeforeFirstLine mimics vlselect, which holds even the
// response headers of a gzip-negotiated tail until the first line arrives.
func TestServerTailHeadersBeforeFirstLine(t *testing.T) {
done := make(chan struct{})
h := newHarness(t, Config{TailIdleTimeout: time.Minute}, func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
select {
case <-done:
case <-r.Context().Done():
}
return
}
tailUpstream(nil, done)(w, r)
})
// Registered after the harness so it runs before the servers close and
// releases a held upstream when the assertion below fails.
t.Cleanup(func() { close(done) })
req, err := http.NewRequest(http.MethodGet, h.proxy.URL+logsRoot+"tail?query=*", nil)
if err != nil {
t.Fatal(err)
}
req.Header.Set("Authorization", "Bearer good")
got := make(chan *http.Response, 1)
go func() {
resp, err := http.DefaultClient.Do(req)
if err != nil {
close(got)
return
}
got <- resp
}()
select {
case resp, ok := <-got:
if !ok {
t.Fatal("tail request failed")
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
t.Fatalf("status = %d, want 200", resp.StatusCode)
}
case <-time.After(2 * time.Second):
t.Fatal("tail response headers did not reach the client before the first line")
}
if ae := h.requests()[0].headers.Get("Accept-Encoding"); ae != "identity" {
t.Fatalf("upstream tail Accept-Encoding = %q, want identity", ae)
}
}
func TestServerTailCapsAndIdle(t *testing.T) {
done := make(chan struct{})
t.Cleanup(func() { close(done) })