feat(forwarder): support plugin-based rewriter in HTTP body rewrite rules
Each rewriteBody/rewriteResponseBody/rewriteRequestBody rule can now optionally reference a named rewriter plugin (HTTP/gRPC backend) via the rewriter field. When set, body rewriting delegates to the plugin rather than applying pattern/replacement, while content-type filtering still applies. Coincident fixes: - rewriter/plugin/grpc: return nil when conn is nil (avoid nil-ptr panic) - rewriter/plugin/http: drain response body in defer to prevent leaks - config/parsing/rewriter: pass TimeoutOption to gRPC plugin - config/parsing/service: warn when referenced rewriter is not registered
This commit is contained in:
@@ -402,6 +402,8 @@ type HTTPBodyRewriteConfig struct {
|
|||||||
Type string
|
Type string
|
||||||
Match string
|
Match string
|
||||||
Replacement string
|
Replacement string
|
||||||
|
// name of the rewriter plugin (via registry)
|
||||||
|
Rewriter string
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeFilterConfig struct {
|
type NodeFilterConfig struct {
|
||||||
|
|||||||
@@ -29,6 +29,29 @@ import (
|
|||||||
// metadata-driven options (so_mark, interface, netns, proxy protocol), sets up
|
// metadata-driven options (so_mark, interface, netns, proxy protocol), sets up
|
||||||
// bypass rules, node filters, HTTP settings, and TLS node settings. The hop
|
// bypass rules, node filters, HTTP settings, and TLS node settings. The hop
|
||||||
// parameter is used only for logging context.
|
// parameter is used only for logging context.
|
||||||
|
|
||||||
|
func parseBodyRewrites(vs []config.HTTPBodyRewriteConfig, log logger.Logger) []chain.HTTPBodyRewriteSettings {
|
||||||
|
var out []chain.HTTPBodyRewriteSettings
|
||||||
|
for _, v := range vs {
|
||||||
|
pattern, _ := regexp.Compile(v.Match)
|
||||||
|
rw := chain.HTTPBodyRewriteSettings{
|
||||||
|
Type: v.Type,
|
||||||
|
Pattern: pattern,
|
||||||
|
Replacement: []byte(v.Replacement),
|
||||||
|
}
|
||||||
|
if v.Rewriter != "" {
|
||||||
|
if !registry.RewriterRegistry().IsRegistered(v.Rewriter) {
|
||||||
|
log.Warnf("rewriter %q not found in registry for rewrite rule", v.Rewriter)
|
||||||
|
}
|
||||||
|
rw.Rewriter = registry.RewriterRegistry().Get(v.Rewriter)
|
||||||
|
}
|
||||||
|
if pattern != nil || rw.Rewriter != nil {
|
||||||
|
out = append(out, rw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
|
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -226,33 +249,9 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, v := range cfg.HTTP.RewriteBody {
|
settings.RewriteResponseBody = append(settings.RewriteResponseBody, parseBodyRewrites(cfg.HTTP.RewriteBody, log)...)
|
||||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
settings.RewriteResponseBody = append(settings.RewriteResponseBody, parseBodyRewrites(cfg.HTTP.RewriteResponseBody, log)...)
|
||||||
settings.RewriteResponseBody = append(settings.RewriteResponseBody, chain.HTTPBodyRewriteSettings{
|
settings.RewriteRequestBody = append(settings.RewriteRequestBody, parseBodyRewrites(cfg.HTTP.RewriteRequestBody, log)...)
|
||||||
Type: v.Type,
|
|
||||||
Pattern: pattern,
|
|
||||||
Replacement: []byte(v.Replacement),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, v := range cfg.HTTP.RewriteResponseBody {
|
|
||||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
|
||||||
settings.RewriteResponseBody = append(settings.RewriteResponseBody, chain.HTTPBodyRewriteSettings{
|
|
||||||
Type: v.Type,
|
|
||||||
Pattern: pattern,
|
|
||||||
Replacement: []byte(v.Replacement),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, v := range cfg.HTTP.RewriteRequestBody {
|
|
||||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
|
||||||
settings.RewriteRequestBody = append(settings.RewriteRequestBody, chain.HTTPBodyRewriteSettings{
|
|
||||||
Type: v.Type,
|
|
||||||
Pattern: pattern,
|
|
||||||
Replacement: []byte(v.Replacement),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opts = append(opts, chain.HTTPNodeOption(settings))
|
opts = append(opts, chain.HTTPNodeOption(settings))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ func ParseRewriter(cfg *config.RewriterConfig) rewriter.Rewriter {
|
|||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
"github.com/go-gost/core/hop"
|
"github.com/go-gost/core/hop"
|
||||||
"github.com/go-gost/core/rewriter"
|
|
||||||
"github.com/go-gost/core/listener"
|
"github.com/go-gost/core/listener"
|
||||||
|
"github.com/go-gost/core/rewriter"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/core/observer/stats"
|
"github.com/go-gost/core/observer/stats"
|
||||||
"github.com/go-gost/core/recorder"
|
"github.com/go-gost/core/recorder"
|
||||||
@@ -356,6 +356,9 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
|
|
||||||
var rew rewriter.Rewriter
|
var rew rewriter.Rewriter
|
||||||
if cfg.Rewriter != "" {
|
if cfg.Rewriter != "" {
|
||||||
|
if !registry.RewriterRegistry().IsRegistered(cfg.Rewriter) {
|
||||||
|
serviceLogger.Warnf("rewriter %q not found in registry", cfg.Rewriter)
|
||||||
|
}
|
||||||
rew = registry.RewriterRegistry().Get(cfg.Rewriter)
|
rew = registry.RewriterRegistry().Get(cfg.Rewriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||||
github.com/gin-contrib/cors v1.7.2
|
github.com/gin-contrib/cors v1.7.2
|
||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
github.com/go-gost/core v0.5.0
|
github.com/go-gost/core v0.5.1
|
||||||
github.com/go-gost/go-shadowsocks2 v0.1.3
|
github.com/go-gost/go-shadowsocks2 v0.1.3
|
||||||
github.com/go-gost/gosocks4 v0.1.0
|
github.com/go-gost/gosocks4 v0.1.0
|
||||||
github.com/go-gost/gosocks5 v0.5.0
|
github.com/go-gost/gosocks5 v0.5.0
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
|||||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/go-gost/core v0.5.0 h1:3SkPojjqw2Av9l5OSyRVlBpRvWhaXgU5BWMXB9aR4SM=
|
github.com/go-gost/core v0.5.0 h1:3SkPojjqw2Av9l5OSyRVlBpRvWhaXgU5BWMXB9aR4SM=
|
||||||
github.com/go-gost/core v0.5.0/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
|
github.com/go-gost/core v0.5.0/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
|
||||||
|
github.com/go-gost/core v0.5.1 h1:HbIn3naEOC661Z4SwzaSYUzffWSRbU0P6gkxiHmuG0I=
|
||||||
|
github.com/go-gost/core v0.5.1/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
|
||||||
github.com/go-gost/go-shadowsocks2 v0.1.3 h1:6CUZLp+mTWXnKP2aK8/Z9ZP+ERMX9gSbywmPu4kGX/A=
|
github.com/go-gost/go-shadowsocks2 v0.1.3 h1:6CUZLp+mTWXnKP2aK8/Z9ZP+ERMX9gSbywmPu4kGX/A=
|
||||||
github.com/go-gost/go-shadowsocks2 v0.1.3/go.mod h1:866zFNNI3He6Wef1M/IvAjTal74WhcfKfBgRpTlkKys=
|
github.com/go-gost/go-shadowsocks2 v0.1.3/go.mod h1:866zFNNI3He6Wef1M/IvAjTal74WhcfKfBgRpTlkKys=
|
||||||
github.com/go-gost/gosocks4 v0.1.0 h1:eAzev6qw4fzkFQKC9uCHLVNnnPdHyqCggbnfNN80Pmk=
|
github.com/go-gost/gosocks4 v0.1.0 h1:eAzev6qw4fzkFQKC9uCHLVNnnPdHyqCggbnfNN80Pmk=
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriteCloser,
|
|||||||
|
|
||||||
// Rewrite request body before wrapping for recording,
|
// Rewrite request body before wrapping for recording,
|
||||||
// so the recorder sees the rewritten content.
|
// so the recorder sees the rewritten content.
|
||||||
if err = rewriteReqBody(req, reqBodyRewrites...); err != nil {
|
if err = rewriteReqBody(ctx, req, reqBodyRewrites...); err != nil {
|
||||||
log.Errorf("rewrite request body: %v", err)
|
log.Errorf("rewrite request body: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -446,7 +446,7 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriteCloser,
|
|||||||
resp.Header.Set("Connection", "close")
|
resp.Header.Set("Connection", "close")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = rewriteRespBody(resp, respBodyRewrites...); err != nil {
|
if err = rewriteRespBody(ctx, resp, respBodyRewrites...); err != nil {
|
||||||
log.Errorf("rewrite body: %v", err)
|
log.Errorf("rewrite body: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func (h *Sniffer) handleUpgradeResponse(ctx context.Context, rw, cc io.ReadWrite
|
|||||||
return xnet.Pipe(ctx, rw, cc)
|
return xnet.Pipe(ctx, rw, cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rewriteRespBody(resp *http.Response, rewrites ...chain.HTTPBodyRewriteSettings) error {
|
func rewriteRespBody(ctx context.Context, resp *http.Response, rewrites ...chain.HTTPBodyRewriteSettings) error {
|
||||||
if resp == nil || len(rewrites) == 0 || resp.ContentLength <= 0 {
|
if resp == nil || len(rewrites) == 0 || resp.ContentLength <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,12 @@ func rewriteRespBody(resp *http.Response, rewrites ...chain.HTTPBodyRewriteSetti
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if rewrite.Pattern != nil {
|
if rewrite.Rewriter != nil {
|
||||||
|
body, err = rewrite.Rewriter.Rewrite(ctx, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if rewrite.Pattern != nil {
|
||||||
body = rewrite.Pattern.ReplaceAll(body, rewrite.Replacement)
|
body = rewrite.Pattern.ReplaceAll(body, rewrite.Replacement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +95,7 @@ func drainBody(b io.ReadCloser) (body []byte, err error) {
|
|||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rewriteReqBody(req *http.Request, rewrites ...chain.HTTPBodyRewriteSettings) error {
|
func rewriteReqBody(ctx context.Context, req *http.Request, rewrites ...chain.HTTPBodyRewriteSettings) error {
|
||||||
if req == nil || len(rewrites) == 0 || req.Body == nil || req.ContentLength <= 0 {
|
if req == nil || len(rewrites) == 0 || req.Body == nil || req.ContentLength <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -114,7 +119,12 @@ func rewriteReqBody(req *http.Request, rewrites ...chain.HTTPBodyRewriteSettings
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if rewrite.Pattern != nil {
|
if rewrite.Rewriter != nil {
|
||||||
|
body, err = rewrite.Rewriter.Rewrite(ctx, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if rewrite.Pattern != nil {
|
||||||
body = rewrite.Pattern.ReplaceAll(body, rewrite.Replacement)
|
body = rewrite.Pattern.ReplaceAll(body, rewrite.Replacement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func TestRewriteReqBody(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if tt.req != nil {
|
if tt.req != nil {
|
||||||
_ = rewriteReqBody(tt.req, tt.rewrites...)
|
_ = rewriteReqBody(context.Background(), tt.req, tt.rewrites...)
|
||||||
if tt.req.Body != nil {
|
if tt.req.Body != nil {
|
||||||
body, _ := io.ReadAll(tt.req.Body)
|
body, _ := io.ReadAll(tt.req.Body)
|
||||||
tt.req.Body.Close()
|
tt.req.Body.Close()
|
||||||
@@ -178,7 +178,7 @@ func TestRewriteReqBody(t *testing.T) {
|
|||||||
t.Errorf("ContentLength = %d, want %d", tt.req.ContentLength, tt.wantCL)
|
t.Errorf("ContentLength = %d, want %d", tt.req.ContentLength, tt.wantCL)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = rewriteReqBody(nil) // should not panic
|
_ = rewriteReqBody(context.Background(), nil) // should not panic
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -344,8 +344,8 @@ func TestDrainBody(t *testing.T) {
|
|||||||
|
|
||||||
func TestRewriteRespBody(t *testing.T) {
|
func TestRewriteRespBody(t *testing.T) {
|
||||||
t.Run("nil response", func(t *testing.T) {
|
t.Run("nil response", func(t *testing.T) {
|
||||||
if err := rewriteRespBody(nil); err != nil {
|
if err := rewriteRespBody(context.Background(), nil); err != nil {
|
||||||
t.Errorf("rewriteRespBody(nil) = %v, want nil", err)
|
t.Errorf("rewriteRespBody(context.Background(), nil) = %v, want nil", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ func TestRewriteRespBody(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
_ = rewriteRespBody(resp)
|
_ = rewriteRespBody(context.Background(), resp)
|
||||||
// Body unchanged
|
// Body unchanged
|
||||||
got, _ := io.ReadAll(resp.Body)
|
got, _ := io.ReadAll(resp.Body)
|
||||||
if string(got) != "original" {
|
if string(got) != "original" {
|
||||||
@@ -367,7 +367,7 @@ func TestRewriteRespBody(t *testing.T) {
|
|||||||
ContentLength: 0,
|
ContentLength: 0,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
_ = rewriteRespBody(resp, chain.HTTPBodyRewriteSettings{
|
_ = rewriteRespBody(context.Background(), resp, chain.HTTPBodyRewriteSettings{
|
||||||
Pattern: regexp.MustCompile("."),
|
Pattern: regexp.MustCompile("."),
|
||||||
Type: "*",
|
Type: "*",
|
||||||
Replacement: []byte("replaced"),
|
Replacement: []byte("replaced"),
|
||||||
@@ -385,7 +385,7 @@ func TestRewriteRespBody(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
_ = rewriteRespBody(resp, chain.HTTPBodyRewriteSettings{
|
_ = rewriteRespBody(context.Background(), resp, chain.HTTPBodyRewriteSettings{
|
||||||
Type: "*",
|
Type: "*",
|
||||||
Replacement: []byte("replaced"),
|
Replacement: []byte("replaced"),
|
||||||
})
|
})
|
||||||
@@ -413,7 +413,7 @@ func TestRewriteRespBodyContentTypeFilter(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
_ = rewriteRespBody(resp, makeRewrite("text/html", "replaced"))
|
_ = rewriteRespBody(context.Background(), resp, makeRewrite("text/html", "replaced"))
|
||||||
got, _ := io.ReadAll(resp.Body)
|
got, _ := io.ReadAll(resp.Body)
|
||||||
if string(got) != "replaced" {
|
if string(got) != "replaced" {
|
||||||
t.Errorf("body = %q, want %q", string(got), "replaced")
|
t.Errorf("body = %q, want %q", string(got), "replaced")
|
||||||
@@ -426,7 +426,7 @@ func TestRewriteRespBodyContentTypeFilter(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("hello")),
|
Body: io.NopCloser(strings.NewReader("hello")),
|
||||||
}
|
}
|
||||||
_ = rewriteRespBody(resp, makeRewrite("*", "world"))
|
_ = rewriteRespBody(context.Background(), resp, makeRewrite("*", "world"))
|
||||||
got, _ := io.ReadAll(resp.Body)
|
got, _ := io.ReadAll(resp.Body)
|
||||||
if string(got) != "world" {
|
if string(got) != "world" {
|
||||||
t.Errorf("body = %q, want %q", string(got), "world")
|
t.Errorf("body = %q, want %q", string(got), "world")
|
||||||
@@ -439,7 +439,7 @@ func TestRewriteRespBodyContentTypeFilter(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
_ = rewriteRespBody(resp, makeRewrite("text/html", "replaced"))
|
_ = rewriteRespBody(context.Background(), resp, makeRewrite("text/html", "replaced"))
|
||||||
got, _ := io.ReadAll(resp.Body)
|
got, _ := io.ReadAll(resp.Body)
|
||||||
if string(got) != "original" {
|
if string(got) != "original" {
|
||||||
t.Errorf("body = %q, want %q (unchanged)", string(got), "original")
|
t.Errorf("body = %q, want %q (unchanged)", string(got), "original")
|
||||||
@@ -454,7 +454,7 @@ func TestRewriteRespBodyContentTypeFilter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Empty type defaults to "text/html", and response has no Content-Type -> "" containing "text/html"? No.
|
// Empty type defaults to "text/html", and response has no Content-Type -> "" containing "text/html"? No.
|
||||||
// strings.Contains("text/html", "") is always true, so rewrite should happen.
|
// strings.Contains("text/html", "") is always true, so rewrite should happen.
|
||||||
_ = rewriteRespBody(resp, makeRewrite("", "replaced"))
|
_ = rewriteRespBody(context.Background(), resp, makeRewrite("", "replaced"))
|
||||||
got, _ := io.ReadAll(resp.Body)
|
got, _ := io.ReadAll(resp.Body)
|
||||||
if string(got) != "replaced" {
|
if string(got) != "replaced" {
|
||||||
t.Errorf("body = %q, want %q (should be rewritten)", string(got), "replaced")
|
t.Errorf("body = %q, want %q (should be rewritten)", string(got), "replaced")
|
||||||
@@ -1146,7 +1146,7 @@ func TestRewriteRespBody_PatternReplacement(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("<title>Old Title</title>")),
|
Body: io.NopCloser(strings.NewReader("<title>Old Title</title>")),
|
||||||
}
|
}
|
||||||
err := rewriteRespBody(resp, chain.HTTPBodyRewriteSettings{
|
err := rewriteRespBody(context.Background(), resp, chain.HTTPBodyRewriteSettings{
|
||||||
Pattern: regexp.MustCompile("Old"),
|
Pattern: regexp.MustCompile("Old"),
|
||||||
Type: "text/html",
|
Type: "text/html",
|
||||||
Replacement: []byte("New"),
|
Replacement: []byte("New"),
|
||||||
@@ -1169,7 +1169,7 @@ func TestRewriteRespBody_MultipleRewrites(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("hello")),
|
Body: io.NopCloser(strings.NewReader("hello")),
|
||||||
}
|
}
|
||||||
err := rewriteRespBody(resp,
|
err := rewriteRespBody(context.Background(), resp,
|
||||||
chain.HTTPBodyRewriteSettings{
|
chain.HTTPBodyRewriteSettings{
|
||||||
Pattern: regexp.MustCompile("h.*o"),
|
Pattern: regexp.MustCompile("h.*o"),
|
||||||
Type: "text/html",
|
Type: "text/html",
|
||||||
@@ -1196,7 +1196,7 @@ func TestRewriteRespBody_NilPattern(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
err := rewriteRespBody(resp, chain.HTTPBodyRewriteSettings{
|
err := rewriteRespBody(context.Background(), resp, chain.HTTPBodyRewriteSettings{
|
||||||
Pattern: nil,
|
Pattern: nil,
|
||||||
Type: "*",
|
Type: "*",
|
||||||
Replacement: []byte("replaced"),
|
Replacement: []byte("replaced"),
|
||||||
@@ -1215,7 +1215,7 @@ func TestRewriteRespBody_EmptyContentTypeUsesDefault(t *testing.T) {
|
|||||||
ContentLength: 100,
|
ContentLength: 100,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
err := rewriteRespBody(resp, chain.HTTPBodyRewriteSettings{
|
err := rewriteRespBody(context.Background(), resp, chain.HTTPBodyRewriteSettings{
|
||||||
Pattern: regexp.MustCompile(".*"),
|
Pattern: regexp.MustCompile(".*"),
|
||||||
Type: "",
|
Type: "",
|
||||||
Replacement: []byte("rewritten"),
|
Replacement: []byte("rewritten"),
|
||||||
@@ -1235,7 +1235,7 @@ func TestRewriteRespBody_NegativeContentLength(t *testing.T) {
|
|||||||
ContentLength: -1,
|
ContentLength: -1,
|
||||||
Body: io.NopCloser(strings.NewReader("original")),
|
Body: io.NopCloser(strings.NewReader("original")),
|
||||||
}
|
}
|
||||||
err := rewriteRespBody(resp, chain.HTTPBodyRewriteSettings{
|
err := rewriteRespBody(context.Background(), resp, chain.HTTPBodyRewriteSettings{
|
||||||
Pattern: regexp.MustCompile(".*"),
|
Pattern: regexp.MustCompile(".*"),
|
||||||
Type: "*",
|
Type: "*",
|
||||||
Replacement: []byte("replaced"),
|
Replacement: []byte("replaced"),
|
||||||
|
|||||||
@@ -33,14 +33,15 @@ func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) rewriter.Rew
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
if conn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
p := &grpcPlugin{
|
p := &grpcPlugin{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
}
|
}
|
||||||
if conn != nil {
|
p.client = proto.NewRewriterClient(conn)
|
||||||
p.client = proto.NewRewriterClient(conn)
|
|
||||||
}
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
@@ -84,6 +85,7 @@ func (p *httpPlugin) Rewrite(ctx context.Context, b []byte, opts ...rewriter.Rew
|
|||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
defer io.Copy(io.Discard, resp.Body)
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return b, nil
|
return b, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user