@marcel91200 / caddy Mirror · read-only / commits / 2eb35933
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

Begin implementing HTTP replacer and static responder

Matthew Holt committed May 4, 2019 at 13:21 UTC 2eb35933271d5b2ef1ca29aaa8f00f6748c56424
6 files changed +161 -30
admin_test.go
+1 -1
@@ -9,7 +9,7 @@ func BenchmarkLoad(b *testing.B) {
9 for i := 0; i < b.N; i++ {
10 r := strings.NewReader(`{
11 "testval": "Yippee!",
12 - "modules": {
12 + "apps": {
13 "http": {
14 "servers": {
15 "myserver": {
cmd/caddy2/main.go
+4 -14
@@ -1,28 +1,18 @@
1 package main
2
3 import (
4 - "log"
5 -
6 - _ "net/http/pprof"
7 -
8 - "bitbucket.org/lightcodelabs/caddy2"
4 + caddycmd "bitbucket.org/lightcodelabs/caddy2/cmd"
5
6 // this is where modules get plugged in
7
8 _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
9 _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/caddylog"
10 + _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/reverseproxy"
11 _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/staticfiles"
12 + _ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/staticresp"
13 _ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls"
16 - _ "bitbucket.org/lightcodelabs/dynamicconfig"
17 - _ "bitbucket.org/lightcodelabs/proxy"
14 )
15
16 func main() {
21 - err := caddy2.StartAdmin("127.0.0.1:1234")
22 - if err != nil {
23 - log.Fatal(err)
24 - }
25 - defer caddy2.StopAdmin()
26 -
27 - select {}
17 + caddycmd.Main()
18 }
modules/caddyhttp/caddyhttp.go
+17 -8
@@ -167,14 +167,15 @@ func (hc *httpModuleConfig) automaticHTTPS(handle caddy2.Handle) error {
167 var defaultALPN = []string{"h2", "http/1.1"}
168
169 type httpServerConfig struct {
170 - Listen []string `json:"listen"`
171 - ReadTimeout caddy2.Duration `json:"read_timeout"`
172 - ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
173 - HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state
174 - Routes routeList `json:"routes"`
175 - Errors httpErrorConfig `json:"errors"`
176 - TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"`
177 - DisableAutoHTTPS bool `json:"disable_auto_https"`
170 + Listen []string `json:"listen"`
171 + ReadTimeout caddy2.Duration `json:"read_timeout"`
172 + ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
173 + HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state
174 + Routes routeList `json:"routes"`
175 + Errors httpErrorConfig `json:"errors"`
176 + TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"`
177 + DisableAutoHTTPS bool `json:"disable_auto_https"`
178 + DisableAutoHTTPSRedir bool `json:"disable_auto_https_redir"`
179
180 tlsApp *caddytls.TLS
181 }
@@ -190,6 +191,12 @@ func (s httpServerConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) {
191 return
192 }
193
194 + // set up the replacer
195 + repl := &Replacer{req: r, resp: w, custom: make(map[string]string)}
196 + ctx := context.WithValue(r.Context(), ReplacerCtxKey, repl)
197 + r = r.WithContext(ctx)
198 +
199 + // build and execute the main middleware chain
200 stack := s.Routes.buildMiddlewareChain(w, r)
201 err := executeMiddlewareChain(w, r, stack)
202 if err != nil {
@@ -329,5 +336,7 @@ func (mrw middlewareResponseWriter) Write(b []byte) (int, error) {
336 return mrw.ResponseWriterWrapper.Write(b)
337 }
338
339 +const ReplacerCtxKey caddy2.CtxKey = "replacer"
340 +
341 // Interface guards
342 var _ HTTPInterfaces = middlewareResponseWriter{}
modules/caddyhttp/matchers.go
+7 -7
@@ -139,11 +139,11 @@ func (m matchHeader) Match(r *http.Request) bool {
139
140 // Interface guards
141 var (
142 - _ RouteMatcher = matchHost{}
143 - _ RouteMatcher = matchPath{}
144 - _ RouteMatcher = matchMethod{}
145 - _ RouteMatcher = matchQuery{}
146 - _ RouteMatcher = matchHeader{}
147 - _ RouteMatcher = new(matchProtocol)
148 - _ RouteMatcher = new(matchScript)
142 + _ RouteMatcher = (*matchHost)(nil)
143 + _ RouteMatcher = (*matchPath)(nil)
144 + _ RouteMatcher = (*matchMethod)(nil)
145 + _ RouteMatcher = (*matchQuery)(nil)
146 + _ RouteMatcher = (*matchHeader)(nil)
147 + _ RouteMatcher = (*matchProtocol)(nil)
148 + _ RouteMatcher = (*matchScript)(nil)
149 )
modules/caddyhttp/replacer.go new
+75
@@ -0,0 +1,75 @@
1 +package caddyhttp
2 +
3 +import (
4 + "net/http"
5 + "strings"
6 +)
7 +
8 +type Replacer struct {
9 + req *http.Request
10 + resp http.ResponseWriter
11 + custom map[string]string
12 +}
13 +
14 +// Map sets a custom variable mapping to a value.
15 +func (r *Replacer) Map(variable, value string) {
16 + r.custom[variable] = value
17 +}
18 +
19 +// Replace replaces placeholders in input with the value. If
20 +// the value is empty string, the placeholder is substituted
21 +// with the value empty.
22 +func (r *Replacer) Replace(input, empty string) string {
23 + if !strings.Contains(input, phOpen) {
24 + return input
25 + }
26 +
27 + input = r.replaceAll(input, empty, r.defaults())
28 + input = r.replaceAll(input, empty, r.custom)
29 +
30 + return input
31 +}
32 +
33 +func (r *Replacer) replaceAll(input, empty string, mapping map[string]string) string {
34 + for key, val := range mapping {
35 + if val == "" {
36 + val = empty
37 + }
38 + input = strings.ReplaceAll(input, phOpen+key+phClose, val)
39 + }
40 + return input
41 +}
42 +
43 +func (r *Replacer) defaults() map[string]string {
44 + m := map[string]string{
45 + "host": r.req.Host,
46 + "method": r.req.Method,
47 + "scheme": func() string {
48 + if r.req.TLS != nil {
49 + return "https"
50 + }
51 + return "http"
52 + }(),
53 + "uri": r.req.URL.RequestURI(),
54 + }
55 +
56 + for field, vals := range r.req.Header {
57 + m[">"+strings.ToLower(field)] = strings.Join(vals, ",")
58 + }
59 +
60 + for field, vals := range r.resp.Header() {
61 + m["<"+strings.ToLower(field)] = strings.Join(vals, ",")
62 + }
63 +
64 + for _, cookie := range r.req.Cookies() {
65 + m["~"+cookie.Name] = cookie.Value
66 + }
67 +
68 + for param, vals := range r.req.URL.Query() {
69 + m["?"+param] = strings.Join(vals, ",")
70 + }
71 +
72 + return m
73 +}
74 +
75 +const phOpen, phClose = "{", "}"
modules/caddyhttp/staticresp/staticresp.go new
+57
@@ -0,0 +1,57 @@
1 +package staticresp
2 +
3 +import (
4 + "fmt"
5 + "net/http"
6 +
7 + "bitbucket.org/lightcodelabs/caddy2"
8 + "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
9 +)
10 +
11 +func init() {
12 + caddy2.RegisterModule(caddy2.Module{
13 + Name: "http.responders.static",
14 + New: func() (interface{}, error) { return new(Static), nil },
15 + })
16 +}
17 +
18 +// Static implements a simple responder for static responses.
19 +type Static struct {
20 + StatusCode int `json:"status_code"`
21 + Headers map[string][]string `json:"headers"`
22 + Body string `json:"body"`
23 + Close bool `json:"close"`
24 +}
25 +
26 +func (s Static) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
27 + repl := r.Context().Value(caddyhttp.ReplacerCtxKey).(*caddyhttp.Replacer)
28 +
29 + // close the connection
30 + r.Close = s.Close
31 +
32 + // set all headers, with replacements
33 + for field, vals := range s.Headers {
34 + field = repl.Replace(field, "")
35 + for i := range vals {
36 + vals[i] = repl.Replace(vals[i], "")
37 + }
38 + w.Header()[field] = vals
39 + }
40 +
41 + // write the headers with a status code
42 + statusCode := s.StatusCode
43 + if statusCode == 0 {
44 + statusCode = http.StatusOK
45 + }
46 + w.WriteHeader(statusCode)
47 +
48 + // write the response body, with replacements
49 + if s.Body != "" {
50 + fmt.Fprint(w, repl.Replace(s.Body, ""))
51 + }
52 +
53 + return nil
54 +}
55 +
56 +// Interface guard
57 +var _ caddyhttp.Handler = (*Static)(nil)