Skip to content

Commit

Permalink
Add escape support and support Caddy as middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
darkweak committed Sep 10, 2022
1 parent 37af074 commit 083d71e
Show file tree
Hide file tree
Showing 32 changed files with 2,361 additions and 61 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/non-regression.yml
@@ -0,0 +1,21 @@
name: Build container and validate lint/tests

on:
pull_request:
workflow_dispatch:

jobs:
lint-and-tests:
name: Validate Go code linting
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.18
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
- name: tests
run: go test -v -race ./...
34 changes: 34 additions & 0 deletions .github/workflows/plugins.yml
@@ -0,0 +1,34 @@
name: Build and validate go-esi as plugins

on:
- pull_request

jobs:
build-caddy-validator:
name: Check that go-esi build as caddy module
runs-on: ubuntu-latest
steps:
-
name: Add domain.com host to /etc/hosts
run: |
sudo echo "127.0.0.1 domain.com" | sudo tee -a /etc/hosts
-
name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.18
-
name: Checkout code
uses: actions/checkout@v2
-
name: Install xcaddy
run: go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
-
name: Build go-esi as caddy module
run: cd plugins/caddy && xcaddy build --with github.com/darkweak/go-esi/middleware/caddy=./ --with github.com/darkweak/go-esi@latest=../..
-
name: Run Caddy tests
run: cd plugins/caddy && go test -v ./...
-
name: Run detached caddy
run: cd plugins/caddy && ./caddy run &
30 changes: 30 additions & 0 deletions .github/workflows/release.yml
@@ -0,0 +1,30 @@
name: Build container and publish to docker hub

on:
create:
tags: ["v*"]

jobs:
generate-artifacts:
name: Deploy to goreleaser
runs-on: ubuntu-latest
steps:
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
version: latest
args: release --rm-dist
workdir: ./middleware/server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Empty file added .gitignore
Empty file.
3 changes: 3 additions & 0 deletions .golangci.yml
@@ -0,0 +1,3 @@
run:
timeout: 30s
issues-exit-code: 1
33 changes: 33 additions & 0 deletions .goreleaser.yml
@@ -0,0 +1,33 @@
before:
hooks:
- go mod download
builds:
- <<: &build_defaults
ldflags:
- -s -w
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- 386
- amd64
- arm
- arm64
ignore:
- goos: windows
goarch: arm64
archives:
-
replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
format_overrides:
-
goos: windows
format: zip
7 changes: 7 additions & 0 deletions Makefile
@@ -0,0 +1,7 @@
.PHONY: lint run-caddy run-roadrunner

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
cd middleware/caddy && $(MAKE) build && $(MAKE) run
30 changes: 26 additions & 4 deletions README.md
Expand Up @@ -11,15 +11,37 @@ https://www.w3.org/TR/esi-lang/
go get -u github.com/darkweak/go-esi
```

## Usage
```go
import (
// ...
github.com/darkweak/go-esi/esi
)

//...

func functionToParseESITags(b []byte, r *http.Request) []byte {
// Returns the parsed response.
res := esi.Parse(b, r)

//...
return res
}
```

## Available as middleware
- [ ] Caddy
- [ ] Træfik
- [ ] Roadrunner
- [x] Caddy

### Caddy middleware
```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.

## TODO
- [x] choose tag
- [x] comment tag
- [ ] escape tag
- [x] escape tag
- [x] include tag
- [x] remove tag
- [x] otherwise tag
Expand Down
1 change: 1 addition & 0 deletions esi.go
@@ -0,0 +1 @@
package go_esi
45 changes: 21 additions & 24 deletions esi/choose.go
Expand Up @@ -5,49 +5,46 @@ import (
"regexp"
)

const (
choose = "choose"
otherwise = "otherwise"
when = "when"
)
const choose = "choose"

var (
closeChoose = regexp.MustCompile("</esi:choose>")
whenRg = regexp.MustCompile(`(?s)<esi:when test="(.+?)">(.+?)</esi:when>`)
otherwiseRg = regexp.MustCompile(`(?s)<esi:otherwise>(.+?)</esi:otherwise>`)
testAttribute = regexp.MustCompile(`test="(.+?)" ?>`)
closeChoose = regexp.MustCompile("</esi:choose>")
whenRg = regexp.MustCompile(`(?s)<esi:when test="(.+?)">(.+?)</esi:when>`)
otherwiseRg = regexp.MustCompile(`(?s)<esi:otherwise>(.+?)</esi:otherwise>`)
)

