From 094a4aa7239abbf894fd69998f6be77e0fbc42e9 Mon Sep 17 00:00:00 2001
From: xiantang
Date: Sat, 24 Jan 2026 00:36:59 +0800
Subject: [PATCH] fix: handle brotli HTML proxy injection (#878)
* fix: handle brotli HTML proxy injection
* Run go mod tidy
---
go.mod | 1 +
go.sum | 4 +++
runner/proxy.go | 62 ++++++++++++++++++++++++---------------
runner/proxy_test.go | 69 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 113 insertions(+), 23 deletions(-)
diff --git a/go.mod b/go.mod
index b54bf3c..3e9b5f9 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.25
require (
dario.cat/mergo v1.0.2
+ github.com/andybalholm/brotli v1.2.0
github.com/fatih/color v1.18.0
github.com/fsnotify/fsnotify v1.9.0
github.com/gohugoio/hugo v0.149.1
diff --git a/go.sum b/go.sum
index 1faf00d..4454c07 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,8 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RX
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
+github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
+github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@@ -172,6 +174,8 @@ github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZB
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
+github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
+github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
diff --git a/runner/proxy.go b/runner/proxy.go
index 308c25b..3c2ab4c 100644
--- a/runner/proxy.go
+++ b/runner/proxy.go
@@ -12,6 +12,8 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/andybalholm/brotli"
)
var (
@@ -30,6 +32,15 @@ type Streamer interface {
Stop()
}
+// contentEncoding represents the type of content encoding used in HTTP responses.
+type contentEncoding int
+
+const (
+ encodingNone contentEncoding = iota
+ encodingGzip
+ encodingBrotli
+)
+
type Proxy struct {
server *http.Server
client *http.Client
@@ -71,32 +82,37 @@ func (p *Proxy) BuildFailed(msg BuildFailedMsg) {
}
func (p *Proxy) injectLiveReload(resp *http.Response) (string, bool, error) {
- reader := resp.Body
- decodedGzip := false
- if isGzipEncoded(resp.Header) {
+ var reader io.Reader = resp.Body
+ decoded := false
+
+ switch detectContentEncoding(resp.Header) {
+ case encodingGzip:
gzipReader, err := gzip.NewReader(resp.Body)
if err != nil {
return "", false, fmt.Errorf("proxy inject: failed to init gzip reader: %w", err)
}
defer gzipReader.Close()
reader = gzipReader
- decodedGzip = true
+ decoded = true
+ case encodingBrotli:
+ reader = brotli.NewReader(resp.Body)
+ decoded = true
}
buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(reader); err != nil {
- return "", decodedGzip, fmt.Errorf("proxy inject: failed to read body from http response: %w", err)
+ return "", decoded, fmt.Errorf("proxy inject: failed to read body from http response: %w", err)
}
page := buf.String()
// the script will be injected before the end of the body tag. In case the tag is missing, the injection will be skipped with no error.
body := strings.LastIndex(page, "
brotli