summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2019-08-21 11:28:03 -0600
committerGitHub <noreply@github.com>2019-08-21 11:28:03 -0600
commit0544f0266a11874efb305a46db762248e64bc62d (patch)
tree3e75e6f59047bb967c52915e1e75aee6ad67c0ea /modules
parent42f75a4ca94ef3fb5e15a74e5dc9ef8b4f1f0b39 (diff)
parentb2aa679c33f63ebec5bc1a21bca01f345dffebdd (diff)
Merge pull request #2699 from caddyserver/cfadapter
v2: Implement config adapters and WIP Caddyfile adapter
Diffstat (limited to 'modules')
-rw-r--r--modules/caddyhttp/caddyhttp.go105
-rw-r--r--modules/caddyhttp/caddylog/log.go13
-rw-r--r--modules/caddyhttp/encode/brotli/brotli.go36
-rw-r--r--modules/caddyhttp/encode/caddyfile.go99
-rw-r--r--modules/caddyhttp/encode/encode.go45
-rw-r--r--modules/caddyhttp/encode/gzip/gzip.go38
-rw-r--r--modules/caddyhttp/encode/zstd/zstd.go26
-rw-r--r--modules/caddyhttp/fileserver/caddyfile.go104
-rw-r--r--modules/caddyhttp/fileserver/matcher.go52
-rw-r--r--modules/caddyhttp/fileserver/staticfiles.go17
-rw-r--r--modules/caddyhttp/headers/caddyfile.go91
-rw-r--r--modules/caddyhttp/headers/headers.go13
-rw-r--r--modules/caddyhttp/markdown/markdown.go13
-rw-r--r--modules/caddyhttp/matchers.go258
-rw-r--r--modules/caddyhttp/replacer.go37
-rw-r--r--modules/caddyhttp/requestbody/requestbody.go13
-rwxr-xr-xmodules/caddyhttp/reverseproxy/module.go34
-rwxr-xr-xmodules/caddyhttp/reverseproxy/upstream.go22
-rw-r--r--modules/caddyhttp/rewrite/caddyfile.go37
-rw-r--r--modules/caddyhttp/rewrite/rewrite.go13
-rw-r--r--modules/caddyhttp/routes.go53
-rw-r--r--modules/caddyhttp/server.go14
-rw-r--r--modules/caddyhttp/staticerror.go56
-rw-r--r--modules/caddyhttp/staticresp.go56
-rw-r--r--modules/caddyhttp/staticresp_test.go2
-rw-r--r--modules/caddyhttp/subroute.go13
-rw-r--r--modules/caddyhttp/table.go55
-rw-r--r--modules/caddyhttp/templates/caddyfile.go62
-rw-r--r--modules/caddyhttp/templates/templates.go16
-rw-r--r--modules/caddyhttp/templates/tplcontext.go13
-rw-r--r--modules/caddyhttp/vars.go81
-rw-r--r--modules/caddytls/acmemanager.go19
-rw-r--r--modules/caddytls/connpolicy.go8
-rw-r--r--modules/caddytls/fileloader.go21
-rw-r--r--modules/caddytls/folderloader.go21
-rw-r--r--modules/caddytls/matchers.go13
-rw-r--r--modules/caddytls/sessiontickets.go2
-rw-r--r--modules/caddytls/standardstek/stek.go13
-rw-r--r--modules/caddytls/tls.go52
-rw-r--r--modules/caddytls/values.go36
40 files changed, 1300 insertions, 372 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index 467b40f..b4b1ec6 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -15,9 +15,12 @@
package caddyhttp
import (
+ "bytes"
"context"
"crypto/tls"
+ "encoding/json"
"fmt"
+ "io"
"log"
weakrand "math/rand"
"net"
@@ -34,10 +37,7 @@ import (
func init() {
weakrand.Seed(time.Now().UnixNano())
- err := caddy.RegisterModule(caddy.Module{
- Name: "http",
- New: func() interface{} { return new(App) },
- })
+ err := caddy.RegisterModule(App{})
if err != nil {
log.Fatal(err)
}
@@ -55,6 +55,14 @@ type App struct {
ctx caddy.Context
}
+// CaddyModule returns the Caddy module information.
+func (App) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http",
+ New: func() caddy.Module { return new(App) },
+ }
+}
+
// Provision sets up the app.
func (app *App) Provision(ctx caddy.Context) error {
app.ctx = ctx
@@ -224,7 +232,7 @@ func (app *App) automaticHTTPS() error {
// find all qualifying domain names, de-duplicated
domainSet := make(map[string]struct{})
for _, route := range srv.Routes {
- for _, matcherSet := range route.matcherSets {
+ for _, matcherSet := range route.MatcherSets {
for _, m := range matcherSet {
if hm, ok := m.(*MatchHost); ok {
for _, d := range *hm {
@@ -244,6 +252,14 @@ func (app *App) automaticHTTPS() error {
for d := range domainSet {
domains = append(domains, d)
if !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) {
+ // if a certificate for this name is already loaded,
+ // don't obtain another one for it, unless we are
+ // supposed to ignore loaded certificates
+ if !srv.AutoHTTPS.IgnoreLoadedCerts &&
+ len(tlsApp.CertificatesWithSAN(d)) > 0 {
+ log.Printf("[INFO][%s] Skipping automatic certificate management because a certificate with that SAN is already loaded", d)
+ continue
+ }
domainsForCerts = append(domainsForCerts, d)
}
}
@@ -319,16 +335,16 @@ func (app *App) automaticHTTPS() error {
}
redirTo += "{http.request.uri}"
- redirRoutes = append(redirRoutes, ServerRoute{
- matcherSets: []MatcherSet{
+ redirRoutes = append(redirRoutes, Route{
+ MatcherSets: []MatcherSet{
{
MatchProtocol("http"),
MatchHost(domains),
},
},
- handlers: []MiddlewareHandler{
+ Handlers: []MiddlewareHandler{
StaticResponse{
- StatusCode: weakString(strconv.Itoa(http.StatusTemporaryRedirect)), // TODO: use permanent redirect instead
+ StatusCode: WeakString(strconv.Itoa(http.StatusTemporaryRedirect)), // TODO: use permanent redirect instead
Headers: http.Header{
"Location": []string{redirTo},
"Connection": []string{"close"},
@@ -431,6 +447,77 @@ type MiddlewareHandler interface {
// emptyHandler is used as a no-op handler.
var emptyHandler HandlerFunc = func(http.ResponseWriter, *http.Request) error { return nil }
+// WeakString is a type that unmarshals any JSON value
+// as a string literal, with the following exceptions:
+// 1) actual string values are decoded as strings, and
+// 2) null is decoded as empty string
+// and provides methods for getting the value as various
+// primitive types. However, using this type removes any
+// type safety as far as deserializing JSON is concerned.
+type WeakString string
+
+// UnmarshalJSON satisfies json.Unmarshaler according to
+// this type's documentation.
+func (ws *WeakString) UnmarshalJSON(b []byte) error {
+ if len(b) == 0 {
+ return io.EOF
+ }
+ if b[0] == byte('"') && b[len(b)-1] == byte('"') {
+ var s string
+ err := json.Unmarshal(b, &s)
+ if err != nil {
+ return err
+ }
+ *ws = WeakString(s)
+ return nil
+ }
+ if bytes.Equal(b, []byte("null")) {
+ return nil
+ }
+ *ws = WeakString(b)
+ return nil
+}
+
+// MarshalJSON marshals was a boolean if true or false,
+// a number if an integer, or a string otherwise.
+func (ws WeakString) MarshalJSON() ([]byte, error) {
+ if ws == "true" {
+ return []byte("true"), nil
+ }
+ if ws == "false" {
+ return []byte("false"), nil
+ }
+ if num, err := strconv.Atoi(string(ws)); err == nil {
+ return json.Marshal(num)
+ }
+ return json.Marshal(string(ws))
+}
+
+// Int returns ws as an integer. If ws is not an
+// integer, 0 is returned.
+func (ws WeakString) Int() int {
+ num, _ := strconv.Atoi(string(ws))
+ return num
+}
+
+// Float64 returns ws as a float64. If ws is not a
+// float value, the zero value is returned.
+func (ws WeakString) Float64() float64 {
+ num, _ := strconv.ParseFloat(string(ws), 64)
+ return num
+}
+
+// Bool returns ws as a boolean. If ws is not a
+// boolean, false is returned.
+func (ws WeakString) Bool() bool {
+ return string(ws) == "true"
+}
+
+// String returns ws as a string.
+func (ws WeakString) String() string {
+ return string(ws)
+}
+
const (
// DefaultHTTPPort is the default port for HTTP.
DefaultHTTPPort = 80
diff --git a/modules/caddyhttp/caddylog/log.go b/modules/caddyhttp/caddylog/log.go
index 902f60f..3f636d1 100644
--- a/modules/caddyhttp/caddylog/log.go
+++ b/modules/caddyhttp/caddylog/log.go
@@ -24,10 +24,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.log",
- New: func() interface{} { return new(Log) },
- })
+ caddy.RegisterModule(Log{})
}
// Log implements a simple logging middleware.
@@ -36,6 +33,14 @@ type Log struct {
counter int
}
+// CaddyModule returns the Caddy module information.
+func (Log) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.log",
+ New: func() caddy.Module { return new(Log) },
+ }
+}
+
func (l *Log) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
start := time.Now()
diff --git a/modules/caddyhttp/encode/brotli/brotli.go b/modules/caddyhttp/encode/brotli/brotli.go
index 0890d43..cf055aa 100644
--- a/modules/caddyhttp/encode/brotli/brotli.go
+++ b/modules/caddyhttp/encode/brotli/brotli.go
@@ -16,17 +16,16 @@ package caddybrotli
import (
"fmt"
+ "strconv"
"github.com/andybalholm/brotli"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/encode"
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.encoders.brotli",
- New: func() interface{} { return new(Brotli) },
- })
+ caddy.RegisterModule(Brotli{})
}
// Brotli can create brotli encoders. Note that brotli
@@ -35,6 +34,30 @@ type Brotli struct {
Quality *int `json:"quality,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (Brotli) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.encoders.brotli",
+ New: func() caddy.Module { return new(Brotli) },
+ }
+}
+
+// UnmarshalCaddyfile sets up the handler from Caddyfile tokens.
+func (b *Brotli) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ if !d.NextArg() {
+ continue
+ }
+ qualityStr := d.Val()
+ quality, err := strconv.Atoi(qualityStr)
+ if err != nil {
+ return err
+ }
+ b.Quality = &quality
+ }
+ return nil
+}
+
// Validate validates b's configuration.
func (b Brotli) Validate() error {
if b.Quality != nil {
@@ -64,6 +87,7 @@ func (b Brotli) NewEncoder() encode.Encoder {
// Interface guards
var (
- _ encode.Encoding = (*Brotli)(nil)
- _ caddy.Validator = (*Brotli)(nil)
+ _ encode.Encoding = (*Brotli)(nil)
+ _ caddy.Validator = (*Brotli)(nil)
+ _ caddyfile.Unmarshaler = (*Brotli)(nil)
)
diff --git a/modules/caddyhttp/encode/caddyfile.go b/modules/caddyhttp/encode/caddyfile.go
new file mode 100644
index 0000000..5762bd3
--- /dev/null
+++ b/modules/caddyhttp/encode/caddyfile.go
@@ -0,0 +1,99 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package encode
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
+)
+
+func init() {
+ httpcaddyfile.RegisterHandlerDirective("encode", parseCaddyfile)
+}
+
+// TODO: This is a good example of why UnmarshalCaddyfile is still a good idea... hmm.
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+ enc := new(Encode)
+ err := enc.UnmarshalCaddyfile(h.Dispenser)
+ if err != nil {
+ return nil, err
+ }
+ return enc, nil
+}
+
+// TODO: Keep UnmarshalCaddyfile pattern?
+
+// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
+//
+// encode [<matcher>] <formats...> {
+// gzip [<level>]
+// zstd
+// brotli [<quality>]
+// }
+//
+// Specifying the formats on the first line will use those formats' defaults.
+func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ for _, arg := range d.RemainingArgs() {
+ mod, err := caddy.GetModule("http.encoders." + arg)
+ if err != nil {
+ return fmt.Errorf("finding encoder module '%s': %v", mod.Name, err)
+ }
+ encoding, ok := mod.New().(Encoding)
+ if !ok {
+ return fmt.Errorf("module %s is not an HTTP encoding", mod.Name)
+ }
+ if enc.EncodingsRaw == nil {
+ enc.EncodingsRaw = make(map[string]json.RawMessage)
+ }
+ enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)
+ }
+
+ for d.NextBlock() {
+ name := d.Val()
+ mod, err := caddy.GetModule("http.encoders." + name)
+ if err != nil {
+ return fmt.Errorf("getting encoder module '%s': %v", mod.Name, err)
+ }
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
+ if !ok {
+ return fmt.Errorf("encoder module '%s' is not a Caddyfile unmarshaler", mod.Name)
+ }
+ err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
+ if err != nil {
+ return err
+ }
+ encoding, ok := unm.(Encoding)
+ if !ok {
+ return fmt.Errorf("module %s is not an HTTP encoding", mod.Name)
+ }
+ if enc.EncodingsRaw == nil {
+ enc.EncodingsRaw = make(map[string]json.RawMessage)
+ }
+ enc.EncodingsRaw[name] = caddyconfig.JSON(encoding, nil)
+ }
+ }
+
+ return nil
+}
+
+// Interface guard
+var _ caddyfile.Unmarshaler = (*Encode)(nil)
diff --git a/modules/caddyhttp/encode/encode.go b/modules/caddyhttp/encode/encode.go
index b2c1327..723b988 100644
--- a/modules/caddyhttp/encode/encode.go
+++ b/modules/caddyhttp/encode/encode.go
@@ -35,10 +35,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.encode",
- New: func() interface{} { return new(Encode) },
- })
+ caddy.RegisterModule(Encode{})
}
// Encode is a middleware which can encode responses.
@@ -50,21 +47,25 @@ type Encode struct {
writerPools map[string]*sync.Pool // TODO: these pools do not get reused through config reloads...
}
+// CaddyModule returns the Caddy module information.
+func (Encode) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.encode",
+ New: func() caddy.Module { return new(Encode) },
+ }
+}
+
// Provision provisions enc.
func (enc *Encode) Provision(ctx caddy.Context) error {
- enc.writerPools = make(map[string]*sync.Pool)
-
for modName, rawMsg := range enc.EncodingsRaw {
val, err := ctx.LoadModule("http.encoders."+modName, rawMsg)
if err != nil {
return fmt.Errorf("loading encoder module '%s': %v", modName, err)
}
- encoder := val.(Encoding)
-
- enc.writerPools[encoder.AcceptEncoding()] = &sync.Pool{
- New: func() interface{} {
- return encoder.NewEncoder()
- },
+ encoding := val.(Encoding)
+ err = enc.addEncoding(encoding)
+ if err != nil {
+ return err
}
}
enc.EncodingsRaw = nil // allow GC to deallocate - TODO: Does this help?
@@ -85,10 +86,28 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
defer w.(*responseWriter).Close()
break
}
-
return next.ServeHTTP(w, r)
}
+func (enc *Encode) addEncoding(e Encoding) error {
+ ae := e.AcceptEncoding()
+ if ae == "" {
+ return fmt.Errorf("encoder does not specify an Accept-Encoding value")
+ }
+ if _, ok := enc.writerPools[ae]; ok {
+ return fmt.Errorf("encoder already added: %s", ae)
+ }
+ if enc.writerPools == nil {
+ enc.writerPools = make(map[string]*sync.Pool)
+ }
+ enc.writerPools[ae] = &sync.Pool{
+ New: func() interface{} {
+ return e.NewEncoder()
+ },
+ }
+ return nil
+}
+
// openResponseWriter creates a new response writer that may (or may not)
// encode the response with encodingName. The returned response writer MUST
// be closed after the handler completes.
diff --git a/modules/caddyhttp/encode/gzip/gzip.go b/modules/caddyhttp/encode/gzip/gzip.go
index 45c5f54..d6d67f7 100644
--- a/modules/caddyhttp/encode/gzip/gzip.go
+++ b/modules/caddyhttp/encode/gzip/gzip.go
@@ -18,16 +18,15 @@ import (
"compress/flate"
"compress/gzip" // TODO: consider using https://github.com/klauspost/compress/gzip
"fmt"
+ "strconv"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/encode"
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.encoders.gzip",
- New: func() interface{} { return new(Gzip) },
- })
+ caddy.RegisterModule(Gzip{})
}
// Gzip can create gzip encoders.
@@ -35,6 +34,30 @@ type Gzip struct {
Level int `json:"level,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (Gzip) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.encoders.gzip",
+ New: func() caddy.Module { return new(Gzip) },
+ }
+}
+
+// UnmarshalCaddyfile sets up the handler from Caddyfile tokens.
+func (g *Gzip) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ if !d.NextArg() {
+ continue
+ }
+ levelStr := d.Val()
+ level, err := strconv.Atoi(levelStr)
+ if err != nil {
+ return err
+ }
+ g.Level = level
+ }
+ return nil
+}
+
// Provision provisions g's configuration.
func (g *Gzip) Provision(ctx caddy.Context) error {
if g.Level == 0 {
@@ -69,7 +92,8 @@ var defaultGzipLevel = 5
// Interface guards
var (
- _ encode.Encoding = (*Gzip)(nil)
- _ caddy.Provisioner = (*Gzip)(nil)
- _ caddy.Validator = (*Gzip)(nil)
+ _ encode.Encoding = (*Gzip)(nil)
+ _ caddy.Provisioner = (*Gzip)(nil)
+ _ caddy.Validator = (*Gzip)(nil)
+ _ caddyfile.Unmarshaler = (*Gzip)(nil)
)
diff --git a/modules/caddyhttp/encode/zstd/zstd.go b/modules/caddyhttp/encode/zstd/zstd.go
index acebff5..f2b4e85 100644
--- a/modules/caddyhttp/encode/zstd/zstd.go
+++ b/modules/caddyhttp/encode/zstd/zstd.go
@@ -16,20 +16,31 @@ package caddyzstd
import (
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/encode"
"github.com/klauspost/compress/zstd"
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.encoders.zstd",
- New: func() interface{} { return new(Zstd) },
- })
+ caddy.RegisterModule(Zstd{})
}
// Zstd can create Zstandard encoders.
type Zstd struct{}
+// CaddyModule returns the Caddy module information.
+func (Zstd) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.encoders.zstd",
+ New: func() caddy.Module { return new(Zstd) },
+ }
+}
+
+// UnmarshalCaddyfile sets up the handler from Caddyfile tokens.
+func (z *Zstd) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ return nil
+}
+
// AcceptEncoding returns the name of the encoding as
// used in the Accept-Encoding request headers.
func (Zstd) AcceptEncoding() string { return "zstd" }
@@ -40,5 +51,8 @@ func (z Zstd) NewEncoder() encode.Encoder {
return writer
}
-// Interface guard
-var _ encode.Encoding = (*Zstd)(nil)
+// Interface guards
+var (
+ _ encode.Encoding = (*Zstd)(nil)
+ _ caddyfile.Unmarshaler = (*Zstd)(nil)
+)
diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go
new file mode 100644
index 0000000..6fa94e7
--- /dev/null
+++ b/modules/caddyhttp/fileserver/caddyfile.go
@@ -0,0 +1,104 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fileserver
+
+import (
+ "encoding/json"
+
+ "github.com/caddyserver/caddy/modules/caddyhttp/rewrite"
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
+)
+
+func init() {
+ httpcaddyfile.RegisterHandlerDirective("file_server", parseCaddyfile)
+ httpcaddyfile.RegisterDirective("try_files", parseTryFiles)
+}
+
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+ var fsrv FileServer
+
+ for h.Next() {
+ args := h.RemainingArgs()
+ switch len(args) {
+ case 0:
+ case 1:
+ if args[0] != "browse" {
+ return nil, h.ArgErr()
+ }
+ fsrv.Browse = new(Browse)
+ default:
+ return nil, h.ArgErr()
+ }
+
+ for h.NextBlock() {
+ switch h.Val() {
+ case "hide":
+ fsrv.Hide = h.RemainingArgs()
+ if len(fsrv.Hide) == 0 {
+ return nil, h.ArgErr()
+ }
+ case "index":
+ fsrv.IndexNames = h.RemainingArgs()
+ if len(fsrv.Hide) == 0 {
+ return nil, h.ArgErr()
+ }
+ case "root":
+ if !h.Args(&fsrv.Root) {
+ return nil, h.ArgErr()
+ }
+ case "browse":
+ if fsrv.Browse != nil {
+ return nil, h.Err("browsing is already configured")
+ }
+ fsrv.Browse = new(Browse)
+ h.Args(&fsrv.Browse.TemplateFile)
+ default:
+ return nil, h.Errf("unknown subdirective '%s'", h.Val())
+ }
+ }
+ }
+
+ // if no root was configured explicitly, use site root
+ if fsrv.Root == "" {
+ fsrv.Root = "{http.var.root}"
+ }
+
+ return &fsrv, nil
+}
+
+func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
+ if !h.Next() {
+ return nil, h.ArgErr()
+ }
+
+ try := h.RemainingArgs()
+ if len(try) == 0 {
+ return nil, h.ArgErr()
+ }
+
+ handler := rewrite.Rewrite{
+ URI: "{http.matchers.file.relative}{http.request.uri.query}",
+ }
+
+ matcherSet := map[string]json.RawMessage{
+ "file": h.JSON(MatchFile{
+ Root: "{http.var.root}",
+ TryFiles: try,
+ }, nil),
+ }
+
+ return h.NewRoute(matcherSet, handler), nil
+}
diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go
index 17d5c11..b091250 100644
--- a/modules/caddyhttp/fileserver/matcher.go
+++ b/modules/caddyhttp/fileserver/matcher.go
@@ -21,14 +21,12 @@ import (
"time"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.file",
- New: func() interface{} { return new(MatchFile) },
- })
+ caddy.RegisterModule(MatchFile{})
}
// MatchFile is an HTTP request matcher that can match
@@ -51,6 +49,50 @@ type MatchFile struct {
TryPolicy string `json:"try_policy,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (MatchFile) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.file",
+ New: func() caddy.Module { return new(MatchFile) },
+ }
+}
+
+// UnmarshalCaddyfile sets up the matcher from Caddyfile tokens. Syntax:
+//
+// file {
+// root <path>
+// try_files <files...>
+// try_policy first_exist|smallest_size|largest_size|most_recent_modified
+// }
+//
+func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ for d.NextBlock() {
+ switch d.Val() {
+ case "root":
+ if !d.NextArg() {
+ return d.ArgErr()
+ }
+ m.Root = d.Val()
+ case "try_files":
+ m.TryFiles = d.RemainingArgs()
+ if len(m.TryFiles) == 0 {
+ return d.ArgErr()
+ }
+ case "try_policy":
+ if !d.NextArg() {
+ return d.ArgErr()
+ }
+ m.TryPolicy = d.Val()
+ }
+ }
+ }
+ if m.Root == "" {
+ m.Root = "{http.var.root}"
+ }
+ return nil
+}
+
// Validate ensures m has a valid configuration.
func (m MatchFile) Validate() error {
switch m.TryPolicy {
@@ -87,7 +129,7 @@ func (m MatchFile) Match(r *http.Request) bool {
func (m MatchFile) selectFile(r *http.Request) (rel, abs string, matched bool) {
repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
- root := repl.ReplaceAll(m.Root, "")
+ root := repl.ReplaceAll(m.Root, ".")
// if list of files to try was omitted entirely,
// assume URL path
diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go
index a66b753..cdac453 100644
--- a/modules/caddyhttp/fileserver/staticfiles.go
+++ b/modules/caddyhttp/fileserver/staticfiles.go
@@ -36,10 +36,7 @@ import (
func init() {
weakrand.Seed(time.Now().UnixNano())
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.file_server",
- New: func() interface{} { return new(FileServer) },
- })
+ caddy.RegisterModule(FileServer{})
}
// FileServer implements a static file server responder for Caddy.
@@ -48,8 +45,14 @@ type FileServer struct {
Hide []string `json:"hide,omitempty"`
IndexNames []string `json:"index_names,omitempty"`
Browse *Browse `json:"browse,omitempty"`
+}
- // TODO: Content negotiation
+// CaddyModule returns the Caddy module information.
+func (FileServer) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.file_server",
+ New: func() caddy.Module { return new(FileServer) },
+ }
}
// Provision sets up the static files responder.
@@ -83,7 +86,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd
filesToHide := fsrv.transformHidePaths(repl)
- root := repl.ReplaceAll(fsrv.Root, "")
+ root := repl.ReplaceAll(fsrv.Root, ".")
suffix := repl.ReplaceAll(r.URL.Path, "")
filename := sanitizedPathJoin(root, suffix)
@@ -302,7 +305,7 @@ func calculateEtag(d os.FileInfo) string {
return `"` + t + s + `"`
}
-var defaultIndexNames = []string{"index.html"}
+var defaultIndexNames = []string{"index.html", "index.txt"}
var bufPool = sync.Pool{
New: func() interface{} {
diff --git a/modules/caddyhttp/headers/caddyfile.go b/modules/caddyhttp/headers/caddyfile.go
new file mode 100644
index 0000000..5eaf064
--- /dev/null
+++ b/modules/caddyhttp/headers/caddyfile.go
@@ -0,0 +1,91 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package headers
+
+import (
+ "net/http"
+ "strings"
+
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
+)
+
+func init() {
+ httpcaddyfile.RegisterHandlerDirective("headers", parseCaddyfile)
+}
+
+// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
+//
+// headers [<matcher>] [[+|-]<field> <value>] {
+// [+][<field>] [<value>]
+// [-<field>]
+// }
+//
+// Either a block can be opened or a single header field can be configured
+// in the first line, but not both in the same directive.
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+ hdr := new(Headers)
+ for h.Next() {
+ // first see if headers are in the initial line
+ var hasArgs bool
+ if h.NextArg() {
+ hasArgs = true
+ field := h.Val()
+ h.NextArg()
+ value := h.Val()
+ processCaddyfileLine(hdr, field, value)
+ }
+
+ // if not, they should be in a block
+ for h.NextBlock() {
+ if hasArgs {
+ return nil, h.Err("cannot specify headers in both arguments and block")
+ }
+ field := h.Val()
+ var value string
+ if h.NextArg() {
+ value = h.Val()
+ }
+ processCaddyfileLine(hdr, field, value)
+ }
+ }
+ return hdr, nil
+}
+
+func processCaddyfileLine(hdr *Headers, field, value string) {
+ if strings.HasPrefix(field, "+") {
+ if hdr.Response == nil {
+ hdr.Response = &RespHeaderOps{HeaderOps: new(HeaderOps)}
+ }
+ if hdr.Response.Add == nil {
+ hdr.Response.Add = make(http.Header)
+ }
+ hdr.Response.Add.Set(field[1:], value)
+ } else if strings.HasPrefix(field, "-") {
+ if hdr.Response == nil {
+ hdr.Response = &RespHeaderOps{HeaderOps: new(HeaderOps)}
+ }
+ hdr.Response.Delete = append(hdr.Response.Delete, field[1:])
+ hdr.Response.Deferred = true
+ } else {
+ if hdr.Response == nil {
+ hdr.Response = &RespHeaderOps{HeaderOps: new(HeaderOps)}
+ }
+ if hdr.Response.Set == nil {
+ hdr.Response.Set = make(http.Header)
+ }
+ hdr.Response.Set.Set(field, value)
+ }
+}
diff --git a/modules/caddyhttp/headers/headers.go b/modules/caddyhttp/headers/headers.go
index 8f4976a..e740004 100644
--- a/modules/caddyhttp/headers/headers.go
+++ b/modules/caddyhttp/headers/headers.go
@@ -23,10 +23,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.headers",
- New: func() interface{} { return new(Headers) },
- })
+ caddy.RegisterModule(Headers{})
}
// Headers is a middleware which can mutate HTTP headers.
@@ -35,6 +32,14 @@ type Headers struct {
Response *RespHeaderOps `json:"response,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (Headers) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.headers",
+ New: func() caddy.Module { return new(Headers) },
+ }
+}
+
// HeaderOps defines some operations to
// perform on HTTP headers.
type HeaderOps struct {
diff --git a/modules/caddyhttp/markdown/markdown.go b/modules/caddyhttp/markdown/markdown.go
index 3ba4d02..122aad6 100644
--- a/modules/caddyhttp/markdown/markdown.go
+++ b/modules/caddyhttp/markdown/markdown.go
@@ -28,16 +28,21 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.markdown",
- New: func() interface{} { return new(Markdown) },
- })
+ caddy.RegisterModule(Markdown{})
}
// Markdown is a middleware for rendering a Markdown response body.
type Markdown struct {
}
+// CaddyModule returns the Caddy module information.
+func (Markdown) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.markdown",
+ New: func() caddy.Module { return new(Markdown) },
+ }
+}
+
func (m Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index 6c5a23e..0dac151 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -28,6 +28,7 @@ import (
"strings"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/pkg/caddyscript"
"go.starlark.net/starlark"
)
@@ -79,50 +80,31 @@ type (
)
func init() {
- caddy.RegisterModule(caddy.Module{
+ caddy.RegisterModule(MatchHost{})
+ caddy.RegisterModule(MatchPath{})
+ caddy.RegisterModule(MatchPathRE{})
+ caddy.RegisterModule(MatchMethod{})
+ caddy.RegisterModule(MatchQuery{})
+ caddy.RegisterModule(MatchHeader{})
+ caddy.RegisterModule(MatchHeaderRE{})
+ caddy.RegisterModule(new(MatchProtocol))
+ caddy.RegisterModule(MatchRemoteIP{})
+ caddy.RegisterModule(MatchNegate{})
+ caddy.RegisterModule(new(MatchStarlarkExpr))
+}
+
+// CaddyModule returns the Caddy module information.
+func (MatchHost) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
Name: "http.matchers.host",
- New: func() interface{} { return new(MatchHost) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.path",
- New: func() interface{} { return new(MatchPath) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.path_regexp",
- New: func() interface{} { return new(MatchPathRE) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.method",
- New: func() interface{} { return new(MatchMethod) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.query",
- New: func() interface{} { return new(MatchQuery) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.header",
- New: func() interface{} { return new(MatchHeader) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.header_regexp",
- New: func() interface{} { return new(MatchHeaderRE) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.protocol",
- New: func() interface{} { return new(MatchProtocol) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.remote_ip",
- New: func() interface{} { return new(MatchRemoteIP) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.not",
- New: func() interface{} { return new(MatchNegate) },
- })
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.starlark_expr",
- New: func() interface{} { return new(MatchStarlarkExpr) },
- })
+ New: func() caddy.Module { return new(MatchHost) },
+ }
+}
+
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ *m = d.RemainingArgs()
+ return nil
}
// Match returns true if r matches m.
@@ -158,6 +140,14 @@ outer:
return false
}
+// CaddyModule returns the Caddy module information.
+func (MatchPath) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.path",
+ New: func() caddy.Module { return new(MatchPath) },
+ }
+}
+
// Match returns true if r matches m.
func (m MatchPath) Match(r *http.Request) bool {
for _, matchPath := range m {
@@ -177,12 +167,44 @@ func (m MatchPath) Match(r *http.Request) bool {
return false
}
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchPath) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ *m = d.RemainingArgs()
+ }
+ return nil
+}
+
+// CaddyModule returns the Caddy module information.
+func (MatchPathRE) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.path_regexp",
+ New: func() caddy.Module { return new(MatchPathRE) },
+ }
+}
+
// Match returns true if r matches m.
func (m MatchPathRE) Match(r *http.Request) bool {
repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
return m.MatchRegexp.Match(r.URL.Path, repl, "path_regexp")
}
+// CaddyModule returns the Caddy module information.
+func (MatchMethod) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.method",
+ New: func() caddy.Module { return new(MatchMethod) },
+ }
+}
+
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ *m = d.RemainingArgs()
+ }
+ return nil
+}
+
// Match returns true if r matches m.
func (m MatchMethod) Match(r *http.Request) bool {
for _, method := range m {
@@ -193,6 +215,26 @@ func (m MatchMethod) Match(r *http.Request) bool {
return false
}
+// CaddyModule returns the Caddy module information.
+func (MatchQuery) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.query",
+ New: func() caddy.Module { return new(MatchQuery) },
+ }
+}
+
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ parts := strings.SplitN(d.Val(), "=", 2)
+ if len(parts) != 2 {
+ return d.Errf("malformed query matcher token: %s; must be in param=val format", d.Val())
+ }
+ url.Values(*m).Set(parts[0], parts[1])
+ }
+ return nil
+}
+
// Match returns true if r matches m.
func (m MatchQuery) Match(r *http.Request) bool {
for param, vals := range m {
@@ -206,6 +248,26 @@ func (m MatchQuery) Match(r *http.Request) bool {
return false
}
+// CaddyModule returns the Caddy module information.
+func (MatchHeader) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.header",
+ New: func() caddy.Module { return new(MatchHeader) },
+ }
+}
+
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ var field, val string
+ if !d.Args(&field, &val) {
+ return d.Errf("expected both field and value")
+ }
+ http.Header(*m).Set(field, val)
+ }
+ return nil
+}
+
// Match returns true if r matches m.
func (m MatchHeader) Match(r *http.Request) bool {
for field, allowedFieldVals := range m {
@@ -227,6 +289,29 @@ func (m MatchHeader) Match(r *http.Request) bool {
return true
}
+// CaddyModule returns the Caddy module information.
+func (MatchHeaderRE) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.header_regexp",
+ New: func() caddy.Module { return new(MatchHeaderRE) },
+ }
+}
+
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ if *m == nil {
+ *m = make(map[string]*MatchRegexp)
+ }
+ for d.Next() {
+ var field, val string
+ if !d.Args(&field, &val) {
+ return d.ArgErr()
+ }
+ (*m)[field] = &MatchRegexp{Pattern: val}
+ }
+ return nil
+}
+
// Match returns true if r matches m.
func (m MatchHeaderRE) Match(r *http.Request) bool {
for field, rm := range m {
@@ -261,6 +346,14 @@ func (m MatchHeaderRE) Validate() error {
return nil
}
+// CaddyModule returns the Caddy module information.
+func (MatchProtocol) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.protocol",
+ New: func() caddy.Module { return new(MatchProtocol) },
+ }
+}
+
// Match returns true if r matches m.
func (m MatchProtocol) Match(r *http.Request) bool {
switch string(m) {
@@ -274,6 +367,26 @@ func (m MatchProtocol) Match(r *http.Request) bool {
return false
}
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchProtocol) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ var proto string
+ if !d.Args(&proto) {
+ return d.Err("expected exactly one protocol")
+ }
+ *m = MatchProtocol(proto)
+ }
+ return nil
+}
+
+// CaddyModule returns the Caddy module information.
+func (MatchNegate) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.not",
+ New: func() caddy.Module { return new(MatchNegate) },
+ }
+}
+
// UnmarshalJSON unmarshals data into m's unexported map field.
// This is done because we cannot embed the map directly into
// the struct, but we need a struct because we need another
@@ -282,6 +395,12 @@ func (m *MatchNegate) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &m.matchersRaw)
}
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchNegate) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ // TODO: figure out how this will work
+ return nil
+}
+
// Provision loads the matcher modules to be negated.
func (m *MatchNegate) Provision(ctx caddy.Context) error {
for modName, rawMsg := range m.matchersRaw {
@@ -301,6 +420,22 @@ func (m MatchNegate) Match(r *http.Request) bool {
return !m.matchers.Match(r)
}
+// CaddyModule returns the Caddy module information.
+func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.remote_ip",
+ New: func() caddy.Module { return new(MatchRemoteIP) },
+ }
+}
+
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ m.Ranges = d.RemainingArgs()
+ }
+ return nil
+}
+
// Provision parses m's IP ranges, either from IP or CIDR expressions.
func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
for _, str := range m.Ranges {
@@ -362,6 +497,14 @@ func (m MatchRemoteIP) Match(r *http.Request) bool {
return false
}
+// CaddyModule returns the Caddy module information.
+func (MatchStarlarkExpr) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.starlark_expr", // TODO: Rename to 'starlark'?
+ New: func() caddy.Module { return new(MatchStarlarkExpr) },
+ }
+}
+
// Match returns true if r matches m.
func (m MatchStarlarkExpr) Match(r *http.Request) bool {
input := string(m)
@@ -379,7 +522,7 @@ func (m MatchStarlarkExpr) Match(r *http.Request) bool {
// MatchRegexp is an embeddable type for matching
// using regular expressions.
type MatchRegexp struct {
- Name string `json:"name"`
+ Name string `json:"name,omitempty"`
Pattern string `json:"pattern"`
compiled *regexp.Regexp
}
@@ -431,6 +574,23 @@ func (mre *MatchRegexp) Match(input string, repl caddy.Replacer, scope string) b
return true
}
+// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
+func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ args := d.RemainingArgs()
+ switch len(args) {
+ case 1:
+ mre.Pattern = args[0]
+ case 2:
+ mre.Name = args[0]
+ mre.Pattern = args[1]
+ default:
+ return d.ArgErr()
+ }
+ }
+ return nil
+}
+
// ResponseMatcher is a type which can determine if a given response
// status code and its headers match some criteria.
type ResponseMatcher struct {
@@ -506,4 +666,14 @@ var (
_ caddy.Provisioner = (*MatchNegate)(nil)
_ RequestMatcher = (*MatchStarlarkExpr)(nil)
_ caddy.Provisioner = (*MatchRegexp)(nil)
+
+ _ caddyfile.Unmarshaler = (*MatchHost)(nil)
+ _ caddyfile.Unmarshaler = (*MatchPath)(nil)
+ _ caddyfile.Unmarshaler = (*MatchPathRE)(nil)
+ _ caddyfile.Unmarshaler = (*MatchMethod)(nil)
+ _ caddyfile.Unmarshaler = (*MatchQuery)(nil)
+ _ caddyfile.Unmarshaler = (*MatchHeader)(nil)
+ _ caddyfile.Unmarshaler = (*MatchHeaderRE)(nil)
+ _ caddyfile.Unmarshaler = (*MatchProtocol)(nil)
+ _ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil)
)
diff --git a/modules/caddyhttp/replacer.go b/modules/caddyhttp/replacer.go
index 439d245..cc29789 100644
--- a/modules/caddyhttp/replacer.go
+++ b/modules/caddyhttp/replacer.go
@@ -15,6 +15,7 @@
package caddyhttp
import (
+ "fmt"
"net"
"net/http"
"net/textproto"
@@ -28,6 +29,7 @@ import (
func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.ResponseWriter) {
httpVars := func(key string) (string, bool) {
if req != nil {
+ // query string parameters
if strings.HasPrefix(key, queryReplPrefix) {
vals := req.URL.Query()[key[len(queryReplPrefix):]]
// always return true, since the query param might
@@ -35,6 +37,7 @@ func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.Respon
return strings.Join(vals, ","), true
}
+ // request header fields
if strings.HasPrefix(key, reqHeaderReplPrefix) {
field := key[len(reqHeaderReplPrefix):]
vals := req.Header[textproto.CanonicalMIMEHeaderKey(field)]
@@ -43,6 +46,7 @@ func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.Respon
return strings.Join(vals, ","), true
}
+ // cookies
if strings.HasPrefix(key, cookieReplPrefix) {
name := key[len(cookieReplPrefix):]
for _, cookie := range req.Cookies() {
@@ -87,14 +91,7 @@ func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.Respon
return req.URL.RawQuery, true
}
- if strings.HasPrefix(key, respHeaderReplPrefix) {
- field := key[len(respHeaderReplPrefix):]
- vals := w.Header()[textproto.CanonicalMIMEHeaderKey(field)]
- // always return true, since the header field might
- // be present only in some requests
- return strings.Join(vals, ","), true
- }
-
+ // hostname labels
if strings.HasPrefix(key, hostLabelReplPrefix) {
idxStr := key[len(hostLabelReplPrefix):]
idx, err := strconv.Atoi(idxStr)
@@ -111,6 +108,7 @@ func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.Respon
return hostLabels[idx], true
}
+ // path parts
if strings.HasPrefix(key, pathPartsReplPrefix) {
idxStr := key[len(pathPartsReplPrefix):]
idx, err := strconv.Atoi(idxStr)
@@ -129,9 +127,31 @@ func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.Respon
}
return pathParts[idx], true
}
+
+ // middleware variables
+ if strings.HasPrefix(key, varsReplPrefix) {
+ varName := key[len(varsReplPrefix):]
+ tbl := req.Context().Value(VarCtxKey).(map[string]interface{})
+ raw, ok := tbl[varName]
+ if !ok {
+ // variables can be dynamic, so always return true
+ // even when it may not be set; treat as empty
+ return "", true
+ }
+ // do our best to convert it to a string efficiently
+ switch val := raw.(type) {
+ case string:
+ return val, true
+ case fmt.Stringer:
+ return val.String(), true
+ default:
+ return fmt.Sprintf("%s", val), true
+ }
+ }
}
if w != nil {
+ // response header fields
if strings.HasPrefix(key, respHeaderReplPrefix) {
field := key[len(respHeaderReplPrefix):]
vals := w.Header()[textproto.CanonicalMIMEHeaderKey(field)]
@@ -153,5 +173,6 @@ const (
cookieReplPrefix = "http.request.cookie."
hostLabelReplPrefix = "http.request.host.labels."
pathPartsReplPrefix = "http.request.uri.path."
+ varsReplPrefix = "http.var."
respHeaderReplPrefix = "http.response.header."
)
diff --git a/modules/caddyhttp/requestbody/requestbody.go b/modules/caddyhttp/requestbody/requestbody.go
index 3763cfe..9b16250 100644
--- a/modules/caddyhttp/requestbody/requestbody.go
+++ b/modules/caddyhttp/requestbody/requestbody.go
@@ -22,10 +22,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.request_body",
- New: func() interface{} { return new(RequestBody) },
- })
+ caddy.RegisterModule(RequestBody{})
}
// RequestBody is a middleware for manipulating the request body.
@@ -33,6 +30,14 @@ type RequestBody struct {
MaxSize int64 `json:"max_size,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (RequestBody) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.request_body", // TODO: better name for this?
+ New: func() caddy.Module { return new(RequestBody) },
+ }
+}
+
func (rb RequestBody) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
if r.Body == nil {
return next.ServeHTTP(w, r)
diff --git a/modules/caddyhttp/reverseproxy/module.go b/modules/caddyhttp/reverseproxy/module.go
index 2e6a338..21aca1d 100755
--- a/modules/caddyhttp/reverseproxy/module.go
+++ b/modules/caddyhttp/reverseproxy/module.go
@@ -16,12 +16,38 @@ package reverseproxy
import (
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
-// Register caddy module.
func init() {
- caddy.RegisterModule(caddy.Module{
+ caddy.RegisterModule(new(LoadBalanced))
+ httpcaddyfile.RegisterHandlerDirective("reverse_proxy", parseCaddyfile) // TODO: "proxy"?
+}
+
+// CaddyModule returns the Caddy module information.
+func (*LoadBalanced) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
Name: "http.handlers.reverse_proxy",
- New: func() interface{} { return new(LoadBalanced) },
- })
+ New: func() caddy.Module { return new(LoadBalanced) },
+ }
+}
+
+// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
+//
+// proxy [<matcher>] <to>
+//
+// TODO: This needs to be finished. It definitely needs to be able to open a block...
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+ lb := new(LoadBalanced)
+ for h.Next() {
+ allTo := h.RemainingArgs()
+ if len(allTo) == 0 {
+ return nil, h.ArgErr()
+ }
+ for _, to := range allTo {
+ lb.Upstreams = append(lb.Upstreams, &UpstreamConfig{Host: to})
+ }
+ }
+ return lb, nil
}
diff --git a/modules/caddyhttp/reverseproxy/upstream.go b/modules/caddyhttp/reverseproxy/upstream.go
index 10df80b..1f0693e 100755
--- a/modules/caddyhttp/reverseproxy/upstream.go
+++ b/modules/caddyhttp/reverseproxy/upstream.go
@@ -179,21 +179,21 @@ type LoadBalanced struct {
// The following struct fields are set by caddy configuration.
// TryInterval is the max duration for which request retrys will be performed for a request.
- TryInterval string `json:"try_interval"`
+ TryInterval string `json:"try_interval,omitempty"`
// Upstreams are the configs for upstream hosts
- Upstreams []*UpstreamConfig `json:"upstreams"`
+ Upstreams []*UpstreamConfig `json:"upstreams,omitempty"`
// LoadBalanceType is the string representation of what loadbalancing algorithm to use. i.e. "random" or "round_robin".
- LoadBalanceType string `json:"load_balance_type"`
+ LoadBalanceType string `json:"load_balance_type,omitempty"`
// NoHealthyUpstreamsMessage is returned as a response when there are no healthy upstreams to loadbalance to.
- NoHealthyUpstreamsMessage string `json:"no_healthy_upstreams_message"`
+ NoHealthyUpstreamsMessage string `json:"no_healthy_upstreams_message,omitempty"`
// TODO :- store healthcheckers as package level state where each upstream gets a single healthchecker
// currently a healthchecker is created for each upstream defined, even if a healthchecker was previously created
// for that upstream
- HealthCheckers []*HealthChecker
+ HealthCheckers []*HealthChecker `json:"health_checkers,omitempty"`
}
// Cleanup stops all health checkers on a loadbalanced reverse proxy.
@@ -320,22 +320,22 @@ func (lb *LoadBalanced) random() *upstream {
// UpstreamConfig represents the config of an upstream.
type UpstreamConfig struct {
// Host is the host name of the upstream server.
- Host string `json:"host"`
+ Host string `json:"host,omitempty"`
// FastHealthCheckDuration is the duration for which a health check is performed when a node is considered unhealthy.
- FastHealthCheckDuration string `json:"fast_health_check_duration"`
+ FastHealthCheckDuration string `json:"fast_health_check_duration,omitempty"`
- CircuitBreaker json.RawMessage `json:"circuit_breaker"`
+ CircuitBreaker json.RawMessage `json:"circuit_breaker,omitempty"`
// // CircuitBreakerConfig is the config passed to setup a circuit breaker.
- // CircuitBreakerConfig *circuitbreaker.Config `json:"circuit_breaker"`
+ // CircuitBreakerConfig *circuitbreaker.Config `json:"circuit_breaker,omitempty"`
circuitbreaker CircuitBreaker
// HealthCheckDuration is the default duration for which a health check is performed.
- HealthCheckDuration string `json:"health_check_duration"`
+ HealthCheckDuration string `json:"health_check_duration,omitempty"`
// HealthCheckPath is the path at the upstream host to use for healthchecks.
- HealthCheckPath string `json:"health_check_path"`
+ HealthCheckPath string `json:"health_check_path,omitempty"`
}
// upstream represents an upstream host.
diff --git a/modules/caddyhttp/rewrite/caddyfile.go b/modules/caddyhttp/rewrite/caddyfile.go
new file mode 100644
index 0000000..a977a72
--- /dev/null
+++ b/modules/caddyhttp/rewrite/caddyfile.go
@@ -0,0 +1,37 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rewrite
+
+import (
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
+)
+
+func init() {
+ httpcaddyfile.RegisterHandlerDirective("rewrite", parseCaddyfile)
+}
+
+// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
+//
+// rewrite [<matcher>] <to>
+//
+// The <to> parameter becomes the new URI.
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+ var rewr Rewrite
+ for h.Next() {
+ rewr.URI = h.Val()
+ }
+ return rewr, nil
+}
diff --git a/modules/caddyhttp/rewrite/rewrite.go b/modules/caddyhttp/rewrite/rewrite.go
index ac113ff..f434a38 100644
--- a/modules/caddyhttp/rewrite/rewrite.go
+++ b/modules/caddyhttp/rewrite/rewrite.go
@@ -24,10 +24,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.rewrite",
- New: func() interface{} { return new(Rewrite) },
- })
+ caddy.RegisterModule(Rewrite{})
}
// Rewrite is a middleware which can rewrite HTTP requests.
@@ -37,6 +34,14 @@ type Rewrite struct {
Rehandle bool `json:"rehandle,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (Rewrite) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.rewrite",
+ New: func() caddy.Module { return new(Rewrite) },
+ }
+}
+
func (rewr Rewrite) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
var rehandleNeeded bool
diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go
index b0672b1..1efbad6 100644
--- a/modules/caddyhttp/routes.go
+++ b/modules/caddyhttp/routes.go
@@ -22,37 +22,38 @@ import (
"github.com/caddyserver/caddy/v2"
)
-// ServerRoute represents a set of matching rules,
+// Route represents a set of matching rules,
// middlewares, and a responder for handling HTTP
// requests.
-type ServerRoute struct {
- Group string `json:"group,omitempty"`
- MatcherSets []map[string]json.RawMessage `json:"match,omitempty"`
- Handle []json.RawMessage `json:"handle,omitempty"`
- Terminal bool `json:"terminal,omitempty"`
+type Route struct {
+ Group string `json:"group,omitempty"`
+ MatcherSetsRaw []map[string]json.RawMessage `json:"match,omitempty"`
+ HandlersRaw []json.RawMessage `json:"handle,omitempty"`
+ Terminal bool `json:"terminal,omitempty"`
// decoded values
- matcherSets []MatcherSet
- handlers []MiddlewareHandler
+ MatcherSets []MatcherSet `json:"-"`
+ Handlers []MiddlewareHandler `json:"-"`
}
// Empty returns true if the route has all zero/default values.
-func (sr ServerRoute) Empty() bool {
- return len(sr.MatcherSets) == 0 &&
- len(sr.Handle) == 0 &&
- len(sr.handlers) == 0 &&
- !sr.Terminal &&
- sr.Group == ""
+func (r Route) Empty() bool {
+ return len(r.MatcherSetsRaw) == 0 &&
+ len(r.MatcherSets) == 0 &&
+ len(r.HandlersRaw) == 0 &&
+ len(r.Handlers) == 0 &&
+ !r.Terminal &&
+ r.Group == ""
}
-func (sr ServerRoute) anyMatcherSetMatches(r *http.Request) bool {
- for _, ms := range sr.matcherSets {
- if ms.Match(r) {
+func (r Route) anyMatcherSetMatches(req *http.Request) bool {
+ for _, ms := range r.MatcherSets {
+ if ms.Match(req) {
return true
}
}
// if no matchers, always match
- return len(sr.matcherSets) == 0
+ return len(r.MatcherSets) == 0
}
// MatcherSet is a set of matchers which
@@ -73,13 +74,13 @@ func (mset MatcherSet) Match(r *http.Request) bool {
// RouteList is a list of server routes that can
// create a middleware chain.
-type RouteList []ServerRoute
+type RouteList []Route
// Provision sets up all the routes by loading the modules.
func (routes RouteList) Provision(ctx caddy.Context) error {
for i, route := range routes {
// matchers
- for _, matcherSet := range route.MatcherSets {
+ for _, matcherSet := range route.MatcherSetsRaw {
var matchers MatcherSet
for modName, rawMsg := range matcherSet {
val, err := ctx.LoadModule("http.matchers."+modName, rawMsg)
@@ -88,19 +89,19 @@ func (routes RouteList) Provision(ctx caddy.Context) error {
}
matchers = append(matchers, val.(RequestMatcher))
}
- routes[i].matcherSets = append(routes[i].matcherSets, matchers)
+ routes[i].MatcherSets = append(routes[i].MatcherSets, matchers)
}
- routes[i].MatcherSets = nil // allow GC to deallocate - TODO: Does this help?
+ routes[i].MatcherSetsRaw = nil // allow GC to deallocate - TODO: Does this help?
// handlers
- for j, rawMsg := range route.Handle {
+ for j, rawMsg := range route.HandlersRaw {
mh, err := ctx.LoadModuleInline("handler", "http.handlers", rawMsg)
if err != nil {
return fmt.Errorf("loading handler module in position %d: %v", j, err)
}
- routes[i].handlers = append(routes[i].handlers, mh.(MiddlewareHandler))
+ routes[i].Handlers = append(routes[i].Handlers, mh.(MiddlewareHandler))
}
- routes[i].Handle = nil // allow GC to deallocate - TODO: Does this help?
+ routes[i].HandlersRaw = nil // allow GC to deallocate - TODO: Does this help?
}
return nil
}
@@ -135,7 +136,7 @@ func (routes RouteList) BuildCompositeRoute(req *http.Request) Handler {
}
// apply the rest of the route
- for _, mh := range route.handlers {
+ for _, mh := range route.Handlers {
// we have to be sure to wrap mh outside
// of our current stack frame so that the
// reference to this mh isn't overwritten
diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go
index d79d8d3..f820f71 100644
--- a/modules/caddyhttp/server.go
+++ b/modules/caddyhttp/server.go
@@ -57,7 +57,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
repl := caddy.NewReplacer()
ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
ctx = context.WithValue(ctx, ServerCtxKey, s)
- ctx = context.WithValue(ctx, TableCtxKey, make(map[string]interface{})) // TODO: Implement this
+ ctx = context.WithValue(ctx, VarCtxKey, make(map[string]interface{}))
r = r.WithContext(ctx)
// once the pointer to the request won't change
@@ -201,6 +201,14 @@ type AutoHTTPSConfig struct {
// that certificates will not be provisioned and managed
// for these names.
SkipCerts []string `json:"skip_certificates,omitempty"`
+
+ // By default, automatic HTTPS will obtain and renew
+ // certificates for qualifying hostnames. However, if
+ // a certificate with a matching SAN is already loaded
+ // into the cache, certificate management will not be
+ // enabled. To force automated certificate management
+ // regardless of loaded certificates, set this to true.
+ IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
}
// Skipped returns true if name is in skipSlice, which
@@ -225,6 +233,6 @@ const (
// For referencing the server instance
ServerCtxKey caddy.CtxKey = "server"
- // For the request's variable table (TODO: implement this)
- TableCtxKey caddy.CtxKey = "table"
+ // For the request's variable table
+ VarCtxKey caddy.CtxKey = "vars"
)
diff --git a/modules/caddyhttp/staticerror.go b/modules/caddyhttp/staticerror.go
index 3a8e8bc..3a45366 100644
--- a/modules/caddyhttp/staticerror.go
+++ b/modules/caddyhttp/staticerror.go
@@ -18,22 +18,26 @@ import (
"fmt"
"net/http"
"strconv"
- "strings"
"github.com/caddyserver/caddy/v2"
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.error",
- New: func() interface{} { return new(StaticError) },
- })
+ caddy.RegisterModule(StaticError{})
}
// StaticError implements a simple handler that returns an error.
type StaticError struct {
Error string `json:"error,omitempty"`
- StatusCode weakString `json:"status_code,omitempty"`
+ StatusCode WeakString `json:"status_code,omitempty"`
+}
+
+// CaddyModule returns the Caddy module information.
+func (StaticError) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.error",
+ New: func() caddy.Module { return new(StaticError) },
+ }
}
func (e StaticError) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Handler) error {
@@ -53,43 +57,3 @@ func (e StaticError) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Handler
// Interface guard
var _ MiddlewareHandler = (*StaticError)(nil)
-
-// weakString is a type that unmarshals any JSON value
-// as a string literal, and provides methods for
-// getting the value as different primitive types.
-// However, using this type removes any type safety
-// as far as deserializing JSON is concerned.
-type weakString string
-
-// UnmarshalJSON satisfies json.Unmarshaler. It
-// unmarshals b by always interpreting it as a
-// string literal.
-func (ws *weakString) UnmarshalJSON(b []byte) error {
- *ws = weakString(strings.Trim(string(b), `"`))
- return nil
-}
-
-// Int returns ws as an integer. If ws is not an
-// integer, 0 is returned.
-func (ws weakString) Int() int {
- num, _ := strconv.Atoi(string(ws))
- return num
-}
-
-// Float64 returns ws as a float64. If ws is not a
-// float value, the zero value is returned.
-func (ws weakString) Float64() float64 {
- num, _ := strconv.ParseFloat(string(ws), 64)
- return num
-}
-
-// Bool returns ws as a boolean. If ws is not a
-// boolean, false is returned.
-func (ws weakString) Bool() bool {
- return string(ws) == "true"
-}
-
-// String returns ws as a string.
-func (ws weakString) String() string {
- return string(ws)
-}
diff --git a/modules/caddyhttp/staticresp.go b/modules/caddyhttp/staticresp.go
index 291d992..942459b 100644
--- a/modules/caddyhttp/staticresp.go
+++ b/modules/caddyhttp/staticresp.go
@@ -20,21 +20,61 @@ import (
"strconv"
"github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.static_response",
- New: func() interface{} { return new(StaticResponse) },
- })
+ caddy.RegisterModule(StaticResponse{})
+ // TODO: Caddyfile directive
}
// StaticResponse implements a simple responder for static responses.
type StaticResponse struct {
- StatusCode weakString `json:"status_code"`
- Headers http.Header `json:"headers"`
- Body string `json:"body"`
- Close bool `json:"close"`
+ StatusCode WeakString `json:"status_code,omitempty"`
+ Headers http.Header `json:"headers,omitempty"`
+ Body string `json:"body,omitempty"`
+ Close bool `json:"close,omitempty"`
+}
+
+// CaddyModule returns the Caddy module information.
+func (StaticResponse) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.static_response",
+ New: func() caddy.Module { return new(StaticResponse) },
+ }
+}
+
+// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
+//
+// static_response [<matcher>] <status> {
+// body <text>
+// close
+// }
+//
+func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
+ for d.Next() {
+ var statusCodeStr string
+ if d.Args(&statusCodeStr) {
+ s.StatusCode = WeakString(statusCodeStr)
+ }
+ for d.NextBlock() {
+ switch d.Val() {
+ case "body":
+ if s.Body != "" {
+ return d.Err("body already specified")
+ }
+ if !d.Args(&s.Body) {
+ return d.ArgErr()
+ }
+ case "close":
+ if s.Close {
+ return d.Err("close already specified")
+ }
+ s.Close = true
+ }
+ }
+ }
+ return nil
}
func (s StaticResponse) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Handler) error {
diff --git a/modules/caddyhttp/staticresp_test.go b/modules/caddyhttp/staticresp_test.go
index 49adedd..cd0d1a1 100644
--- a/modules/caddyhttp/staticresp_test.go
+++ b/modules/caddyhttp/staticresp_test.go
@@ -30,7 +30,7 @@ func TestStaticResponseHandler(t *testing.T) {
w := httptest.NewRecorder()
s := StaticResponse{
- StatusCode: weakString(strconv.Itoa(http.StatusNotFound)),
+ StatusCode: WeakString(strconv.Itoa(http.StatusNotFound)),
Headers: http.Header{
"X-Test": []string{"Testing"},
},
diff --git a/modules/caddyhttp/subroute.go b/modules/caddyhttp/subroute.go
index 9172146..3b0d718 100644
--- a/modules/caddyhttp/subroute.go
+++ b/modules/caddyhttp/subroute.go
@@ -22,10 +22,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.subroute",
- New: func() interface{} { return new(Subroute) },
- })
+ caddy.RegisterModule(Subroute{})
}
// Subroute implements a handler that compiles and executes routes.
@@ -37,6 +34,14 @@ type Subroute struct {
Routes RouteList `json:"routes,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (Subroute) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.subroute",
+ New: func() caddy.Module { return new(Subroute) },
+ }
+}
+
// Provision sets up subrouting.
func (sr *Subroute) Provision(ctx caddy.Context) error {
if sr.Routes != nil {
diff --git a/modules/caddyhttp/table.go b/modules/caddyhttp/table.go
deleted file mode 100644
index 5b1fed5..0000000
--- a/modules/caddyhttp/table.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2015 Matthew Holt and The Caddy Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package caddyhttp
-
-import (
- "net/http"
-
- "github.com/caddyserver/caddy/v2"
-)
-
-func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.table",
- New: func() interface{} { return new(tableMiddleware) },
- })
-
- caddy.RegisterModule(caddy.Module{
- Name: "http.matchers.table",
- New: func() interface{} { return new(tableMatcher) },
- })
-}
-
-type tableMiddleware struct {
-}
-
-func (t tableMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
- // tbl := r.Context().Value(TableCtxKey).(map[string]interface{})
-
- // TODO: implement this...
-
- return nil
-}
-
-type tableMatcher struct {
-}
-
-func (m tableMatcher) Match(r *http.Request) bool {
- return false // TODO: implement
-}
-
-// Interface guards
-var _ MiddlewareHandler = (*tableMiddleware)(nil)
-var _ RequestMatcher = (*tableMatcher)(nil)
diff --git a/modules/caddyhttp/templates/caddyfile.go b/modules/caddyhttp/templates/caddyfile.go
new file mode 100644
index 0000000..d948da0
--- /dev/null
+++ b/modules/caddyhttp/templates/caddyfile.go
@@ -0,0 +1,62 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package templates
+
+import (
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
+)
+
+func init() {
+ httpcaddyfile.RegisterHandlerDirective("templates", parseCaddyfile)
+}
+
+// parseCaddyfile sets up the handler from Caddyfile tokens. Syntax:
+//
+// templates [<matcher>] {
+// mime <types...>
+// between <open_delim> <close_delim>
+// root <path>
+// }
+//
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
+ t := new(Templates)
+ for h.Next() {
+ for h.NextBlock() {
+ switch h.Val() {
+ case "mime":
+ t.MIMETypes = h.RemainingArgs()
+ if len(t.MIMETypes) == 0 {
+ return nil, h.ArgErr()
+ }
+ case "between":
+ t.Delimiters = h.RemainingArgs()
+ if len(t.Delimiters) != 2 {
+ return nil, h.ArgErr()
+ }
+ case "root":
+ if !h.Args(&t.IncludeRoot) {
+ return nil, h.ArgErr()
+ }
+ }
+ }
+ }
+
+ if t.IncludeRoot == "" {
+ t.IncludeRoot = "{http.var.root}"
+ }
+
+ return t, nil
+}
diff --git a/modules/caddyhttp/templates/templates.go b/modules/caddyhttp/templates/templates.go
index 9a41b6d..1cd347c 100644
--- a/modules/caddyhttp/templates/templates.go
+++ b/modules/caddyhttp/templates/templates.go
@@ -27,10 +27,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "http.handlers.templates",
- New: func() interface{} { return new(Templates) },
- })
+ caddy.RegisterModule(Templates{})
}
// Templates is a middleware which execute response bodies as templates.
@@ -40,6 +37,14 @@ type Templates struct {
Delimiters []string `json:"delimiters,omitempty"`
}
+// CaddyModule returns the Caddy module information.
+func (Templates) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.templates",
+ New: func() caddy.Module { return new(Templates) },
+ }
+}
+
// Provision provisions t.
func (t *Templates) Provision(ctx caddy.Context) error {
if t.MIMETypes == nil {
@@ -108,7 +113,8 @@ func (t *Templates) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddy
func (t *Templates) executeTemplate(rr caddyhttp.ResponseRecorder, r *http.Request) error {
var fs http.FileSystem
if t.IncludeRoot != "" {
- fs = http.Dir(t.IncludeRoot)
+ repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
+ fs = http.Dir(repl.ReplaceAll(t.IncludeRoot, "."))
}
ctx := &templateContext{
diff --git a/modules/caddyhttp/templates/tplcontext.go b/modules/caddyhttp/templates/tplcontext.go
index ffcc636..a51e54b 100644
--- a/modules/caddyhttp/templates/tplcontext.go
+++ b/modules/caddyhttp/templates/tplcontext.go
@@ -136,19 +136,6 @@ func (c templateContext) Cookie(name string) string {
return ""
}
-// Hostname gets the (remote) hostname of the client making the request.
-// Performance warning: This involves a DNS lookup.
-func (c templateContext) Hostname() string {
- ip := c.RemoteIP()
-
- hostnameList, err := net.LookupAddr(ip)
- if err != nil || len(hostnameList) == 0 {
- return c.Req.RemoteAddr
- }
-
- return hostnameList[0]
-}
-
// RemoteIP gets the IP address of the client making the request.
func (c templateContext) RemoteIP() string {
ip, _, err := net.SplitHostPort(c.Req.RemoteAddr)
diff --git a/modules/caddyhttp/vars.go b/modules/caddyhttp/vars.go
new file mode 100644
index 0000000..bbd4568
--- /dev/null
+++ b/modules/caddyhttp/vars.go
@@ -0,0 +1,81 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package caddyhttp
+
+import (
+ "net/http"
+
+ "github.com/caddyserver/caddy/v2"
+)
+
+func init() {
+ caddy.RegisterModule(VarsMiddleware{})
+ caddy.RegisterModule(VarsMatcher{})
+}
+
+// VarsMiddleware is an HTTP middleware which sets variables
+// in the context, mainly for use by placeholders.
+type VarsMiddleware map[string]string
+
+// CaddyModule returns the Caddy module information.
+func (VarsMiddleware) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.handlers.vars",
+ New: func() caddy.Module { return new(VarsMiddleware) },
+ }
+}
+
+func (t VarsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
+ vars := r.Context().Value(VarCtxKey).(map[string]interface{})
+ repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
+ for k, v := range t {
+ keyExpanded := repl.ReplaceAll(k, "")
+ valExpanded := repl.ReplaceAll(v, "")
+ vars[keyExpanded] = valExpanded
+ }
+ return next.ServeHTTP(w, r)
+}
+
+// VarsMatcher is an HTTP request matcher which can match
+// requests based on variables in the context.
+type VarsMatcher map[string]string
+
+// CaddyModule returns the Caddy module information.
+func (VarsMatcher) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "http.matchers.vars",
+ New: func() caddy.Module { return new(VarsMatcher) },
+ }
+}
+
+// Match matches a request based on variables in the context.
+func (m VarsMatcher) Match(r *http.Request) bool {
+ vars := r.Context().Value(VarCtxKey).(map[string]string)
+ repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
+ for k, v := range m {
+ keyExpanded := repl.ReplaceAll(k, "")
+ valExpanded := repl.ReplaceAll(v, "")
+ if vars[keyExpanded] != valExpanded {
+ return false
+ }
+ }
+ return true
+}
+
+// Interface guards
+var (
+ _ MiddlewareHandler = (*VarsMiddleware)(nil)
+ _ RequestMatcher = (*VarsMatcher)(nil)
+)
diff --git a/modules/caddytls/acmemanager.go b/modules/caddytls/acmemanager.go
index 578cdb3..36f1c21 100644
--- a/modules/caddytls/acmemanager.go
+++ b/modules/caddytls/acmemanager.go
@@ -28,10 +28,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "tls.management.acme",
- New: func() interface{} { return new(ACMEManagerMaker) },
- })
+ caddy.RegisterModule(ACMEManagerMaker{})
}
// ACMEManagerMaker makes an ACME manager
@@ -57,9 +54,17 @@ type ACMEManagerMaker struct {
keyType certcrypto.KeyType
}
-// newManager is a no-op to satisfy the ManagerMaker interface,
+// CaddyModule returns the Caddy module information.
+func (ACMEManagerMaker) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "tls.management.acme",
+ New: func() caddy.Module { return new(ACMEManagerMaker) },
+ }
+}
+
+// NewManager is a no-op to satisfy the ManagerMaker interface,
// because this manager type is a special case.
-func (m *ACMEManagerMaker) newManager(interactive bool) (certmagic.Manager, error) {
+func (m ACMEManagerMaker) NewManager(interactive bool) (certmagic.Manager, error) {
return nil, nil
}
@@ -203,4 +208,4 @@ func onDemandAskRequest(ask string, name string) error {
}
// Interface guard
-var _ managerMaker = (*ACMEManagerMaker)(nil)
+var _ ManagerMaker = (*ACMEManagerMaker)(nil)
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index 8cb6ffe..e061281 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -172,7 +172,7 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
// add all the cipher suites in order, without duplicates
cipherSuitesAdded := make(map[uint16]struct{})
for _, csName := range p.CipherSuites {
- csID := supportedCipherSuites[csName]
+ csID := SupportedCipherSuites[csName]
if _, ok := cipherSuitesAdded[csID]; !ok {
cipherSuitesAdded[csID] = struct{}{}
cfg.CipherSuites = append(cfg.CipherSuites, csID)
@@ -182,7 +182,7 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
// add all the curve preferences in order, without duplicates
curvesAdded := make(map[tls.CurveID]struct{})
for _, curveName := range p.Curves {
- curveID := supportedCurves[curveName]
+ curveID := SupportedCurves[curveName]
if _, ok := curvesAdded[curveID]; !ok {
curvesAdded[curveID] = struct{}{}
cfg.CurvePreferences = append(cfg.CurvePreferences, curveID)
@@ -203,10 +203,10 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
// min and max protocol versions
if p.ProtocolMin != "" {
- cfg.MinVersion = supportedProtocols[p.ProtocolMin]
+ cfg.MinVersion = SupportedProtocols[p.ProtocolMin]
}
if p.ProtocolMax != "" {
- cfg.MaxVersion = supportedProtocols[p.ProtocolMax]
+ cfg.MaxVersion = SupportedProtocols[p.ProtocolMax]
}
if p.ProtocolMin > p.ProtocolMax {
return fmt.Errorf("protocol min (%x) cannot be greater than protocol max (%x)", p.ProtocolMin, p.ProtocolMax)
diff --git a/modules/caddytls/fileloader.go b/modules/caddytls/fileloader.go
index 5f277c8..b2cc132 100644
--- a/modules/caddytls/fileloader.go
+++ b/modules/caddytls/fileloader.go
@@ -23,14 +23,19 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "tls.certificates.load_files",
- New: func() interface{} { return fileLoader{} },
- })
+ caddy.RegisterModule(FileLoader{})
}
-// fileLoader loads certificates and their associated keys from disk.
-type fileLoader []CertKeyFilePair
+// FileLoader loads certificates and their associated keys from disk.
+type FileLoader []CertKeyFilePair
+
+// CaddyModule returns the Caddy module information.
+func (FileLoader) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "tls.certificates.load_files",
+ New: func() caddy.Module { return new(FileLoader) },
+ }
+}
// CertKeyFilePair pairs certificate and key file names along with their
// encoding format so that they can be loaded from disk.
@@ -42,7 +47,7 @@ type CertKeyFilePair struct {
}
// LoadCertificates returns the certificates to be loaded by fl.
-func (fl fileLoader) LoadCertificates() ([]Certificate, error) {
+func (fl FileLoader) LoadCertificates() ([]Certificate, error) {
var certs []Certificate
for _, pair := range fl {
certData, err := ioutil.ReadFile(pair.Certificate)
@@ -73,4 +78,4 @@ func (fl fileLoader) LoadCertificates() ([]Certificate, error) {
}
// Interface guard
-var _ CertificateLoader = (fileLoader)(nil)
+var _ CertificateLoader = (FileLoader)(nil)
diff --git a/modules/caddytls/folderloader.go b/modules/caddytls/folderloader.go
index 24a7fbb..da1dff0 100644
--- a/modules/caddytls/folderloader.go
+++ b/modules/caddytls/folderloader.go
@@ -28,22 +28,27 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "tls.certificates.load_folders",
- New: func() interface{} { return folderLoader{} },
- })
+ caddy.RegisterModule(FolderLoader{})
}
-// folderLoader loads certificates and their associated keys from disk
+// FolderLoader loads certificates and their associated keys from disk
// by recursively walking the specified directories, looking for PEM
// files which contain both a certificate and a key.
-type folderLoader []string
+type FolderLoader []string
+
+// CaddyModule returns the Caddy module information.
+func (FolderLoader) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "tls.certificates.load_folders",
+ New: func() caddy.Module { return new(FolderLoader) },
+ }
+}
// LoadCertificates loads all the certificates+keys in the directories
// listed in fl from all files ending with .pem. This method of loading
// certificates expects the certificate and key to be bundled into the
// same file.
-func (fl folderLoader) LoadCertificates() ([]Certificate, error) {
+func (fl FolderLoader) LoadCertificates() ([]Certificate, error) {
var certs []Certificate
for _, dir := range fl {
err := filepath.Walk(dir, func(fpath string, info os.FileInfo, err error) error {
@@ -135,4 +140,4 @@ func x509CertFromCertAndKeyPEMFile(fpath string) (tls.Certificate, error) {
return cert, nil
}
-var _ CertificateLoader = (folderLoader)(nil)
+var _ CertificateLoader = (FolderLoader)(nil)
diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go
index ee146d4..47fb296 100644
--- a/modules/caddytls/matchers.go
+++ b/modules/caddytls/matchers.go
@@ -20,14 +20,19 @@ import (
"github.com/caddyserver/caddy/v2"
)
+func init() {
+ caddy.RegisterModule(MatchServerName{})
+}
+
// MatchServerName matches based on SNI.
type MatchServerName []string
-func init() {
- caddy.RegisterModule(caddy.Module{
+// CaddyModule returns the Caddy module information.
+func (MatchServerName) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
Name: "tls.handshake_match.sni",
- New: func() interface{} { return MatchServerName{} },
- })
+ New: func() caddy.Module { return new(MatchServerName) },
+ }
}
// Match matches hello based on SNI.
diff --git a/modules/caddytls/sessiontickets.go b/modules/caddytls/sessiontickets.go
index c47f823..2eb0773 100644
--- a/modules/caddytls/sessiontickets.go
+++ b/modules/caddytls/sessiontickets.go
@@ -29,7 +29,7 @@ import (
// SessionTicketService configures and manages TLS session tickets.
type SessionTicketService struct {
KeySource json.RawMessage `json:"key_source,omitempty"`
- RotationInterval caddy.Duration `json:"rotation_interval,omitempty"`
+ RotationInterval caddy.Duration `json:"rotation_interval,omitempty"`
MaxKeys int `json:"max_keys,omitempty"`
DisableRotation bool `json:"disable_rotation,omitempty"`
Disabled bool `json:"disabled,omitempty"`
diff --git a/modules/caddytls/standardstek/stek.go b/modules/caddytls/standardstek/stek.go
index 6a4b1c8..6d10c76 100644
--- a/modules/caddytls/standardstek/stek.go
+++ b/modules/caddytls/standardstek/stek.go
@@ -24,10 +24,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "tls.stek.standard",
- New: func() interface{} { return new(standardSTEKProvider) },
- })
+ caddy.RegisterModule(standardSTEKProvider{})
}
type standardSTEKProvider struct {
@@ -35,6 +32,14 @@ type standardSTEKProvider struct {
timer *time.Timer
}
+// CaddyModule returns the Caddy module information.
+func (standardSTEKProvider) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "tls.stek.standard",
+ New: func() caddy.Module { return new(standardSTEKProvider) },
+ }
+}
+
// Initialize sets the configuration for s and returns the starting keys.
func (s *standardSTEKProvider) Initialize(config *caddytls.SessionTicketService) ([][32]byte, error) {
// keep a reference to the config; we'll need it when rotating keys
diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go
index e70fbd1..88b7790 100644
--- a/modules/caddytls/tls.go
+++ b/modules/caddytls/tls.go
@@ -30,10 +30,7 @@ import (
)
func init() {
- caddy.RegisterModule(caddy.Module{
- Name: "tls",
- New: func() interface{} { return new(TLS) },
- })
+ caddy.RegisterModule(TLS{})
// opt-in TLS 1.3 for Go1.12
// TODO: remove this line when Go1.13 is released.
@@ -45,14 +42,22 @@ func init() {
// TLS represents a process-wide TLS configuration.
type TLS struct {
Certificates map[string]json.RawMessage `json:"certificates,omitempty"`
- Automation AutomationConfig `json:"automation,omitempty"`
- SessionTickets SessionTicketService `json:"session_tickets,omitempty"`
+ Automation AutomationConfig `json:"automation"`
+ SessionTickets SessionTicketService `json:"session_tickets"`
certificateLoaders []CertificateLoader
certCache *certmagic.Cache
ctx caddy.Context
}
+// CaddyModule returns the Caddy module information.
+func (TLS) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "tls",
+ New: func() caddy.Module { return new(TLS) },
+ }
+}
+
// Provision sets up the configuration for the TLS app.
func (t *TLS) Provision(ctx caddy.Context) error {
t.ctx = ctx
@@ -73,7 +78,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
if err != nil {
return fmt.Errorf("loading TLS automation management module: %s", err)
}
- t.Automation.Policies[i].Management = val.(managerMaker)
+ t.Automation.Policies[i].Management = val.(ManagerMaker)
t.Automation.Policies[i].ManagementRaw = nil // allow GC to deallocate - TODO: Does this help?
}
@@ -105,16 +110,12 @@ func (t *TLS) Provision(ctx caddy.Context) error {
onDemandRateLimiter.SetLimit(0)
}
- return nil
-}
-
-// Start activates the TLS module.
-func (t *TLS) Start() error {
+ // load manual/static (unmanaged) certificates - we do this in
+ // provision so that other apps (such as http) can know which
+ // certificates have been manually loaded
magic := certmagic.New(t.certCache, certmagic.Config{
- Storage: t.ctx.Storage(),
+ Storage: ctx.Storage(),
})
-
- // load manual/static (unmanaged) certificates
for _, loader := range t.certificateLoaders {
certs, err := loader.LoadCertificates()
if err != nil {
@@ -128,6 +129,11 @@ func (t *TLS) Start() error {
}
}
+ return nil
+}
+
+// Start activates the TLS module.
+func (t *TLS) Start() error {
// load automated (managed) certificates
if automatedRawMsg, ok := t.Certificates[automateKey]; ok {
var names []string
@@ -204,6 +210,12 @@ func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy {
return AutomationPolicy{Management: mgmt}
}
+// CertificatesWithSAN returns the list of all certificates
+// in the cache the match the given SAN value.
+func (t *TLS) CertificatesWithSAN(san string) []certmagic.Certificate {
+ return t.certCache.CertificatesWithSAN(san)
+}
+
// CertificateLoader is a type that can load certificates.
// Certificates can optionally be associated with tags.
type CertificateLoader interface {
@@ -230,7 +242,7 @@ type AutomationPolicy struct {
Hosts []string `json:"hosts,omitempty"`
ManagementRaw json.RawMessage `json:"management,omitempty"`
- Management managerMaker `json:"-"`
+ Management ManagerMaker `json:"-"`
}
// makeCertMagicConfig converts ap into a CertMagic config. Passing onDemand
@@ -245,7 +257,7 @@ func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
}
return certmagic.Config{
- NewManager: ap.Management.newManager,
+ NewManager: ap.Management.NewManager,
}
}
@@ -283,9 +295,9 @@ type RateLimit struct {
Burst int `json:"burst,omitempty"`
}
-// managerMaker makes a certificate manager.
-type managerMaker interface {
- newManager(interactive bool) (certmagic.Manager, error)
+// ManagerMaker makes a certificate manager.
+type ManagerMaker interface {
+ NewManager(interactive bool) (certmagic.Manager, error)
}
// These perpetual values are used for on-demand TLS.
diff --git a/modules/caddytls/values.go b/modules/caddytls/values.go
index 0c62058..b10fe22 100644
--- a/modules/caddytls/values.go
+++ b/modules/caddytls/values.go
@@ -22,12 +22,16 @@ import (
"github.com/klauspost/cpuid"
)
-// supportedCipherSuites is the unordered map of cipher suite
+// SupportedCipherSuites is the unordered map of cipher suite
// string names to their definition in crypto/tls. All values
// should be IANA-reserved names. See
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
+// Two of the cipher suite constants in the standard lib do not use the
+// full IANA name, but we do; see:
+// https://github.com/golang/go/issues/32061 and
+// https://github.com/golang/go/issues/30325#issuecomment-512862374.
// TODO: might not be needed much longer: https://github.com/golang/go/issues/30325
-var supportedCipherSuites = map[string]uint16{
+var SupportedCipherSuites = map[string]uint16{
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
@@ -84,22 +88,24 @@ func getOptimalDefaultCipherSuites() []uint16 {
return defaultCipherSuitesWithoutAESNI
}
-// supportedCurves is the unordered map of supported curves.
+// SupportedCurves is the unordered map of supported curves.
// https://golang.org/pkg/crypto/tls/#CurveID
-var supportedCurves = map[string]tls.CurveID{
- "X25519": tls.X25519,
- "P256": tls.CurveP256,
- "P384": tls.CurveP384,
- "P521": tls.CurveP521,
+var SupportedCurves = map[string]tls.CurveID{
+ // TODO: Use IANA names, probably? see https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
+ // All named crypto/elliptic curves have secpXXXr1 IANA names.
+ "x25519": tls.X25519, // x25519, 29
+ "p256": tls.CurveP256, // secp256r1, 23
+ "p384": tls.CurveP384, // secp384r1, 24
+ "p521": tls.CurveP521, // secp521r1, 25
}
// supportedCertKeyTypes is all the key types that are supported
// for certificates that are obtained through ACME.
var supportedCertKeyTypes = map[string]certcrypto.KeyType{
- "RSA2048": certcrypto.RSA2048,
- "RSA4096": certcrypto.RSA4096,
- "P256": certcrypto.EC256,
- "P384": certcrypto.EC384,
+ "rsa_2048": certcrypto.RSA2048,
+ "rsa_4096": certcrypto.RSA4096,
+ "ec_p256": certcrypto.EC256,
+ "ec_p384": certcrypto.EC384,
}
// defaultCurves is the list of only the curves we want to use
@@ -115,9 +121,9 @@ var defaultCurves = []tls.CurveID{
tls.CurveP256,
}
-// supportedProtocols is a map of supported protocols.
-// HTTP/2 only supports TLS 1.2 and higher.
-var supportedProtocols = map[string]uint16{
+// SupportedProtocols is a map of supported protocols.
+// Note that HTTP/2 only supports TLS 1.2 and higher.
+var SupportedProtocols = map[string]uint16{
"tls1.0": tls.VersionTLS10,
"tls1.1": tls.VersionTLS11,
"tls1.2": tls.VersionTLS12,