├── .github └── workflows │ ├── middlewares.yml │ ├── non-regression.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .traefik.yml ├── LICENSE ├── Makefile ├── README.md ├── docs ├── esi_1.jpg └── esi_2.jpg ├── esi.go ├── esi ├── choose.go ├── comment.go ├── errors.go ├── escape.go ├── esi.go ├── esi_test.go ├── include.go ├── remove.go ├── tags.go ├── try.go ├── type.go ├── vars.go ├── vars_test.go ├── when.go └── when_test.go ├── fixtures ├── alt ├── choose ├── comment ├── escape ├── full.html ├── include ├── remove ├── try └── vars ├── go.mod ├── middleware ├── caddy │ ├── .gitignore │ ├── Caddyfile │ ├── Makefile │ ├── esi.go │ ├── esi_test.go │ ├── go.mod │ └── go.sum ├── roadrunner │ ├── Makefile │ ├── README.md │ ├── esi.go │ ├── esi_test.go │ ├── examples │ │ ├── .gitignore │ │ ├── .rr.yaml │ │ ├── Dockerfile.test │ │ ├── composer.json │ │ ├── composer.lock │ │ ├── configuration.toml │ │ ├── docker-compose.yml.test │ │ └── psr-worker.php │ ├── go.mod │ └── go.sum ├── server │ └── main.go ├── standalone │ └── main.go └── traefik │ ├── Makefile │ ├── docker-compose.yml │ ├── esi-configuration.yml │ ├── esi.go │ ├── go.mod │ ├── go.sum │ ├── traefik.yml │ └── vendor │ ├── github.com │ └── darkweak │ │ └── go-esi │ │ ├── LICENSE │ │ ├── esi │ │ ├── choose.go │ │ ├── comment.go │ │ ├── errors.go │ │ ├── escape.go │ │ ├── esi.go │ │ ├── include.go │ │ ├── remove.go │ │ ├── tags.go │ │ ├── try.go │ │ ├── type.go │ │ ├── vars.go │ │ └── when.go │ │ └── writer │ │ └── writer.go │ └── modules.txt └── writer └── writer.go /.github/workflows/middlewares.yml: -------------------------------------------------------------------------------- 1 | name: Build and validate go-esi as middleware 2 | 3 | on: 4 | - pull_request 5 | 6 | jobs: 7 | build-caddy-validator: 8 | name: Check that go-esi build as caddy module 9 | runs-on: ubuntu-latest 10 | steps: 11 | - 12 | name: Add domain.com host to /etc/hosts 13 | run: | 14 | sudo echo "127.0.0.1 domain.com" | sudo tee -a /etc/hosts 15 | - 16 | name: Install Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.19 20 | - 21 | name: Checkout code 22 | uses: actions/checkout@v3 23 | - 24 | name: Install xcaddy 25 | run: go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest 26 | - 27 | name: Build go-esi as caddy module 28 | run: cd middleware/caddy && xcaddy build --with github.com/darkweak/go-esi/middleware/caddy=./ --with github.com/darkweak/go-esi@latest=../.. 29 | - 30 | name: Run Caddy tests 31 | run: cd middleware/caddy && go test -v ./... 32 | - 33 | name: Run detached caddy 34 | run: cd middleware/caddy && ./caddy run & 35 | 36 | build-roadrunner-validator: 37 | name: Check that go-esi build as roadrunner middleware 38 | runs-on: ubuntu-latest 39 | steps: 40 | - 41 | name: Install Go 42 | uses: actions/setup-go@v3 43 | with: 44 | go-version: 1.19 45 | - 46 | name: Checkout code 47 | uses: actions/checkout@v3 48 | - 49 | name: Run Roadrunner tests 50 | run: cd middleware/roadrunner && go test -v ./... 51 | -------------------------------------------------------------------------------- /.github/workflows/non-regression.yml: -------------------------------------------------------------------------------- 1 | name: Build container and validate lint/tests 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | lint-and-tests: 9 | name: lint and static tests 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Add domain.com host to /etc/hosts 14 | run: | 15 | sudo echo "127.0.0.1 domain.com" | sudo tee -a /etc/hosts 16 | - 17 | name: checkout code 18 | uses: actions/checkout@v3 19 | - 20 | name: install Go 21 | uses: actions/setup-go@v3 22 | with: 23 | go-version: 1.19 24 | - 25 | name: golangci-lint 26 | uses: golangci/golangci-lint-action@v3 27 | - 28 | name: install xcaddy 29 | run: go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest 30 | - 31 | name: build caddy binary 32 | run: cd middleware/caddy && make build && cd - 33 | - 34 | name: run caddy binary as detached mode 35 | run: cd middleware/caddy && make run & 36 | - 37 | name: tests 38 | run: go test -v -race ./... 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build container and publish to docker hub 2 | 3 | on: 4 | create: 5 | tags: ["v*"] 6 | 7 | jobs: 8 | generate-artifacts: 9 | name: Deploy to goreleaser 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Set up Go 14 | uses: actions/setup-go@v2 15 | with: 16 | go-version: 1.19 17 | - 18 | name: Checkout 19 | uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v3 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | workdir: ./middleware/server 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkweak/go-esi/dfdfaa8466f9c7cc840040aeb753ee0887c0d431/.gitignore -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 30s 3 | issues-exit-code: 1 4 | 5 | linters: 6 | enable-all: true 7 | disable: 8 | - bodyclose 9 | - cyclop 10 | - exhaustivestruct 11 | - exhaustruct 12 | - forbidigo 13 | - gochecknoglobals 14 | - gci 15 | - golint 16 | - gomnd 17 | - ifshort 18 | - ireturn 19 | - nestif 20 | - nonamedreturns 21 | - nosnakecase 22 | - revive 23 | - testpackage 24 | - stylecheck 25 | - varnamelen 26 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod download 4 | builds: 5 | - <<: &build_defaults 6 | ldflags: 7 | - -s -w 8 | env: 9 | - CGO_ENABLED=0 10 | goos: 11 | - linux 12 | - darwin 13 | - windows 14 | goarch: 15 | - 386 16 | - amd64 17 | - arm 18 | - arm64 19 | ignore: 20 | - goos: windows 21 | goarch: arm64 22 | archives: 23 | - 24 | replacements: 25 | darwin: Darwin 26 | linux: Linux 27 | windows: Windows 28 | 386: i386 29 | amd64: x86_64 30 | format_overrides: 31 | - 32 | goos: windows 33 | format: zip 34 | -------------------------------------------------------------------------------- /.traefik.yml: -------------------------------------------------------------------------------- 1 | displayName: ESI 2 | type: middleware 3 | import: github.com/darkweak/go-esi/middleware/traefik 4 | summary: 'Pure implementation of the ESI process' 5 | 6 | testData: {} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 darkweak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: bump-version lint run-caddy run-roadrunner run-server run-traefik vendor 2 | MIDDLEWARES_LIST=caddy roadrunner server traefik 3 | 4 | bump-version: 5 | test $(from) 6 | test $(to) 7 | sed -i '' 's/version: $(from)/version: $(to)/' README.md 8 | for middleware in $(MIDDLEWARES_LIST) ; do \ 9 | sed -i '' 's/github.com\/darkweak\/go-esi $(from)/github.com\/darkweak\/go-esi $(to)/' middleware/$$middleware/go.mod ; \ 10 | done 11 | 12 | lint: ## Run golangci-lint to ensure the code quality 13 | docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint golangci-lint run 14 | 15 | run-caddy: ## Build and run caddy binary 16 | cd middleware/caddy && $(MAKE) build && $(MAKE) run 17 | 18 | run-roadrunner: ## Build and run roadrunner 19 | cd middleware/roadrunner && $(MAKE) build && $(MAKE) run 20 | 21 | run-server: ## Run server main.go 22 | go run middleware/server/main.go 23 | 24 | run-traefik: ## Build and run træfik 25 | cd middleware/traefik && $(MAKE) build && $(MAKE) run 26 | 27 | vendor: ## Generate and prepare vendors for each plugin 28 | go mod tidy && go mod download 29 | for middleware in $(MIDDLEWARES_LIST) ; do \ 30 | cd middleware/$$middleware && ($(MAKE) build || true) && cd -; done 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-esi 2 | ------ 3 | 4 | go-esi is the implementation of the non-standard ESI (Edge-Side-Include) specification from the w3. With that you'll be able to use the ESI tags and process them in your favorite golang servers. 5 | 6 | ## What are the ESI tags 7 | The ESI tags were introduced by Akamai to add some dynamic tags and only re-render these parts on the server-side. 8 | The goal of that is to render only specific parts. For example, we want to render a full e-commerce webpage but only the cart is user-dependent. So we could render the "static" parts and store with a predefined TTL (e.g. 60 minutes), and only the cart would be requested to render the block. 9 | 10 | There are multiple `esi` tags that we can use but the most used is the `esi:include` because that's the one to request another resource. 11 | 12 | We can have many `esi:include` tags in a single response, and each `esi:include` tags can itself have one or more `esi:include` tags. 13 | 14 | ![esi page example](https://github.com/darkweak/go-esi/blob/master/docs/esi_2.jpg?sanitize=true) 15 | 16 | We can have multiple `esi:include` tags in the page to request another resource and add its content to the main page. 17 | 18 | ![esi process example](https://github.com/darkweak/go-esi/blob/master/docs/esi_1.jpg?sanitize=true) 19 | 20 | ## References 21 | https://www.w3.org/TR/esi-lang/ 22 | 23 | ## Install 24 | ```bash 25 | go get -u github.com/darkweak/go-esi 26 | ``` 27 | 28 | ## Usage 29 | ```go 30 | import ( 31 | // ... 32 | github.com/darkweak/go-esi/esi 33 | ) 34 | 35 | //... 36 | 37 | func functionToParseESITags(b []byte, r *http.Request) []byte { 38 | // Returns the parsed response. 39 | res := esi.Parse(b, r) 40 | 41 | //... 42 | return res 43 | } 44 | ``` 45 | 46 | ## Available as middleware 47 | - [x] Caddy 48 | - [x] Træfik 49 | - [x] Roadrunner 50 | 51 | ### Caddy middleware 52 | ```bash 53 | xcaddy build --with github.com/darkweak/go-esi/middleware/caddy 54 | ``` 55 | Refer to the [sample Caddyfile](https://github.com/darkweak/go-esi/blob/master/middleware/caddy/Caddyfile) to know how to use that. 56 | 57 | ### Roadrunner middleware 58 | To use the `go-esi` processor as Roadrunner middleware, you just have to follow the steps below. 59 | You have to build your `rr` binary with the `go-esi` dependency. 60 | ```toml 61 | [velox] 62 | build_args = ['-trimpath', '-ldflags', '-s -X github.com/roadrunner-server/roadrunner/v2/internal/meta.version=v2.12.0 -X github.com/roadrunner-server/roadrunner/v2/internal/meta.buildTime=10:00:00'] 63 | 64 | [roadrunner] 65 | ref = "v2.12.3" 66 | 67 | [github] 68 | [github.token] 69 | token = "GH_TOKEN" 70 | 71 | [github.plugins] 72 | logger = { ref = "v3.2.0", owner = "roadrunner-server", repository = "logger" } 73 | esi = { ref = "master", owner = "darkweak", repository = "go-esi", folder = "middleware/roadrunner", replace = "/opt/middleware/roadrunner" } 74 | server = { ref = "v3.2.0", owner = "roadrunner-server", repository = "server" } 75 | gzip = { ref = "v3.2.0", owner = "roadrunner-server", repository = "gzip" } 76 | http = { ref = "v3.2.0", owner = "roadrunner-server", repository = "http" } 77 | 78 | [log] 79 | level = "debug" 80 | mode = "development" 81 | ``` 82 | 83 | After that, you'll be able to set enable and add the esi processor to the middleware chain. 84 | ```yaml 85 | # .rr.yaml 86 | http: 87 | # Other http sub keys 88 | esi: {} 89 | middleware: 90 | - headers 91 | - gzip 92 | - esi 93 | ``` 94 | 95 | ### Træfik middleware 96 | ```yaml 97 | # anywhere/traefik.yml 98 | experimental: 99 | plugins: 100 | souin: 101 | moduleName: github.com/darkweak/go-esi 102 | version: v0.0.6 103 | ``` 104 | ```yaml 105 | # anywhere/dynamic-configuration 106 | http: 107 | routers: 108 | whoami: 109 | middlewares: 110 | - esi 111 | service: whoami 112 | rule: Host(`domain.com`) 113 | middlewares: 114 | esi: 115 | plugin: 116 | esi: {} 117 | ``` 118 | 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. 119 | 120 | ## TODO 121 | - [x] choose tag 122 | - [x] comment tag 123 | - [x] escape tag 124 | - [x] include tag 125 | - [x] remove tag 126 | - [x] otherwise tag 127 | - [ ] try tag 128 | - [x] vars tag 129 | - [x] when tag -------------------------------------------------------------------------------- /docs/esi_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkweak/go-esi/dfdfaa8466f9c7cc840040aeb753ee0887c0d431/docs/esi_1.jpg -------------------------------------------------------------------------------- /docs/esi_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkweak/go-esi/dfdfaa8466f9c7cc840040aeb753ee0887c0d431/docs/esi_2.jpg -------------------------------------------------------------------------------- /esi.go: -------------------------------------------------------------------------------- 1 | package go_esi 2 | -------------------------------------------------------------------------------- /esi/choose.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const choose = "choose" 9 | 10 | var ( 11 | closeChoose = regexp.MustCompile("") 12 | whenRg = regexp.MustCompile(`(?s)(.+?)`) 13 | otherwiseRg = regexp.MustCompile(`(?s)(.+?)`) 14 | ) 15 | 16 | type chooseTag struct { 17 | *baseTag 18 | } 19 | 20 | // Input (e.g. 21 | // 22 | // 23 | // 24 | // 25 | // 26 | // 27 | // 28 | // 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // ). 35 | func (c *chooseTag) Process(b []byte, req *http.Request) ([]byte, int) { 36 | found := closeChoose.FindIndex(b) 37 | if found == nil { 38 | return nil, len(b) 39 | } 40 | 41 | c.length = found[1] 42 | tagIdxs := whenRg.FindAllSubmatch(b, -1) 43 | 44 | var res []byte 45 | 46 | for _, v := range tagIdxs { 47 | if validateTest(v[1], req) { 48 | res = Parse(v[2], req) 49 | 50 | return res, c.length 51 | } 52 | } 53 | 54 | tagIdx := otherwiseRg.FindSubmatch(b) 55 | if tagIdx != nil { 56 | res = Parse(tagIdx[1], req) 57 | } 58 | 59 | return res, c.length 60 | } 61 | 62 | func (*chooseTag) HasClose(b []byte) bool { 63 | return closeChoose.FindIndex(b) != nil 64 | } 65 | 66 | func (*chooseTag) GetClosePosition(b []byte) int { 67 | if idx := closeChoose.FindIndex(b); idx != nil { 68 | return idx[1] 69 | } 70 | 71 | return 0 72 | } 73 | -------------------------------------------------------------------------------- /esi/comment.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const comment = "comment" 9 | 10 | var closeComment = regexp.MustCompile("/>((\n| +)+)?") 11 | 12 | type commentTag struct { 13 | *baseTag 14 | } 15 | 16 | // Input (e.g. comment text="This is a comment." />). 17 | func (c *commentTag) Process(b []byte, req *http.Request) ([]byte, int) { 18 | found := closeComment.FindIndex(b) 19 | if found == nil { 20 | return nil, len(b) 21 | } 22 | 23 | return []byte{}, found[1] 24 | } 25 | 26 | func (*commentTag) HasClose(b []byte) bool { 27 | return closeComment.FindIndex(b) != nil 28 | } 29 | 30 | func (*commentTag) GetClosePosition(b []byte) int { 31 | if idx := closeComment.FindIndex(b); idx != nil { 32 | return idx[1] 33 | } 34 | 35 | return 0 36 | } 37 | -------------------------------------------------------------------------------- /esi/errors.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import "errors" 4 | 5 | var errNotFound = errors.New("not found") 6 | -------------------------------------------------------------------------------- /esi/escape.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const escape = "") 13 | startEscape = regexp.MustCompile("((\n| +)+)?") 14 | ) 15 | 16 | type escapeTag struct { 17 | *baseTag 18 | } 19 | 20 | func (e *escapeTag) Process(b []byte, req *http.Request) ([]byte, int) { 21 | closeIdx := closeEscape.FindIndex(b) 22 | 23 | if closeIdx == nil { 24 | return nil, len(b) 25 | } 26 | 27 | startPosition := 0 28 | if startIdx := startEscape.FindIndex(b); startIdx != nil { 29 | startPosition = startIdx[1] 30 | } 31 | 32 | e.length = closeIdx[1] 33 | b = b[startPosition:closeIdx[0]] 34 | 35 | return b, e.length 36 | } 37 | 38 | func (*escapeTag) HasClose(b []byte) bool { 39 | return closeEscape.FindIndex(b) != nil 40 | } 41 | 42 | func (*escapeTag) GetClosePosition(b []byte) int { 43 | if idx := closeEscape.FindIndex(b); idx != nil { 44 | return idx[1] 45 | } 46 | 47 | return 0 48 | } 49 | -------------------------------------------------------------------------------- /esi/esi.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func findTagName(b []byte) Tag { 8 | name := tagname.FindSubmatch(b) 9 | if name == nil { 10 | return nil 11 | } 12 | 13 | switch string(name[1]) { 14 | case comment: 15 | return &commentTag{ 16 | baseTag: newBaseTag(), 17 | } 18 | case choose: 19 | return &chooseTag{ 20 | baseTag: newBaseTag(), 21 | } 22 | case escape: 23 | return &escapeTag{ 24 | baseTag: newBaseTag(), 25 | } 26 | case include: 27 | return &includeTag{ 28 | baseTag: newBaseTag(), 29 | } 30 | case remove: 31 | return &removeTag{ 32 | baseTag: newBaseTag(), 33 | } 34 | case try: 35 | case vars: 36 | return &varsTag{ 37 | baseTag: newBaseTag(), 38 | } 39 | default: 40 | return nil 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func HasOpenedTags(b []byte) bool { 47 | return esi.FindIndex(b) != nil || escapeRg.FindIndex(b) != nil 48 | } 49 | 50 | func CanProcess(b []byte) bool { 51 | if tag := findTagName(b); tag != nil { 52 | return tag.HasClose(b) 53 | } 54 | 55 | return false 56 | } 57 | 58 | func ReadToTag(next []byte, pointer int) (startTagPosition, esiPointer int, t Tag) { 59 | var isEscapeTag bool 60 | 61 | tagIdx := esi.FindIndex(next) 62 | 63 | if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) { 64 | tagIdx = escIdx 65 | tagIdx[1] = escIdx[0] 66 | isEscapeTag = true 67 | } 68 | 69 | if tagIdx == nil { 70 | return len(next), 0, nil 71 | } 72 | 73 | esiPointer = tagIdx[1] 74 | startTagPosition = tagIdx[0] 75 | t = findTagName(next[esiPointer:]) 76 | 77 | if isEscapeTag { 78 | esiPointer += 7 79 | } 80 | 81 | return 82 | } 83 | 84 | func Parse(b []byte, req *http.Request) []byte { 85 | pointer := 0 86 | 87 | for pointer < len(b) { 88 | var escapeTag bool 89 | 90 | next := b[pointer:] 91 | tagIdx := esi.FindIndex(next) 92 | 93 | if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) { 94 | tagIdx = escIdx 95 | tagIdx[1] = escIdx[0] 96 | escapeTag = true 97 | } 98 | 99 | if tagIdx == nil { 100 | break 101 | } 102 | 103 | esiPointer := tagIdx[1] 104 | t := findTagName(next[esiPointer:]) 105 | 106 | if escapeTag { 107 | esiPointer += 7 108 | } 109 | 110 | res, p := t.Process(next[esiPointer:], req) 111 | esiPointer += p 112 | 113 | b = append(b[:pointer], append(next[:tagIdx[0]], append(res, next[esiPointer:]...)...)...) 114 | pointer += len(res) + tagIdx[0] 115 | } 116 | 117 | return b 118 | } 119 | -------------------------------------------------------------------------------- /esi/esi_test.go: -------------------------------------------------------------------------------- 1 | package esi_test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "os" 7 | "testing" 8 | 9 | "github.com/darkweak/go-esi/esi" 10 | ) 11 | 12 | func loadFromFixtures(name string) []byte { 13 | b, e := os.ReadFile("../fixtures/" + name) 14 | if e != nil { 15 | panic("The file " + name + " doesn't exist.") 16 | } 17 | 18 | return b 19 | } 20 | 21 | func getRequest() *http.Request { 22 | return httptest.NewRequest(http.MethodGet, "http://domain.com:9080", nil) 23 | } 24 | 25 | var expected = map[string]string{ 26 | "include": `

