mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
Begin implementing HTTP replacer and static responder
This commit is contained in:
parent
1136e2cfee
commit
2eb3593327
6 changed files with 161 additions and 30 deletions
|
@ -9,7 +9,7 @@ func BenchmarkLoad(b *testing.B) {
|
|||
for i := 0; i < b.N; i++ {
|
||||
r := strings.NewReader(`{
|
||||
"testval": "Yippee!",
|
||||
"modules": {
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"myserver": {
|
||||
|
|
|
@ -1,28 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
_ "net/http/pprof"
|
||||
|
||||
"bitbucket.org/lightcodelabs/caddy2"
|
||||
caddycmd "bitbucket.org/lightcodelabs/caddy2/cmd"
|
||||
|
||||
// this is where modules get plugged in
|
||||
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/caddylog"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/reverseproxy"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/staticfiles"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/staticresp"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls"
|
||||
_ "bitbucket.org/lightcodelabs/dynamicconfig"
|
||||
_ "bitbucket.org/lightcodelabs/proxy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := caddy2.StartAdmin("127.0.0.1:1234")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer caddy2.StopAdmin()
|
||||
|
||||
select {}
|
||||
caddycmd.Main()
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
75
modules/caddyhttp/replacer.go
Normal file
75
modules/caddyhttp/replacer.go
Normal file
|
@ -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 = "{", "}"
|
57
modules/caddyhttp/staticresp/staticresp.go
Normal file
57
modules/caddyhttp/staticresp/staticresp.go
Normal file
|
@ -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)
|
Loading…
Reference in a new issue