Skip to content

Commit

Permalink
Merge branch 'master' into http3-default
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Aug 3, 2022
2 parents 920dd34 + 1960a0d commit 312458d
Show file tree
Hide file tree
Showing 134 changed files with 4,974 additions and 1,412 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.go text eol=lf
25 changes: 14 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,16 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
go: [ '1.17', '1.18' ]
go: [ '1.18', '1.19' ]

include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.17'
GO_SEMVER: '~1.17.9'

- go: '1.18'
GO_SEMVER: '~1.18.1'
GO_SEMVER: '~1.18.4'

# Go 1.18.1 isn't released yet for Mac as of Apr 13 2022
- go: '1.18'
os: 'macos-latest'
GO_SEMVER: '1.18.0'
- go: '1.19'
GO_SEMVER: '~1.19.0'

# Set some variables per OS, usable via ${{ matrix.VAR }}
# CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
Expand Down Expand Up @@ -83,12 +78,20 @@ jobs:
printf "Git version: $(git version)\n\n"
# Calculate the short SHA1 hash of the git commit
echo "::set-output name=short_sha::$(git rev-parse --short HEAD)"
echo "::set-output name=go_cache::$(go env GOCACHE)"
- name: Cache the build cache
uses: actions/cache@v2
with:
path: ${{ steps.vars.outputs.go_cache }}
# In order:
# * Module download cache
# * Build cache (Linux)
# * Build cache (Mac)
# * Build cache (Windows)
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
~\AppData\Local\go-build
key: ${{ runner.os }}-${{ matrix.go }}-go-ci-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go }}-go-ci
Expand Down
14 changes: 9 additions & 5 deletions .github/workflows/cross-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ jobs:
fail-fast: false
matrix:
goos: ['android', 'linux', 'solaris', 'illumos', 'dragonfly', 'freebsd', 'openbsd', 'plan9', 'windows', 'darwin', 'netbsd']
go: [ '1.18' ]
go: [ '1.19' ]

include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.18'
GO_SEMVER: '~1.18.1'
- go: '1.19'
GO_SEMVER: '~1.19.0'

runs-on: ubuntu-latest
continue-on-error: true
Expand All @@ -42,12 +42,16 @@ jobs:
go env
printf "\n\nSystem environment:\n\n"
env
echo "::set-output name=go_cache::$(go env GOCACHE)"
- name: Cache the build cache
uses: actions/cache@v2
with:
path: ${{ steps.vars.outputs.go_cache }}
# In order:
# * Module download cache
# * Build cache (Linux)
path: |
~/go/pkg/mod
~/.cache/go-build
key: cross-build-go${{ matrix.go }}-${{ matrix.goos }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
cross-build-go${{ matrix.go }}-${{ matrix.goos }}
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ jobs:
# From https://github.com/golangci/golangci-lint-action
golangci:
name: lint
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '~1.17.9'
go-version: '~1.18.4'
check-latest: true

- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.44
version: v1.47
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
14 changes: 9 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest ]
go: [ '1.18' ]
go: [ '1.19' ]

include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.18'
GO_SEMVER: '~1.18.1'
- go: '1.19'
GO_SEMVER: '~1.19.0'

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -56,7 +56,6 @@ jobs:
env
echo "::set-output name=version_tag::${GITHUB_REF/refs\/tags\//}"
echo "::set-output name=short_sha::$(git rev-parse --short HEAD)"
echo "::set-output name=go_cache::$(go env GOCACHE)"
# Add "pip install" CLI tools to PATH
echo ~/.local/bin >> $GITHUB_PATH
Expand Down Expand Up @@ -91,7 +90,12 @@ jobs:
- name: Cache the build cache
uses: actions/cache@v2
with:
path: ${{ steps.vars.outputs.go_cache }}
# In order:
# * Module download cache
# * Build cache (Linux)
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go${{ matrix.go }}-release-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-release
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
_gitignore/
*.log
Caddyfile
Caddyfile.*
!caddyfile/

# artifacts from pprof tooling
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ For other install options, see https://caddyserver.com/docs/install.

Requirements:

