summaryrefslogtreecommitdiff
path: root/modules/caddyhttp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/caddyhttp')
-rw-r--r--modules/caddyhttp/caddyhttp.go25
-rw-r--r--modules/caddyhttp/matchers.go14
-rw-r--r--modules/caddyhttp/replacer.go75
-rw-r--r--modules/caddyhttp/staticresp/staticresp.go57
4 files changed, 156 insertions, 15 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index de62b79..0731fea 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -167,14 +167,15 @@ func (hc *httpModuleConfig) automaticHTTPS(handle caddy2.Handle) error {
var defaultALPN = []string{"h2", "http/1.1"}
type httpServerConfig struct {
- Listen []string `json:"listen"`
- ReadTimeout caddy2.Duration `json:"read_timeout"`
- ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
- HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state
- Routes routeList `json:"routes"`
- Errors httpErrorConfig `json:"errors"`
- TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"`
- DisableAutoHTTPS bool `json:"disable_auto_https"`
+ Listen []string `json:"listen"`
+ ReadTimeout caddy2.Duration `json:"read_timeout"`
+ ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
+ HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state
+ 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"`
tlsApp *caddytls.TLS
}
@@ -190,6 +191,12 @@ func (s httpServerConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
+ // set up the replacer
+ repl := &Replacer{req: r, resp: w, custom: make(map[string]string)}
+ ctx := context.WithValue(r.Context(), ReplacerCtxKey, repl)
+ r = r.WithContext(ctx)
+
+ // build and execute the main middleware chain
stack := s.Routes.buildMiddlewareChain(w, r)
err := executeMiddlewareChain(w, r, stack)
if err != nil {
@@ -329,5 +336,7 @@ func (mrw middlewareResponseWriter) Write(b []byte) (int, error) {
return mrw.ResponseWriterWrapper.Write(b)
}
+const ReplacerCtxKey caddy2.CtxKey = "replacer"
+
// Interface guards
var _ HTTPInterfaces = middlewareResponseWriter{}
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index 21cc19f..731832b 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -139,11 +139,11 @@ func (m matchHeader) Match(r *http.Request) bool {
// Interface guards
var (
- _ RouteMatcher = matchHost{}
- _ RouteMatcher = matchPath{}
- _ RouteMatcher = matchMethod{}
- _ RouteMatcher = matchQuery{}
- _ RouteMatcher = matchHeader{}
- _ RouteMatcher = new(matchProtocol)
- _ RouteMatcher = new(matchScript)
+ _ RouteMatcher = (*matchHost)(nil)
+ _ RouteMatcher = (*matchPath)(nil)
+ _ RouteMatcher = (*matchMethod)(nil)
+ _ RouteMatcher = (*matchQuery)(nil)
+ _ RouteMatcher = (*matchHeader)(nil)
+ _ RouteMatcher = (*matchProtocol)(nil)
+ _ RouteMatcher = (*matchScript)(nil)
)
diff --git a/modules/caddyhttp/replacer.go b/modules/caddyhttp/replacer.go
new file mode 100644
index 0000000..6a2ecd1
--- /dev/null
+++ b/modules/caddyhttp/replacer.go
@@ -0,0 +1,75 @@
+package caddyhttp
+
+import (
+ "net/http"
+ "strings"
+)
+
+type Replacer struct {
+ req *http.Request
+ resp http.ResponseWriter
+ custom map[string]string
+}
+
+// Map sets a custom variable mapping to a value.
+func (r *Replacer) Map(variable, value string) {
+ r.custom[variable] = value
+}
+
+// Replace replaces placeholders in input with the value. If
+// the value is empty string, the placeholder is substituted
+// with the value empty.
+func (r *Replacer) Replace(input, empty string) string {
+ if !strings.Contains(input, phOpen) {
+ return input
+ }
+
+ input = r.replaceAll(input, empty, r.defaults())
+ input = r.replaceAll(input, empty, r.custom)
+
+ return input
+}
+
+func (r *Replacer) replaceAll(input, empty string, mapping map[string]string) string {
+ for key, val := range mapping {
+ if val == "" {
+ val = empty
+ }
+ input = strings.ReplaceAll(input, phOpen+key+phClose, val)
+ }
+ return input
+}
+
+func (r *Replacer) defaults() map[string]string {
+ m := map[string]string{
+ "host": r.req.Host,
+ "method": r.req.Method,
+ "scheme": func() string {
+ if r.req.TLS != nil {
+ return "https"
+ }
+ return "http"
+ }(),
+ "uri": r.req.URL.RequestURI(),
+ }
+
+ for field, vals := range r.req.Header {
+ m[">"+strings.ToLower(field)] = strings.Join(vals, ",")
+ }
+
+ for field, vals := range r.resp.Header() {
+ m["<"+strings.ToLower(field)] = strings.Join(vals, ",")
+ }
+
+ for _, cookie := range r.req.Cookies() {
+ m["~"+cookie.Name] = cookie.Value
+ }
+
+ for param, vals := range r.req.URL.Query() {
+ m["?"+param] = strings.Join(vals, ",")
+ }
+
+ return m
+}
+
+const phOpen, phClose = "{", "}"
diff --git a/modules/caddyhttp/staticresp/staticresp.go b/modules/caddyhttp/staticresp/staticresp.go
new file mode 100644
index 0000000..e169133
--- /dev/null
+++ b/modules/caddyhttp/staticresp/staticresp.go
@@ -0,0 +1,57 @@
+package staticresp
+
+import (
+ "fmt"
+ "net/http"
+
+ "bitbucket.org/lightcodelabs/caddy2"
+ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
+)
+
+func init() {
+ caddy2.RegisterModule(caddy2.Module{
+ Name: "http.responders.static",
+ New: func() (interface{}, error) { return new(Static), nil },
+ })
+}
+
+// Static implements a simple responder for static responses.
+type Static struct {
+ StatusCode int `json:"status_code"`
+ Headers map[string][]string `json:"headers"`
+ Body string `json:"body"`
+ Close bool `json:"close"`
+}
+
+func (s Static) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
+ repl := r.Context().Value(caddyhttp.ReplacerCtxKey).(*caddyhttp.Replacer)
+
+ // close the connection
+ r.Close = s.Close
+
+ // set all headers, with replacements
+ for field, vals := range s.Headers {
+ field = repl.Replace(field, "")
+ for i := range vals {
+ vals[i] = repl.Replace(vals[i], "")
+ }
+ w.Header()[field] = vals
+ }
+
+ // write the headers with a status code
+ statusCode := s.StatusCode
+ if statusCode == 0 {
+ statusCode = http.StatusOK
+ }
+ w.WriteHeader(statusCode)
+
+ // write the response body, with replacements
+ if s.Body != "" {
+ fmt.Fprint(w, repl.Replace(s.Body, ""))
+ }
+
+ return nil
+}
+
+// Interface guard
+var _ caddyhttp.Handler = (*Static)(nil)