Commit 97b29c8fac
97b29c8fac7e5810a15a895c333482e925cc3f50
parent: a582e3d398
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-23 05:51 UTC
httpd: gzipWriter passes a flush through
Ref #250
internal/httpd/compress.go
+14
| @@ -83,3 +83,17 @@ func (g *gzipWriter) Close() { |
| 83 | 83 | g.gz.Close() |
| 84 | 84 | } |
| 85 | 85 | } |
| 86 | |
| 87 | // Flush sends what the gzip stream holds, then flushes the connection, so |
| 88 | // a streamed page reaches the browser as it is written. |
| 89 | func (g *gzipWriter) Flush() { |
| 90 | if !g.decided { |
| 91 | g.decide(http.StatusOK) |
| 92 | } |
| 93 | if g.gz != nil { |
| 94 | g.gz.Flush() |
| 95 | } |
| 96 | http.NewResponseController(g.ResponseWriter).Flush() |
| 97 | } |
| 98 | |
| 99 | func (g *gzipWriter) Unwrap() http.ResponseWriter { return g.ResponseWriter } |
internal/httpd/compress_test.go
+29
| @@ -6,6 +6,7 @@ import ( |
| 6 | 6 | "io" |
| 7 | 7 | "net/http" |
| 8 | 8 | "net/http/httptest" |
| 9 | "strings" |
| 9 | 10 | "testing" |
| 10 | 11 | |
| 11 | 12 | "gitbay.org/gitbay/internal/config" |
| @@ -79,3 +80,31 @@ func TestBinaryResponsesPassThrough(t *testing.T) { |
| 79 | 80 | t.Fatalf("git transport touched: %q %q", rec.Header().Get("Content-Encoding"), rec.Body.String()) |
| 80 | 81 | } |
| 81 | 82 | } |
| 83 | |
| 84 | // A flush mid-response reaches the connection with what was written so |
| 85 | // far decodable, which is what lets a page stream through gzip. |
| 86 | func TestGzipWriterFlushes(t *testing.T) { |
| 87 | rec := httptest.NewRecorder() |
| 88 | h := compressed(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 89 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 90 | io.WriteString(w, "<p>first</p>") |
| 91 | if err := http.NewResponseController(w).Flush(); err != nil { |
| 92 | t.Fatalf("flush: %v", err) |
| 93 | } |
| 94 | if !rec.Flushed { |
| 95 | t.Fatal("the flush did not reach the connection") |
| 96 | } |
| 97 | zr, err := gzip.NewReader(bytes.NewReader(rec.Body.Bytes())) |
| 98 | if err != nil { |
| 99 | t.Fatalf("gzip header: %v", err) |
| 100 | } |
| 101 | got, _ := io.ReadAll(zr) // no trailer yet: ends in ErrUnexpectedEOF |
| 102 | if !strings.Contains(string(got), "<p>first</p>") { |
| 103 | t.Fatalf("flushed body decodes to %q", got) |
| 104 | } |
| 105 | io.WriteString(w, "<p>second</p>") |
| 106 | })) |
| 107 | req := httptest.NewRequest("GET", "/", nil) |
| 108 | req.Header.Set("Accept-Encoding", "gzip") |
| 109 | h.ServeHTTP(rec, req) |
| 110 | } |