Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Compress hosted site responses too

a76b2f38cb6692f7fe88c83e4552205d398f30df · Keenan Nemetz · 12d ago · parent 306f36fc

Compress hosted site responses too

The gzip middleware covered the forge UI and nothing else: server.Handler
dispatches a site host to server/pages before the forge middleware runs, so
every hosted site was serving raw HTML, CSS and JavaScript. A site is a static
file server, which is the best case gzip has.

The compressor moves from server/web to internal/compress so both surfaces share
one implementation of the negotiation and header rules, with two entry points:
New for the forge, which needs the writer inside its own status recorder and
panic handling, and Handler for the site dispatcher.

Site responses keep their ETag and Last-Modified, so revalidation is still a 304
off the blob sha; Vary: Accept-Encoding names the representation; a 304 and a
301 carry no body to compress; and a client that does not ask for gzip still
gets the exact bytes and the exact Content-Length.

Measured on the site path, an HTML page with repeated markup goes from 1.5 KB to
83 bytes.

server/server.go +9−1

@@ -21,6 +21,7 @@ import (
21 21 "github.com/rodent-software/vole/forge/pageview"
22 22 "github.com/rodent-software/vole/forge/repo"
23 23 "github.com/rodent-software/vole/forge/session"
24 "github.com/rodent-software/vole/internal/compress"
24 25 "github.com/rodent-software/vole/server/githttp"
25 26 "github.com/rodent-software/vole/server/pages"
26 27 "github.com/rodent-software/vole/server/ssh"
@@ -185,14 +186,21 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error)
185 186
186 187 // Handler returns the HTTP surface. Site hosts are dispatched first: a request
187 188 // for <repo>.<pages_domain> is a page, and everything else is the forge UI.
189 //
190 // Sites are compressed here rather than inside the pages package because the
191 // dispatcher is the last point both surfaces pass through, and a site is a static
192 // file server: HTML, CSS and JavaScript, which is exactly what gzip is for. The
193 // forge UI does its own, inside its middleware, because the compressor has to sit
194 // under the status recorder there.
188 195 func (s *Server) Handler() http.Handler {
189 196 forge := s.Web.Middleware(s.mux)
190 197 if s.Pages == nil {
191 198 return forge
192 199 }
200 sites := compress.Handler(s.Pages)
193 201 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
194 202 if _, ok := s.Pages.Host(r.Host); ok {
195 s.Pages.ServeHTTP(w, r)
203 sites.ServeHTTP(w, r)
196 204 return
197 205 }
198 206 forge.ServeHTTP(w, r)