CHAINED 2

`, 27 | "comment": `

CHAINED 2

`, 28 | "choose": ` 29 |
30 |

CHAINED 2

31 |
32 | `, 33 | "full.html": ` 34 | 35 | Hello from domain.com:9080 36 | 37 | 38 | 39 | 40 |

CHAINED 2

41 |

ALTERNATE ESI INCLUDE

42 | 43 |
44 |

ESI INCLUDE

45 |
46 | 47 | 48 | 49 | `, 50 | "escape": `

Hello, $(HTTP_COOKIE{name})!

`, 51 | "remove": `

CHAINED 2

52 | 53 |

CHAINED 2

54 |

CHAINED 2

`, 55 | "vars": ` 56 | 57 | 58 | 59 | `, 60 | } 61 | 62 | func verify(t *testing.T, fixture string) { 63 | t.Helper() 64 | 65 | if result := string(esi.Parse(loadFromFixtures(fixture), getRequest())); result != expected[fixture] { 66 | t.Errorf("ESI parsing mismatch from `%s` expected\nExpected:\n%+v\nGiven:\n%+v\n", fixture, expected[fixture], result) 67 | } 68 | } 69 | 70 | func Test_Parse_includeMock(t *testing.T) { 71 | t.Parallel() 72 | verify(t, "include") 73 | } 74 | 75 | func Test_Parse_commentMock(t *testing.T) { 76 | t.Parallel() 77 | verify(t, "comment") 78 | } 79 | 80 | func Test_Parse_chooseMock(t *testing.T) { 81 | t.Parallel() 82 | verify(t, "choose") 83 | } 84 | 85 | func Test_Parse_escapeMock(t *testing.T) { 86 | t.Parallel() 87 | verify(t, "escape") 88 | } 89 | 90 | func Test_Parse_removeMock(t *testing.T) { 91 | t.Parallel() 92 | verify(t, "remove") 93 | } 94 | 95 | func Test_Parse_varsMock(t *testing.T) { 96 | t.Parallel() 97 | 98 | req := httptest.NewRequest(http.MethodGet, "http://domain.com:9080", nil) 99 | req.AddCookie(&http.Cookie{ 100 | Name: "type", 101 | Value: "my_value", 102 | }) 103 | req.Header.Add("Accept-Language", "en") 104 | 105 | if result := string(esi.Parse(loadFromFixtures("vars"), req)); result != expected["vars"] { 106 | t.Errorf("ESI parsing mismatch from `%s` expected\nExpected:\n%+v\nGiven:\n%+v\n", "vars", expected["vars"], result) 107 | } 108 | } 109 | 110 | func Test_Parse_fullMock(t *testing.T) { 111 | t.Parallel() 112 | verify(t, "full.html") 113 | } 114 | 115 | // Benchmarks. 116 | func BenchmarkInclude(b *testing.B) { 117 | for i := 0; i < b.N; i++ { 118 | esi.Parse( 119 | []byte( 120 | ``, 121 | ), 122 | httptest.NewRequest(http.MethodGet, "http://domain.com:9080", nil), 123 | ) 124 | } 125 | } 126 | 127 | var remove = ` 128 | 129 | www.example.com 130 | 131 | 132 | ` 133 | 134 | func BenchmarkRemove(b *testing.B) { 135 | for i := 0; i < b.N; i++ { 136 | esi.Parse( 137 | []byte(remove), 138 | httptest.NewRequest(http.MethodGet, "http://domain.com:9080", nil), 139 | ) 140 | } 141 | } 142 | 143 | const full = ` 144 | 145 | <esi:vars>Hello from $(HTTP_HOST)</esi:vars> 146 | 147 | 148 | 149 | 150 | 151 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
165 | 166 |
167 |
168 |
169 | 170 | 171 | ` 172 | 173 | func BenchmarkFull(b *testing.B) { 174 | for i := 0; i < b.N; i++ { 175 | esi.Parse( 176 | []byte(full), 177 | httptest.NewRequest(http.MethodGet, "http://domain.com:9080", nil), 178 | ) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /esi/include.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | "regexp" 10 | ) 11 | 12 | const include = "include" 13 | 14 | var ( 15 | closeInclude = regexp.MustCompile("/>") 16 | srcAttribute = regexp.MustCompile(`src="?(.+?)"?( |/>)`) 17 | altAttribute = regexp.MustCompile(`alt="?(.+?)"?( |/>)`) 18 | onErrorAttribute = regexp.MustCompile(`onerror="?(.+?)"?( |/>)`) 19 | ) 20 | 21 | // safe to pass to any origin. 22 | var headersSafe = []string{ 23 | "Accept", 24 | "Accept-Language", 25 | } 26 | 27 | // safe to pass only to same-origin (same scheme, same host, same port). 28 | var headersUnsafe = []string{ 29 | "Cookie", 30 | "Authorization", 31 | } 32 | 33 | type includeTag struct { 34 | *baseTag 35 | silent bool 36 | alt string 37 | src string 38 | } 39 | 40 | func (i *includeTag) loadAttributes(b []byte) error { 41 | src := srcAttribute.FindSubmatch(b) 42 | if src == nil { 43 | return errNotFound 44 | } 45 | 46 | i.src = string(src[1]) 47 | 48 | alt := altAttribute.FindSubmatch(b) 49 | if alt != nil { 50 | i.alt = string(alt[1]) 51 | } 52 | 53 | onError := onErrorAttribute.FindSubmatch(b) 54 | if onError != nil { 55 | i.silent = string(onError[1]) == "continue" 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func sanitizeURL(u string, reqURL *url.URL) string { 62 | parsed, _ := url.Parse(u) 63 | 64 | return reqURL.ResolveReference(parsed).String() 65 | } 66 | 67 | func addHeaders(headers []string, req *http.Request, rq *http.Request) { 68 | for _, h := range headers { 69 | v := req.Header.Get(h) 70 | if v != "" { 71 | rq.Header.Add(h, v) 72 | } 73 | } 74 | } 75 | 76 | // Input (e.g. include src="https://domain.com/esi-include" alt="https://domain.com/alt-esi-include" />) 77 | // With or without the alt 78 | // With or without a space separator before the closing 79 | // With or without the quotes around the src/alt value. 80 | func (i *includeTag) Process(b []byte, req *http.Request) ([]byte, int) { 81 | closeIdx := closeInclude.FindIndex(b) 82 | 83 | if closeIdx == nil { 84 | return nil, len(b) 85 | } 86 | 87 | i.length = closeIdx[1] 88 | if e := i.loadAttributes(b[8:i.length]); e != nil { 89 | return nil, len(b) 90 | } 91 | 92 | rq, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, sanitizeURL(i.src, req.URL), nil) 93 | addHeaders(headersSafe, req, rq) 94 | 95 | if rq.URL.Scheme == req.URL.Scheme && rq.URL.Host == req.URL.Host { 96 | addHeaders(headersUnsafe, req, rq) 97 | } 98 | 99 | client := &http.Client{} 100 | response, err := client.Do(rq) 101 | req = rq 102 | 103 | if (err != nil || response.StatusCode >= 400) && i.alt != "" { 104 | rq, _ = http.NewRequestWithContext(context.Background(), http.MethodGet, sanitizeURL(i.alt, req.URL), nil) 105 | addHeaders(headersSafe, req, rq) 106 | 107 | if rq.URL.Scheme == req.URL.Scheme && rq.URL.Host == req.URL.Host { 108 | addHeaders(headersUnsafe, req, rq) 109 | } 110 | 111 | response, err = client.Do(rq) 112 | req = rq 113 | 114 | if !i.silent && (err != nil || response.StatusCode >= 400) { 115 | return nil, len(b) 116 | } 117 | } 118 | 119 | if response == nil { 120 | return nil, i.length 121 | } 122 | 123 | var buf bytes.Buffer 124 | 125 | defer response.Body.Close() 126 | _, _ = io.Copy(&buf, response.Body) 127 | 128 | b = Parse(buf.Bytes(), req) 129 | 130 | return b, i.length 131 | } 132 | 133 | func (*includeTag) HasClose(b []byte) bool { 134 | return closeInclude.FindIndex(b) != nil 135 | } 136 | 137 | func (*includeTag) GetClosePosition(b []byte) int { 138 | if idx := closeInclude.FindIndex(b); idx != nil { 139 | return idx[1] 140 | } 141 | 142 | return 0 143 | } 144 | -------------------------------------------------------------------------------- /esi/remove.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const remove = "remove" 9 | 10 | var closeRemove = regexp.MustCompile("") 11 | 12 | type removeTag struct { 13 | *baseTag 14 | } 15 | 16 | func (r *removeTag) Process(b []byte, req *http.Request) ([]byte, int) { 17 | closeIdx := closeRemove.FindIndex(b) 18 | if closeIdx == nil { 19 | return []byte{}, len(b) 20 | } 21 | 22 | r.length = closeIdx[1] 23 | 24 | return []byte{}, r.length 25 | } 26 | 27 | func (*removeTag) HasClose(b []byte) bool { 28 | return closeRemove.FindIndex(b) != nil 29 | } 30 | 31 | func (*removeTag) GetClosePosition(b []byte) int { 32 | if idx := closeRemove.FindIndex(b); idx != nil { 33 | return idx[1] 34 | } 35 | 36 | return 0 37 | } 38 | -------------------------------------------------------------------------------- /esi/tags.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import "regexp" 4 | 5 | const try = "try" 6 | 7 | var ( 8 | esi = regexp.MustCompile("") 26 | ) 27 | 28 | func parseVariables(b []byte, req *http.Request) string { 29 | interprets := interpretedVar.FindSubmatch(b) 30 | 31 | if interprets != nil { 32 | switch string(interprets[1]) { 33 | case httpAcceptLanguage: 34 | if strings.Contains(req.Header.Get("Accept-Language"), string(interprets[3])) { 35 | return "true" 36 | } else { 37 | return "false" 38 | } 39 | case httpCookie: 40 | if c, e := req.Cookie(string(interprets[3])); e == nil && c.Value != "" { 41 | return c.Value 42 | } 43 | case httpHost: 44 | return req.Host 45 | case httpReferrer: 46 | return req.Referer() 47 | case httpUserAgent: 48 | return req.UserAgent() 49 | case httpQueryString: 50 | if q := req.URL.Query().Get(string(interprets[3])); q != "" { 51 | return q 52 | } 53 | } 54 | 55 | if len(interprets) > 3 { 56 | defaultValues := defaultExtractor.FindSubmatch(interprets[4]) 57 | 58 | if len(defaultValues) > 2 { 59 | return string(defaultValues[2]) 60 | } 61 | 62 | return "" 63 | } 64 | } else { 65 | strs := stringExtractor.FindSubmatch(b) 66 | 67 | if len(strs) > 2 { 68 | return string(strs[2]) 69 | } 70 | } 71 | 72 | return string(b) 73 | } 74 | 75 | type varsTag struct { 76 | *baseTag 77 | } 78 | 79 | // Input (e.g. comment text="This is a comment." />). 80 | func (c *varsTag) Process(b []byte, req *http.Request) ([]byte, int) { 81 | found := closeVars.FindIndex(b) 82 | if found == nil { 83 | return nil, len(b) 84 | } 85 | 86 | c.length = found[1] 87 | 88 | return interpretedVar.ReplaceAllFunc(b[5:found[0]], func(b []byte) []byte { 89 | return []byte(parseVariables(b, req)) 90 | }), c.length 91 | } 92 | 93 | func (*varsTag) HasClose(b []byte) bool { 94 | return closeVars.FindIndex(b) != nil 95 | } 96 | 97 | func (*varsTag) GetClosePosition(b []byte) int { 98 | if idx := closeVars.FindIndex(b); idx != nil { 99 | return idx[1] 100 | } 101 | 102 | return 0 103 | } 104 | -------------------------------------------------------------------------------- /esi/vars_test.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | func Test_parseVars(t *testing.T) { 10 | t.Parallel() 11 | parseVariables(logicalAndTest, httptest.NewRequest(http.MethodGet, "http://domain.com", nil)) 12 | } 13 | -------------------------------------------------------------------------------- /esi/when.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | unaryNegation = regexp.MustCompile(`!\((\$\((.+)\)|(.+))\)`) 11 | comparison = regexp.MustCompile(`(.+)(==|!=|<=|>=|<|>)(.+)`) 12 | logicalAnd = regexp.MustCompile(`\((.+?)\)&\((.+?)\)`) 13 | logicalOr = regexp.MustCompile(`\((.+?)\)\|\((.+?)\)`) 14 | ) 15 | 16 | func validateTest(b []byte, req *http.Request) bool { 17 | if r := unaryNegation.FindSubmatch(b); r != nil { 18 | return !validateTest(r[1], req) 19 | } else if r := logicalAnd.FindSubmatch(b); r != nil { 20 | return validateTest(r[1], req) && validateTest(r[2], req) 21 | } else if r := logicalOr.FindSubmatch(b); r != nil { 22 | return validateTest(r[1], req) || validateTest(r[2], req) 23 | } else if r := comparison.FindSubmatch(b); r != nil { 24 | r1 := strings.TrimSpace(parseVariables(r[1], req)) 25 | r2 := strings.TrimSpace(parseVariables(r[3], req)) 26 | switch string(r[2]) { 27 | case "==": 28 | return r1 == r2 29 | case "!=": 30 | return r1 != r2 31 | case "<": 32 | return r1 < r2 33 | case ">": 34 | return r1 > r2 35 | case "<=": 36 | return r1 <= r2 37 | case ">=": 38 | return r1 >= r2 39 | } 40 | } else { 41 | vars := interpretedVar.FindSubmatch(b) 42 | if vars == nil { 43 | return false 44 | } 45 | 46 | return parseVariables(vars[0], req) == "true" 47 | } 48 | 49 | return false 50 | } 51 | -------------------------------------------------------------------------------- /esi/when_test.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | unaryNegationTest = []byte("!(1==1)") 11 | comparisonTest = []byte("!('a'<='c')") 12 | logicalOrTest = []byte("(1==1)|('abc'=='def')") 13 | logicalAndTest = []byte("(4!=5)&(4==5)") 14 | complexTest = []byte("$(HTTP_ACCEPT_LANGUAGE{en-gb})") 15 | ) 16 | 17 | func Test_validateTest(t *testing.T) { 18 | t.Parallel() 19 | 20 | if validateTest(unaryNegationTest, httptest.NewRequest(http.MethodGet, "http://domain.com", nil)) { 21 | t.Error("The unaryNegationTest must return false because we return the opposite of true (1==1)") 22 | } 23 | 24 | if validateTest(comparisonTest, httptest.NewRequest(http.MethodGet, "http://domain.com", nil)) { 25 | t.Error("The comparisonTest must return false because we return the opposite of true (a < c)") 26 | } 27 | 28 | if !validateTest(logicalOrTest, httptest.NewRequest(http.MethodGet, "http://domain.com", nil)) { 29 | t.Error("The logicalOrTest must return true because we return true or false (1==1)|('abc'=='def')") 30 | } 31 | 32 | if validateTest(logicalAndTest, httptest.NewRequest(http.MethodGet, "http://domain.com", nil)) { 33 | t.Error("The logicalAndTest must return false because we return true and false (4!=5)&(4==5)") 34 | } 35 | 36 | rq := httptest.NewRequest(http.MethodGet, "http://domain.com", nil) 37 | rq.Header.Add("Accept-Language", "en-gb") 38 | rq.Header.Add("Accept-Language", "fr-fr") 39 | 40 | if !validateTest(complexTest, rq) { 41 | t.Error("The complexTest must return true") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /fixtures/alt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fixtures/choose: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 |
13 |
-------------------------------------------------------------------------------- /fixtures/comment: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fixtures/escape: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fixtures/full.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | <esi:vars>Hello from $(HTTP_HOST)</esi:vars> 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /fixtures/include: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fixtures/remove: -------------------------------------------------------------------------------- 1 | 2 | 3 | www.example.com 4 | 5 | 6 | -------------------------------------------------------------------------------- /fixtures/try: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | www.example.com 9 | 10 | -------------------------------------------------------------------------------- /fixtures/vars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/darkweak/go-esi 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /middleware/caddy/.gitignore: -------------------------------------------------------------------------------- 1 | caddy -------------------------------------------------------------------------------- /middleware/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | debug 3 | order esi before basicauth 4 | esi 5 | http_port 9080 6 | } 7 | 8 | :9080 { 9 | route /chained-esi-include-1 { 10 | header Content-Type text/html 11 | respond `` 12 | } 13 | 14 | route /chained-esi-include-2 { 15 | header Content-Type text/html 16 | respond "

CHAINED 2

" 17 | } 18 | 19 | route /esi-include { 20 | header Content-Type text/html 21 | respond "

ESI INCLUDE

" 22 | } 23 | 24 | route /alt-esi-include { 25 | header Content-Type text/html 26 | respond "

ALTERNATE ESI INCLUDE

" 27 | } 28 | 29 | route /* { 30 | esi 31 | root * ../../fixtures 32 | file_server 33 | } 34 | } -------------------------------------------------------------------------------- /middleware/caddy/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build run 2 | 3 | build: ## Build caddy binary 4 | go mod tidy 5 | go mod download 6 | xcaddy build --with github.com/darkweak/go-esi@latest=../.. --with github.com/darkweak/go-esi/middleware/caddy=./ 7 | 8 | run: ## Run caddy with go-esi 9 | ./caddy run -------------------------------------------------------------------------------- /middleware/caddy/esi.go: -------------------------------------------------------------------------------- 1 | package caddy_esi 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "sync" 7 | 8 | "github.com/caddyserver/caddy/v2" 9 | "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" 10 | "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" 11 | "github.com/caddyserver/caddy/v2/modules/caddyhttp" 12 | "github.com/darkweak/go-esi/writer" 13 | ) 14 | 15 | var bufPool *sync.Pool = &sync.Pool{ 16 | New: func() any { 17 | return &bytes.Buffer{} 18 | }, 19 | } 20 | 21 | func init() { 22 | caddy.RegisterModule(ESI{}) 23 | httpcaddyfile.RegisterGlobalOption("esi", func(h *caddyfile.Dispenser, _ interface{}) (interface{}, error) { 24 | return &ESI{}, nil 25 | }) 26 | httpcaddyfile.RegisterHandlerDirective("esi", func(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { 27 | return &ESI{}, nil 28 | }) 29 | } 30 | 31 | // ESI to handle, process and serve ESI tags. 32 | type ESI struct{} 33 | 34 | // CaddyModule returns the Caddy module information. 35 | func (ESI) CaddyModule() caddy.ModuleInfo { 36 | return caddy.ModuleInfo{ 37 | ID: "http.handlers.esi", 38 | New: func() caddy.Module { return new(ESI) }, 39 | } 40 | } 41 | 42 | // ServeHTTP implements caddyhttp.MiddlewareHandler 43 | func (e *ESI) ServeHTTP(rw http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { 44 | buf := bufPool.Get().(*bytes.Buffer) 45 | buf.Reset() 46 | defer bufPool.Put(buf) 47 | cw := writer.NewWriter(buf, rw, r) 48 | go func(w *writer.Writer) { 49 | var i = 0 50 | for { 51 | if len(w.AsyncBuf) <= i { 52 | continue 53 | } 54 | rs := <-w.AsyncBuf[i] 55 | if rs == nil { 56 | w.Done <- true 57 | break 58 | } 59 | _, _ = rw.Write(rs) 60 | i++ 61 | } 62 | }(cw) 63 | next.ServeHTTP(cw, r) 64 | cw.Header().Del("Content-Length") 65 | if cw.Rq.ProtoMajor == 1 { 66 | cw.Header().Set("Content-Encoding", "chunked") 67 | } 68 | cw.AsyncBuf = append(cw.AsyncBuf, make(chan []byte)) 69 | go func(w *writer.Writer, iteration int) { 70 | w.AsyncBuf[iteration] <- nil 71 | }(cw, cw.Iteration) 72 | 73 | <-cw.Done 74 | 75 | return nil 76 | } 77 | 78 | // Provision implements caddy.Provisioner 79 | func (*ESI) Provision(caddy.Context) error { 80 | return nil 81 | } 82 | 83 | func (s ESI) Start() error { return nil } 84 | 85 | func (s ESI) Stop() error { return nil } 86 | 87 | // Interface guards 88 | var ( 89 | _ caddyhttp.MiddlewareHandler = (*ESI)(nil) 90 | _ caddy.Module = (*ESI)(nil) 91 | _ caddy.Provisioner = (*ESI)(nil) 92 | _ caddy.App = (*ESI)(nil) 93 | ) 94 | -------------------------------------------------------------------------------- /middleware/caddy/esi_test.go: -------------------------------------------------------------------------------- 1 | package caddy_esi 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/caddyserver/caddy/v2/caddytest" 8 | ) 9 | 10 | const expectedOutput = ` 11 | 12 | Hello from domain.com:9080 13 | 14 | 15 | 16 | 17 |

