diff --git a/Makefile b/Makefile
index 7380ef6..6e061fe 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,13 @@
-.PHONY: lint run-caddy run-roadrunner
+.PHONY: lint run-caddy run-server run-traefik
lint: ## Run golangci-lint to ensure the code quality
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint golangci-lint run
-run-caddy: ## Build caddy binary
+run-caddy: ## Build and run caddy binary
cd middleware/caddy && $(MAKE) build && $(MAKE) run
+
+run-server: ## Run server main.go
+ go run middleware/server/main.go
+
+run-traefik: ## Run server main.go
+ cd middleware/traefik && $(MAKE) build && $(MAKE) run
diff --git a/README.md b/README.md
index 3bf574c..71a5ffe 100644
--- a/README.md
+++ b/README.md
@@ -51,18 +51,18 @@ func functionToParseESITags(b []byte, r *http.Request) []byte {
```bash
xcaddy build --with github.com/darkweak/go-esi/middleware/caddy
```
-Refer to the [sample Caddyfile](https://github.com/darkweak/go-esi/blob/master/middleware/caddy/Caddyfile) to know how to configure that.
+Refer to the [sample Caddyfile](https://github.com/darkweak/go-esi/blob/master/middleware/caddy/Caddyfile) to know how to use that.
### Træfik middleware
-```bash
+```yaml
# anywhere/traefik.yml
experimental:
plugins:
souin:
moduleName: github.com/darkweak/go-esi
- version: v0.0.4
+ version: v0.0.5
```
-```bash
+```yaml
# anywhere/dynamic-configuration
http:
routers:
@@ -74,12 +74,9 @@ http:
middlewares:
esi:
plugin:
- esi:
- # We don't care about the configuration but we have ot declare that block
- # due to shitty træfik empty configuration handle.
- disable: false
+ esi: {}
```
-Refer to the [sample Caddyfile](https://github.com/darkweak/go-esi/blob/master/middleware/caddy/Caddyfile) to know how to configure that.
+Refer to the [sample traefik file](https://github.com/darkweak/go-esi/blob/master/middleware/traefik/esi-configuration.yml) to know how to use that.
## TODO
- [x] choose tag
diff --git a/esi/choose.go b/esi/choose.go
index 38a24f7..572fdb3 100644
--- a/esi/choose.go
+++ b/esi/choose.go
@@ -30,7 +30,7 @@ type chooseTag struct {
//
//
// ).
-func (c *chooseTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (c *chooseTag) Process(b []byte, req *http.Request) ([]byte, int) {
found := closeChoose.FindIndex(b)
if found == nil {
return nil, len(b)
@@ -56,3 +56,14 @@ func (c *chooseTag) process(b []byte, req *http.Request) ([]byte, int) {
return res, c.length
}
+
+func (*chooseTag) HasClose(b []byte) bool {
+ return closeChoose.FindIndex(b) != nil
+}
+
+func (*chooseTag) GetClosePosition(b []byte) int {
+ if idx := closeChoose.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/esi/comment.go b/esi/comment.go
index 1e0b596..e9c3137 100644
--- a/esi/comment.go
+++ b/esi/comment.go
@@ -14,7 +14,7 @@ type commentTag struct {
}
// Input (e.g. comment text="This is a comment." />).
-func (c *commentTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (c *commentTag) Process(b []byte, req *http.Request) ([]byte, int) {
found := closeComment.FindIndex(b)
if found == nil {
return nil, len(b)
@@ -22,3 +22,14 @@ func (c *commentTag) process(b []byte, req *http.Request) ([]byte, int) {
return []byte{}, found[1]
}
+
+func (*commentTag) HasClose(b []byte) bool {
+ return closeComment.FindIndex(b) != nil
+}
+
+func (*commentTag) GetClosePosition(b []byte) int {
+ if idx := closeComment.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/esi/escape.go b/esi/escape.go
index 7a8ece2..a3129c1 100644
--- a/esi/escape.go
+++ b/esi/escape.go
@@ -16,7 +16,7 @@ type escapeTag struct {
*baseTag
}
-func (e *escapeTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (e *escapeTag) Process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeEscape.FindIndex(b)
if closeIdx == nil {
@@ -28,3 +28,14 @@ func (e *escapeTag) process(b []byte, req *http.Request) ([]byte, int) {
return b, e.length
}
+
+func (*escapeTag) HasClose(b []byte) bool {
+ return closeEscape.FindIndex(b) != nil
+}
+
+func (*escapeTag) GetClosePosition(b []byte) int {
+ if idx := closeEscape.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/esi/esi.go b/esi/esi.go
index b20d45a..9deea63 100644
--- a/esi/esi.go
+++ b/esi/esi.go
@@ -4,7 +4,7 @@ import (
"net/http"
)
-func findTagName(b []byte) tag {
+func findTagName(b []byte) Tag {
name := tagname.FindSubmatch(b)
if name == nil {
return nil
@@ -43,6 +43,43 @@ func findTagName(b []byte) tag {
return nil
}
+func HasOpenedTags(b []byte) bool {
+ return esi.FindIndex(b) != nil || escapeRg.FindIndex(b) != nil
+}
+
+func CanProcess(b []byte) bool {
+ if tag := findTagName(b); tag != nil {
+ return tag.HasClose(b)
+ }
+
+ return false
+}
+
+func ReadToTag(next []byte, pointer int) (startTagPosition, esiPointer int, t Tag) {
+ tagIdx := esi.FindIndex(next)
+ var isEscapeTag bool
+
+ if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) {
+ tagIdx = escIdx
+ tagIdx[1] = escIdx[0]
+ isEscapeTag = true
+ }
+
+ if tagIdx == nil {
+ return len(next), 0, nil
+ }
+
+ esiPointer = tagIdx[1]
+ startTagPosition = tagIdx[0]
+ t = findTagName(next[esiPointer:])
+
+ if isEscapeTag {
+ esiPointer += 7
+ }
+
+ return
+}
+
func Parse(b []byte, req *http.Request) []byte {
pointer := 0
@@ -69,7 +106,7 @@ func Parse(b []byte, req *http.Request) []byte {
esiPointer += 7
}
- res, p := t.process(next[esiPointer:], req)
+ res, p := t.Process(next[esiPointer:], req)
esiPointer += p
b = append(b[:pointer], append(next[:tagIdx[0]], append(res, next[esiPointer:]...)...)...)
diff --git a/esi/include.go b/esi/include.go
index dcdf95b..1ecebe5 100644
--- a/esi/include.go
+++ b/esi/include.go
@@ -40,7 +40,7 @@ func (i *includeTag) loadAttributes(b []byte) error {
// With or without the alt
// With or without a space separator before the closing
// With or without the quotes around the src/alt value.
-func (i *includeTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (i *includeTag) Process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeInclude.FindIndex(b)
if closeIdx == nil {
@@ -71,3 +71,14 @@ func (i *includeTag) process(b []byte, req *http.Request) ([]byte, int) {
return b, i.length
}
+
+func (*includeTag) HasClose(b []byte) bool {
+ return closeInclude.FindIndex(b) != nil
+}
+
+func (*includeTag) GetClosePosition(b []byte) int {
+ if idx := closeInclude.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/esi/remove.go b/esi/remove.go
index c73b1f7..3c16157 100644
--- a/esi/remove.go
+++ b/esi/remove.go
@@ -13,7 +13,7 @@ type removeTag struct {
*baseTag
}
-func (r *removeTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (r *removeTag) Process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeRemove.FindIndex(b)
if closeIdx == nil {
return []byte{}, len(b)
@@ -23,3 +23,14 @@ func (r *removeTag) process(b []byte, req *http.Request) ([]byte, int) {
return []byte{}, r.length
}
+
+func (*removeTag) HasClose(b []byte) bool {
+ return closeRemove.FindIndex(b) != nil
+}
+
+func (*removeTag) GetClosePosition(b []byte) int {
+ if idx := closeRemove.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/esi/type.go b/esi/type.go
index 232f2f8..8388015 100644
--- a/esi/type.go
+++ b/esi/type.go
@@ -5,8 +5,10 @@ import (
)
type (
- tag interface {
- process([]byte, *http.Request) ([]byte, int)
+ Tag interface {
+ Process([]byte, *http.Request) ([]byte, int)
+ HasClose([]byte) bool
+ GetClosePosition([]byte) int
}
baseTag struct {
@@ -18,6 +20,6 @@ func newBaseTag() *baseTag {
return &baseTag{length: 0}
}
-func (b *baseTag) process(content []byte, _ *http.Request) ([]byte, int) {
+func (b *baseTag) Process(content []byte, _ *http.Request) ([]byte, int) {
return []byte{}, len(content)
}
diff --git a/esi/vars.go b/esi/vars.go
index a3bdc26..39bfb5c 100644
--- a/esi/vars.go
+++ b/esi/vars.go
@@ -75,7 +75,7 @@ type varsTag struct {
}
// Input (e.g. comment text="This is a comment." />).
-func (c *varsTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (c *varsTag) Process(b []byte, req *http.Request) ([]byte, int) {
found := closeVars.FindIndex(b)
if found == nil {
return nil, len(b)
@@ -87,3 +87,14 @@ func (c *varsTag) process(b []byte, req *http.Request) ([]byte, int) {
return []byte(parseVariables(b, req))
}), c.length
}
+
+func (*varsTag) HasClose(b []byte) bool {
+ return closeVars.FindIndex(b) != nil
+}
+
+func (*varsTag) GetClosePosition(b []byte) int {
+ if idx := closeVars.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/caddy/Caddyfile b/middleware/caddy/Caddyfile
index a61b0f4..fe74f7a 100644
--- a/middleware/caddy/Caddyfile
+++ b/middleware/caddy/Caddyfile
@@ -5,7 +5,7 @@
esi
}
-:80 {
+localhost:443 {
route /chained-esi-include-1 {
header Content-Type text/html
respond ``
@@ -28,8 +28,8 @@
route /* {
esi
-
- root * ../../fixtures
- file_server
+ reverse_proxy 127.0.0.1:81
+ # root * ../../fixtures
+ # file_server
}
}
\ No newline at end of file
diff --git a/middleware/caddy/esi.go b/middleware/caddy/esi.go
index 15967f2..f0831a8 100644
--- a/middleware/caddy/esi.go
+++ b/middleware/caddy/esi.go
@@ -2,7 +2,6 @@ package caddy_esi
import (
"bytes"
- "fmt"
"net/http"
"sync"
@@ -10,7 +9,7 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
- "github.com/darkweak/go-esi/esi"
+ "github.com/darkweak/go-esi/writer"
)
var bufPool *sync.Pool = &sync.Pool{
@@ -45,14 +44,33 @@ func (e *ESI) ServeHTTP(rw http.ResponseWriter, r *http.Request, next caddyhttp.
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
defer bufPool.Put(buf)
- cw := newWriter(buf, rw)
+ cw := writer.NewWriter(buf, rw, r)
+ go func(w *writer.Writer) {
+ w.Header().Del("Content-Length")
+ if w.Rq.ProtoMajor == 1 {
+ w.Header().Set("Content-Encoding", "chunked")
+ }
+ var i = 0
+ for {
+ if len(cw.AsyncBuf) <= i {
+ continue
+ }
+ rs := <-cw.AsyncBuf[i]
+ if rs == nil {
+ cw.Done <- true
+ break
+ }
+ _, _ = rw.Write(rs)
+ i++
+ }
+ }(cw)
next.ServeHTTP(cw, r)
+ cw.AsyncBuf = append(cw.AsyncBuf, make(chan []byte))
+ go func(w *writer.Writer, iteration int) {
+ w.AsyncBuf[iteration] <- nil
+ }(cw, cw.Iteration)
- b := esi.Parse(cw.buf.Bytes(), r)
-
- rw.Header().Set("Content-Length", fmt.Sprintf("%d", len(b)))
- rw.WriteHeader(cw.status)
- _, _ = rw.Write(b)
+ <-cw.Done
return nil
}
diff --git a/middleware/caddy/go.mod b/middleware/caddy/go.mod
index d5763e2..07c2b38 100644
--- a/middleware/caddy/go.mod
+++ b/middleware/caddy/go.mod
@@ -4,7 +4,7 @@ go 1.18
require (
github.com/caddyserver/caddy/v2 v2.5.2
- github.com/darkweak/go-esi v0.0.4
+ github.com/darkweak/go-esi v0.0.5
)
require (
@@ -111,4 +111,4 @@ require (
howett.net/plist v1.0.0 // indirect
)
-replace github.com/darkweak/go-esi v0.0.4 => ../..
+replace github.com/darkweak/go-esi v0.0.5 => ../..
diff --git a/middleware/caddy/writer.go b/middleware/caddy/writer.go
deleted file mode 100644
index a7fb39e..0000000
--- a/middleware/caddy/writer.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package caddy_esi
-
-import (
- "bytes"
- "net/http"
-)
-
-type writer struct {
- buf *bytes.Buffer
- rw http.ResponseWriter
- status int
-}
-
-func newWriter(buf *bytes.Buffer, rw http.ResponseWriter) *writer {
- return &writer{
- buf: buf,
- rw: rw,
- }
-}
-
-// Header implements http.ResponseWriter
-func (w *writer) Header() http.Header {
- return w.rw.Header()
-}
-
-// WriteHeader implements http.ResponseWriter
-func (w *writer) WriteHeader(statusCode int) {
- w.status = statusCode
-}
-
-// Write will write the response body
-func (w *writer) Write(b []byte) (int, error) {
- w.buf.Write(b)
- return len(b), nil
-}
-
-var _ http.ResponseWriter = (*writer)(nil)
diff --git a/middleware/server/main.go b/middleware/server/main.go
index b5a57f5..febcc93 100644
--- a/middleware/server/main.go
+++ b/middleware/server/main.go
@@ -2,11 +2,44 @@ package main
import (
"net/http"
+ "time"
"github.com/darkweak/go-esi/esi"
)
+var respond = []byte(`
+
+ Hello from $(HTTP_HOST)
+
+
+
+
+
+
+
+
+
+
+`)
+
func main() {
rq, _ := http.NewRequest(http.MethodGet, "domain.com/", nil)
esi.Parse([]byte{}, rq)
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write(respond[0:97])
+ w.(http.Flusher).Flush()
+ time.Sleep(3 * time.Second)
+ _, _ = w.Write(respond[97:194])
+ time.Sleep(3 * time.Second)
+ _, _ = w.Write(respond[194:291])
+ time.Sleep(3 * time.Second)
+ _, _ = w.Write(respond[291:388])
+ })
+
+ _ = http.ListenAndServe(":81", nil)
}
diff --git a/middleware/traefik/Makefile b/middleware/traefik/Makefile
new file mode 100644
index 0000000..2d83d3b
--- /dev/null
+++ b/middleware/traefik/Makefile
@@ -0,0 +1,8 @@
+.PHONY: build run
+
+build: ## Build caddy binary
+ go mod tidy
+ go mod download
+
+run: ## Run caddy with go-esi
+ docker-compose up --remove-orphans
\ No newline at end of file
diff --git a/middleware/traefik/esi-configuration.yml b/middleware/traefik/esi-configuration.yml
index 40cd7b2..307e7a6 100644
--- a/middleware/traefik/esi-configuration.yml
+++ b/middleware/traefik/esi-configuration.yml
@@ -18,7 +18,4 @@ http:
middlewares:
esi:
plugin:
- esi:
- # We don't care about the configuration but we have ot declare that block
- # due to shitty træfik empty configuration handle.
- disable: false
\ No newline at end of file
+ esi: {}
\ No newline at end of file
diff --git a/middleware/traefik/esi.go b/middleware/traefik/esi.go
index 7145238..d798bb6 100644
--- a/middleware/traefik/esi.go
+++ b/middleware/traefik/esi.go
@@ -1,35 +1,71 @@
package traefik
import (
+ "bytes"
"context"
"net/http"
+ "sync"
- "github.com/darkweak/go-esi/esi"
+ "github.com/darkweak/go-esi/writer"
)
-// Config the plugin configuration.
+var bufPool *sync.Pool = &sync.Pool{
+ New: func() any {
+ return &bytes.Buffer{}
+ },
+}
+
+// Config the ESI plugin configuration.
type Config struct{}
-// CreateConfig creates the default plugin configuration.
+// CreateConfig creates the ESI plugin configuration.
func CreateConfig() *Config {
return &Config{}
}
-// Demo a Demo plugin.
-type Demo struct {
+// ESI is a plugin that allow users to process the ESI tags.
+type ESI struct {
next http.Handler
name string
}
-// New created a new Demo plugin.
+// New created a new ESI plugin.
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
- return &Demo{
+ return &ESI{
next: next,
name: name,
}, nil
}
-func (a *Demo) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
- esi.Parse([]byte{}, req)
- a.next.ServeHTTP(rw, req)
+func (e *ESI) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+ buf := bufPool.Get().(*bytes.Buffer)
+ buf.Reset()
+ defer bufPool.Put(buf)
+ cw := writer.NewWriter(buf, rw, req)
+ go func(w *writer.Writer) {
+ w.Header().Del("Content-Length")
+ if w.Rq.ProtoMajor == 1 {
+ w.Header().Set("Content-Encoding", "chunked")
+ }
+ var i = 0
+ for {
+ if len(cw.AsyncBuf) <= i {
+ continue
+ }
+ rs := <-cw.AsyncBuf[i]
+ if rs == nil {
+ cw.Done <- true
+ break
+ }
+ _, _ = rw.Write(rs)
+ i++
+ }
+ }(cw)
+ e.next.ServeHTTP(cw, req)
+ cw.AsyncBuf = append(cw.AsyncBuf, make(chan []byte))
+ go func(w *writer.Writer, iteration int) {
+ w.AsyncBuf[iteration] <- nil
+ }(cw, cw.Iteration)
+
+ <-cw.Done
}
diff --git a/middleware/traefik/go.mod b/middleware/traefik/go.mod
index e6747d7..66a3ec0 100644
--- a/middleware/traefik/go.mod
+++ b/middleware/traefik/go.mod
@@ -2,6 +2,6 @@ module github.com/darkweak/go-esi/middleware/traefik
go 1.18
-require github.com/darkweak/go-esi v0.0.4
+require github.com/darkweak/go-esi v0.0.5
-replace github.com/darkweak/go-esi v0.0.4 => ../..
+replace github.com/darkweak/go-esi v0.0.5 => ../..
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/choose.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/choose.go
index 38a24f7..572fdb3 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/choose.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/choose.go
@@ -30,7 +30,7 @@ type chooseTag struct {
//
//
// ).
-func (c *chooseTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (c *chooseTag) Process(b []byte, req *http.Request) ([]byte, int) {
found := closeChoose.FindIndex(b)
if found == nil {
return nil, len(b)
@@ -56,3 +56,14 @@ func (c *chooseTag) process(b []byte, req *http.Request) ([]byte, int) {
return res, c.length
}
+
+func (*chooseTag) HasClose(b []byte) bool {
+ return closeChoose.FindIndex(b) != nil
+}
+
+func (*chooseTag) GetClosePosition(b []byte) int {
+ if idx := closeChoose.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/comment.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/comment.go
index 1e0b596..e9c3137 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/comment.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/comment.go
@@ -14,7 +14,7 @@ type commentTag struct {
}
// Input (e.g. comment text="This is a comment." />).
-func (c *commentTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (c *commentTag) Process(b []byte, req *http.Request) ([]byte, int) {
found := closeComment.FindIndex(b)
if found == nil {
return nil, len(b)
@@ -22,3 +22,14 @@ func (c *commentTag) process(b []byte, req *http.Request) ([]byte, int) {
return []byte{}, found[1]
}
+
+func (*commentTag) HasClose(b []byte) bool {
+ return closeComment.FindIndex(b) != nil
+}
+
+func (*commentTag) GetClosePosition(b []byte) int {
+ if idx := closeComment.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/escape.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/escape.go
index 7a8ece2..a3129c1 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/escape.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/escape.go
@@ -16,7 +16,7 @@ type escapeTag struct {
*baseTag
}
-func (e *escapeTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (e *escapeTag) Process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeEscape.FindIndex(b)
if closeIdx == nil {
@@ -28,3 +28,14 @@ func (e *escapeTag) process(b []byte, req *http.Request) ([]byte, int) {
return b, e.length
}
+
+func (*escapeTag) HasClose(b []byte) bool {
+ return closeEscape.FindIndex(b) != nil
+}
+
+func (*escapeTag) GetClosePosition(b []byte) int {
+ if idx := closeEscape.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/esi.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/esi.go
index b20d45a..9deea63 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/esi.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/esi.go
@@ -4,7 +4,7 @@ import (
"net/http"
)
-func findTagName(b []byte) tag {
+func findTagName(b []byte) Tag {
name := tagname.FindSubmatch(b)
if name == nil {
return nil
@@ -43,6 +43,43 @@ func findTagName(b []byte) tag {
return nil
}
+func HasOpenedTags(b []byte) bool {
+ return esi.FindIndex(b) != nil || escapeRg.FindIndex(b) != nil
+}
+
+func CanProcess(b []byte) bool {
+ if tag := findTagName(b); tag != nil {
+ return tag.HasClose(b)
+ }
+
+ return false
+}
+
+func ReadToTag(next []byte, pointer int) (startTagPosition, esiPointer int, t Tag) {
+ tagIdx := esi.FindIndex(next)
+ var isEscapeTag bool
+
+ if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) {
+ tagIdx = escIdx
+ tagIdx[1] = escIdx[0]
+ isEscapeTag = true
+ }
+
+ if tagIdx == nil {
+ return len(next), 0, nil
+ }
+
+ esiPointer = tagIdx[1]
+ startTagPosition = tagIdx[0]
+ t = findTagName(next[esiPointer:])
+
+ if isEscapeTag {
+ esiPointer += 7
+ }
+
+ return
+}
+
func Parse(b []byte, req *http.Request) []byte {
pointer := 0
@@ -69,7 +106,7 @@ func Parse(b []byte, req *http.Request) []byte {
esiPointer += 7
}
- res, p := t.process(next[esiPointer:], req)
+ res, p := t.Process(next[esiPointer:], req)
esiPointer += p
b = append(b[:pointer], append(next[:tagIdx[0]], append(res, next[esiPointer:]...)...)...)
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/include.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/include.go
index dcdf95b..1ecebe5 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/include.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/include.go
@@ -40,7 +40,7 @@ func (i *includeTag) loadAttributes(b []byte) error {
// With or without the alt
// With or without a space separator before the closing
// With or without the quotes around the src/alt value.
-func (i *includeTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (i *includeTag) Process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeInclude.FindIndex(b)
if closeIdx == nil {
@@ -71,3 +71,14 @@ func (i *includeTag) process(b []byte, req *http.Request) ([]byte, int) {
return b, i.length
}
+
+func (*includeTag) HasClose(b []byte) bool {
+ return closeInclude.FindIndex(b) != nil
+}
+
+func (*includeTag) GetClosePosition(b []byte) int {
+ if idx := closeInclude.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/remove.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/remove.go
index c73b1f7..3c16157 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/remove.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/remove.go
@@ -13,7 +13,7 @@ type removeTag struct {
*baseTag
}
-func (r *removeTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (r *removeTag) Process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeRemove.FindIndex(b)
if closeIdx == nil {
return []byte{}, len(b)
@@ -23,3 +23,14 @@ func (r *removeTag) process(b []byte, req *http.Request) ([]byte, int) {
return []byte{}, r.length
}
+
+func (*removeTag) HasClose(b []byte) bool {
+ return closeRemove.FindIndex(b) != nil
+}
+
+func (*removeTag) GetClosePosition(b []byte) int {
+ if idx := closeRemove.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/type.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/type.go
index 232f2f8..8388015 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/type.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/type.go
@@ -5,8 +5,10 @@ import (
)
type (
- tag interface {
- process([]byte, *http.Request) ([]byte, int)
+ Tag interface {
+ Process([]byte, *http.Request) ([]byte, int)
+ HasClose([]byte) bool
+ GetClosePosition([]byte) int
}
baseTag struct {
@@ -18,6 +20,6 @@ func newBaseTag() *baseTag {
return &baseTag{length: 0}
}
-func (b *baseTag) process(content []byte, _ *http.Request) ([]byte, int) {
+func (b *baseTag) Process(content []byte, _ *http.Request) ([]byte, int) {
return []byte{}, len(content)
}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/vars.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/vars.go
index a3bdc26..39bfb5c 100644
--- a/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/vars.go
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/esi/vars.go
@@ -75,7 +75,7 @@ type varsTag struct {
}
// Input (e.g. comment text="This is a comment." />).
-func (c *varsTag) process(b []byte, req *http.Request) ([]byte, int) {
+func (c *varsTag) Process(b []byte, req *http.Request) ([]byte, int) {
found := closeVars.FindIndex(b)
if found == nil {
return nil, len(b)
@@ -87,3 +87,14 @@ func (c *varsTag) process(b []byte, req *http.Request) ([]byte, int) {
return []byte(parseVariables(b, req))
}), c.length
}
+
+func (*varsTag) HasClose(b []byte) bool {
+ return closeVars.FindIndex(b) != nil
+}
+
+func (*varsTag) GetClosePosition(b []byte) int {
+ if idx := closeVars.FindIndex(b); idx != nil {
+ return idx[1]
+ }
+ return 0
+}
diff --git a/middleware/traefik/vendor/github.com/darkweak/go-esi/writer/writer.go b/middleware/traefik/vendor/github.com/darkweak/go-esi/writer/writer.go
new file mode 100644
index 0000000..459a836
--- /dev/null
+++ b/middleware/traefik/vendor/github.com/darkweak/go-esi/writer/writer.go
@@ -0,0 +1,97 @@
+package writer
+
+import (
+ "bytes"
+ "net/http"
+
+ "github.com/darkweak/go-esi/esi"
+)
+
+type Writer struct {
+ buf *bytes.Buffer
+ rw http.ResponseWriter
+ Rq *http.Request
+ AsyncBuf []chan []byte
+ Done chan bool
+ flushed bool
+ Iteration int
+}
+
+func NewWriter(buf *bytes.Buffer, rw http.ResponseWriter, rq *http.Request) *Writer {
+ return &Writer{
+ buf: buf,
+ Rq: rq,
+ rw: rw,
+ AsyncBuf: make([]chan []byte, 0),
+ Done: make(chan bool),
+ }
+}
+
+// Header implements http.ResponseWriter
+func (w *Writer) Header() http.Header {
+ return w.rw.Header()
+}
+
+// WriteHeader implements http.ResponseWriter
+func (w *Writer) WriteHeader(statusCode int) {
+ if statusCode == 0 {
+ w.rw.WriteHeader(http.StatusOK)
+ }
+}
+
+// Flush implements http.Flusher
+func (w *Writer) Flush() {
+ if !w.flushed {
+ w.rw.(http.Flusher).Flush()
+ w.flushed = true
+ }
+}
+
+// Write will write the response body
+func (w *Writer) Write(b []byte) (int, error) {
+ buf := append(w.buf.Bytes(), b...)
+ w.buf.Reset()
+
+ if esi.HasOpenedTags(buf) {
+ position := 0
+ for position < len(buf) {
+ startPos, nextPos, t := esi.ReadToTag(buf[position:], position)
+
+ if startPos != 0 {
+ w.AsyncBuf = append(w.AsyncBuf, make(chan []byte))
+ go func(tmpBuf []byte, i int, cw *Writer) {
+ cw.AsyncBuf[i] <- tmpBuf
+ }(buf[position:position+startPos], w.Iteration, w)
+ w.Iteration++
+ }
+
+ if t == nil {
+ break
+ }
+
+ closePosition := t.GetClosePosition(buf[position+startPos:])
+ if closePosition == 0 {
+ position += startPos
+ break
+ }
+
+ position += nextPos
+ w.AsyncBuf = append(w.AsyncBuf, make(chan []byte))
+ go func(currentTag esi.Tag, tmpBuf []byte, cw *Writer, Iteration int) {
+ p, _ := currentTag.Process(tmpBuf, cw.Rq)
+ cw.AsyncBuf[Iteration] <- p
+ }(t, buf[position:(position-nextPos)+startPos+closePosition], w, w.Iteration)
+ position += startPos + closePosition - nextPos
+ w.Iteration++
+ }
+ w.buf.Write(buf[position:])
+ return len(b), nil
+ }
+
+ w.AsyncBuf = append(w.AsyncBuf, make(chan []byte))
+ w.AsyncBuf[w.Iteration] <- buf
+ w.Iteration++
+ return len(b), nil
+}
+
+var _ http.ResponseWriter = (*Writer)(nil)
diff --git a/middleware/traefik/vendor/modules.txt b/middleware/traefik/vendor/modules.txt
index 78e242b..dfd38ea 100644
--- a/middleware/traefik/vendor/modules.txt
+++ b/middleware/traefik/vendor/modules.txt
@@ -1,3 +1,4 @@
-# github.com/darkweak/go-esi v0.0.4 => ../..
+# github.com/darkweak/go-esi v0.0.5 => ../..
## explicit; go 1.18
github.com/darkweak/go-esi/esi
+github.com/darkweak/go-esi/writer
diff --git a/writer/writer.go b/writer/writer.go
new file mode 100644
index 0000000..459a836
--- /dev/null
+++ b/writer/writer.go
@@ -0,0 +1,97 @@
+package writer
+
+import (
+ "bytes"
+ "net/http"
+
+ "github.com/darkweak/go-esi/esi"
+)
+
+type Writer struct {
+ buf *bytes.Buffer
+ rw http.ResponseWriter
+ Rq *http.Request
+ AsyncBuf []chan []byte
+ Done chan bool
+ flushed bool
+ Iteration int
+}
+
+func NewWriter(buf *bytes.Buffer, rw http.ResponseWriter, rq *http.Request) *Writer {
+ return &Writer{
+ buf: buf,
+ Rq: rq,
+ rw: rw,
+ AsyncBuf: make([]chan []byte, 0),
+ Done: make(chan bool),
+ }
+}
+
+// Header implements http.ResponseWriter
+func (w *Writer) Header() http.Header {
+ return w.rw.Header()
+}
+
+// WriteHeader implements http.ResponseWriter
+func (w *Writer) WriteHeader(statusCode int) {
+ if statusCode == 0 {
+ w.rw.WriteHeader(http.StatusOK)
+ }
+}
+
+// Flush implements http.Flusher
+func (w *Writer) Flush() {
+ if !w.flushed {
+ w.rw.(http.Flusher).Flush()
+ w.flushed = true
+ }
+}
+
+// Write will write the response body
+func (w *Writer) Write(b []byte) (int, error) {
+ buf := append(w.buf.Bytes(), b...)
+ w.buf.Reset()
+
+ if esi.HasOpenedTags(buf) {
+ position := 0
+ for position < len(buf) {
+ startPos, nextPos, t := esi.ReadToTag(buf[position:], position)
+
+ if startPos != 0 {
+ w.AsyncBuf = append(w.AsyncBuf, make(chan []byte))
+ go func(tmpBuf []byte, i int, cw *Writer) {
+ cw.AsyncBuf[i] <- tmpBuf
+ }(buf[position:position+startPos], w.Iteration, w)
+ w.Iteration++
+ }
+
+ if t == nil {
+ break
+ }
+
+ closePosition := t.GetClosePosition(buf[position+startPos:])
+ if closePosition == 0 {
+ position += startPos
+ break
+ }
+
+ position += nextPos
+ w.AsyncBuf = append(w.AsyncBuf, make(chan []byte))
+ go func(currentTag esi.Tag, tmpBuf []byte, cw *Writer, Iteration int) {
+ p, _ := currentTag.Process(tmpBuf, cw.Rq)
+ cw.AsyncBuf[Iteration] <- p
+ }(t, buf[position:(position-nextPos)+startPos+closePosition], w, w.Iteration)
+ position += startPos + closePosition - nextPos
+ w.Iteration++
+ }
+ w.buf.Write(buf[position:])
+ return len(b), nil
+ }
+
+ w.AsyncBuf = append(w.AsyncBuf, make(chan []byte))
+ w.AsyncBuf[w.Iteration] <- buf
+ w.Iteration++
+ return len(b), nil
+}
+
+var _ http.ResponseWriter = (*Writer)(nil)