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++ {
|
for i := 0; i < b.N; i++ {
|
||||||
r := strings.NewReader(`{
|
r := strings.NewReader(`{
|
||||||
"testval": "Yippee!",
|
"testval": "Yippee!",
|
||||||
"modules": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"servers": {
|
"servers": {
|
||||||
"myserver": {
|
"myserver": {
|
||||||
|
|
|
@ -1,28 +1,18 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
caddycmd "bitbucket.org/lightcodelabs/caddy2/cmd"
|
||||||
|
|
||||||
_ "net/http/pprof"
|
|
||||||
|
|
||||||
"bitbucket.org/lightcodelabs/caddy2"
|
|
||||||
|
|
||||||
// this is where modules get plugged in
|
// this is where modules get plugged in
|
||||||
|
|
||||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
|
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp"
|
||||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/caddylog"
|
_ "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/staticfiles"
|
||||||
|
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/staticresp"
|
||||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls"
|
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls"
|
||||||
_ "bitbucket.org/lightcodelabs/dynamicconfig"
|
|
||||||
_ "bitbucket.org/lightcodelabs/proxy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := caddy2.StartAdmin("127.0.0.1:1234")
|
caddycmd.Main()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer caddy2.StopAdmin()
|
|
||||||
|
|
||||||
select {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,14 +167,15 @@ func (hc *httpModuleConfig) automaticHTTPS(handle caddy2.Handle) error {
|
||||||
var defaultALPN = []string{"h2", "http/1.1"}
|
var defaultALPN = []string{"h2", "http/1.1"}
|
||||||
|
|
||||||
type httpServerConfig struct {
|
type httpServerConfig struct {
|
||||||
Listen []string `json:"listen"`
|
Listen []string `json:"listen"`
|
||||||
ReadTimeout caddy2.Duration `json:"read_timeout"`
|
ReadTimeout caddy2.Duration `json:"read_timeout"`
|
||||||
ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
|
ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"`
|
||||||
HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state
|
HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state
|
||||||
Routes routeList `json:"routes"`
|
Routes routeList `json:"routes"`
|
||||||
Errors httpErrorConfig `json:"errors"`
|
Errors httpErrorConfig `json:"errors"`
|
||||||
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"`
|
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"`
|
||||||
DisableAutoHTTPS bool `json:"disable_auto_https"`
|
DisableAutoHTTPS bool `json:"disable_auto_https"`
|
||||||
|
DisableAutoHTTPSRedir bool `json:"disable_auto_https_redir"`
|
||||||
|
|
||||||
tlsApp *caddytls.TLS
|
tlsApp *caddytls.TLS
|
||||||
}
|
}
|
||||||
|
@ -190,6 +191,12 @@ func (s httpServerConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
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)
|
stack := s.Routes.buildMiddlewareChain(w, r)
|
||||||
err := executeMiddlewareChain(w, r, stack)
|
err := executeMiddlewareChain(w, r, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -329,5 +336,7 @@ func (mrw middlewareResponseWriter) Write(b []byte) (int, error) {
|
||||||
return mrw.ResponseWriterWrapper.Write(b)
|
return mrw.ResponseWriterWrapper.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ReplacerCtxKey caddy2.CtxKey = "replacer"
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var _ HTTPInterfaces = middlewareResponseWriter{}
|
var _ HTTPInterfaces = middlewareResponseWriter{}
|
||||||
|
|
|
@ -139,11 +139,11 @@ func (m matchHeader) Match(r *http.Request) bool {
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ RouteMatcher = matchHost{}
|
_ RouteMatcher = (*matchHost)(nil)
|
||||||
_ RouteMatcher = matchPath{}
|
_ RouteMatcher = (*matchPath)(nil)
|
||||||
_ RouteMatcher = matchMethod{}
|
_ RouteMatcher = (*matchMethod)(nil)
|
||||||
_ RouteMatcher = matchQuery{}
|
_ RouteMatcher = (*matchQuery)(nil)
|
||||||
_ RouteMatcher = matchHeader{}
|
_ RouteMatcher = (*matchHeader)(nil)
|
||||||
_ RouteMatcher = new(matchProtocol)
|
_ RouteMatcher = (*matchProtocol)(nil)
|
||||||
_ RouteMatcher = new(matchScript)
|
_ 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