Skip to content

Commit b5479e9

Browse files
committed
add proxy support + encoding gzip/zstd
1 parent 9346c90 commit b5479e9

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,23 @@ the [official documentation for caddy](https://caddyserver.com/docs/build#packag
7272

7373
## Example Caddyfile
7474

75+
### Using file_server
7576
```plaintext
7677
localhost {
77-
root * /your-images-directory
78+
root /your-images-directory
7879
file_server
7980
image_processor
8081
}
8182
```
8283

84+
### Using reverse_proxy
85+
```plaintext
86+
localhost {
87+
reverse_proxy your-domain.com
88+
image_processor
89+
}
90+
```
91+
8392
In this example, all requests undergo processing by the image processor module before being served by the
8493
caddy.
8594

module.go

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
99
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
1010
"github.com/h2non/bimg"
11+
"github.com/klauspost/compress/gzip"
12+
"github.com/klauspost/compress/zstd"
13+
"go.uber.org/zap"
14+
"io"
1115
"net/http"
1216
"strconv"
1317
)
@@ -18,7 +22,9 @@ func init() {
1822
httpcaddyfile.RegisterDirectiveOrder("image_processor", "before", "respond")
1923
}
2024

21-
type Middleware struct{}
25+
type Middleware struct {
26+
logger *zap.Logger
27+
}
2228

2329
func (Middleware) CaddyModule() caddy.ModuleInfo {
2430
return caddy.ModuleInfo{
@@ -27,6 +33,11 @@ func (Middleware) CaddyModule() caddy.ModuleInfo {
2733
}
2834
}
2935

36+
func (m Middleware) Provision(ctx caddy.Context) error {
37+
m.logger = ctx.Logger()
38+
return nil
39+
}
40+
3041
func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
3142

3243
//Automatic return if not options set
@@ -46,26 +57,85 @@ func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddy
4657
return responseRecorder.WriteResponse()
4758
}
4859

60+
decoded, err := m.getDecodedBufferFromResponse(&responseRecorder)
61+
if err != nil {
62+
m.logger.Error("error getting initial response", zap.Error(err))
63+
return responseRecorder.WriteResponse()
64+
}
65+
4966
options, err := getOptions(r)
5067
if err != nil {
51-
return err
68+
m.logger.Error("error parsing options", zap.Error(err))
69+
return responseRecorder.WriteResponse()
5270
}
5371

54-
newImage, err := bimg.NewImage(responseRecorder.Buffer().Bytes()).Process(options)
72+
newImage, err := bimg.NewImage(decoded).Process(options)
5573
if err != nil {
74+
m.logger.Error("error processing image", zap.Error(err))
5675
return responseRecorder.WriteResponse()
5776
}
5877

78+
// Remove intercepted headers from buffer
79+
for header, _ := range w.Header() {
80+
w.Header().Del(header)
81+
}
82+
5983
w.Header().Set("Content-Length", strconv.Itoa(len(newImage)))
6084
w.Header().Set("Content-Type", "image/"+bimg.DetermineImageTypeName(newImage))
6185

6286
if _, err = w.Write(newImage); err != nil {
63-
return err
87+
m.logger.Error("error writing processed image", zap.Error(err))
88+
return responseRecorder.WriteResponse()
6489
}
6590

6691
return nil
6792
}
6893

94+
func (m *Middleware) getDecodedBufferFromResponse(r *caddyhttp.ResponseRecorder) ([]byte, error) {
95+
96+
encoding := (*r).Header().Get("Content-Encoding")
97+
if encoding == "" {
98+
return (*r).Buffer().Bytes(), nil
99+
}
100+
101+
if encoding == "gzip" {
102+
decoder, err := gzip.NewReader((*r).Buffer())
103+
if err != nil {
104+
return nil, err
105+
}
106+
defer func(decoder *gzip.Reader) {
107+
err := decoder.Close()
108+
if err != nil {
109+
return
110+
}
111+
}(decoder)
112+
113+
decodedOut := bytes.Buffer{}
114+
_, err = io.Copy(&decodedOut, decoder)
115+
if err != nil {
116+
return nil, err
117+
}
118+
return decodedOut.Bytes(), nil
119+
}
120+
121+
if encoding == "zstd" {
122+
// Try decode zstd
123+
var decoder, err = zstd.NewReader((*r).Buffer(), zstd.WithDecoderConcurrency(0))
124+
if err != nil {
125+
return nil, err
126+
}
127+
defer decoder.Close()
128+
decodedOut := bytes.Buffer{}
129+
_, err = io.Copy(&decodedOut, decoder)
130+
if err != nil {
131+
return nil, err
132+
}
133+
return decodedOut.Bytes(), nil
134+
}
135+
136+
return nil, fmt.Errorf("unsupported encoding: %s", encoding)
137+
138+
}
69139
func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
70140
return nil
71141

@@ -239,6 +309,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
239309

240310
// Interface guards
241311
var (
312+
_ caddy.Provisioner = (*Middleware)(nil)
242313
_ caddyhttp.MiddlewareHandler = (*Middleware)(nil)
243314
_ caddyfile.Unmarshaler = (*Middleware)(nil)
244315
)

0 commit comments

Comments
 (0)