Export types and fields necessary to build configs (for config adapters)

Also flag most fields with 'omitempty' for JSON marshaling
This commit is contained in:
Matthew Holt 2019-05-22 12:32:36 -06:00
parent be9b6e7b57
commit bc00d840e8
11 changed files with 133 additions and 131 deletions

View File

@ -136,10 +136,10 @@ type App interface {
// Config represents a Caddy configuration.
type Config struct {
StorageRaw json.RawMessage `json:"storage"`
StorageRaw json.RawMessage `json:"storage,omitempty"`
storage certmagic.Storage
AppsRaw map[string]json.RawMessage `json:"apps"`
AppsRaw map[string]json.RawMessage `json:"apps,omitempty"`
// apps stores the decoded Apps values,
// keyed by module name.

View File

@ -32,10 +32,10 @@ func init() {
// App is the HTTP app for Caddy.
type App struct {
HTTPPort int `json:"http_port"`
HTTPSPort int `json:"https_port"`
GracePeriod caddy2.Duration `json:"grace_period"`
Servers map[string]*Server `json:"servers"`
HTTPPort int `json:"http_port,omitempty"`
HTTPSPort int `json:"https_port,omitempty"`
GracePeriod caddy2.Duration `json:"grace_period,omitempty"`
Servers map[string]*Server `json:"servers,omitempty"`
servers []*http.Server
@ -188,7 +188,7 @@ func (app *App) automaticHTTPS() error {
domainSet := make(map[string]struct{})
for _, route := range srv.Routes {
for _, m := range route.matchers {
if hm, ok := m.(*matchHost); ok {
if hm, ok := m.(*MatchHost); ok {
for _, d := range *hm {
if !certmagic.HostQualifies(d) {
continue
@ -246,8 +246,8 @@ func (app *App) automaticHTTPS() error {
redirRoutes = append(redirRoutes, ServerRoute{
matchers: []RequestMatcher{
matchProtocol("http"),
matchHost(domains),
MatchProtocol("http"),
MatchHost(domains),
},
responder: Static{
StatusCode: http.StatusTemporaryRedirect, // TODO: use permanent redirect instead

View File

@ -15,7 +15,7 @@ import (
// Browse configures directory browsing.
type Browse struct {
TemplateFile string `json:"template_file"`
TemplateFile string `json:"template_file,omitempty"`
template *template.Template
}

View File

@ -27,14 +27,14 @@ func init() {
// FileServer implements a static file server responder for Caddy.
type FileServer struct {
Root string `json:"root"` // default is current directory
Hide []string `json:"hide"`
IndexNames []string `json:"index_names"`
Files []string `json:"files"` // all relative to the root; default is request URI path
SelectionPolicy string `json:"selection_policy"`
Rehandle bool `json:"rehandle"` // issue a rehandle (internal redirect) if request is rewritten
Fallback caddyhttp.RouteList `json:"fallback"`
Browse *Browse `json:"browse"`
Root string `json:"root,omitempty"` // default is current directory
Hide []string `json:"hide,omitempty"`
IndexNames []string `json:"index_names,omitempty"`
Files []string `json:"files,omitempty"` // all relative to the root; default is request URI path
SelectionPolicy string `json:"selection_policy,omitempty"`
Rehandle bool `json:"rehandle,omitempty"` // issue a rehandle (internal redirect) if request is rewritten
Fallback caddyhttp.RouteList `json:"fallback,omitempty"`
Browse *Browse `json:"browse,omitempty"`
// TODO: Etag
// TODO: Content negotiation
}

View File

@ -17,22 +17,22 @@ func init() {
// Headers is a middleware which can mutate HTTP headers.
type Headers struct {
Request HeaderOps `json:"request"`
Response RespHeaderOps `json:"response"`
Request *HeaderOps `json:"request,omitempty"`
Response *RespHeaderOps `json:"response,omitempty"`
}
// HeaderOps defines some operations to
// perform on HTTP headers.
type HeaderOps struct {
Add http.Header `json:"add"`
Set http.Header `json:"set"`
Delete []string `json:"delete"`
Add http.Header `json:"add,omitempty"`
Set http.Header `json:"set,omitempty"`
Delete []string `json:"delete,omitempty"`
}
// RespHeaderOps is like HeaderOps, but
// optionally deferred until response time.
type RespHeaderOps struct {
HeaderOps
*HeaderOps
Deferred bool `json:"deferred"`
}
@ -51,7 +51,7 @@ func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
return next.ServeHTTP(w, r)
}
func apply(ops HeaderOps, hdr http.Header, repl caddy2.Replacer) {
func apply(ops *HeaderOps, hdr http.Header, repl caddy2.Replacer) {
for fieldName, vals := range ops.Add {
fieldName = repl.ReplaceAll(fieldName, "")
for _, v := range vals {
@ -75,7 +75,7 @@ func apply(ops HeaderOps, hdr http.Header, repl caddy2.Replacer) {
type responseWriterWrapper struct {
*caddyhttp.ResponseWriterWrapper
replacer caddy2.Replacer
headerOps HeaderOps
headerOps *HeaderOps
wroteHeader bool
}

View File

@ -17,58 +17,58 @@ import (
)
type (
matchHost []string
matchPath []string
matchPathRE struct{ matchRegexp }
matchMethod []string
matchQuery url.Values
matchHeader http.Header
matchHeaderRE map[string]*matchRegexp
matchProtocol string
matchStarlarkExpr string
matchTable string // TODO: finish implementing
MatchHost []string
MatchPath []string
MatchPathRE struct{ matchRegexp }
MatchMethod []string
MatchQuery url.Values
MatchHeader http.Header
MatchHeaderRE map[string]*matchRegexp
MatchProtocol string
MatchStarlarkExpr string
MatchTable string // TODO: finish implementing
)
func init() {
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.host",
New: func() interface{} { return matchHost{} },
New: func() interface{} { return MatchHost{} },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.path",
New: func() interface{} { return matchPath{} },
New: func() interface{} { return MatchPath{} },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.path_regexp",
New: func() interface{} { return new(matchPathRE) },
New: func() interface{} { return new(MatchPathRE) },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.method",
New: func() interface{} { return matchMethod{} },
New: func() interface{} { return MatchMethod{} },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.query",
New: func() interface{} { return matchQuery{} },
New: func() interface{} { return MatchQuery{} },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.header",
New: func() interface{} { return matchHeader{} },
New: func() interface{} { return MatchHeader{} },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.header_regexp",
New: func() interface{} { return matchHeaderRE{} },
New: func() interface{} { return MatchHeaderRE{} },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.protocol",
New: func() interface{} { return new(matchProtocol) },
New: func() interface{} { return new(MatchProtocol) },
})
caddy2.RegisterModule(caddy2.Module{
Name: "http.matchers.starlark_expr",
New: func() interface{} { return new(matchStarlarkExpr) },
New: func() interface{} { return new(MatchStarlarkExpr) },
})
}
func (m matchHost) Match(r *http.Request) bool {
func (m MatchHost) Match(r *http.Request) bool {
outer:
for _, host := range m {
if strings.Contains(host, "*") {
@ -93,7 +93,7 @@ outer:
return false
}
func (m matchPath) Match(r *http.Request) bool {
func (m MatchPath) Match(r *http.Request) bool {
for _, matchPath := range m {
compare := r.URL.Path
if strings.HasPrefix(matchPath, "*") {
@ -111,12 +111,12 @@ func (m matchPath) Match(r *http.Request) bool {
return false
}
func (m matchPathRE) Match(r *http.Request) bool {
func (m MatchPathRE) Match(r *http.Request) bool {
repl := r.Context().Value(caddy2.ReplacerCtxKey).(caddy2.Replacer)
return m.match(r.URL.Path, repl, "path_regexp")
}
func (m matchMethod) Match(r *http.Request) bool {
func (m MatchMethod) Match(r *http.Request) bool {
for _, method := range m {
if r.Method == method {
return true
@ -125,7 +125,7 @@ func (m matchMethod) Match(r *http.Request) bool {
return false
}
func (m matchQuery) Match(r *http.Request) bool {
func (m MatchQuery) Match(r *http.Request) bool {
for param, vals := range m {
paramVal := r.URL.Query().Get(param)
for _, v := range vals {
@ -137,7 +137,7 @@ func (m matchQuery) Match(r *http.Request) bool {
return false
}
func (m matchHeader) Match(r *http.Request) bool {
func (m MatchHeader) Match(r *http.Request) bool {
for field, allowedFieldVals := range m {
var match bool
actualFieldVals := r.Header[textproto.CanonicalMIMEHeaderKey(field)]
@ -157,7 +157,7 @@ func (m matchHeader) Match(r *http.Request) bool {
return true
}
func (m matchHeaderRE) Match(r *http.Request) bool {
func (m MatchHeaderRE) Match(r *http.Request) bool {
for field, rm := range m {
repl := r.Context().Value(caddy2.ReplacerCtxKey).(caddy2.Replacer)
match := rm.match(r.Header.Get(field), repl, "header_regexp")
@ -168,7 +168,7 @@ func (m matchHeaderRE) Match(r *http.Request) bool {
return true
}
func (m matchHeaderRE) Provision() error {
func (m MatchHeaderRE) Provision() error {
for _, rm := range m {
err := rm.Provision()
if err != nil {
@ -178,7 +178,7 @@ func (m matchHeaderRE) Provision() error {
return nil
}
func (m matchHeaderRE) Validate() error {
func (m MatchHeaderRE) Validate() error {
for _, rm := range m {
err := rm.Validate()
if err != nil {
@ -188,7 +188,7 @@ func (m matchHeaderRE) Validate() error {
return nil
}
func (m matchProtocol) Match(r *http.Request) bool {
func (m MatchProtocol) Match(r *http.Request) bool {
switch string(m) {
case "grpc":
return r.Header.Get("content-type") == "application/grpc"
@ -200,7 +200,7 @@ func (m matchProtocol) Match(r *http.Request) bool {
return false
}
func (m matchStarlarkExpr) Match(r *http.Request) bool {
func (m MatchStarlarkExpr) Match(r *http.Request) bool {
input := string(m)
thread := new(starlark.Thread)
env := caddyscript.MatcherEnv(r)
@ -264,13 +264,13 @@ var wordRE = regexp.MustCompile(`\w+`)
// Interface guards
var (
_ RequestMatcher = (*matchHost)(nil)
_ RequestMatcher = (*matchPath)(nil)
_ RequestMatcher = (*matchPathRE)(nil)
_ RequestMatcher = (*matchMethod)(nil)
_ RequestMatcher = (*matchQuery)(nil)
_ RequestMatcher = (*matchHeader)(nil)
_ RequestMatcher = (*matchHeaderRE)(nil)
_ RequestMatcher = (*matchProtocol)(nil)
_ RequestMatcher = (*matchStarlarkExpr)(nil)
_ RequestMatcher = (*MatchHost)(nil)
_ RequestMatcher = (*MatchPath)(nil)
_ RequestMatcher = (*MatchPathRE)(nil)
_ RequestMatcher = (*MatchMethod)(nil)
_ RequestMatcher = (*MatchQuery)(nil)
_ RequestMatcher = (*MatchHeader)(nil)
_ RequestMatcher = (*MatchHeaderRE)(nil)
_ RequestMatcher = (*MatchProtocol)(nil)
_ RequestMatcher = (*MatchStarlarkExpr)(nil)
)

View File

@ -13,77 +13,77 @@ import (
func TestHostMatcher(t *testing.T) {
for i, tc := range []struct {
match matchHost
match MatchHost
input string
expect bool
}{
{
match: matchHost{},
match: MatchHost{},
input: "example.com",
expect: false,
},
{
match: matchHost{"example.com"},
match: MatchHost{"example.com"},
input: "example.com",
expect: true,
},
{
match: matchHost{"example.com"},
match: MatchHost{"example.com"},
input: "foo.example.com",
expect: false,
},
{
match: matchHost{"example.com"},
match: MatchHost{"example.com"},
input: "EXAMPLE.COM",
expect: true,
},
{
match: matchHost{"foo.example.com"},
match: MatchHost{"foo.example.com"},
input: "foo.example.com",
expect: true,
},
{
match: matchHost{"foo.example.com"},
match: MatchHost{"foo.example.com"},
input: "bar.example.com",
expect: false,
},
{
match: matchHost{"*.example.com"},
match: MatchHost{"*.example.com"},
input: "example.com",
expect: false,
},
{
match: matchHost{"*.example.com"},
match: MatchHost{"*.example.com"},
input: "foo.example.com",
expect: true,
},
{
match: matchHost{"*.example.com"},
match: MatchHost{"*.example.com"},
input: "foo.bar.example.com",
expect: false,
},
{
match: matchHost{"*.example.com", "example.net"},
match: MatchHost{"*.example.com", "example.net"},
input: "example.net",
expect: true,
},
{
match: matchHost{"example.net", "*.example.com"},
match: MatchHost{"example.net", "*.example.com"},
input: "foo.example.com",
expect: true,
},
{
match: matchHost{"*.example.net", "*.*.example.com"},
match: MatchHost{"*.example.net", "*.*.example.com"},
input: "foo.bar.example.com",
expect: true,
},
{
match: matchHost{"*.example.net", "sub.*.example.com"},
match: MatchHost{"*.example.net", "sub.*.example.com"},
input: "sub.foo.example.com",
expect: true,
},
{
match: matchHost{"*.example.net", "sub.*.example.com"},
match: MatchHost{"*.example.net", "sub.*.example.com"},
input: "sub.foo.example.net",
expect: false,
},
@ -99,57 +99,57 @@ func TestHostMatcher(t *testing.T) {
func TestPathMatcher(t *testing.T) {
for i, tc := range []struct {
match matchPath
match MatchPath
input string
expect bool
}{
{
match: matchPath{},
match: MatchPath{},
input: "/",
expect: false,
},
{
match: matchPath{"/"},
match: MatchPath{"/"},
input: "/",
expect: true,
},
{
match: matchPath{"/foo/bar"},
match: MatchPath{"/foo/bar"},
input: "/",
expect: false,
},
{
match: matchPath{"/foo/bar"},
match: MatchPath{"/foo/bar"},
input: "/foo/bar",
expect: true,
},
{
match: matchPath{"/foo/bar/"},
match: MatchPath{"/foo/bar/"},
input: "/foo/bar",
expect: false,
},
{
match: matchPath{"/foo/bar/", "/other"},
match: MatchPath{"/foo/bar/", "/other"},
input: "/other/",
expect: true,
},
{
match: matchPath{"*.ext"},
match: MatchPath{"*.ext"},
input: "foo.ext",
expect: true,
},
{
match: matchPath{"*.ext"},
match: MatchPath{"*.ext"},
input: "/foo/bar.ext",
expect: true,
},
{
match: matchPath{"/foo/*/baz"},
match: MatchPath{"/foo/*/baz"},
input: "/foo/bar/baz",
expect: true,
},
{
match: matchPath{"/foo/*/baz/bam"},
match: MatchPath{"/foo/*/baz/bam"},
input: "/foo/bar/bam",
expect: false,
},
@ -165,49 +165,49 @@ func TestPathMatcher(t *testing.T) {
func TestPathREMatcher(t *testing.T) {
for i, tc := range []struct {
match matchPathRE
match MatchPathRE
input string
expect bool
expectRepl map[string]string
}{
{
match: matchPathRE{},
match: MatchPathRE{},
input: "/",
expect: true,
},
{
match: matchPathRE{matchRegexp{Pattern: "/"}},
match: MatchPathRE{matchRegexp{Pattern: "/"}},
input: "/",
expect: true,
},
{
match: matchPathRE{matchRegexp{Pattern: "/foo"}},
match: MatchPathRE{matchRegexp{Pattern: "/foo"}},
input: "/foo",
expect: true,
},
{
match: matchPathRE{matchRegexp{Pattern: "/foo"}},
match: MatchPathRE{matchRegexp{Pattern: "/foo"}},
input: "/foo/",
expect: true,
},
{
match: matchPathRE{matchRegexp{Pattern: "/bar"}},
match: MatchPathRE{matchRegexp{Pattern: "/bar"}},
input: "/foo/",
expect: false,
},
{
match: matchPathRE{matchRegexp{Pattern: "^/bar"}},
match: MatchPathRE{matchRegexp{Pattern: "^/bar"}},
input: "/foo/bar",
expect: false,
},
{
match: matchPathRE{matchRegexp{Pattern: "^/foo/(.*)/baz$", Name: "name"}},
match: MatchPathRE{matchRegexp{Pattern: "^/foo/(.*)/baz$", Name: "name"}},
input: "/foo/bar/baz",
expect: true,
expectRepl: map[string]string{"name.1": "bar"},
},
{
match: matchPathRE{matchRegexp{Pattern: "^/foo/(?P<myparam>.*)/baz$", Name: "name"}},
match: MatchPathRE{matchRegexp{Pattern: "^/foo/(?P<myparam>.*)/baz$", Name: "name"}},
input: "/foo/bar/baz",
expect: true,
expectRepl: map[string]string{"name.myparam": "bar"},
@ -253,47 +253,47 @@ func TestPathREMatcher(t *testing.T) {
func TestHeaderMatcher(t *testing.T) {
for i, tc := range []struct {
match matchHeader
match MatchHeader
input http.Header // make sure these are canonical cased (std lib will do that in a real request)
expect bool
}{
{
match: matchHeader{"Field": []string{"foo"}},
match: MatchHeader{"Field": []string{"foo"}},
input: http.Header{"Field": []string{"foo"}},
expect: true,
},
{
match: matchHeader{"Field": []string{"foo", "bar"}},
match: MatchHeader{"Field": []string{"foo", "bar"}},
input: http.Header{"Field": []string{"bar"}},
expect: true,
},
{
match: matchHeader{"Field": []string{"foo", "bar"}},
match: MatchHeader{"Field": []string{"foo", "bar"}},
input: http.Header{"Alakazam": []string{"kapow"}},
expect: false,
},
{
match: matchHeader{"Field": []string{"foo", "bar"}},
match: MatchHeader{"Field": []string{"foo", "bar"}},
input: http.Header{"Field": []string{"kapow"}},
expect: false,
},
{
match: matchHeader{"Field": []string{"foo", "bar"}},
match: MatchHeader{"Field": []string{"foo", "bar"}},
input: http.Header{"Field": []string{"kapow", "foo"}},
expect: true,
},
{
match: matchHeader{"Field1": []string{"foo"}, "Field2": []string{"bar"}},
match: MatchHeader{"Field1": []string{"foo"}, "Field2": []string{"bar"}},
input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"bar"}},
expect: true,
},
{
match: matchHeader{"field1": []string{"foo"}, "field2": []string{"bar"}},
match: MatchHeader{"field1": []string{"foo"}, "field2": []string{"bar"}},
input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"bar"}},
expect: true,
},
{
match: matchHeader{"field1": []string{"foo"}, "field2": []string{"bar"}},
match: MatchHeader{"field1": []string{"foo"}, "field2": []string{"bar"}},
input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"kapow"}},
expect: false,
},
@ -309,23 +309,23 @@ func TestHeaderMatcher(t *testing.T) {
func TestHeaderREMatcher(t *testing.T) {
for i, tc := range []struct {
match matchHeaderRE
match MatchHeaderRE
input http.Header // make sure these are canonical cased (std lib will do that in a real request)
expect bool
expectRepl map[string]string
}{
{
match: matchHeaderRE{"Field": &matchRegexp{Pattern: "foo"}},
match: MatchHeaderRE{"Field": &matchRegexp{Pattern: "foo"}},
input: http.Header{"Field": []string{"foo"}},
expect: true,
},
{
match: matchHeaderRE{"Field": &matchRegexp{Pattern: "$foo^"}},
match: MatchHeaderRE{"Field": &matchRegexp{Pattern: "$foo^"}},
input: http.Header{"Field": []string{"foobar"}},
expect: false,
},
{
match: matchHeaderRE{"Field": &matchRegexp{Pattern: "^foo(.*)$", Name: "name"}},
match: MatchHeaderRE{"Field": &matchRegexp{Pattern: "^foo(.*)$", Name: "name"}},
input: http.Header{"Field": []string{"foobar"}},
expect: true,
expectRepl: map[string]string{"name.1": "bar"},

View File

@ -37,6 +37,8 @@ const (
// TypeBalanceRandom represents the value to use for configuring a load balanced reverse proxy to use random load balancing.
TypeBalanceRandom
// TODO: add random with two choices
// msgNoHealthyUpstreams is returned if there are no upstreams that are healthy to proxy a request to
msgNoHealthyUpstreams = "No healthy upstreams."

View File

@ -18,9 +18,9 @@ func init() {
// Rewrite is a middleware which can rewrite HTTP requests.
type Rewrite struct {
Method string `json:"method"`
URI string `json:"uri"`
Rehandle bool `json:"rehandle"`
Method string `json:"method,omitempty"`
URI string `json:"uri,omitempty"`
Rehandle bool `json:"rehandle,omitempty"`
}
func (rewr Rewrite) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {

View File

@ -12,12 +12,12 @@ import (
// middlewares, and a responder for handling HTTP
// requests.
type ServerRoute struct {
Group string `json:"group"`
Matchers map[string]json.RawMessage `json:"match"`
Apply []json.RawMessage `json:"apply"`
Respond json.RawMessage `json:"respond"`
Group string `json:"group,omitempty"`
Matchers map[string]json.RawMessage `json:"match,omitempty"`
Apply []json.RawMessage `json:"apply,omitempty"`
Respond json.RawMessage `json:"respond,omitempty"`
Terminal bool `json:"terminal"`
Terminal bool `json:"terminal,omitempty"`
// decoded values
matchers []RequestMatcher

View File

@ -13,15 +13,15 @@ import (
// Server is an HTTP server.
type Server struct {
Listen []string `json:"listen"`
ReadTimeout caddy2.Duration `json:"read_timeout"`
ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
Routes RouteList `json:"routes"`
Errors httpErrorConfig `json:"errors"`
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"`
DisableAutoHTTPS bool `json:"disable_auto_https"`
DisableAutoHTTPSRedir bool `json:"disable_auto_https_redir"`
MaxRehandles int `json:"max_rehandles"`
Listen []string `json:"listen,omitempty"`
ReadTimeout caddy2.Duration `json:"read_timeout,omitempty"`
ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout,omitempty"`
Routes RouteList `json:"routes,omitempty"`
Errors *httpErrorConfig `json:"errors,omitempty"`
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"`
DisableAutoHTTPS bool `json:"disable_auto_https,omitempty"`
DisableAutoHTTPSRedir bool `json:"disable_auto_https_redir,omitempty"`
MaxRehandles int `json:"max_rehandles,omitempty"`
tlsApp *caddytls.TLS
}
@ -99,7 +99,7 @@ func (s *Server) executeCompositeRoute(w http.ResponseWriter, r *http.Request, s
}
type httpErrorConfig struct {
Routes RouteList `json:"routes"`
Routes RouteList `json:"routes,omitempty"`
// TODO: some way to configure the logging of errors, probably? standardize
// the logging configuration first.
}