type chooseTag struct {
*baseTag
}

// Input (e.g.
// <esi:choose>
// <esi:when test="$(HTTP_COOKIE{group})=='Advanced'">
// <esi:include src="http://www.example.com/advanced.html"/>
// </esi:when>
// <esi:when test="$(HTTP_COOKIE{group})=='Basic User'">
// <esi:include src="http://www.example.com/basic.html"/>
// </esi:when>
// <esi:otherwise>
// <esi:include src="http://www.example.com/new_user.html"/>
// </esi:otherwise>
// </esi:choose>
//)
// <esi:choose>
// <esi:when test="$(HTTP_COOKIE{group})=='Advanced'">
// <esi:include src="http://www.example.com/advanced.html"/>
// </esi:when>
// <esi:when test="$(HTTP_COOKIE{group})=='Basic User'">
// <esi:include src="http://www.example.com/basic.html"/>
// </esi:when>
// <esi:otherwise>
// <esi:include src="http://www.example.com/new_user.html"/>
// </esi:otherwise>
// </esi:choose>
// ).
func (c *chooseTag) process(b []byte, req *http.Request) ([]byte, int) {
found := closeChoose.FindIndex(b)
if found == nil {
return nil, len(b)
}
c.length = found[1]

// first when esi tag
c.length = found[1]
tagIdxs := whenRg.FindAllSubmatch(b, -1)

var res []byte

for _, v := range tagIdxs {
if validateTest(v[1], req) {
res = Parse(v[2], req)

break
}
}
Expand All @@ -57,5 +54,5 @@ func (c *chooseTag) process(b []byte, req *http.Request) ([]byte, int) {
res = Parse(tagIdx[1], req)
}

return res, len(b)
return res, c.length
}
5 changes: 2 additions & 3 deletions esi/comment.go
Expand Up @@ -13,13 +13,12 @@ type commentTag struct {
*baseTag
}

// Input (e.g. comment text="This is a comment." />)
// Input (e.g. comment text="This is a comment." />).
func (c *commentTag) process(b []byte, req *http.Request) ([]byte, int) {
found := closeComment.FindIndex(b)
if found == nil {
return nil, len(b)
}
c.length = found[1]

return []byte{}, len(b)
return []byte{}, found[1]
}
29 changes: 26 additions & 3 deletions esi/escape.go
@@ -1,7 +1,30 @@
package esi

import "regexp"
import (
"net/http"
"regexp"
)

const escape = "escape"
const escape = "<!--esi"

var closeEscape = regexp.MustCompile("-->")
var (
escapeRg = regexp.MustCompile("<!--esi")
closeEscape = regexp.MustCompile("-->")
)

type escapeTag struct {
*baseTag
}

func (e *escapeTag) process(b []byte, req *http.Request) ([]byte, int) {
closeIdx := closeEscape.FindIndex(b)

if closeIdx == nil {
return nil, len(b)
}

e.length = closeIdx[1]
b = b[:closeIdx[0]]

return b, e.length
}
17 changes: 16 additions & 1 deletion esi/esi.go
Expand Up @@ -20,6 +20,9 @@ func findTagName(b []byte) tag {
baseTag: newBaseTag(),
}
case escape:
return &escapeTag{
baseTag: newBaseTag(),
}
case include:
return &includeTag{
baseTag: newBaseTag(),
Expand All @@ -44,21 +47,33 @@ func Parse(b []byte, req *http.Request) []byte {
pointer := 0

for pointer < len(b) {
var escapeTag bool

next := b[pointer:]
tagIdx := esi.FindIndex(next)

if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) {
tagIdx = escIdx
tagIdx[1] = escIdx[0]
escapeTag = true
}

if tagIdx == nil {
break
}

esiPointer := tagIdx[1]
t := findTagName(next[esiPointer:])

if escapeTag {
esiPointer += 7
}

res, p := t.process(next[esiPointer:], req)
esiPointer += p

b = append(b[:pointer], append(next[:tagIdx[0]], append(res, next[esiPointer:]...)...)...)
pointer += len(res)
pointer += len(res) + tagIdx[0]
}

return b
Expand Down

0 comments on commit 083d71e

Please sign in to comment.