CHAINED 2

18 |

ALTERNATE ESI INCLUDE

19 | 20 |
21 |

ESI INCLUDE

22 |
23 | 24 | 25 | 26 | ` 27 | 28 | func loadConfiguration(t *testing.T) *caddytest.Tester { 29 | tester := caddytest.NewTester(t) 30 | tester.InitServer(` 31 | { 32 | admin localhost:2999 33 | order esi before basicauth 34 | esi 35 | http_port 9080 36 | } 37 | domain.com:9080 { 38 | route /chained-esi-include-1 { 39 | header Content-Type text/html 40 | respond `+"``"+` 41 | } 42 | 43 | route /chained-esi-include-2 { 44 | header Content-Type text/html 45 | respond "

CHAINED 2

" 46 | } 47 | 48 | route /esi-include { 49 | header Content-Type text/html 50 | respond "

ESI INCLUDE

" 51 | } 52 | 53 | route /alt-esi-include { 54 | header Content-Type text/html 55 | respond "

ALTERNATE ESI INCLUDE

" 56 | } 57 | 58 | route /* { 59 | esi 60 | root * ../../fixtures 61 | file_server 62 | } 63 | }`, "caddyfile") 64 | 65 | return tester 66 | } 67 | 68 | func TestFullHTML(t *testing.T) { 69 | tester := loadConfiguration(t) 70 | _, _ = tester.AssertGetResponse(`http://domain.com:9080/full.html`, http.StatusOK, expectedOutput) 71 | } 72 | 73 | func TestUnitary(t *testing.T) { 74 | tester := loadConfiguration(t) 75 | 76 | _, _ = tester.AssertGetResponse(`http://domain.com:9080/escape`, http.StatusOK, `

Hello, $(HTTP_COOKIE{name})!

`) 77 | _, _ = tester.AssertGetResponse(`http://domain.com:9080/include`, http.StatusOK, "

CHAINED 2

") 78 | _, _ = tester.AssertGetResponse(`http://domain.com:9080/alt`, http.StatusOK, "

ALTERNATE ESI INCLUDE

") 79 | } 80 | -------------------------------------------------------------------------------- /middleware/caddy/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/darkweak/go-esi/middleware/caddy 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/caddyserver/caddy/v2 v2.6.4 7 | github.com/darkweak/go-esi v0.0.6 8 | ) 9 | 10 | require ( 11 | filippo.io/edwards25519 v1.0.0 // indirect 12 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect 13 | github.com/BurntSushi/toml v1.2.1 // indirect 14 | github.com/Masterminds/goutils v1.1.1 // indirect 15 | github.com/Masterminds/semver/v3 v3.2.0 // indirect 16 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect 17 | github.com/Microsoft/go-winio v0.6.0 // indirect 18 | github.com/alecthomas/chroma/v2 v2.5.0 // indirect 19 | github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect 20 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect 21 | github.com/beorn7/perks v1.0.1 // indirect 22 | github.com/caddyserver/certmagic v0.17.2 // indirect 23 | github.com/cenkalti/backoff/v4 v4.1.2 // indirect 24 | github.com/cespare/xxhash v1.1.0 // indirect 25 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 26 | github.com/chzyer/readline v1.5.1 // indirect 27 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 28 | github.com/dgraph-io/badger v1.6.2 // indirect 29 | github.com/dgraph-io/badger/v2 v2.2007.4 // indirect 30 | github.com/dgraph-io/ristretto v0.1.1 // indirect 31 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect 32 | github.com/dlclark/regexp2 v1.7.0 // indirect 33 | github.com/dustin/go-humanize v1.0.1 // indirect 34 | github.com/felixge/httpsnoop v1.0.3 // indirect 35 | github.com/fxamacker/cbor/v2 v2.4.0 // indirect 36 | github.com/go-chi/chi v4.1.2+incompatible // indirect 37 | github.com/go-kit/kit v0.12.0 // indirect 38 | github.com/go-kit/log v0.2.1 // indirect 39 | github.com/go-logfmt/logfmt v0.6.0 // indirect 40 | github.com/go-logr/logr v1.2.3 // indirect 41 | github.com/go-logr/stdr v1.2.2 // indirect 42 | github.com/go-sql-driver/mysql v1.7.0 // indirect 43 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect 44 | github.com/golang/glog v1.1.1 // indirect 45 | github.com/golang/mock v1.6.0 // indirect 46 | github.com/golang/protobuf v1.5.3 // indirect 47 | github.com/golang/snappy v0.0.4 // indirect 48 | github.com/google/cel-go v0.13.0 // indirect 49 | github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect 50 | github.com/google/uuid v1.3.0 // indirect 51 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect 52 | github.com/huandu/xstrings v1.4.0 // indirect 53 | github.com/imdario/mergo v0.3.14 // indirect 54 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 55 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 56 | github.com/jackc/pgconn v1.14.0 // indirect 57 | github.com/jackc/pgio v1.0.0 // indirect 58 | github.com/jackc/pgpassfile v1.0.0 // indirect 59 | github.com/jackc/pgproto3/v2 v2.3.2 // indirect 60 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 61 | github.com/jackc/pgtype v1.14.0 // indirect 62 | github.com/jackc/pgx/v4 v4.18.1 // indirect 63 | github.com/klauspost/compress v1.16.3 // indirect 64 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 65 | github.com/libdns/libdns v0.2.1 // indirect 66 | github.com/manifoldco/promptui v0.9.0 // indirect 67 | github.com/mattn/go-colorable v0.1.13 // indirect 68 | github.com/mattn/go-isatty v0.0.18 // indirect 69 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 70 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 71 | github.com/mholt/acmez v1.1.0 // indirect 72 | github.com/micromdm/scep/v2 v2.1.0 // indirect 73 | github.com/miekg/dns v1.1.52 // indirect 74 | github.com/mitchellh/copystructure v1.2.0 // indirect 75 | github.com/mitchellh/go-ps v1.0.0 // indirect 76 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 77 | github.com/onsi/ginkgo/v2 v2.9.1 // indirect 78 | github.com/pkg/errors v0.9.1 // indirect 79 | github.com/prometheus/client_golang v1.14.0 // indirect 80 | github.com/prometheus/client_model v0.3.0 // indirect 81 | github.com/prometheus/common v0.42.0 // indirect 82 | github.com/prometheus/procfs v0.9.0 // indirect 83 | github.com/quic-go/qpack v0.4.0 // indirect 84 | github.com/quic-go/qtls-go1-19 v0.3.0 // indirect 85 | github.com/quic-go/qtls-go1-20 v0.2.0 // indirect 86 | github.com/quic-go/quic-go v0.33.0 // indirect 87 | github.com/rs/xid v1.4.0 // indirect 88 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 89 | github.com/shopspring/decimal v1.3.1 // indirect 90 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 91 | github.com/sirupsen/logrus v1.9.0 // indirect 92 | github.com/slackhq/nebula v1.6.1 // indirect 93 | github.com/smallstep/certificates v0.23.2 // indirect 94 | github.com/smallstep/nosql v0.6.0 // indirect 95 | github.com/smallstep/truststore v0.12.1 // indirect 96 | github.com/spf13/cast v1.5.0 // indirect 97 | github.com/spf13/cobra v1.6.1 // indirect 98 | github.com/spf13/pflag v1.0.5 // indirect 99 | github.com/stoewer/go-strcase v1.2.1 // indirect 100 | github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2 // indirect 101 | github.com/urfave/cli v1.22.12 // indirect 102 | github.com/x448/float16 v0.8.4 // indirect 103 | github.com/yuin/goldmark v1.5.4 // indirect 104 | github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 // indirect 105 | go.etcd.io/bbolt v1.3.7 // indirect 106 | go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect 107 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.39.0 // indirect 108 | go.opentelemetry.io/otel v1.13.0 // indirect 109 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0 // indirect 110 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0 // indirect 111 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0 // indirect 112 | go.opentelemetry.io/otel/metric v0.36.0 // indirect 113 | go.opentelemetry.io/otel/sdk v1.13.0 // indirect 114 | go.opentelemetry.io/otel/trace v1.13.0 // indirect 115 | go.opentelemetry.io/proto/otlp v0.12.0 // indirect 116 | go.step.sm/cli-utils v0.7.5 // indirect 117 | go.step.sm/crypto v0.27.0 // indirect 118 | go.step.sm/linkedca v0.19.0 // indirect 119 | go.uber.org/atomic v1.10.0 // indirect 120 | go.uber.org/multierr v1.10.0 // indirect 121 | go.uber.org/zap v1.24.0 // indirect 122 | golang.org/x/crypto v0.7.0 // indirect 123 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect 124 | golang.org/x/mod v0.9.0 // indirect 125 | golang.org/x/net v0.8.0 // indirect 126 | golang.org/x/sync v0.1.0 // indirect 127 | golang.org/x/sys v0.6.0 // indirect 128 | golang.org/x/term v0.6.0 // indirect 129 | golang.org/x/text v0.8.0 // indirect 130 | golang.org/x/tools v0.7.0 // indirect 131 | google.golang.org/genproto v0.0.0-20230322174352-cde4c949918d // indirect 132 | google.golang.org/grpc v1.54.0 // indirect 133 | google.golang.org/protobuf v1.30.0 // indirect 134 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 135 | gopkg.in/square/go-jose.v2 v2.6.0 // indirect 136 | gopkg.in/yaml.v3 v3.0.1 // indirect 137 | howett.net/plist v1.0.0 // indirect 138 | ) 139 | 140 | replace github.com/darkweak/go-esi v0.0.6 => ../.. 141 | -------------------------------------------------------------------------------- /middleware/caddy/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= 4 | cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= 5 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 6 | cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= 7 | cloud.google.com/go/kms v1.9.0 h1:b0votJQa/9DSsxgHwN33/tTLA7ZHVzfWhDCrfiXijSo= 8 | filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= 9 | filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= 10 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= 11 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 12 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 13 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 14 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 15 | github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 16 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 17 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 18 | github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 19 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 20 | github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= 21 | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 22 | github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= 23 | github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= 24 | github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= 25 | github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= 26 | github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= 27 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 28 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 29 | github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= 30 | github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= 31 | github.com/alecthomas/chroma/v2 v2.5.0 h1:CQCdj1BiBV17sD4Bd32b/Bzuiq/EqoNTrnIhyQAZ+Rk= 32 | github.com/alecthomas/chroma/v2 v2.5.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= 33 | github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= 34 | github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= 35 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 36 | github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= 37 | github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= 38 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 39 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw= 40 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 41 | github.com/aws/aws-sdk-go v1.44.220 h1:yAj99qAt0Htjle9Up3DglgHfOP77lmFPrElA4jKnrBo= 42 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 43 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 44 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 45 | github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= 46 | github.com/caddyserver/caddy/v2 v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8= 47 | github.com/caddyserver/caddy/v2 v2.6.4/go.mod h1:p/PqEgaIrg249zmTLdgNMnQO4mBQ8uWYdi+TDOPwejc= 48 | github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= 49 | github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= 50 | github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= 51 | github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 52 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 53 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 54 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 55 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 56 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 57 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 58 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 59 | github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= 60 | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= 61 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 62 | github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= 63 | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= 64 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 65 | github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= 66 | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= 67 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 68 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 69 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 70 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 71 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 72 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 73 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 74 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 75 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 76 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 77 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 78 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 79 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 80 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 81 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 82 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 83 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 84 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 85 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 86 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 87 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 88 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 89 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 90 | github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= 91 | github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= 92 | github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= 93 | github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= 94 | github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 95 | github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 96 | github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= 97 | github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= 98 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 99 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= 100 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 101 | github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 102 | github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= 103 | github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 104 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 105 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 106 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 107 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 108 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 109 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 110 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 111 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 112 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 113 | github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= 114 | github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 115 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 116 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 117 | github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= 118 | github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= 119 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 120 | github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= 121 | github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= 122 | github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 123 | github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= 124 | github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= 125 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 126 | github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= 127 | github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 128 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 129 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 130 | github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 131 | github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 132 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 133 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 134 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 135 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 136 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 137 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 138 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 139 | github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 140 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 141 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= 142 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 143 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 144 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 145 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 146 | github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= 147 | github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= 148 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 149 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 150 | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 151 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 152 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 153 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 154 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 155 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 156 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 157 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 158 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 159 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 160 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 161 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 162 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 163 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 164 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 165 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 166 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 167 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 168 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 169 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 170 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 171 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 172 | github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU= 173 | github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s= 174 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 175 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 176 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 177 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 178 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 179 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 180 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 181 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 182 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 183 | github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ= 184 | github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= 185 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 186 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 187 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 188 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 189 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 190 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= 191 | github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= 192 | github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 193 | github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 194 | github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= 195 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 196 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 197 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 198 | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= 199 | github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 200 | github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 201 | github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= 202 | github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 203 | github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 204 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 205 | github.com/imdario/mergo v0.3.14 h1:fOqeC1+nCuuk6PKQdg9YmosXX7Y7mHX6R/0ZldI9iHo= 206 | github.com/imdario/mergo v0.3.14/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 207 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 208 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 209 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 210 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 211 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 212 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 213 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 214 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 215 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 216 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 217 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 218 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 219 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 220 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 221 | github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= 222 | github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= 223 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 224 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 225 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 226 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 227 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 228 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 229 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 230 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 231 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 232 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 233 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 234 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 235 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 236 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 237 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 238 | github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= 239 | github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 240 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 241 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 242 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 243 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 244 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 245 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 246 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 247 | github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= 248 | github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 249 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 250 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 251 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 252 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 253 | github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= 254 | github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= 255 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 256 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 257 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 258 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 259 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 260 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 261 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 262 | github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 263 | github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= 264 | github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 265 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 266 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 267 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 268 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 269 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 270 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 271 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 272 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 273 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 274 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 275 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 276 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 277 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 278 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 279 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 280 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= 281 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 282 | github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= 283 | github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= 284 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 285 | github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= 286 | github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= 287 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 288 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 289 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 290 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 291 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 292 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 293 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 294 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 295 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 296 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 297 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 298 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= 299 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 300 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 301 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 302 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 303 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 304 | github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY= 305 | github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs= 306 | github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU= 307 | github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc= 308 | github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= 309 | github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 310 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 311 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 312 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 313 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 314 | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= 315 | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= 316 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 317 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 318 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 319 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 320 | github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= 321 | github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= 322 | github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= 323 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 324 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 325 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 326 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 327 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 328 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 329 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 330 | github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= 331 | github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 332 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 333 | github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= 334 | github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 335 | github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= 336 | github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= 337 | github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= 338 | github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= 339 | github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= 340 | github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 341 | github.com/quic-go/qtls-go1-19 v0.3.0 h1:aUBoQdpHzUWtPw5tQZbsD2GnrWCNu7/RIX1PtqGeLYY= 342 | github.com/quic-go/qtls-go1-19 v0.3.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= 343 | github.com/quic-go/qtls-go1-20 v0.2.0 h1:jUHn+obJ6WI5JudqBO0Iy1ra5Vh5vsitQ1gXQvkmN+E= 344 | github.com/quic-go/qtls-go1-20 v0.2.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= 345 | github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= 346 | github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= 347 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 348 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 349 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 350 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 351 | github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= 352 | github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 353 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 354 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 355 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 356 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 357 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 358 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 359 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 360 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 361 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 362 | github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= 363 | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 364 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 365 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 366 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 367 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 368 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 369 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 370 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 371 | github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM= 372 | github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI= 373 | github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= 374 | github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= 375 | github.com/smallstep/certificates v0.23.2 h1:7KSx9WfZ3CILV0XlsTrl+PK58YE4CHSgqobB6+ieQWs= 376 | github.com/smallstep/certificates v0.23.2/go.mod h1:YuQAlNuYrRA5z+meSH9D0XsUFH45TyAhNgyV5kYuQpQ= 377 | github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc= 378 | github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= 379 | github.com/smallstep/truststore v0.12.1 h1:guLUKkc1UlsXeS3t6BuVMa4leOOpdiv02PCRTiy1WdY= 380 | github.com/smallstep/truststore v0.12.1/go.mod h1:M4mebeNy28KusGX3lJxpLARIktLcyqBOrj3ZiZ46pqw= 381 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 382 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 383 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 384 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 385 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 386 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 387 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 388 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 389 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 390 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 391 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 392 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 393 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 394 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 395 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 396 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 397 | github.com/stoewer/go-strcase v1.2.1 h1:/1JWd+AcWPzkcGLEmjUCka99YqGOtTnp1H/wcP+uap4= 398 | github.com/stoewer/go-strcase v1.2.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= 399 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 400 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 401 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 402 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 403 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 404 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 405 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 406 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 407 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 408 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 409 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 410 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 411 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 412 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 413 | github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2 h1:TrgfmCXwtWyFw85UkRGXt9qZRzdzt3nWt2Rerdecn0w= 414 | github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= 415 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 416 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 417 | github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= 418 | github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= 419 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 420 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 421 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 422 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 423 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 424 | github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 425 | github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= 426 | github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 427 | github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 h1:Py16JEzkSdKAtEFJjiaYLYBOWGXc1r/xHj/Q/5lA37k= 428 | github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= 429 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 430 | go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= 431 | go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 432 | go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= 433 | go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= 434 | go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= 435 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 436 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.39.0 h1:vFEBG7SieZJzvnRWQ81jxpuEqe6J8Ex+hgc9CqOTzHc= 437 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.39.0/go.mod h1:9rgTcOKdIhDOC0IcAu8a+R+FChqSUBihKpM1lVNi6T0= 438 | go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk= 439 | go.opentelemetry.io/otel v1.13.0 h1:1ZAKnNQKwBBxFtww/GwxNUyTf0AxkZzrukO8MeXqe4Y= 440 | go.opentelemetry.io/otel v1.13.0/go.mod h1:FH3RtdZCzRkJYFTCsAKDy9l/XYjMdNv6QrkFFB8DvVg= 441 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0 h1:j7AwzDdAQBJjcqayAaYbvpYeZzII7cEe5qJTu+De6UY= 442 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= 443 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0 h1:lRpP10E8oTGVmY1nVXcwelCT1Z8ca41/l5ce7AqLAss= 444 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.0/go.mod h1:3oS+j2WUoJVyj6/BzQN/52G17lNJDulngsOxDm1w2PY= 445 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0 h1:buSx4AMC/0Z232slPhicN/fU5KIlj0bMngct5pcZhkI= 446 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.0/go.mod h1:ew1NcwkHo0QFT3uTm3m2IVZMkZdVIpbOYNPasgWwpdk= 447 | go.opentelemetry.io/otel/metric v0.36.0 h1:t0lgGI+L68QWt3QtOIlqM9gXoxqxWLhZ3R/e5oOAY0Q= 448 | go.opentelemetry.io/otel/metric v0.36.0/go.mod h1:wKVw57sd2HdSZAzyfOM9gTqqE8v7CbqWsYL6AyrH9qk= 449 | go.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE= 450 | go.opentelemetry.io/otel/sdk v1.13.0 h1:BHib5g8MvdqS65yo2vV1s6Le42Hm6rrw08qU6yz5JaM= 451 | go.opentelemetry.io/otel/sdk v1.13.0/go.mod h1:YLKPx5+6Vx/o1TCUYYs+bpymtkmazOMT6zoRrC7AQ7I= 452 | go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE= 453 | go.opentelemetry.io/otel/trace v1.13.0 h1:CBgRZ6ntv+Amuj1jDsMhZtlAPT6gbyIRdaIzFhfBSdY= 454 | go.opentelemetry.io/otel/trace v1.13.0/go.mod h1:muCvmmO9KKpvuXSf3KKAXXB2ygNYHQ+ZfI5X08d3tds= 455 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 456 | go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c= 457 | go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ= 458 | go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk= 459 | go.step.sm/cli-utils v0.7.5/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I= 460 | go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= 461 | go.step.sm/crypto v0.27.0 h1:MLRvcVCibCMcbcPlj9A6oOteyFqzy6lFfRAcE/ZTAqY= 462 | go.step.sm/crypto v0.27.0/go.mod h1:cee0F+IAmWe7AHIUcEBuOOCltHhcCON3kUSKaYjcn7c= 463 | go.step.sm/linkedca v0.19.0 h1:xuagkR35wrJI2gnu6FAM+q3VmjwsHScvGcJsfZ0GdsI= 464 | go.step.sm/linkedca v0.19.0/go.mod h1:b7vWPrHfYLEOTSUZitFEcztVCpTc+ileIN85CwEAluM= 465 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 466 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 467 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 468 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 469 | go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= 470 | go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 471 | go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= 472 | go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 473 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 474 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 475 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 476 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 477 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 478 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 479 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 480 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 481 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 482 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 483 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 484 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 485 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 486 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 487 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 488 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 489 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 490 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 491 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 492 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 493 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 494 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 495 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 496 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 497 | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 498 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 499 | golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= 500 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 501 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 502 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= 503 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 504 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 505 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 506 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 507 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 508 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 509 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 510 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 511 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 512 | golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= 513 | golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 514 | golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 515 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 516 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 517 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 518 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 519 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 520 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 521 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 522 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 523 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 524 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 525 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 526 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 527 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 528 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 529 | golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 530 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 531 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 532 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 533 | golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= 534 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 535 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 536 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 537 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 538 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 539 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 540 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 541 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 542 | golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 543 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 544 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 545 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 546 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 547 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 548 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 549 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 550 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 551 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 552 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 553 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 554 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 555 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 556 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 557 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 558 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 559 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 560 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 561 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 562 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 563 | golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 564 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 565 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 566 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 567 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 568 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 569 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 570 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 571 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 572 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 573 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 574 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 575 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 576 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 577 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 578 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 579 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 580 | golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= 581 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 582 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 583 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 584 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 585 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 586 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 587 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 588 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 589 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 590 | golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= 591 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 592 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 593 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 594 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 595 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 596 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 597 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 598 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 599 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 600 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 601 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 602 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 603 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 604 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 605 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 606 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 607 | golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= 608 | golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 609 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 610 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 611 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 612 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 613 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 614 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 615 | google.golang.org/api v0.112.0 h1:iDmzvZ4C086R3+en4nSyIf07HlQKMOX1Xx2dmia/+KQ= 616 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 617 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 618 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 619 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 620 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 621 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 622 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 623 | google.golang.org/genproto v0.0.0-20230322174352-cde4c949918d h1:OE8TncEeAei3Tehf/P/Jdt/K+8GnTUrRY6wzYpbCes4= 624 | google.golang.org/genproto v0.0.0-20230322174352-cde4c949918d/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= 625 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 626 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 627 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 628 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 629 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 630 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 631 | google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 632 | google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 633 | google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= 634 | google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= 635 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 636 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 637 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 638 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 639 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 640 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 641 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 642 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 643 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 644 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 645 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 646 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 647 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 648 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 649 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 650 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 651 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 652 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 653 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 654 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 655 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 656 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 657 | gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 658 | gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= 659 | gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 660 | gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= 661 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 662 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 663 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 664 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 665 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 666 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 667 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 668 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 669 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 670 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 671 | howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= 672 | howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= 673 | -------------------------------------------------------------------------------- /middleware/roadrunner/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build log run 2 | 3 | build: ## Prepare roadrunner deps 4 | go mod tidy 5 | go mod download 6 | 7 | log: ## Display container logs 8 | cd examples && docker-compose -f docker-compose.yml.test logs -f 9 | 10 | run: ## Run roadrunner with go-esi 11 | cd examples && docker-compose -f docker-compose.yml.test up --remove-orphans --build 12 | -------------------------------------------------------------------------------- /middleware/roadrunner/README.md: -------------------------------------------------------------------------------- 1 | Roadrunner middleware: Souin 2 | ================================ 3 | 4 | This is a distributed HTTP cache module for Roadrunner based on [Souin](https://github.com/darkweak/souin) cache. 5 | 6 | ## Features 7 | 8 | * [RFC 7234](https://httpwg.org/specs/rfc7234.html) compliant HTTP Cache. 9 | * Sets [the `Cache-Status` HTTP Response Header](https://httpwg.org/http-extensions/draft-ietf-httpbis-cache-header.html) 10 | * REST API to purge the cache and list stored resources. 11 | * Builtin support for distributed cache. 12 | * Tag-based invalidation. 13 | 14 | ## Build the roadrunner binary 15 | ```toml 16 | [velox] 17 | build_args = ['-trimpath', '-ldflags', '-s -X github.com/roadrunner-server/roadrunner/v2/internal/meta.version=${VERSION} -X github.com/roadrunner-server/roadrunner/v2/internal/meta.buildTime=${TIME}'] 18 | 19 | [roadrunner] 20 | ref = "master" 21 | 22 | [github] 23 | [github.token] 24 | token = "GH_TOKEN" 25 | 26 | [github.plugins] 27 | logger = { ref = "master", owner = "roadrunner-server", repository = "logger" } 28 | cache = { ref = "master", owner = "darkweak", repository = "souin", folder = "/plugins/roadrunner" } 29 | # others ... 30 | 31 | [log] 32 | level = "debug" 33 | mode = "development" 34 | ``` 35 | 36 | ## Example configuration 37 | You can set each Souin configuration key under the `http.cache` key. There is a configuration example below. 38 | ```yaml 39 | # .rr.yaml 40 | http: 41 | # Other http sub keys 42 | cache: 43 | api: 44 | basepath: /httpcache_api 45 | prometheus: 46 | basepath: /anything-for-prometheus-metrics 47 | souin: {} 48 | default_cache: 49 | allowed_http_verbs: 50 | - GET 51 | - POST 52 | - HEAD 53 | cdn: 54 | api_key: XXXX 55 | dynamic: true 56 | hostname: XXXX 57 | network: XXXX 58 | provider: fastly 59 | strategy: soft 60 | headers: 61 | - Authorization 62 | regex: 63 | exclude: '/excluded' 64 | timeout: 65 | backend: 5s 66 | cache: 1ms 67 | ttl: 5s 68 | stale: 10s 69 | log_level: debug 70 | ykeys: 71 | The_First_Test: 72 | headers: 73 | Content-Type: '.+' 74 | The_Second_Test: 75 | url: 'the/second/.+' 76 | surrogate_keys: 77 | The_First_Test: 78 | headers: 79 | Content-Type: '.+' 80 | The_Second_Test: 81 | url: 'the/second/.+' 82 | middleware: 83 | - cache 84 | # Other middlewares 85 | ``` 86 | 87 | 88 | Other resources 89 | --------------- 90 | You can find an example for a docker-compose stack inside the `examples` folder. 91 | See the [Souin](https://github.com/darkweak/souin) configuration for the full configuration, and its associated [development roadrunner middleware](https://github.com/darkweak/souin/blob/master/plugins/roadrunner) 92 | 93 | -------------------------------------------------------------------------------- /middleware/roadrunner/esi.go: -------------------------------------------------------------------------------- 1 | package roadrunner_esi 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "sync" 7 | 8 | "github.com/darkweak/go-esi/writer" 9 | "github.com/roadrunner-server/errors" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | var bufPool *sync.Pool = &sync.Pool{ 14 | New: func() any { 15 | return &bytes.Buffer{} 16 | }, 17 | } 18 | 19 | const configurationKey = "http.esi" 20 | 21 | type ( 22 | // Configurer interface used to parse yaml configuration. 23 | // Implementation will be provided by the RoadRunner automatically via Init method. 24 | Configurer interface { 25 | // Get used to get config section 26 | Get(name string) any 27 | // Has checks if config section exists. 28 | Has(name string) bool 29 | } 30 | 31 | Plugin struct{} 32 | ) 33 | 34 | // Name is the plugin name 35 | func (p *Plugin) Name() string { 36 | return "esi" 37 | } 38 | 39 | // Init allows the user to set up an efficient esi processor. 40 | func (p *Plugin) Init(cfg Configurer, log *zap.Logger) error { 41 | const op = errors.Op("esi_middleware_init") 42 | if !cfg.Has(configurationKey) { 43 | return errors.E(op, errors.Disabled) 44 | } 45 | 46 | return nil 47 | } 48 | 49 | // Middleware is the request entrypoint to catch the response and 50 | // process the esi tags if present. 51 | func (p *Plugin) Middleware(next http.Handler) http.Handler { 52 | return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 53 | buf := bufPool.Get().(*bytes.Buffer) 54 | buf.Reset() 55 | defer bufPool.Put(buf) 56 | cw := writer.NewWriter(buf, rw, r) 57 | go func(w *writer.Writer) { 58 | var i = 0 59 | for { 60 | if len(w.AsyncBuf) <= i { 61 | continue 62 | } 63 | rs := <-w.AsyncBuf[i] 64 | if rs == nil { 65 | w.Done <- true 66 | break 67 | } 68 | _, _ = rw.Write(rs) 69 | i++ 70 | } 71 | }(cw) 72 | next.ServeHTTP(cw, r) 73 | cw.Header().Del("Content-Length") 74 | if cw.Rq.ProtoMajor == 1 { 75 | cw.Header().Set("Content-Encoding", "chunked") 76 | } 77 | cw.AsyncBuf = append(cw.AsyncBuf, make(chan []byte)) 78 | go func(w *writer.Writer, iteration int) { 79 | w.AsyncBuf[iteration] <- nil 80 | }(cw, cw.Iteration) 81 | 82 | <-cw.Done 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /middleware/roadrunner/esi_test.go: -------------------------------------------------------------------------------- 1 | package roadrunner_esi 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | ) 9 | 10 | type ( 11 | configWrapper struct{} 12 | next struct{} 13 | ) 14 | 15 | var nextFilter = &next{} 16 | 17 | func (n *next) ServeHTTP(rw http.ResponseWriter, rq *http.Request) { 18 | rw.WriteHeader(http.StatusOK) 19 | if rq.RequestURI == "/esi-include-1" { 20 | _, _ = rw.Write([]byte("Awesome first included ESI tag!")) 21 | 22 | return 23 | } 24 | if rq.RequestURI == "/esi-include-2" { 25 | _, _ = rw.Write([]byte("Another included ESI tag!")) 26 | 27 | return 28 | } 29 | _, _ = rw.Write([]byte(`Hello Roadrunner! `)) 30 | } 31 | 32 | func (*configWrapper) Get(name string) any { 33 | return nil 34 | } 35 | func (*configWrapper) Has(name string) bool { 36 | return true 37 | } 38 | 39 | func Test_Plugin_Init(t *testing.T) { 40 | p := &Plugin{} 41 | 42 | if p.Init(&configWrapper{}, nil) != nil { 43 | t.Error("The Init method must not crash if a valid configuration is given.") 44 | } 45 | 46 | defer func() { 47 | if recover() == nil { 48 | t.Error("The Init method must crash if a nil configuration is given.") 49 | } 50 | }() 51 | err := p.Init(nil, nil) 52 | if err != nil { 53 | t.Error(err.Error()) 54 | } 55 | } 56 | 57 | func prepare(endpoint string) (req *http.Request, res1 *httptest.ResponseRecorder) { 58 | req = httptest.NewRequest(http.MethodGet, endpoint, nil) 59 | res1 = httptest.NewRecorder() 60 | 61 | return 62 | } 63 | 64 | func Test_Plugin_Middleware(t *testing.T) { 65 | p := &Plugin{} 66 | _ = p.Init(&configWrapper{}, nil) 67 | handler := p.Middleware(nextFilter) 68 | req, res := prepare("http://localhost/handled") 69 | handler.ServeHTTP(res, req) 70 | rs := res.Result() 71 | defer rs.Body.Close() 72 | b, err := io.ReadAll(rs.Body) 73 | if err != nil { 74 | t.Error("body read error") 75 | } 76 | 77 | if string(b) != "Hello Roadrunner! " { 78 | t.Error(`The returned response must be equal to "Hello Roadrunner! " because of non running service.`) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | rr 3 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/.rr.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | command: "php psr-worker.php" 3 | http: 4 | address: 0.0.0.0:80 5 | pool: 6 | num_workers: 4 7 | esi: {} 8 | middleware: [ "headers", "gzip", "esi" ] 9 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/roadrunner-server/velox:latest as velox 2 | 3 | ARG CURRENT_SHA 4 | ARG GH_TOKEN 5 | ENV CGO_ENABLED=0 6 | ENV VERSION=v2.12.0 7 | ENV TIME="$(date +%H:%M)" 8 | 9 | RUN apk add git 10 | 11 | COPY . /opt 12 | WORKDIR /opt/middleware/roadrunner 13 | RUN go get -u "github.com/darkweak/go-esi@${CURRENT_SHA}" 14 | WORKDIR /opt/middleware/roadrunner/examples 15 | RUN sed -i "s/GH_TOKEN/${GH_TOKEN}/" configuration.toml 16 | RUN sed -i "s/CURRENT_SHA/${CURRENT_SHA}/" configuration.toml 17 | 18 | RUN vx build -c configuration.toml -o /usr/bin/ 19 | 20 | FROM composer:latest AS development-runner 21 | COPY --from=velox /usr/bin/rr /usr/bin/rr 22 | 23 | RUN apk add linux-headers 24 | RUN docker-php-ext-install sockets 25 | RUN composer require spiral/roadrunner nyholm/psr7 26 | 27 | COPY middleware/roadrunner/examples . 28 | 29 | CMD ["/usr/bin/rr", "serve"] -------------------------------------------------------------------------------- /middleware/roadrunner/examples/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "spiral/roadrunner": "^2.10", 4 | "nyholm/psr7": "^1.5" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "0941953bf9ca779dea29d031a72ae0f8", 8 | "packages": [ 9 | { 10 | "name": "composer/semver", 11 | "version": "3.3.2", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/composer/semver.git", 15 | "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", 20 | "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^5.3.2 || ^7.0 || ^8.0" 25 | }, 26 | "require-dev": { 27 | "phpstan/phpstan": "^1.4", 28 | "symfony/phpunit-bridge": "^4.2 || ^5" 29 | }, 30 | "type": "library", 31 | "extra": { 32 | "branch-alias": { 33 | "dev-main": "3.x-dev" 34 | } 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Composer\\Semver\\": "src" 39 | } 40 | }, 41 | "notification-url": "https://packagist.org/downloads/", 42 | "license": [ 43 | "MIT" 44 | ], 45 | "authors": [ 46 | { 47 | "name": "Nils Adermann", 48 | "email": "naderman@naderman.de", 49 | "homepage": "http://www.naderman.de" 50 | }, 51 | { 52 | "name": "Jordi Boggiano", 53 | "email": "j.boggiano@seld.be", 54 | "homepage": "http://seld.be" 55 | }, 56 | { 57 | "name": "Rob Bast", 58 | "email": "rob.bast@gmail.com", 59 | "homepage": "http://robbast.nl" 60 | } 61 | ], 62 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 63 | "keywords": [ 64 | "semantic", 65 | "semver", 66 | "validation", 67 | "versioning" 68 | ], 69 | "support": { 70 | "irc": "irc://irc.freenode.org/composer", 71 | "issues": "https://github.com/composer/semver/issues", 72 | "source": "https://github.com/composer/semver/tree/3.3.2" 73 | }, 74 | "funding": [ 75 | { 76 | "url": "https://packagist.com", 77 | "type": "custom" 78 | }, 79 | { 80 | "url": "https://github.com/composer", 81 | "type": "github" 82 | }, 83 | { 84 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 85 | "type": "tidelift" 86 | } 87 | ], 88 | "time": "2022-04-01T19:23:25+00:00" 89 | }, 90 | { 91 | "name": "nyholm/psr7", 92 | "version": "1.5.1", 93 | "source": { 94 | "type": "git", 95 | "url": "https://github.com/Nyholm/psr7.git", 96 | "reference": "f734364e38a876a23be4d906a2a089e1315be18a" 97 | }, 98 | "dist": { 99 | "type": "zip", 100 | "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", 101 | "reference": "f734364e38a876a23be4d906a2a089e1315be18a", 102 | "shasum": "" 103 | }, 104 | "require": { 105 | "php": ">=7.1", 106 | "php-http/message-factory": "^1.0", 107 | "psr/http-factory": "^1.0", 108 | "psr/http-message": "^1.0" 109 | }, 110 | "provide": { 111 | "psr/http-factory-implementation": "1.0", 112 | "psr/http-message-implementation": "1.0" 113 | }, 114 | "require-dev": { 115 | "http-interop/http-factory-tests": "^0.9", 116 | "php-http/psr7-integration-tests": "^1.0", 117 | "phpunit/phpunit": "^7.5 || 8.5 || 9.4", 118 | "symfony/error-handler": "^4.4" 119 | }, 120 | "type": "library", 121 | "extra": { 122 | "branch-alias": { 123 | "dev-master": "1.4-dev" 124 | } 125 | }, 126 | "autoload": { 127 | "psr-4": { 128 | "Nyholm\\Psr7\\": "src/" 129 | } 130 | }, 131 | "notification-url": "https://packagist.org/downloads/", 132 | "license": [ 133 | "MIT" 134 | ], 135 | "authors": [ 136 | { 137 | "name": "Tobias Nyholm", 138 | "email": "tobias.nyholm@gmail.com" 139 | }, 140 | { 141 | "name": "Martijn van der Ven", 142 | "email": "martijn@vanderven.se" 143 | } 144 | ], 145 | "description": "A fast PHP7 implementation of PSR-7", 146 | "homepage": "https://tnyholm.se", 147 | "keywords": [ 148 | "psr-17", 149 | "psr-7" 150 | ], 151 | "support": { 152 | "issues": "https://github.com/Nyholm/psr7/issues", 153 | "source": "https://github.com/Nyholm/psr7/tree/1.5.1" 154 | }, 155 | "funding": [ 156 | { 157 | "url": "https://github.com/Zegnat", 158 | "type": "github" 159 | }, 160 | { 161 | "url": "https://github.com/nyholm", 162 | "type": "github" 163 | } 164 | ], 165 | "time": "2022-06-22T07:13:36+00:00" 166 | }, 167 | { 168 | "name": "php-http/message-factory", 169 | "version": "v1.0.2", 170 | "source": { 171 | "type": "git", 172 | "url": "https://github.com/php-http/message-factory.git", 173 | "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" 174 | }, 175 | "dist": { 176 | "type": "zip", 177 | "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", 178 | "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", 179 | "shasum": "" 180 | }, 181 | "require": { 182 | "php": ">=5.4", 183 | "psr/http-message": "^1.0" 184 | }, 185 | "type": "library", 186 | "extra": { 187 | "branch-alias": { 188 | "dev-master": "1.0-dev" 189 | } 190 | }, 191 | "autoload": { 192 | "psr-4": { 193 | "Http\\Message\\": "src/" 194 | } 195 | }, 196 | "notification-url": "https://packagist.org/downloads/", 197 | "license": [ 198 | "MIT" 199 | ], 200 | "authors": [ 201 | { 202 | "name": "Márk Sági-Kazár", 203 | "email": "mark.sagikazar@gmail.com" 204 | } 205 | ], 206 | "description": "Factory interfaces for PSR-7 HTTP Message", 207 | "homepage": "http://php-http.org", 208 | "keywords": [ 209 | "factory", 210 | "http", 211 | "message", 212 | "stream", 213 | "uri" 214 | ], 215 | "support": { 216 | "issues": "https://github.com/php-http/message-factory/issues", 217 | "source": "https://github.com/php-http/message-factory/tree/master" 218 | }, 219 | "time": "2015-12-19T14:08:53+00:00" 220 | }, 221 | { 222 | "name": "psr/container", 223 | "version": "2.0.2", 224 | "source": { 225 | "type": "git", 226 | "url": "https://github.com/php-fig/container.git", 227 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" 228 | }, 229 | "dist": { 230 | "type": "zip", 231 | "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", 232 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", 233 | "shasum": "" 234 | }, 235 | "require": { 236 | "php": ">=7.4.0" 237 | }, 238 | "type": "library", 239 | "extra": { 240 | "branch-alias": { 241 | "dev-master": "2.0.x-dev" 242 | } 243 | }, 244 | "autoload": { 245 | "psr-4": { 246 | "Psr\\Container\\": "src/" 247 | } 248 | }, 249 | "notification-url": "https://packagist.org/downloads/", 250 | "license": [ 251 | "MIT" 252 | ], 253 | "authors": [ 254 | { 255 | "name": "PHP-FIG", 256 | "homepage": "https://www.php-fig.org/" 257 | } 258 | ], 259 | "description": "Common Container Interface (PHP FIG PSR-11)", 260 | "homepage": "https://github.com/php-fig/container", 261 | "keywords": [ 262 | "PSR-11", 263 | "container", 264 | "container-interface", 265 | "container-interop", 266 | "psr" 267 | ], 268 | "support": { 269 | "issues": "https://github.com/php-fig/container/issues", 270 | "source": "https://github.com/php-fig/container/tree/2.0.2" 271 | }, 272 | "time": "2021-11-05T16:47:00+00:00" 273 | }, 274 | { 275 | "name": "psr/http-factory", 276 | "version": "1.0.1", 277 | "source": { 278 | "type": "git", 279 | "url": "https://github.com/php-fig/http-factory.git", 280 | "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" 281 | }, 282 | "dist": { 283 | "type": "zip", 284 | "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", 285 | "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", 286 | "shasum": "" 287 | }, 288 | "require": { 289 | "php": ">=7.0.0", 290 | "psr/http-message": "^1.0" 291 | }, 292 | "type": "library", 293 | "extra": { 294 | "branch-alias": { 295 | "dev-master": "1.0.x-dev" 296 | } 297 | }, 298 | "autoload": { 299 | "psr-4": { 300 | "Psr\\Http\\Message\\": "src/" 301 | } 302 | }, 303 | "notification-url": "https://packagist.org/downloads/", 304 | "license": [ 305 | "MIT" 306 | ], 307 | "authors": [ 308 | { 309 | "name": "PHP-FIG", 310 | "homepage": "http://www.php-fig.org/" 311 | } 312 | ], 313 | "description": "Common interfaces for PSR-7 HTTP message factories", 314 | "keywords": [ 315 | "factory", 316 | "http", 317 | "message", 318 | "psr", 319 | "psr-17", 320 | "psr-7", 321 | "request", 322 | "response" 323 | ], 324 | "support": { 325 | "source": "https://github.com/php-fig/http-factory/tree/master" 326 | }, 327 | "time": "2019-04-30T12:38:16+00:00" 328 | }, 329 | { 330 | "name": "psr/http-message", 331 | "version": "1.0.1", 332 | "source": { 333 | "type": "git", 334 | "url": "https://github.com/php-fig/http-message.git", 335 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 336 | }, 337 | "dist": { 338 | "type": "zip", 339 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 340 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 341 | "shasum": "" 342 | }, 343 | "require": { 344 | "php": ">=5.3.0" 345 | }, 346 | "type": "library", 347 | "extra": { 348 | "branch-alias": { 349 | "dev-master": "1.0.x-dev" 350 | } 351 | }, 352 | "autoload": { 353 | "psr-4": { 354 | "Psr\\Http\\Message\\": "src/" 355 | } 356 | }, 357 | "notification-url": "https://packagist.org/downloads/", 358 | "license": [ 359 | "MIT" 360 | ], 361 | "authors": [ 362 | { 363 | "name": "PHP-FIG", 364 | "homepage": "http://www.php-fig.org/" 365 | } 366 | ], 367 | "description": "Common interface for HTTP messages", 368 | "homepage": "https://github.com/php-fig/http-message", 369 | "keywords": [ 370 | "http", 371 | "http-message", 372 | "psr", 373 | "psr-7", 374 | "request", 375 | "response" 376 | ], 377 | "support": { 378 | "source": "https://github.com/php-fig/http-message/tree/master" 379 | }, 380 | "time": "2016-08-06T14:39:51+00:00" 381 | }, 382 | { 383 | "name": "psr/log", 384 | "version": "3.0.0", 385 | "source": { 386 | "type": "git", 387 | "url": "https://github.com/php-fig/log.git", 388 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" 389 | }, 390 | "dist": { 391 | "type": "zip", 392 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", 393 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", 394 | "shasum": "" 395 | }, 396 | "require": { 397 | "php": ">=8.0.0" 398 | }, 399 | "type": "library", 400 | "extra": { 401 | "branch-alias": { 402 | "dev-master": "3.x-dev" 403 | } 404 | }, 405 | "autoload": { 406 | "psr-4": { 407 | "Psr\\Log\\": "src" 408 | } 409 | }, 410 | "notification-url": "https://packagist.org/downloads/", 411 | "license": [ 412 | "MIT" 413 | ], 414 | "authors": [ 415 | { 416 | "name": "PHP-FIG", 417 | "homepage": "https://www.php-fig.org/" 418 | } 419 | ], 420 | "description": "Common interface for logging libraries", 421 | "homepage": "https://github.com/php-fig/log", 422 | "keywords": [ 423 | "log", 424 | "psr", 425 | "psr-3" 426 | ], 427 | "support": { 428 | "source": "https://github.com/php-fig/log/tree/3.0.0" 429 | }, 430 | "time": "2021-07-14T16:46:02+00:00" 431 | }, 432 | { 433 | "name": "spiral/goridge", 434 | "version": "v3.2.0", 435 | "source": { 436 | "type": "git", 437 | "url": "https://github.com/spiral/goridge-php.git", 438 | "reference": "3d8e97d7d1cc26b6130d233177b23ecb3c7d4efb" 439 | }, 440 | "dist": { 441 | "type": "zip", 442 | "url": "https://api.github.com/repos/spiral/goridge-php/zipball/3d8e97d7d1cc26b6130d233177b23ecb3c7d4efb", 443 | "reference": "3d8e97d7d1cc26b6130d233177b23ecb3c7d4efb", 444 | "shasum": "" 445 | }, 446 | "require": { 447 | "ext-json": "*", 448 | "ext-sockets": "*", 449 | "php": ">=7.4", 450 | "symfony/polyfill-php80": "^1.22" 451 | }, 452 | "require-dev": { 453 | "google/protobuf": "^3.17", 454 | "infection/infection": "^0.26.1", 455 | "jetbrains/phpstorm-attributes": "^1.0", 456 | "phpunit/phpunit": "^9.5", 457 | "rybakit/msgpack": "^0.7", 458 | "vimeo/psalm": "^4.18.1" 459 | }, 460 | "suggest": { 461 | "ext-msgpack": "MessagePack codec support", 462 | "ext-protobuf": "Protobuf codec support", 463 | "google/protobuf": "(^3.0) Protobuf codec support", 464 | "rybakit/msgpack": "(^0.7) MessagePack codec support" 465 | }, 466 | "type": "goridge", 467 | "extra": { 468 | "branch-alias": { 469 | "dev-master": "3.3.x-dev" 470 | } 471 | }, 472 | "autoload": { 473 | "psr-4": { 474 | "Spiral\\Goridge\\": "src" 475 | } 476 | }, 477 | "notification-url": "https://packagist.org/downloads/", 478 | "license": [ 479 | "MIT" 480 | ], 481 | "authors": [ 482 | { 483 | "name": "Anton Titov / Wolfy-J", 484 | "email": "wolfy.jd@gmail.com" 485 | } 486 | ], 487 | "description": "High-performance PHP-to-Golang RPC bridge", 488 | "support": { 489 | "issues": "https://github.com/spiral/goridge-php/issues", 490 | "source": "https://github.com/spiral/goridge-php/tree/v3.2.0" 491 | }, 492 | "time": "2022-03-21T20:32:19+00:00" 493 | }, 494 | { 495 | "name": "spiral/roadrunner", 496 | "version": "v2.10.7", 497 | "source": { 498 | "type": "git", 499 | "url": "https://github.com/roadrunner-server/roadrunner.git", 500 | "reference": "18a7a98bcb483a680b6ebe7da8bb61e95329daf4" 501 | }, 502 | "dist": { 503 | "type": "zip", 504 | "url": "https://api.github.com/repos/roadrunner-server/roadrunner/zipball/18a7a98bcb483a680b6ebe7da8bb61e95329daf4", 505 | "reference": "18a7a98bcb483a680b6ebe7da8bb61e95329daf4", 506 | "shasum": "" 507 | }, 508 | "require": { 509 | "spiral/roadrunner-cli": "^2.0", 510 | "spiral/roadrunner-http": "^2.0", 511 | "spiral/roadrunner-worker": "^2.0" 512 | }, 513 | "type": "metapackage", 514 | "notification-url": "https://packagist.org/downloads/", 515 | "license": [ 516 | "MIT" 517 | ], 518 | "authors": [ 519 | { 520 | "name": "Anton Titov / Wolfy-J", 521 | "email": "wolfy.jd@gmail.com" 522 | }, 523 | { 524 | "name": "RoadRunner Community", 525 | "homepage": "https://github.com/roadrunner-server/roadrunner/graphs/contributors" 526 | } 527 | ], 528 | "description": "RoadRunner: High-performance PHP application server, load-balancer and process manager written in Golang", 529 | "support": { 530 | "issues": "https://github.com/roadrunner-server/roadrunner/issues", 531 | "source": "https://github.com/roadrunner-server/roadrunner/tree/v2.10.7" 532 | }, 533 | "time": "2022-07-14T09:00:44+00:00" 534 | }, 535 | { 536 | "name": "spiral/roadrunner-cli", 537 | "version": "v2.2.0", 538 | "source": { 539 | "type": "git", 540 | "url": "https://github.com/spiral/roadrunner-cli.git", 541 | "reference": "d8a224137b1d1ace0aac2308df396403393d63a8" 542 | }, 543 | "dist": { 544 | "type": "zip", 545 | "url": "https://api.github.com/repos/spiral/roadrunner-cli/zipball/d8a224137b1d1ace0aac2308df396403393d63a8", 546 | "reference": "d8a224137b1d1ace0aac2308df396403393d63a8", 547 | "shasum": "" 548 | }, 549 | "require": { 550 | "composer/semver": "^3.2", 551 | "ext-json": "*", 552 | "php": ">=7.4", 553 | "spiral/roadrunner-worker": ">=2.0.2", 554 | "symfony/console": "^4.4|^5.0|^6.0", 555 | "symfony/http-client": "^4.4|^5.0|^6.0", 556 | "symfony/polyfill-php80": "^1.22" 557 | }, 558 | "require-dev": { 559 | "jetbrains/phpstorm-attributes": "^1.0", 560 | "symfony/var-dumper": "^4.4|^5.0", 561 | "vimeo/psalm": "^4.4" 562 | }, 563 | "bin": [ 564 | "bin/rr" 565 | ], 566 | "type": "library", 567 | "extra": { 568 | "branch-alias": { 569 | "dev-master": "2.1.x-dev" 570 | } 571 | }, 572 | "autoload": { 573 | "psr-4": { 574 | "Spiral\\RoadRunner\\Console\\": "src" 575 | } 576 | }, 577 | "notification-url": "https://packagist.org/downloads/", 578 | "license": [ 579 | "MIT" 580 | ], 581 | "authors": [ 582 | { 583 | "name": "Anton Titov (wolfy-j)", 584 | "email": "wolfy-j@spiralscout.com" 585 | }, 586 | { 587 | "name": "RoadRunner Community", 588 | "homepage": "https://github.com/spiral/roadrunner/graphs/contributors" 589 | } 590 | ], 591 | "description": "RoadRunner: Command Line Interface", 592 | "support": { 593 | "issues": "https://github.com/spiral/roadrunner-cli/issues", 594 | "source": "https://github.com/spiral/roadrunner-cli/tree/v2.2.0" 595 | }, 596 | "time": "2022-05-17T06:44:24+00:00" 597 | }, 598 | { 599 | "name": "spiral/roadrunner-http", 600 | "version": "v2.1.0", 601 | "source": { 602 | "type": "git", 603 | "url": "https://github.com/spiral/roadrunner-http.git", 604 | "reference": "3be8bac365d436028a9583e7d438bf6e7183e599" 605 | }, 606 | "dist": { 607 | "type": "zip", 608 | "url": "https://api.github.com/repos/spiral/roadrunner-http/zipball/3be8bac365d436028a9583e7d438bf6e7183e599", 609 | "reference": "3be8bac365d436028a9583e7d438bf6e7183e599", 610 | "shasum": "" 611 | }, 612 | "require": { 613 | "ext-json": "*", 614 | "php": ">=7.4", 615 | "psr/http-factory": "^1.0.1", 616 | "psr/http-message": "^1.0.1", 617 | "spiral/roadrunner-worker": "^2.2.0" 618 | }, 619 | "require-dev": { 620 | "jetbrains/phpstorm-attributes": "^1.0", 621 | "nyholm/psr7": "^1.3", 622 | "phpstan/phpstan": "~0.12", 623 | "phpunit/phpunit": "~8.0", 624 | "symfony/var-dumper": "^5.1", 625 | "vimeo/psalm": "^4.22" 626 | }, 627 | "suggest": { 628 | "spiral/roadrunner-cli": "Provides RoadRunner installation and management CLI tools" 629 | }, 630 | "type": "library", 631 | "extra": { 632 | "branch-alias": { 633 | "dev-master": "2.2.x-dev" 634 | } 635 | }, 636 | "autoload": { 637 | "psr-4": { 638 | "Spiral\\RoadRunner\\Http\\": "src" 639 | } 640 | }, 641 | "notification-url": "https://packagist.org/downloads/", 642 | "license": [ 643 | "MIT" 644 | ], 645 | "authors": [ 646 | { 647 | "name": "Anton Titov / Wolfy-J", 648 | "email": "wolfy.jd@gmail.com" 649 | }, 650 | { 651 | "name": "RoadRunner Community", 652 | "homepage": "https://github.com/spiral/roadrunner/graphs/contributors" 653 | } 654 | ], 655 | "description": "RoadRunner: HTTP and PSR-7 worker", 656 | "support": { 657 | "issues": "https://github.com/spiral/roadrunner-http/issues", 658 | "source": "https://github.com/spiral/roadrunner-http/tree/v2.1.0" 659 | }, 660 | "time": "2022-03-22T14:48:00+00:00" 661 | }, 662 | { 663 | "name": "spiral/roadrunner-worker", 664 | "version": "v2.2.0", 665 | "source": { 666 | "type": "git", 667 | "url": "https://github.com/spiral/roadrunner-worker.git", 668 | "reference": "97399e1f45b188e4288817672df858908b641401" 669 | }, 670 | "dist": { 671 | "type": "zip", 672 | "url": "https://api.github.com/repos/spiral/roadrunner-worker/zipball/97399e1f45b188e4288817672df858908b641401", 673 | "reference": "97399e1f45b188e4288817672df858908b641401", 674 | "shasum": "" 675 | }, 676 | "require": { 677 | "composer-runtime-api": "^2.0", 678 | "ext-json": "*", 679 | "ext-sockets": "*", 680 | "php": ">=7.4", 681 | "psr/log": "^1.0|^2.0|^3.0", 682 | "spiral/goridge": "^3.2.0", 683 | "symfony/polyfill-php80": "^1.23" 684 | }, 685 | "require-dev": { 686 | "jetbrains/phpstorm-attributes": "^1.0", 687 | "symfony/var-dumper": "^5.1", 688 | "vimeo/psalm": "^4.4" 689 | }, 690 | "suggest": { 691 | "spiral/roadrunner-cli": "Provides RoadRunner installation and management CLI tools" 692 | }, 693 | "type": "library", 694 | "extra": { 695 | "branch-alias": { 696 | "dev-master": "2.3.x-dev" 697 | } 698 | }, 699 | "autoload": { 700 | "psr-4": { 701 | "Spiral\\RoadRunner\\": "src" 702 | } 703 | }, 704 | "notification-url": "https://packagist.org/downloads/", 705 | "license": [ 706 | "MIT" 707 | ], 708 | "authors": [ 709 | { 710 | "name": "Anton Titov (wolfy-j)", 711 | "email": "wolfy-j@spiralscout.com" 712 | }, 713 | { 714 | "name": "RoadRunner Community", 715 | "homepage": "https://github.com/spiral/roadrunner/graphs/contributors" 716 | } 717 | ], 718 | "description": "RoadRunner: PHP worker", 719 | "support": { 720 | "issues": "https://github.com/spiral/roadrunner-worker/issues", 721 | "source": "https://github.com/spiral/roadrunner-worker/tree/v2.2.0" 722 | }, 723 | "time": "2022-03-22T11:03:47+00:00" 724 | }, 725 | { 726 | "name": "symfony/console", 727 | "version": "v6.1.2", 728 | "source": { 729 | "type": "git", 730 | "url": "https://github.com/symfony/console.git", 731 | "reference": "7a86c1c42fbcb69b59768504c7bca1d3767760b7" 732 | }, 733 | "dist": { 734 | "type": "zip", 735 | "url": "https://api.github.com/repos/symfony/console/zipball/7a86c1c42fbcb69b59768504c7bca1d3767760b7", 736 | "reference": "7a86c1c42fbcb69b59768504c7bca1d3767760b7", 737 | "shasum": "" 738 | }, 739 | "require": { 740 | "php": ">=8.1", 741 | "symfony/deprecation-contracts": "^2.1|^3", 742 | "symfony/polyfill-mbstring": "~1.0", 743 | "symfony/service-contracts": "^1.1|^2|^3", 744 | "symfony/string": "^5.4|^6.0" 745 | }, 746 | "conflict": { 747 | "symfony/dependency-injection": "<5.4", 748 | "symfony/dotenv": "<5.4", 749 | "symfony/event-dispatcher": "<5.4", 750 | "symfony/lock": "<5.4", 751 | "symfony/process": "<5.4" 752 | }, 753 | "provide": { 754 | "psr/log-implementation": "1.0|2.0|3.0" 755 | }, 756 | "require-dev": { 757 | "psr/log": "^1|^2|^3", 758 | "symfony/config": "^5.4|^6.0", 759 | "symfony/dependency-injection": "^5.4|^6.0", 760 | "symfony/event-dispatcher": "^5.4|^6.0", 761 | "symfony/lock": "^5.4|^6.0", 762 | "symfony/process": "^5.4|^6.0", 763 | "symfony/var-dumper": "^5.4|^6.0" 764 | }, 765 | "suggest": { 766 | "psr/log": "For using the console logger", 767 | "symfony/event-dispatcher": "", 768 | "symfony/lock": "", 769 | "symfony/process": "" 770 | }, 771 | "type": "library", 772 | "autoload": { 773 | "psr-4": { 774 | "Symfony\\Component\\Console\\": "" 775 | }, 776 | "exclude-from-classmap": [ 777 | "/Tests/" 778 | ] 779 | }, 780 | "notification-url": "https://packagist.org/downloads/", 781 | "license": [ 782 | "MIT" 783 | ], 784 | "authors": [ 785 | { 786 | "name": "Fabien Potencier", 787 | "email": "fabien@symfony.com" 788 | }, 789 | { 790 | "name": "Symfony Community", 791 | "homepage": "https://symfony.com/contributors" 792 | } 793 | ], 794 | "description": "Eases the creation of beautiful and testable command line interfaces", 795 | "homepage": "https://symfony.com", 796 | "keywords": [ 797 | "cli", 798 | "command line", 799 | "console", 800 | "terminal" 801 | ], 802 | "support": { 803 | "source": "https://github.com/symfony/console/tree/v6.1.2" 804 | }, 805 | "funding": [ 806 | { 807 | "url": "https://symfony.com/sponsor", 808 | "type": "custom" 809 | }, 810 | { 811 | "url": "https://github.com/fabpot", 812 | "type": "github" 813 | }, 814 | { 815 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 816 | "type": "tidelift" 817 | } 818 | ], 819 | "time": "2022-06-26T13:01:30+00:00" 820 | }, 821 | { 822 | "name": "symfony/deprecation-contracts", 823 | "version": "v3.1.1", 824 | "source": { 825 | "type": "git", 826 | "url": "https://github.com/symfony/deprecation-contracts.git", 827 | "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" 828 | }, 829 | "dist": { 830 | "type": "zip", 831 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", 832 | "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", 833 | "shasum": "" 834 | }, 835 | "require": { 836 | "php": ">=8.1" 837 | }, 838 | "type": "library", 839 | "extra": { 840 | "branch-alias": { 841 | "dev-main": "3.1-dev" 842 | }, 843 | "thanks": { 844 | "name": "symfony/contracts", 845 | "url": "https://github.com/symfony/contracts" 846 | } 847 | }, 848 | "autoload": { 849 | "files": [ 850 | "function.php" 851 | ] 852 | }, 853 | "notification-url": "https://packagist.org/downloads/", 854 | "license": [ 855 | "MIT" 856 | ], 857 | "authors": [ 858 | { 859 | "name": "Nicolas Grekas", 860 | "email": "p@tchwork.com" 861 | }, 862 | { 863 | "name": "Symfony Community", 864 | "homepage": "https://symfony.com/contributors" 865 | } 866 | ], 867 | "description": "A generic function and convention to trigger deprecation notices", 868 | "homepage": "https://symfony.com", 869 | "support": { 870 | "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" 871 | }, 872 | "funding": [ 873 | { 874 | "url": "https://symfony.com/sponsor", 875 | "type": "custom" 876 | }, 877 | { 878 | "url": "https://github.com/fabpot", 879 | "type": "github" 880 | }, 881 | { 882 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 883 | "type": "tidelift" 884 | } 885 | ], 886 | "time": "2022-02-25T11:15:52+00:00" 887 | }, 888 | { 889 | "name": "symfony/http-client", 890 | "version": "v6.1.2", 891 | "source": { 892 | "type": "git", 893 | "url": "https://github.com/symfony/http-client.git", 894 | "reference": "ea1af6c8cc479147d67a3fead457dd7b06261041" 895 | }, 896 | "dist": { 897 | "type": "zip", 898 | "url": "https://api.github.com/repos/symfony/http-client/zipball/ea1af6c8cc479147d67a3fead457dd7b06261041", 899 | "reference": "ea1af6c8cc479147d67a3fead457dd7b06261041", 900 | "shasum": "" 901 | }, 902 | "require": { 903 | "php": ">=8.1", 904 | "psr/log": "^1|^2|^3", 905 | "symfony/http-client-contracts": "^3", 906 | "symfony/service-contracts": "^1.0|^2|^3" 907 | }, 908 | "provide": { 909 | "php-http/async-client-implementation": "*", 910 | "php-http/client-implementation": "*", 911 | "psr/http-client-implementation": "1.0", 912 | "symfony/http-client-implementation": "3.0" 913 | }, 914 | "require-dev": { 915 | "amphp/amp": "^2.5", 916 | "amphp/http-client": "^4.2.1", 917 | "amphp/http-tunnel": "^1.0", 918 | "amphp/socket": "^1.1", 919 | "guzzlehttp/promises": "^1.4", 920 | "nyholm/psr7": "^1.0", 921 | "php-http/httplug": "^1.0|^2.0", 922 | "psr/http-client": "^1.0", 923 | "symfony/dependency-injection": "^5.4|^6.0", 924 | "symfony/http-kernel": "^5.4|^6.0", 925 | "symfony/process": "^5.4|^6.0", 926 | "symfony/stopwatch": "^5.4|^6.0" 927 | }, 928 | "type": "library", 929 | "autoload": { 930 | "psr-4": { 931 | "Symfony\\Component\\HttpClient\\": "" 932 | }, 933 | "exclude-from-classmap": [ 934 | "/Tests/" 935 | ] 936 | }, 937 | "notification-url": "https://packagist.org/downloads/", 938 | "license": [ 939 | "MIT" 940 | ], 941 | "authors": [ 942 | { 943 | "name": "Nicolas Grekas", 944 | "email": "p@tchwork.com" 945 | }, 946 | { 947 | "name": "Symfony Community", 948 | "homepage": "https://symfony.com/contributors" 949 | } 950 | ], 951 | "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", 952 | "homepage": "https://symfony.com", 953 | "support": { 954 | "source": "https://github.com/symfony/http-client/tree/v6.1.2" 955 | }, 956 | "funding": [ 957 | { 958 | "url": "https://symfony.com/sponsor", 959 | "type": "custom" 960 | }, 961 | { 962 | "url": "https://github.com/fabpot", 963 | "type": "github" 964 | }, 965 | { 966 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 967 | "type": "tidelift" 968 | } 969 | ], 970 | "time": "2022-06-19T13:02:30+00:00" 971 | }, 972 | { 973 | "name": "symfony/http-client-contracts", 974 | "version": "v3.1.1", 975 | "source": { 976 | "type": "git", 977 | "url": "https://github.com/symfony/http-client-contracts.git", 978 | "reference": "fd038f08c623ab5d22b26e9ba35afe8c79071800" 979 | }, 980 | "dist": { 981 | "type": "zip", 982 | "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/fd038f08c623ab5d22b26e9ba35afe8c79071800", 983 | "reference": "fd038f08c623ab5d22b26e9ba35afe8c79071800", 984 | "shasum": "" 985 | }, 986 | "require": { 987 | "php": ">=8.1" 988 | }, 989 | "suggest": { 990 | "symfony/http-client-implementation": "" 991 | }, 992 | "type": "library", 993 | "extra": { 994 | "branch-alias": { 995 | "dev-main": "3.1-dev" 996 | }, 997 | "thanks": { 998 | "name": "symfony/contracts", 999 | "url": "https://github.com/symfony/contracts" 1000 | } 1001 | }, 1002 | "autoload": { 1003 | "psr-4": { 1004 | "Symfony\\Contracts\\HttpClient\\": "" 1005 | }, 1006 | "exclude-from-classmap": [ 1007 | "/Test/" 1008 | ] 1009 | }, 1010 | "notification-url": "https://packagist.org/downloads/", 1011 | "license": [ 1012 | "MIT" 1013 | ], 1014 | "authors": [ 1015 | { 1016 | "name": "Nicolas Grekas", 1017 | "email": "p@tchwork.com" 1018 | }, 1019 | { 1020 | "name": "Symfony Community", 1021 | "homepage": "https://symfony.com/contributors" 1022 | } 1023 | ], 1024 | "description": "Generic abstractions related to HTTP clients", 1025 | "homepage": "https://symfony.com", 1026 | "keywords": [ 1027 | "abstractions", 1028 | "contracts", 1029 | "decoupling", 1030 | "interfaces", 1031 | "interoperability", 1032 | "standards" 1033 | ], 1034 | "support": { 1035 | "source": "https://github.com/symfony/http-client-contracts/tree/v3.1.1" 1036 | }, 1037 | "funding": [ 1038 | { 1039 | "url": "https://symfony.com/sponsor", 1040 | "type": "custom" 1041 | }, 1042 | { 1043 | "url": "https://github.com/fabpot", 1044 | "type": "github" 1045 | }, 1046 | { 1047 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1048 | "type": "tidelift" 1049 | } 1050 | ], 1051 | "time": "2022-04-22T07:30:54+00:00" 1052 | }, 1053 | { 1054 | "name": "symfony/polyfill-ctype", 1055 | "version": "v1.26.0", 1056 | "source": { 1057 | "type": "git", 1058 | "url": "https://github.com/symfony/polyfill-ctype.git", 1059 | "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" 1060 | }, 1061 | "dist": { 1062 | "type": "zip", 1063 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", 1064 | "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", 1065 | "shasum": "" 1066 | }, 1067 | "require": { 1068 | "php": ">=7.1" 1069 | }, 1070 | "provide": { 1071 | "ext-ctype": "*" 1072 | }, 1073 | "suggest": { 1074 | "ext-ctype": "For best performance" 1075 | }, 1076 | "type": "library", 1077 | "extra": { 1078 | "branch-alias": { 1079 | "dev-main": "1.26-dev" 1080 | }, 1081 | "thanks": { 1082 | "name": "symfony/polyfill", 1083 | "url": "https://github.com/symfony/polyfill" 1084 | } 1085 | }, 1086 | "autoload": { 1087 | "files": [ 1088 | "bootstrap.php" 1089 | ], 1090 | "psr-4": { 1091 | "Symfony\\Polyfill\\Ctype\\": "" 1092 | } 1093 | }, 1094 | "notification-url": "https://packagist.org/downloads/", 1095 | "license": [ 1096 | "MIT" 1097 | ], 1098 | "authors": [ 1099 | { 1100 | "name": "Gert de Pagter", 1101 | "email": "BackEndTea@gmail.com" 1102 | }, 1103 | { 1104 | "name": "Symfony Community", 1105 | "homepage": "https://symfony.com/contributors" 1106 | } 1107 | ], 1108 | "description": "Symfony polyfill for ctype functions", 1109 | "homepage": "https://symfony.com", 1110 | "keywords": [ 1111 | "compatibility", 1112 | "ctype", 1113 | "polyfill", 1114 | "portable" 1115 | ], 1116 | "support": { 1117 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" 1118 | }, 1119 | "funding": [ 1120 | { 1121 | "url": "https://symfony.com/sponsor", 1122 | "type": "custom" 1123 | }, 1124 | { 1125 | "url": "https://github.com/fabpot", 1126 | "type": "github" 1127 | }, 1128 | { 1129 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1130 | "type": "tidelift" 1131 | } 1132 | ], 1133 | "time": "2022-05-24T11:49:31+00:00" 1134 | }, 1135 | { 1136 | "name": "symfony/polyfill-intl-grapheme", 1137 | "version": "v1.26.0", 1138 | "source": { 1139 | "type": "git", 1140 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 1141 | "reference": "433d05519ce6990bf3530fba6957499d327395c2" 1142 | }, 1143 | "dist": { 1144 | "type": "zip", 1145 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", 1146 | "reference": "433d05519ce6990bf3530fba6957499d327395c2", 1147 | "shasum": "" 1148 | }, 1149 | "require": { 1150 | "php": ">=7.1" 1151 | }, 1152 | "suggest": { 1153 | "ext-intl": "For best performance" 1154 | }, 1155 | "type": "library", 1156 | "extra": { 1157 | "branch-alias": { 1158 | "dev-main": "1.26-dev" 1159 | }, 1160 | "thanks": { 1161 | "name": "symfony/polyfill", 1162 | "url": "https://github.com/symfony/polyfill" 1163 | } 1164 | }, 1165 | "autoload": { 1166 | "files": [ 1167 | "bootstrap.php" 1168 | ], 1169 | "psr-4": { 1170 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 1171 | } 1172 | }, 1173 | "notification-url": "https://packagist.org/downloads/", 1174 | "license": [ 1175 | "MIT" 1176 | ], 1177 | "authors": [ 1178 | { 1179 | "name": "Nicolas Grekas", 1180 | "email": "p@tchwork.com" 1181 | }, 1182 | { 1183 | "name": "Symfony Community", 1184 | "homepage": "https://symfony.com/contributors" 1185 | } 1186 | ], 1187 | "description": "Symfony polyfill for intl's grapheme_* functions", 1188 | "homepage": "https://symfony.com", 1189 | "keywords": [ 1190 | "compatibility", 1191 | "grapheme", 1192 | "intl", 1193 | "polyfill", 1194 | "portable", 1195 | "shim" 1196 | ], 1197 | "support": { 1198 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" 1199 | }, 1200 | "funding": [ 1201 | { 1202 | "url": "https://symfony.com/sponsor", 1203 | "type": "custom" 1204 | }, 1205 | { 1206 | "url": "https://github.com/fabpot", 1207 | "type": "github" 1208 | }, 1209 | { 1210 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1211 | "type": "tidelift" 1212 | } 1213 | ], 1214 | "time": "2022-05-24T11:49:31+00:00" 1215 | }, 1216 | { 1217 | "name": "symfony/polyfill-intl-normalizer", 1218 | "version": "v1.26.0", 1219 | "source": { 1220 | "type": "git", 1221 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 1222 | "reference": "219aa369ceff116e673852dce47c3a41794c14bd" 1223 | }, 1224 | "dist": { 1225 | "type": "zip", 1226 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", 1227 | "reference": "219aa369ceff116e673852dce47c3a41794c14bd", 1228 | "shasum": "" 1229 | }, 1230 | "require": { 1231 | "php": ">=7.1" 1232 | }, 1233 | "suggest": { 1234 | "ext-intl": "For best performance" 1235 | }, 1236 | "type": "library", 1237 | "extra": { 1238 | "branch-alias": { 1239 | "dev-main": "1.26-dev" 1240 | }, 1241 | "thanks": { 1242 | "name": "symfony/polyfill", 1243 | "url": "https://github.com/symfony/polyfill" 1244 | } 1245 | }, 1246 | "autoload": { 1247 | "files": [ 1248 | "bootstrap.php" 1249 | ], 1250 | "psr-4": { 1251 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 1252 | }, 1253 | "classmap": [ 1254 | "Resources/stubs" 1255 | ] 1256 | }, 1257 | "notification-url": "https://packagist.org/downloads/", 1258 | "license": [ 1259 | "MIT" 1260 | ], 1261 | "authors": [ 1262 | { 1263 | "name": "Nicolas Grekas", 1264 | "email": "p@tchwork.com" 1265 | }, 1266 | { 1267 | "name": "Symfony Community", 1268 | "homepage": "https://symfony.com/contributors" 1269 | } 1270 | ], 1271 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 1272 | "homepage": "https://symfony.com", 1273 | "keywords": [ 1274 | "compatibility", 1275 | "intl", 1276 | "normalizer", 1277 | "polyfill", 1278 | "portable", 1279 | "shim" 1280 | ], 1281 | "support": { 1282 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" 1283 | }, 1284 | "funding": [ 1285 | { 1286 | "url": "https://symfony.com/sponsor", 1287 | "type": "custom" 1288 | }, 1289 | { 1290 | "url": "https://github.com/fabpot", 1291 | "type": "github" 1292 | }, 1293 | { 1294 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1295 | "type": "tidelift" 1296 | } 1297 | ], 1298 | "time": "2022-05-24T11:49:31+00:00" 1299 | }, 1300 | { 1301 | "name": "symfony/polyfill-mbstring", 1302 | "version": "v1.26.0", 1303 | "source": { 1304 | "type": "git", 1305 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1306 | "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" 1307 | }, 1308 | "dist": { 1309 | "type": "zip", 1310 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", 1311 | "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", 1312 | "shasum": "" 1313 | }, 1314 | "require": { 1315 | "php": ">=7.1" 1316 | }, 1317 | "provide": { 1318 | "ext-mbstring": "*" 1319 | }, 1320 | "suggest": { 1321 | "ext-mbstring": "For best performance" 1322 | }, 1323 | "type": "library", 1324 | "extra": { 1325 | "branch-alias": { 1326 | "dev-main": "1.26-dev" 1327 | }, 1328 | "thanks": { 1329 | "name": "symfony/polyfill", 1330 | "url": "https://github.com/symfony/polyfill" 1331 | } 1332 | }, 1333 | "autoload": { 1334 | "files": [ 1335 | "bootstrap.php" 1336 | ], 1337 | "psr-4": { 1338 | "Symfony\\Polyfill\\Mbstring\\": "" 1339 | } 1340 | }, 1341 | "notification-url": "https://packagist.org/downloads/", 1342 | "license": [ 1343 | "MIT" 1344 | ], 1345 | "authors": [ 1346 | { 1347 | "name": "Nicolas Grekas", 1348 | "email": "p@tchwork.com" 1349 | }, 1350 | { 1351 | "name": "Symfony Community", 1352 | "homepage": "https://symfony.com/contributors" 1353 | } 1354 | ], 1355 | "description": "Symfony polyfill for the Mbstring extension", 1356 | "homepage": "https://symfony.com", 1357 | "keywords": [ 1358 | "compatibility", 1359 | "mbstring", 1360 | "polyfill", 1361 | "portable", 1362 | "shim" 1363 | ], 1364 | "support": { 1365 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" 1366 | }, 1367 | "funding": [ 1368 | { 1369 | "url": "https://symfony.com/sponsor", 1370 | "type": "custom" 1371 | }, 1372 | { 1373 | "url": "https://github.com/fabpot", 1374 | "type": "github" 1375 | }, 1376 | { 1377 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1378 | "type": "tidelift" 1379 | } 1380 | ], 1381 | "time": "2022-05-24T11:49:31+00:00" 1382 | }, 1383 | { 1384 | "name": "symfony/polyfill-php80", 1385 | "version": "v1.26.0", 1386 | "source": { 1387 | "type": "git", 1388 | "url": "https://github.com/symfony/polyfill-php80.git", 1389 | "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" 1390 | }, 1391 | "dist": { 1392 | "type": "zip", 1393 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", 1394 | "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", 1395 | "shasum": "" 1396 | }, 1397 | "require": { 1398 | "php": ">=7.1" 1399 | }, 1400 | "type": "library", 1401 | "extra": { 1402 | "branch-alias": { 1403 | "dev-main": "1.26-dev" 1404 | }, 1405 | "thanks": { 1406 | "name": "symfony/polyfill", 1407 | "url": "https://github.com/symfony/polyfill" 1408 | } 1409 | }, 1410 | "autoload": { 1411 | "files": [ 1412 | "bootstrap.php" 1413 | ], 1414 | "psr-4": { 1415 | "Symfony\\Polyfill\\Php80\\": "" 1416 | }, 1417 | "classmap": [ 1418 | "Resources/stubs" 1419 | ] 1420 | }, 1421 | "notification-url": "https://packagist.org/downloads/", 1422 | "license": [ 1423 | "MIT" 1424 | ], 1425 | "authors": [ 1426 | { 1427 | "name": "Ion Bazan", 1428 | "email": "ion.bazan@gmail.com" 1429 | }, 1430 | { 1431 | "name": "Nicolas Grekas", 1432 | "email": "p@tchwork.com" 1433 | }, 1434 | { 1435 | "name": "Symfony Community", 1436 | "homepage": "https://symfony.com/contributors" 1437 | } 1438 | ], 1439 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 1440 | "homepage": "https://symfony.com", 1441 | "keywords": [ 1442 | "compatibility", 1443 | "polyfill", 1444 | "portable", 1445 | "shim" 1446 | ], 1447 | "support": { 1448 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" 1449 | }, 1450 | "funding": [ 1451 | { 1452 | "url": "https://symfony.com/sponsor", 1453 | "type": "custom" 1454 | }, 1455 | { 1456 | "url": "https://github.com/fabpot", 1457 | "type": "github" 1458 | }, 1459 | { 1460 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1461 | "type": "tidelift" 1462 | } 1463 | ], 1464 | "time": "2022-05-10T07:21:04+00:00" 1465 | }, 1466 | { 1467 | "name": "symfony/service-contracts", 1468 | "version": "v3.1.1", 1469 | "source": { 1470 | "type": "git", 1471 | "url": "https://github.com/symfony/service-contracts.git", 1472 | "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239" 1473 | }, 1474 | "dist": { 1475 | "type": "zip", 1476 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239", 1477 | "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239", 1478 | "shasum": "" 1479 | }, 1480 | "require": { 1481 | "php": ">=8.1", 1482 | "psr/container": "^2.0" 1483 | }, 1484 | "conflict": { 1485 | "ext-psr": "<1.1|>=2" 1486 | }, 1487 | "suggest": { 1488 | "symfony/service-implementation": "" 1489 | }, 1490 | "type": "library", 1491 | "extra": { 1492 | "branch-alias": { 1493 | "dev-main": "3.1-dev" 1494 | }, 1495 | "thanks": { 1496 | "name": "symfony/contracts", 1497 | "url": "https://github.com/symfony/contracts" 1498 | } 1499 | }, 1500 | "autoload": { 1501 | "psr-4": { 1502 | "Symfony\\Contracts\\Service\\": "" 1503 | }, 1504 | "exclude-from-classmap": [ 1505 | "/Test/" 1506 | ] 1507 | }, 1508 | "notification-url": "https://packagist.org/downloads/", 1509 | "license": [ 1510 | "MIT" 1511 | ], 1512 | "authors": [ 1513 | { 1514 | "name": "Nicolas Grekas", 1515 | "email": "p@tchwork.com" 1516 | }, 1517 | { 1518 | "name": "Symfony Community", 1519 | "homepage": "https://symfony.com/contributors" 1520 | } 1521 | ], 1522 | "description": "Generic abstractions related to writing services", 1523 | "homepage": "https://symfony.com", 1524 | "keywords": [ 1525 | "abstractions", 1526 | "contracts", 1527 | "decoupling", 1528 | "interfaces", 1529 | "interoperability", 1530 | "standards" 1531 | ], 1532 | "support": { 1533 | "source": "https://github.com/symfony/service-contracts/tree/v3.1.1" 1534 | }, 1535 | "funding": [ 1536 | { 1537 | "url": "https://symfony.com/sponsor", 1538 | "type": "custom" 1539 | }, 1540 | { 1541 | "url": "https://github.com/fabpot", 1542 | "type": "github" 1543 | }, 1544 | { 1545 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1546 | "type": "tidelift" 1547 | } 1548 | ], 1549 | "time": "2022-05-30T19:18:58+00:00" 1550 | }, 1551 | { 1552 | "name": "symfony/string", 1553 | "version": "v6.1.2", 1554 | "source": { 1555 | "type": "git", 1556 | "url": "https://github.com/symfony/string.git", 1557 | "reference": "1903f2879875280c5af944625e8246d81c2f0604" 1558 | }, 1559 | "dist": { 1560 | "type": "zip", 1561 | "url": "https://api.github.com/repos/symfony/string/zipball/1903f2879875280c5af944625e8246d81c2f0604", 1562 | "reference": "1903f2879875280c5af944625e8246d81c2f0604", 1563 | "shasum": "" 1564 | }, 1565 | "require": { 1566 | "php": ">=8.1", 1567 | "symfony/polyfill-ctype": "~1.8", 1568 | "symfony/polyfill-intl-grapheme": "~1.0", 1569 | "symfony/polyfill-intl-normalizer": "~1.0", 1570 | "symfony/polyfill-mbstring": "~1.0" 1571 | }, 1572 | "conflict": { 1573 | "symfony/translation-contracts": "<2.0" 1574 | }, 1575 | "require-dev": { 1576 | "symfony/error-handler": "^5.4|^6.0", 1577 | "symfony/http-client": "^5.4|^6.0", 1578 | "symfony/translation-contracts": "^2.0|^3.0", 1579 | "symfony/var-exporter": "^5.4|^6.0" 1580 | }, 1581 | "type": "library", 1582 | "autoload": { 1583 | "files": [ 1584 | "Resources/functions.php" 1585 | ], 1586 | "psr-4": { 1587 | "Symfony\\Component\\String\\": "" 1588 | }, 1589 | "exclude-from-classmap": [ 1590 | "/Tests/" 1591 | ] 1592 | }, 1593 | "notification-url": "https://packagist.org/downloads/", 1594 | "license": [ 1595 | "MIT" 1596 | ], 1597 | "authors": [ 1598 | { 1599 | "name": "Nicolas Grekas", 1600 | "email": "p@tchwork.com" 1601 | }, 1602 | { 1603 | "name": "Symfony Community", 1604 | "homepage": "https://symfony.com/contributors" 1605 | } 1606 | ], 1607 | "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", 1608 | "homepage": "https://symfony.com", 1609 | "keywords": [ 1610 | "grapheme", 1611 | "i18n", 1612 | "string", 1613 | "unicode", 1614 | "utf-8", 1615 | "utf8" 1616 | ], 1617 | "support": { 1618 | "source": "https://github.com/symfony/string/tree/v6.1.2" 1619 | }, 1620 | "funding": [ 1621 | { 1622 | "url": "https://symfony.com/sponsor", 1623 | "type": "custom" 1624 | }, 1625 | { 1626 | "url": "https://github.com/fabpot", 1627 | "type": "github" 1628 | }, 1629 | { 1630 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1631 | "type": "tidelift" 1632 | } 1633 | ], 1634 | "time": "2022-06-26T16:35:04+00:00" 1635 | } 1636 | ], 1637 | "packages-dev": [], 1638 | "aliases": [], 1639 | "minimum-stability": "stable", 1640 | "stability-flags": [], 1641 | "prefer-stable": false, 1642 | "prefer-lowest": false, 1643 | "platform": [], 1644 | "platform-dev": [], 1645 | "plugin-api-version": "2.3.0" 1646 | } 1647 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/configuration.toml: -------------------------------------------------------------------------------- 1 | [velox] 2 | build_args = ['-trimpath', '-ldflags', '-s -X github.com/roadrunner-server/roadrunner/v2/internal/meta.version=v2.12.0 -X github.com/roadrunner-server/roadrunner/v2/internal/meta.buildTime=10:00:00'] 3 | 4 | [roadrunner] 5 | ref = "v2.12.3" 6 | 7 | [github] 8 | [github.token] 9 | token = "GH_TOKEN" 10 | 11 | [github.plugins] 12 | logger = { ref = "v3.2.0", owner = "roadrunner-server", repository = "logger" } 13 | esi = { ref = "CURRENT_SHA", owner = "darkweak", repository = "go-esi", folder = "middleware/roadrunner", replace = "/opt/middleware/roadrunner" } 14 | server = { ref = "v3.2.0", owner = "roadrunner-server", repository = "server" } 15 | gzip = { ref = "v3.2.0", owner = "roadrunner-server", repository = "gzip" } 16 | http = { ref = "v3.2.0", owner = "roadrunner-server", repository = "http" } 17 | 18 | [log] 19 | level = "debug" 20 | mode = "development" 21 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/docker-compose.yml.test: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | roadrunner: 5 | build: 6 | context: ../../.. 7 | dockerfile: middleware/roadrunner/examples/Dockerfile.test 8 | target: development-runner 9 | args: 10 | GH_TOKEN: ${GH_APP_TOKEN} 11 | CURRENT_SHA: ${CURRENT_SHA} 12 | ports: 13 | - 80:80 14 | -------------------------------------------------------------------------------- /middleware/roadrunner/examples/psr-worker.php: -------------------------------------------------------------------------------- 1 | waitRequest()) { 18 | try { 19 | $rsp = new Psr7\Response(); 20 | if ($_SERVER['REQUEST_URI'] === 'http://localhost/include') { 21 | $rsp->getBody()->write('Include content'); 22 | } else { 23 | $rsp->getBody()->write('Hello base uri! !'); 24 | } 25 | 26 | $worker->respond($rsp); 27 | } catch (\Throwable $e) { 28 | $worker->getWorker()->error((string)$e); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /middleware/roadrunner/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/darkweak/go-esi/middleware/roadrunner 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/darkweak/go-esi v0.0.6 7 | github.com/roadrunner-server/errors v1.2.0 8 | go.uber.org/zap v1.24.0 9 | ) 10 | 11 | require ( 12 | github.com/pkg/errors v0.9.1 // indirect 13 | github.com/stretchr/testify v1.8.1 // indirect 14 | go.uber.org/atomic v1.10.0 // indirect 15 | go.uber.org/multierr v1.10.0 // indirect 16 | ) 17 | 18 | replace github.com/darkweak/go-esi v0.0.6 => ../.. 19 | -------------------------------------------------------------------------------- /middleware/roadrunner/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 6 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/roadrunner-server/errors v1.2.0 h1:qBmNXt8Iex9QnYTjCkbJKsBZu2EtYkQCM06GUDcQBbI= 10 | github.com/roadrunner-server/errors v1.2.0/go.mod h1:z0ECxZp/dDa5RahtMcy4mBIavVxiZ9vwE5kByl7kFtY= 11 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 12 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 13 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 14 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 15 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 16 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 17 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 18 | go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= 19 | go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 20 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= 21 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 22 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 23 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 24 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 25 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 26 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 27 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 28 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 29 | -------------------------------------------------------------------------------- /middleware/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | ) 7 | 8 | var respond = []byte(` 9 | 10 | <esi:vars>Hello from $(HTTP_HOST)</esi:vars> 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | `) 24 | 25 | func main() { 26 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 27 | w.Header().Set("Content-Type", "text/html") 28 | w.WriteHeader(http.StatusOK) 29 | _, _ = w.Write(respond[0:97]) 30 | if flusher, ok := w.(http.Flusher); ok { 31 | flusher.Flush() 32 | } 33 | time.Sleep(time.Second) 34 | _, _ = w.Write(respond[97:194]) 35 | time.Sleep(time.Second) 36 | _, _ = w.Write(respond[194:291]) 37 | time.Sleep(time.Second) 38 | _, _ = w.Write(respond[291:]) 39 | }) 40 | 41 | server := &http.Server{ 42 | Addr: ":81", 43 | ReadHeaderTimeout: 3 * time.Second, 44 | } 45 | _ = server.ListenAndServe() 46 | } 47 | -------------------------------------------------------------------------------- /middleware/standalone/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | 10 | "github.com/darkweak/go-esi/esi" 11 | ) 12 | 13 | const expected = ` 14 | 15 | Hello from domain.com:81 16 | 17 | 18 | 19 | 20 |

CHAINED 2

21 |

CHAINED 2

22 | 23 | 24 | ` 25 | 26 | func main() { 27 | buf := bytes.NewBuffer([]byte{}) 28 | ctx := context.Background() 29 | rq, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://domain.com:81/", nil) 30 | 31 | res, err := http.DefaultClient.Do(rq) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | defer res.Body.Close() 37 | _, _ = io.Copy(buf, res.Body) 38 | 39 | result := buf.String() 40 | parsed := esi.Parse(buf.Bytes(), rq) 41 | 42 | if string(parsed) != expected { 43 | fmt.Printf("Given:\n%+v\nParsed result:\n%+v\n", result, string(parsed)) 44 | panic(nil) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /middleware/traefik/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build log run 2 | 3 | build: ## Prepare træfik deps 4 | go mod tidy 5 | go mod download 6 | 7 | log: ## Display container logs 8 | docker-compose logs -f 9 | 10 | run: ## Run træfik with go-esi 11 | docker-compose up --remove-orphans --build -------------------------------------------------------------------------------- /middleware/traefik/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | traefik: 5 | image: traefik:latest 6 | volumes: 7 | - /var/run/docker.sock:/var/run/docker.sock 8 | - ../..:/plugins-local/src/github.com/darkweak/go-esi 9 | - ./traefik.yml:/traefik.yml 10 | - ./esi-configuration.yml:/esi-configuration.yml 11 | environment: 12 | GOPATH: /plugins-local 13 | ports: 14 | - 80:80 15 | - 8080:8080 16 | 17 | whoami: 18 | image: traefik/whoami 19 | labels: 20 | - traefik.http.routers.whoami.rule=Host(`domain.com`) -------------------------------------------------------------------------------- /middleware/traefik/esi-configuration.yml: -------------------------------------------------------------------------------- 1 | http: 2 | routers: 3 | whoami: 4 | middlewares: 5 | - esi 6 | entrypoints: 7 | - http 8 | service: whoami 9 | rule: Host(`domain.com`) 10 | 11 | services: 12 | whoami: 13 | loadBalancer: 14 | servers: 15 | - url: http://whoami 16 | passHostHeader: false 17 | 18 | middlewares: 19 | esi: 20 | plugin: 21 | esi: {} -------------------------------------------------------------------------------- /middleware/traefik/esi.go: -------------------------------------------------------------------------------- 1 | package traefik 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "net/http" 7 | "sync" 8 | 9 | "github.com/darkweak/go-esi/writer" 10 | ) 11 | 12 | var bufPool *sync.Pool = &sync.Pool{ 13 | New: func() any { 14 | return &bytes.Buffer{} 15 | }, 16 | } 17 | 18 | // Config the ESI plugin configuration. 19 | type Config struct{} 20 | 21 | // CreateConfig creates the ESI plugin configuration. 22 | func CreateConfig() *Config { 23 | return &Config{} 24 | } 25 | 26 | // ESI is a plugin that allow users to process the ESI tags. 27 | type ESI struct { 28 | next http.Handler 29 | name string 30 | } 31 | 32 | // New created a new ESI plugin. 33 | func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { 34 | return &ESI{ 35 | next: next, 36 | name: name, 37 | }, nil 38 | } 39 | 40 | func (e *ESI) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 41 | buf := bufPool.Get().(*bytes.Buffer) 42 | buf.Reset() 43 | defer bufPool.Put(buf) 44 | cw := writer.NewWriter(buf, rw, req) 45 | go func(w *writer.Writer) { 46 | w.Header().Del("Content-Length") 47 | if w.Rq.ProtoMajor == 1 { 48 | w.Header().Set("Content-Encoding", "chunked") 49 | } 50 | var i = 0 51 | for { 52 | if len(cw.AsyncBuf) <= i { 53 | continue 54 | } 55 | rs := <-cw.AsyncBuf[i] 56 | if rs == nil { 57 | cw.Done <- true 58 | break 59 | } 60 | _, _ = rw.Write(rs) 61 | i++ 62 | } 63 | }(cw) 64 | e.next.ServeHTTP(cw, req) 65 | cw.AsyncBuf = append(cw.AsyncBuf, make(chan []byte)) 66 | go func(w *writer.Writer, iteration int) { 67 | w.AsyncBuf[iteration] <- nil 68 | }(cw, cw.Iteration) 69 | 70 | <-cw.Done 71 | } 72 | -------------------------------------------------------------------------------- /middleware/traefik/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/darkweak/go-esi/middleware/traefik 2 | 3 | go 1.19 4 | 5 | require github.com/darkweak/go-esi v0.0.6 6 | 7 | replace github.com/darkweak/go-esi v0.0.6 => ../.. 8 | -------------------------------------------------------------------------------- /middleware/traefik/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkweak/go-esi/dfdfaa8466f9c7cc840040aeb753ee0887c0d431/middleware/traefik/go.sum -------------------------------------------------------------------------------- /middleware/traefik/traefik.yml: -------------------------------------------------------------------------------- 1 | providers: 2 | file: 3 | filename: /esi-configuration.yml 4 | watch: true 5 | 6 | api: 7 | dashboard: true 8 | debug: true 9 | insecure: true 10 | 11 | pilot: 12 | token: 12926953-a7d1-4223-a092-d7dd95a91fa3 13 | 14 | experimental: 15 | localPlugins: 16 | esi: 17 | moduleName: github.com/darkweak/go-esi 18 | 19 | log: 20 | level: DEBUG 21 | 22 | accessLog: {} -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 darkweak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/choose.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const choose = "choose" 9 | 10 | var ( 11 | closeChoose = regexp.MustCompile("") 12 | whenRg = regexp.MustCompile(`(?s)(.+?)`) 13 | otherwiseRg = regexp.MustCompile(`(?s)(.+?)`) 14 | ) 15 | 16 | type chooseTag struct { 17 | *baseTag 18 | } 19 | 20 | // Input (e.g. 21 | // 22 | // 23 | // 24 | // 25 | // 26 | // 27 | // 28 | // 29 | // 30 | // 31 | // 32 | // ). 33 | func (c *chooseTag) Process(b []byte, req *http.Request) ([]byte, int) { 34 | found := closeChoose.FindIndex(b) 35 | if found == nil { 36 | return nil, len(b) 37 | } 38 | 39 | c.length = found[1] 40 | tagIdxs := whenRg.FindAllSubmatch(b, -1) 41 | 42 | var res []byte 43 | 44 | for _, v := range tagIdxs { 45 | if validateTest(v[1], req) { 46 | res = Parse(v[2], req) 47 | 48 | break 49 | } 50 | } 51 | 52 | tagIdx := otherwiseRg.FindSubmatch(b) 53 | if tagIdx != nil { 54 | res = Parse(tagIdx[1], req) 55 | } 56 | 57 | return res, c.length 58 | } 59 | 60 | func (*chooseTag) HasClose(b []byte) bool { 61 | return closeChoose.FindIndex(b) != nil 62 | } 63 | 64 | func (*chooseTag) GetClosePosition(b []byte) int { 65 | if idx := closeChoose.FindIndex(b); idx != nil { 66 | return idx[1] 67 | } 68 | return 0 69 | } 70 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/comment.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const comment = "comment" 9 | 10 | var closeComment = regexp.MustCompile("/>") 11 | 12 | type commentTag struct { 13 | *baseTag 14 | } 15 | 16 | // Input (e.g. comment text="This is a comment." />). 17 | func (c *commentTag) Process(b []byte, req *http.Request) ([]byte, int) { 18 | found := closeComment.FindIndex(b) 19 | if found == nil { 20 | return nil, len(b) 21 | } 22 | 23 | return []byte{}, found[1] 24 | } 25 | 26 | func (*commentTag) HasClose(b []byte) bool { 27 | return closeComment.FindIndex(b) != nil 28 | } 29 | 30 | func (*commentTag) GetClosePosition(b []byte) int { 31 | if idx := closeComment.FindIndex(b); idx != nil { 32 | return idx[1] 33 | } 34 | return 0 35 | } 36 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/errors.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import "errors" 4 | 5 | var errNotFound = errors.New("not found") 6 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/escape.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const escape = "") 13 | ) 14 | 15 | type escapeTag struct { 16 | *baseTag 17 | } 18 | 19 | func (e *escapeTag) Process(b []byte, req *http.Request) ([]byte, int) { 20 | closeIdx := closeEscape.FindIndex(b) 21 | 22 | if closeIdx == nil { 23 | return nil, len(b) 24 | } 25 | 26 | e.length = closeIdx[1] 27 | b = b[:closeIdx[0]] 28 | 29 | return b, e.length 30 | } 31 | 32 | func (*escapeTag) HasClose(b []byte) bool { 33 | return closeEscape.FindIndex(b) != nil 34 | } 35 | 36 | func (*escapeTag) GetClosePosition(b []byte) int { 37 | if idx := closeEscape.FindIndex(b); idx != nil { 38 | return idx[1] 39 | } 40 | return 0 41 | } 42 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/esi.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func findTagName(b []byte) Tag { 8 | name := tagname.FindSubmatch(b) 9 | if name == nil { 10 | return nil 11 | } 12 | 13 | switch string(name[1]) { 14 | case comment: 15 | return &commentTag{ 16 | baseTag: newBaseTag(), 17 | } 18 | case choose: 19 | return &chooseTag{ 20 | baseTag: newBaseTag(), 21 | } 22 | case escape: 23 | return &escapeTag{ 24 | baseTag: newBaseTag(), 25 | } 26 | case include: 27 | return &includeTag{ 28 | baseTag: newBaseTag(), 29 | } 30 | case remove: 31 | return &removeTag{ 32 | baseTag: newBaseTag(), 33 | } 34 | case try: 35 | case vars: 36 | return &varsTag{ 37 | baseTag: newBaseTag(), 38 | } 39 | default: 40 | return nil 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func HasOpenedTags(b []byte) bool { 47 | return esi.FindIndex(b) != nil || escapeRg.FindIndex(b) != nil 48 | } 49 | 50 | func CanProcess(b []byte) bool { 51 | if tag := findTagName(b); tag != nil { 52 | return tag.HasClose(b) 53 | } 54 | 55 | return false 56 | } 57 | 58 | func ReadToTag(next []byte, pointer int) (startTagPosition, esiPointer int, t Tag) { 59 | tagIdx := esi.FindIndex(next) 60 | var isEscapeTag bool 61 | 62 | if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) { 63 | tagIdx = escIdx 64 | tagIdx[1] = escIdx[0] 65 | isEscapeTag = true 66 | } 67 | 68 | if tagIdx == nil { 69 | return len(next), 0, nil 70 | } 71 | 72 | esiPointer = tagIdx[1] 73 | startTagPosition = tagIdx[0] 74 | t = findTagName(next[esiPointer:]) 75 | 76 | if isEscapeTag { 77 | esiPointer += 7 78 | } 79 | 80 | return 81 | } 82 | 83 | func Parse(b []byte, req *http.Request) []byte { 84 | pointer := 0 85 | 86 | for pointer < len(b) { 87 | var escapeTag bool 88 | 89 | next := b[pointer:] 90 | tagIdx := esi.FindIndex(next) 91 | 92 | if escIdx := escapeRg.FindIndex(next); escIdx != nil && (tagIdx == nil || escIdx[0] < tagIdx[0]) { 93 | tagIdx = escIdx 94 | tagIdx[1] = escIdx[0] 95 | escapeTag = true 96 | } 97 | 98 | if tagIdx == nil { 99 | break 100 | } 101 | 102 | esiPointer := tagIdx[1] 103 | t := findTagName(next[esiPointer:]) 104 | 105 | if escapeTag { 106 | esiPointer += 7 107 | } 108 | 109 | res, p := t.Process(next[esiPointer:], req) 110 | esiPointer += p 111 | 112 | b = append(b[:pointer], append(next[:tagIdx[0]], append(res, next[esiPointer:]...)...)...) 113 | pointer += len(res) + tagIdx[0] 114 | } 115 | 116 | return b 117 | } 118 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/include.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "regexp" 7 | ) 8 | 9 | const include = "include" 10 | 11 | var ( 12 | closeInclude = regexp.MustCompile("/>") 13 | srcAttribute = regexp.MustCompile(`src="?(.+?)"?( |/>)`) 14 | altAttribute = regexp.MustCompile(`alt="?(.+?)"?( |/>)`) 15 | ) 16 | 17 | type includeTag struct { 18 | *baseTag 19 | src string 20 | alt string 21 | } 22 | 23 | func (i *includeTag) loadAttributes(b []byte) error { 24 | src := srcAttribute.FindSubmatch(b) 25 | if src == nil { 26 | return errNotFound 27 | } 28 | 29 | i.src = string(src[1]) 30 | 31 | alt := altAttribute.FindSubmatch(b) 32 | if alt != nil { 33 | i.alt = string(alt[1]) 34 | } 35 | 36 | return nil 37 | } 38 | 39 | // Input (e.g. include src="https://domain.com/esi-include" alt="https://domain.com/alt-esi-include" />) 40 | // With or without the alt 41 | // With or without a space separator before the closing 42 | // With or without the quotes around the src/alt value. 43 | func (i *includeTag) Process(b []byte, req *http.Request) ([]byte, int) { 44 | closeIdx := closeInclude.FindIndex(b) 45 | 46 | if closeIdx == nil { 47 | return nil, len(b) 48 | } 49 | 50 | i.length = closeIdx[1] 51 | if e := i.loadAttributes(b[8:i.length]); e != nil { 52 | return nil, len(b) 53 | } 54 | 55 | rq, _ := http.NewRequest(http.MethodGet, i.src, nil) 56 | client := &http.Client{} 57 | response, err := client.Do(rq) 58 | 59 | if err != nil || response.StatusCode >= 400 { 60 | rq, _ = http.NewRequest(http.MethodGet, i.src, nil) 61 | response, err = client.Do(rq) 62 | 63 | if err != nil || response.StatusCode >= 400 { 64 | return nil, len(b) 65 | } 66 | } 67 | 68 | defer response.Body.Close() 69 | x, _ := io.ReadAll(response.Body) 70 | b = Parse(x, req) 71 | 72 | return b, i.length 73 | } 74 | 75 | func (*includeTag) HasClose(b []byte) bool { 76 | return closeInclude.FindIndex(b) != nil 77 | } 78 | 79 | func (*includeTag) GetClosePosition(b []byte) int { 80 | if idx := closeInclude.FindIndex(b); idx != nil { 81 | return idx[1] 82 | } 83 | return 0 84 | } 85 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/remove.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | ) 7 | 8 | const remove = "remove" 9 | 10 | var closeRemove = regexp.MustCompile("") 11 | 12 | type removeTag struct { 13 | *baseTag 14 | } 15 | 16 | func (r *removeTag) Process(b []byte, req *http.Request) ([]byte, int) { 17 | closeIdx := closeRemove.FindIndex(b) 18 | if closeIdx == nil { 19 | return []byte{}, len(b) 20 | } 21 | 22 | r.length = closeIdx[1] 23 | 24 | return []byte{}, r.length 25 | } 26 | 27 | func (*removeTag) HasClose(b []byte) bool { 28 | return closeRemove.FindIndex(b) != nil 29 | } 30 | 31 | func (*removeTag) GetClosePosition(b []byte) int { 32 | if idx := closeRemove.FindIndex(b); idx != nil { 33 | return idx[1] 34 | } 35 | return 0 36 | } 37 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/tags.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import "regexp" 4 | 5 | const ( 6 | try = "try" 7 | ) 8 | 9 | var ( 10 | esi = regexp.MustCompile(""). 14 | ) 15 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/try.go: -------------------------------------------------------------------------------- 1 | package esi 2 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/type.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | type ( 8 | Tag interface { 9 | Process([]byte, *http.Request) ([]byte, int) 10 | HasClose([]byte) bool 11 | GetClosePosition([]byte) int 12 | } 13 | 14 | baseTag struct { 15 | length int 16 | } 17 | ) 18 | 19 | func newBaseTag() *baseTag { 20 | return &baseTag{length: 0} 21 | } 22 | 23 | func (b *baseTag) Process(content []byte, _ *http.Request) ([]byte, int) { 24 | return []byte{}, len(content) 25 | } 26 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/vars.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | httpAcceptLanguage = "HTTP_ACCEPT_LANGUAGE" 11 | httpCookie = "HTTP_COOKIE" 12 | httpHost = "HTTP_HOST" 13 | httpReferrer = "HTTP_REFERER" 14 | httpUserAgent = "HTTP_USER_AGENT" 15 | httpQueryString = "QUERY_STRING" 16 | 17 | vars = "vars" 18 | ) 19 | 20 | var ( 21 | interpretedVar = regexp.MustCompile(`\$\((.+?)(\{(.+)\}(.+)?)?\)`) 22 | defaultExtractor = regexp.MustCompile(`\|('|")(.+?)('|")`) 23 | stringExtractor = regexp.MustCompile(`('|")(.+)('|")`) 24 | 25 | closeVars = regexp.MustCompile("") 26 | ) 27 | 28 | func parseVariables(b []byte, req *http.Request) string { 29 | interprets := interpretedVar.FindSubmatch(b) 30 | 31 | if interprets != nil { 32 | switch string(interprets[1]) { 33 | case httpAcceptLanguage: 34 | if strings.Contains(req.Header.Get("Accept-Language"), string(interprets[3])) { 35 | return "true" 36 | } 37 | case httpCookie: 38 | if c, e := req.Cookie(string(interprets[3])); e == nil && c.Value != "" { 39 | return c.Value 40 | } 41 | case httpHost: 42 | return req.Host 43 | case httpReferrer: 44 | return req.Referer() 45 | case httpUserAgent: 46 | return req.UserAgent() 47 | case httpQueryString: 48 | if q := req.URL.Query().Get(string(interprets[3])); q != "" { 49 | return q 50 | } 51 | } 52 | 53 | if len(interprets) > 3 { 54 | defaultValues := defaultExtractor.FindSubmatch(interprets[4]) 55 | 56 | if len(defaultValues) > 2 { 57 | return string(defaultValues[2]) 58 | } 59 | 60 | return "" 61 | } 62 | } else { 63 | strs := stringExtractor.FindSubmatch(b) 64 | 65 | if len(strs) > 2 { 66 | return string(strs[2]) 67 | } 68 | } 69 | 70 | return string(b) 71 | } 72 | 73 | type varsTag struct { 74 | *baseTag 75 | } 76 | 77 | // Input (e.g. comment text="This is a comment." />). 78 | func (c *varsTag) Process(b []byte, req *http.Request) ([]byte, int) { 79 | found := closeVars.FindIndex(b) 80 | if found == nil { 81 | return nil, len(b) 82 | } 83 | 84 | c.length = found[1] 85 | 86 | return interpretedVar.ReplaceAllFunc(b[5:found[0]], func(b []byte) []byte { 87 | return []byte(parseVariables(b, req)) 88 | }), c.length 89 | } 90 | 91 | func (*varsTag) HasClose(b []byte) bool { 92 | return closeVars.FindIndex(b) != nil 93 | } 94 | 95 | func (*varsTag) GetClosePosition(b []byte) int { 96 | if idx := closeVars.FindIndex(b); idx != nil { 97 | return idx[1] 98 | } 99 | return 0 100 | } 101 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/esi/when.go: -------------------------------------------------------------------------------- 1 | package esi 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | unaryNegation = regexp.MustCompile(`!\((\$\((.+)\)|(.+))\)`) 11 | comparison = regexp.MustCompile(`(.+)(==|!=|<=|>=|<|>)(.+)`) 12 | logicalAnd = regexp.MustCompile(`\((.+?)\)&\((.+?)\)`) 13 | logicalOr = regexp.MustCompile(`\((.+?)\)\|\((.+?)\)`) 14 | ) 15 | 16 | func validateTest(b []byte, req *http.Request) bool { 17 | if r := unaryNegation.FindSubmatch(b); r != nil { 18 | return !validateTest(r[1], req) 19 | } else if r := logicalAnd.FindSubmatch(b); r != nil { 20 | return validateTest(r[1], req) && validateTest(r[2], req) 21 | } else if r := logicalOr.FindSubmatch(b); r != nil { 22 | return validateTest(r[1], req) || validateTest(r[2], req) 23 | } else if r := comparison.FindSubmatch(b); r != nil { 24 | r1 := strings.TrimSpace(parseVariables(r[1], req)) 25 | r2 := strings.TrimSpace(parseVariables(r[3], req)) 26 | switch string(r[2]) { 27 | case "==": 28 | return r1 == r2 29 | case "!=": 30 | return r1 != r2 31 | case "<": 32 | return r1 < r2 33 | case ">": 34 | return r1 > r2 35 | case "<=": 36 | return r1 <= r2 37 | case ">=": 38 | return r1 >= r2 39 | } 40 | } else { 41 | vars := interpretedVar.FindSubmatch(b) 42 | if vars == nil { 43 | return false 44 | } 45 | 46 | return parseVariables(vars[0], req) == "true" 47 | } 48 | 49 | return false 50 | } 51 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/github.com/darkweak/go-esi/writer/writer.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | 7 | "github.com/darkweak/go-esi/esi" 8 | ) 9 | 10 | type Writer struct { 11 | buf *bytes.Buffer 12 | rw http.ResponseWriter 13 | Rq *http.Request 14 | AsyncBuf []chan []byte 15 | Done chan bool 16 | flushed bool 17 | Iteration int 18 | } 19 | 20 | func NewWriter(buf *bytes.Buffer, rw http.ResponseWriter, rq *http.Request) *Writer { 21 | return &Writer{ 22 | buf: buf, 23 | Rq: rq, 24 | rw: rw, 25 | AsyncBuf: make([]chan []byte, 0), 26 | Done: make(chan bool), 27 | } 28 | } 29 | 30 | // Header implements http.ResponseWriter 31 | func (w *Writer) Header() http.Header { 32 | return w.rw.Header() 33 | } 34 | 35 | // WriteHeader implements http.ResponseWriter 36 | func (w *Writer) WriteHeader(statusCode int) { 37 | if statusCode == 0 { 38 | w.rw.WriteHeader(http.StatusOK) 39 | } 40 | } 41 | 42 | // Flush implements http.Flusher 43 | func (w *Writer) Flush() { 44 | if !w.flushed { 45 | w.rw.(http.Flusher).Flush() 46 | w.flushed = true 47 | } 48 | } 49 | 50 | // Write will write the response body 51 | func (w *Writer) Write(b []byte) (int, error) { 52 | buf := append(w.buf.Bytes(), b...) 53 | w.buf.Reset() 54 | 55 | if esi.HasOpenedTags(buf) { 56 | position := 0 57 | for position < len(buf) { 58 | startPos, nextPos, t := esi.ReadToTag(buf[position:], position) 59 | 60 | if startPos != 0 { 61 | w.AsyncBuf = append(w.AsyncBuf, make(chan []byte)) 62 | go func(tmpBuf []byte, i int, cw *Writer) { 63 | cw.AsyncBuf[i] <- tmpBuf 64 | }(buf[position:position+startPos], w.Iteration, w) 65 | w.Iteration++ 66 | } 67 | 68 | if t == nil { 69 | break 70 | } 71 | 72 | closePosition := t.GetClosePosition(buf[position+startPos:]) 73 | if closePosition == 0 { 74 | position += startPos 75 | break 76 | } 77 | 78 | position += nextPos 79 | w.AsyncBuf = append(w.AsyncBuf, make(chan []byte)) 80 | go func(currentTag esi.Tag, tmpBuf []byte, cw *Writer, Iteration int) { 81 | p, _ := currentTag.Process(tmpBuf, cw.Rq) 82 | cw.AsyncBuf[Iteration] <- p 83 | }(t, buf[position:(position-nextPos)+startPos+closePosition], w, w.Iteration) 84 | position += startPos + closePosition - nextPos 85 | w.Iteration++ 86 | } 87 | w.buf.Write(buf[position:]) 88 | return len(b), nil 89 | } 90 | 91 | w.AsyncBuf = append(w.AsyncBuf, make(chan []byte)) 92 | w.AsyncBuf[w.Iteration] <- buf 93 | w.Iteration++ 94 | return len(b), nil 95 | } 96 | 97 | var _ http.ResponseWriter = (*Writer)(nil) 98 | -------------------------------------------------------------------------------- /middleware/traefik/vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/darkweak/go-esi v0.0.5 => ../.. 2 | ## explicit; go 1.18 3 | github.com/darkweak/go-esi/esi 4 | github.com/darkweak/go-esi/writer 5 | -------------------------------------------------------------------------------- /writer/writer.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | 7 | "github.com/darkweak/go-esi/esi" 8 | ) 9 | 10 | type Writer struct { 11 | buf *bytes.Buffer 12 | rw http.ResponseWriter 13 | Rq *http.Request 14 | AsyncBuf []chan []byte 15 | Done chan bool 16 | flushed bool 17 | Iteration int 18 | } 19 | 20 | func NewWriter(buf *bytes.Buffer, rw http.ResponseWriter, rq *http.Request) *Writer { 21 | if rq.URL.Scheme == "" { 22 | if rq.TLS != nil { 23 | rq.URL.Scheme = "https" 24 | } else { 25 | rq.URL.Scheme = "http" 26 | } 27 | } 28 | 29 | if rq.URL.Host == "" { 30 | rq.URL.Host = rq.Host 31 | } 32 | 33 | return &Writer{ 34 | buf: buf, 35 | Rq: rq, 36 | rw: rw, 37 | AsyncBuf: make([]chan []byte, 0), 38 | Done: make(chan bool), 39 | } 40 | } 41 | 42 | // Header implements http.ResponseWriter. 43 | func (w *Writer) Header() http.Header { 44 | return w.rw.Header() 45 | } 46 | 47 | // WriteHeader implements http.ResponseWriter. 48 | func (w *Writer) WriteHeader(statusCode int) { 49 | if statusCode == 0 { 50 | w.rw.WriteHeader(http.StatusOK) 51 | } 52 | } 53 | 54 | // Flush implements http.Flusher. 55 | func (w *Writer) Flush() { 56 | if !w.flushed { 57 | if flusher, ok := w.rw.(http.Flusher); ok { 58 | flusher.Flush() 59 | } 60 | 61 | w.flushed = true 62 | } 63 | } 64 | 65 | // Write will write the response body. 66 | func (w *Writer) Write(b []byte) (int, error) { 67 | buf := append(w.buf.Bytes(), b...) 68 | w.buf.Reset() 69 | 70 | if esi.HasOpenedTags(buf) { 71 | position := 0 72 | for position < len(buf) { 73 | startPos, nextPos, t := esi.ReadToTag(buf[position:], position) 74 | 75 | if startPos != 0 { 76 | w.AsyncBuf = append(w.AsyncBuf, make(chan []byte)) 77 | go func(tmpBuf []byte, i int, cw *Writer) { 78 | cw.AsyncBuf[i] <- tmpBuf 79 | }(buf[position:position+startPos], w.Iteration, w) 80 | w.Iteration++ 81 | } 82 | 83 | if t == nil { 84 | break 85 | } 86 | 87 | closePosition := t.GetClosePosition(buf[position+startPos:]) 88 | if closePosition == 0 { 89 | position += startPos 90 | 91 | break 92 | } 93 | 94 | position += nextPos 95 | 96 | w.AsyncBuf = append(w.AsyncBuf, make(chan []byte)) 97 | 98 | go func(currentTag esi.Tag, tmpBuf []byte, cw *Writer, Iteration int) { 99 | p, _ := currentTag.Process(tmpBuf, cw.Rq) 100 | cw.AsyncBuf[Iteration] <- p 101 | }(t, buf[position:(position-nextPos)+startPos+closePosition], w, w.Iteration) 102 | 103 | position += startPos + closePosition - nextPos 104 | w.Iteration++ 105 | } 106 | w.buf.Write(buf[position:]) 107 | 108 | return len(b), nil 109 | } 110 | 111 | w.AsyncBuf = append(w.AsyncBuf, make(chan []byte)) 112 | w.AsyncBuf[w.Iteration] <- buf 113 | w.Iteration++ 114 | 115 | return len(b), nil 116 | } 117 | 118 | var _ http.ResponseWriter = (*Writer)(nil) 119 | --------------------------------------------------------------------------------