- [Go 1.17 or newer](https://golang.org/dl/)
- [Go 1.18 or newer](https://golang.org/dl/)

### For development

Expand Down
58 changes: 40 additions & 18 deletions admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"errors"
"expvar"
"fmt"
"hash"
"hash/fnv"
"io"
"net"
"net/http"
Expand Down Expand Up @@ -439,7 +441,7 @@ func manageIdentity(ctx Context, cfg *Config) error {
if err != nil {
return fmt.Errorf("loading identity issuer modules: %s", err)
}
for _, issVal := range val.([]interface{}) {
for _, issVal := range val.([]any) {
cfg.Admin.Identity.issuers = append(cfg.Admin.Identity.issuers, issVal.(certmagic.Issuer))
}
}
Expand Down Expand Up @@ -894,16 +896,36 @@ func (h adminHandler) originAllowed(origin *url.URL) bool {
return false
}

// etagHasher returns a the hasher we used on the config to both
// produce and verify ETags.
func etagHasher() hash.Hash32 { return fnv.New32a() }

// makeEtag returns an Etag header value (including quotes) for
// the given config path and hash of contents at that path.
func makeEtag(path string, hash hash.Hash) string {
return fmt.Sprintf(`"%s %x"`, path, hash.Sum(nil))
}

func handleConfig(w http.ResponseWriter, r *http.Request) error {
switch r.Method {
case http.MethodGet:
w.Header().Set("Content-Type", "application/json")

err := readConfig(r.URL.Path, w)
// Set the ETag as a trailer header.
// The alternative is to write the config to a buffer, and
// then hash that.
w.Header().Set("Trailer", "ETag")

hash := etagHasher()
configWriter := io.MultiWriter(w, hash)
err := readConfig(r.URL.Path, configWriter)
if err != nil {
return APIError{HTTPStatus: http.StatusBadRequest, Err: err}
}

// we could consider setting up a sync.Pool for the summed
// hashes to reduce GC pressure.
w.Header().Set("Etag", makeEtag(r.URL.Path, hash))

return nil

case http.MethodPost,
Expand Down Expand Up @@ -937,7 +959,7 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {

forceReload := r.Header.Get("Cache-Control") == "must-revalidate"

err := changeConfig(r.Method, r.URL.Path, body, forceReload)
err := changeConfig(r.Method, r.URL.Path, body, r.Header.Get("If-Match"), forceReload)
if err != nil && !errors.Is(err, errSameConfig) {
return err
}
Expand Down Expand Up @@ -971,9 +993,9 @@ func handleConfigID(w http.ResponseWriter, r *http.Request) error {
id := parts[2]

// map the ID to the expanded path
currentCfgMu.RLock()
currentCtxMu.RLock()
expanded, ok := rawCfgIndex[id]
defer currentCfgMu.RUnlock()
defer currentCtxMu.RUnlock()
if !ok {
return APIError{
HTTPStatus: http.StatusNotFound,
Expand Down Expand Up @@ -1008,11 +1030,11 @@ func handleStop(w http.ResponseWriter, r *http.Request) error {
// the operation at path according to method, using body and out as
// needed. This is a low-level, unsynchronized function; most callers
// will want to use changeConfig or readConfig instead. This requires a
// read or write lock on currentCfgMu, depending on method (GET needs
// read or write lock on currentCtxMu, depending on method (GET needs
// only a read lock; all others need a write lock).
func unsyncedConfigAccess(method, path string, body []byte, out io.Writer) error {
var err error
var val interface{}
var val any

// if there is a request body, decode it into the
// variable that will be set in the config according
Expand Down Expand Up @@ -1049,16 +1071,16 @@ func unsyncedConfigAccess(method, path string, body []byte, out io.Writer) error
parts = parts[:len(parts)-1]
}

var ptr interface{} = rawCfg
var ptr any = rawCfg

traverseLoop:
for i, part := range parts {
switch v := ptr.(type) {
case map[string]interface{}:
case map[string]any:
// if the next part enters a slice, and the slice is our destination,
// handle it specially (because appending to the slice copies the slice
// header, which does not replace the original one like we want)
if arr, ok := v[part].([]interface{}); ok && i == len(parts)-2 {
if arr, ok := v[part].([]any); ok && i == len(parts)-2 {
var idx int
if method != http.MethodPost {
idxStr := parts[len(parts)-1]
Expand All @@ -1080,7 +1102,7 @@ traverseLoop:
}
case http.MethodPost:
if ellipses {
valArray, ok := val.([]interface{})
valArray, ok := val.([]any)
if !ok {
return fmt.Errorf("final element is not an array")
}
Expand Down Expand Up @@ -1115,9 +1137,9 @@ traverseLoop:
case http.MethodPost:
// if the part is an existing list, POST appends to
// it, otherwise it just sets or creates the value
if arr, ok := v[part].([]interface{}); ok {
if arr, ok := v[part].([]any); ok {
if ellipses {
valArray, ok := val.([]interface{})
valArray, ok := val.([]any)
if !ok {
return fmt.Errorf("final element is not an array")
}
Expand Down Expand Up @@ -1148,12 +1170,12 @@ traverseLoop:
// might not exist yet; that's OK but we need to make them as
// we go, while we still have a pointer from the level above
if v[part] == nil && method == http.MethodPut {
v[part] = make(map[string]interface{})
v[part] = make(map[string]any)
}
ptr = v[part]
}

case []interface{}:
case []any:
partInt, err := strconv.Atoi(part)
if err != nil {
return fmt.Errorf("[/%s] invalid array index '%s': %v",
Expand All @@ -1175,7 +1197,7 @@ traverseLoop:

// RemoveMetaFields removes meta fields like "@id" from a JSON message
// by using a simple regular expression. (An alternate way to do this
// would be to delete them from the raw, map[string]interface{}
// would be to delete them from the raw, map[string]any
// representation as they are indexed, then iterate the index we made
// and add them back after encoding as JSON, but this is simpler.)
func RemoveMetaFields(rawJSON []byte) []byte {
Expand Down Expand Up @@ -1307,7 +1329,7 @@ const (
)

var bufPool = sync.Pool{
New: func() interface{} {
New: func() any {
return new(bytes.Buffer)
},
}
Expand Down

0 comments on commit 312458d

Please sign in to comment.