Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

220 lines
4.6 KiB
Go

package server
import (
"compress/gzip"
"net/http"
"strings"
)
const gzipMinLength = 512
func gzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !shouldGzip(r) {
next.ServeHTTP(w, r)
return
}
gw := &gzipResponseWriter{
ResponseWriter: w,
minLength: gzipMinLength,
}
defer gw.close()
next.ServeHTTP(gw, r)
})
}
func shouldGzip(r *http.Request) bool {
if r.Method == http.MethodHead {
return false
}
if !strings.HasPrefix(r.URL.Path, "/api/") {
return false
}
if r.Header.Get("Range") != "" {
return false
}
if !headerContainsToken(r.Header.Get("Accept-Encoding"), "gzip") {
return false
}
if headerContainsToken(r.Header.Get("Accept"), "text/event-stream") {
return false
}
if isStreamingPath(r.URL.Path) {
return false
}
return true
}
type gzipResponseWriter struct {
http.ResponseWriter
writer *gzip.Writer
minLength int
status int
buffer []byte
wroteHeader bool
sentHeader bool
compressing bool
plain bool
}
func (w *gzipResponseWriter) Header() http.Header {
return w.ResponseWriter.Header()
}
func (w *gzipResponseWriter) WriteHeader(status int) {
if w.wroteHeader {
return
}
w.status = status
w.wroteHeader = true
if !canGzipStatus(status) || w.Header().Get("Content-Encoding") != "" {
w.ResponseWriter.WriteHeader(status)
w.sentHeader = true
return
}
}
func (w *gzipResponseWriter) Write(p []byte) (int, error) {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if w.compressing {
_, err := w.writer.Write(p)
return len(p), err
}
if w.plain || !canGzipStatus(w.status) ||
w.Header().Get("Content-Encoding") != "" ||
isEventStreamContentType(w.Header().Get("Content-Type")) {
w.writePlainHeader()
_, err := w.ResponseWriter.Write(p)
return len(p), err
}
w.buffer = append(w.buffer, p...)
if len(w.buffer) < w.minLength {
return len(p), nil
}
if err := w.startGzip(); err != nil {
return 0, err
}
_, err := w.writer.Write(w.buffer)
w.buffer = nil
return len(p), err
}
func (w *gzipResponseWriter) Flush() {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if w.compressing {
_ = w.writer.Flush()
if f, ok := w.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
return
}
if len(w.buffer) > 0 {
w.writePlainHeader()
_, _ = w.ResponseWriter.Write(w.buffer)
w.buffer = nil
} else if !w.sentHeader {
w.writePlainHeader()
}
if f, ok := w.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
}
func (w *gzipResponseWriter) close() {
if !w.wroteHeader {
return
}
if w.compressing {
_ = w.writer.Close()
return
}
if len(w.buffer) > 0 {
w.writePlainHeader()
_, _ = w.ResponseWriter.Write(w.buffer)
w.buffer = nil
} else if !w.sentHeader {
w.writePlainHeader()
}
}
func (w *gzipResponseWriter) startGzip() error {
if w.plain || w.sentHeader ||
isEventStreamContentType(w.Header().Get("Content-Type")) {
return nil
}
if w.Header().Get("Content-Type") == "" && len(w.buffer) > 0 {
w.Header().Set("Content-Type", http.DetectContentType(w.buffer))
}
w.Header().Del("Content-Length")
w.Header().Set("Content-Encoding", "gzip")
addVaryHeader(w.Header(), "Accept-Encoding")
w.writer = gzip.NewWriter(w.ResponseWriter)
w.compressing = true
w.ResponseWriter.WriteHeader(w.status)
w.sentHeader = true
return nil
}
func (w *gzipResponseWriter) writePlainHeader() {
if w.sentHeader {
w.plain = true
return
}
w.ResponseWriter.WriteHeader(w.status)
w.sentHeader = true
w.plain = true
}
func canGzipStatus(status int) bool {
return status != http.StatusNoContent &&
status != http.StatusNotModified &&
status >= 200
}
func headerContainsToken(value, token string) bool {
for part := range strings.SplitSeq(value, ",") {
if strings.EqualFold(strings.TrimSpace(strings.Split(part, ";")[0]), token) {
return true
}
}
return false
}
func addVaryHeader(h http.Header, value string) {
vary := h.Values("Vary")
for _, line := range vary {
if headerContainsToken(line, value) {
return
}
}
h.Add("Vary", value)
}
func isEventStreamContentType(value string) bool {
mediaType, _, _ := strings.Cut(value, ";")
return strings.EqualFold(strings.TrimSpace(mediaType), "text/event-stream")
}
func isStreamingPath(path string) bool {
switch {
case path == "/api/v1/events",
path == "/api/v1/sync",
path == "/api/v1/resync",
path == "/api/v1/secrets/scan",
path == "/api/v1/insights/generate":
return true
case strings.HasSuffix(path, "/watch"):
return true
case strings.HasPrefix(path, "/api/v1/import/"):
return true
default:
return false
}
}