Matthew Holt
6 years ago
9 changed files with 454 additions and 25 deletions
@ -0,0 +1,37 @@ |
|||
package caddylog |
|||
|
|||
import ( |
|||
"log" |
|||
"net/http" |
|||
"time" |
|||
|
|||
"bitbucket.org/lightcodelabs/caddy2" |
|||
"bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp" |
|||
) |
|||
|
|||
func init() { |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.middleware.log", |
|||
New: func() (interface{}, error) { return &Log{}, nil }, |
|||
}) |
|||
} |
|||
|
|||
// Log implements a simple logging middleware.
|
|||
type Log struct { |
|||
Filename string |
|||
} |
|||
|
|||
func (l *Log) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { |
|||
start := time.Now() |
|||
|
|||
if err := next.ServeHTTP(w, r); err != nil { |
|||
return err |
|||
} |
|||
|
|||
log.Println("latency:", time.Now().Sub(start)) |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// Interface guard
|
|||
var _ caddyhttp.MiddlewareHandler = &Log{} |
@ -0,0 +1,102 @@ |
|||
package caddyhttp |
|||
|
|||
import ( |
|||
"net/http" |
|||
"strings" |
|||
|
|||
"bitbucket.org/lightcodelabs/caddy2" |
|||
) |
|||
|
|||
func init() { |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.matchers.host", |
|||
New: func() (interface{}, error) { return matchHost{}, nil }, |
|||
}) |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.matchers.path", |
|||
New: func() (interface{}, error) { return matchPath{}, nil }, |
|||
}) |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.matchers.method", |
|||
New: func() (interface{}, error) { return matchMethod{}, nil }, |
|||
}) |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.matchers.query", |
|||
New: func() (interface{}, error) { return matchQuery{}, nil }, |
|||
}) |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.matchers.header", |
|||
New: func() (interface{}, error) { return matchHeader{}, nil }, |
|||
}) |
|||
} |
|||
|
|||
// TODO: Matchers should probably support regex of some sort... performance trade-offs?
|
|||
|
|||
type ( |
|||
matchHost []string |
|||
matchPath []string |
|||
matchMethod []string |
|||
matchQuery map[string][]string |
|||
matchHeader map[string][]string |
|||
) |
|||
|
|||
func (m matchHost) Match(r *http.Request) bool { |
|||
for _, host := range m { |
|||
if r.Host == host { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func (m matchPath) Match(r *http.Request) bool { |
|||
for _, path := range m { |
|||
if strings.HasPrefix(r.URL.Path, path) { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func (m matchMethod) Match(r *http.Request) bool { |
|||
for _, method := range m { |
|||
if r.Method == method { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func (m matchQuery) Match(r *http.Request) bool { |
|||
for param, vals := range m { |
|||
paramVal := r.URL.Query().Get(param) |
|||
for _, v := range vals { |
|||
if paramVal == v { |
|||
return true |
|||
} |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func (m matchHeader) Match(r *http.Request) bool { |
|||
for field, vals := range m { |
|||
fieldVals := r.Header[field] |
|||
for _, fieldVal := range fieldVals { |
|||
for _, v := range vals { |
|||
if fieldVal == v { |
|||
return true |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
var ( |
|||
_ RouteMatcher = matchHost{} |
|||
_ RouteMatcher = matchPath{} |
|||
_ RouteMatcher = matchMethod{} |
|||
_ RouteMatcher = matchQuery{} |
|||
_ RouteMatcher = matchHeader{} |
|||
) |
@ -0,0 +1,68 @@ |
|||
package caddyhttp |
|||
|
|||
import ( |
|||
"bufio" |
|||
"fmt" |
|||
"net" |
|||
"net/http" |
|||
) |
|||
|
|||
// ResponseWriterWrapper wraps an underlying ResponseWriter and
|
|||
// promotes its Pusher/Flusher/CloseNotifier/Hijacker methods
|
|||
// as well. To use this type, embed a pointer to it within your
|
|||
// own struct type that implements the http.ResponseWriter
|
|||
// interface, then call methods on the embedded value. You can
|
|||
// make sure your type wraps correctly by asserting that it
|
|||
// implements the HTTPInterfaces interface.
|
|||
type ResponseWriterWrapper struct { |
|||
http.ResponseWriter |
|||
} |
|||
|
|||
// Hijack implements http.Hijacker. It simply calls the underlying
|
|||
// ResponseWriter's Hijack method if there is one, or returns an error.
|
|||
func (rww *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
|||
if hj, ok := rww.ResponseWriter.(http.Hijacker); ok { |
|||
return hj.Hijack() |
|||
} |
|||
return nil, nil, fmt.Errorf("not a hijacker") |
|||
} |
|||
|
|||
// Flush implements http.Flusher. It simply calls the underlying
|
|||
// ResponseWriter's Flush method if there is one, or panics.
|
|||
func (rww *ResponseWriterWrapper) Flush() { |
|||
if f, ok := rww.ResponseWriter.(http.Flusher); ok { |
|||
f.Flush() |
|||
} else { |
|||
panic("not a flusher") |
|||
} |
|||
} |
|||
|
|||
// CloseNotify implements http.CloseNotifier. It simply calls the underlying
|
|||
// ResponseWriter's CloseNotify method if there is one, or panics.
|
|||
func (rww *ResponseWriterWrapper) CloseNotify() <-chan bool { |
|||
if cn, ok := rww.ResponseWriter.(http.CloseNotifier); ok { |
|||
return cn.CloseNotify() |
|||
} |
|||
panic("not a close notifier") |
|||
} |
|||
|
|||
// Push implements http.Pusher. It simply calls the underlying
|
|||
// ResponseWriter's Push method if there is one, or returns an error.
|
|||
func (rww *ResponseWriterWrapper) Push(target string, opts *http.PushOptions) error { |
|||
if pusher, hasPusher := rww.ResponseWriter.(http.Pusher); hasPusher { |
|||
return pusher.Push(target, opts) |
|||
} |
|||
return fmt.Errorf("not a pusher") |
|||
} |
|||
|
|||
// HTTPInterfaces mix all the interfaces that middleware ResponseWriters need to support.
|
|||
type HTTPInterfaces interface { |
|||
http.ResponseWriter |
|||
http.Pusher |
|||
http.Flusher |
|||
http.CloseNotifier |
|||
http.Hijacker |
|||
} |
|||
|
|||
// Interface guards
|
|||
var _ HTTPInterfaces = (*ResponseWriterWrapper)(nil) |
@ -0,0 +1,28 @@ |
|||
package staticfiles |
|||
|
|||
import ( |
|||
"net/http" |
|||
|
|||
"bitbucket.org/lightcodelabs/caddy2" |
|||
"bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp" |
|||
) |
|||
|
|||
func init() { |
|||
caddy2.RegisterModule(caddy2.Module{ |
|||
Name: "http.responders.static_files", |
|||
New: func() (interface{}, error) { return &StaticFiles{}, nil }, |
|||
}) |
|||
} |
|||
|
|||
// StaticFiles implements a static file server responder for Caddy.
|
|||
type StaticFiles struct { |
|||
Root string |
|||
} |
|||
|
|||
func (sf StaticFiles) ServeHTTP(w http.ResponseWriter, r *http.Request) error { |
|||
http.FileServer(http.Dir(sf.Root)).ServeHTTP(w, r) |
|||
return nil |
|||
} |
|||
|
|||
// Interface guard
|
|||
var _ caddyhttp.Handler = StaticFiles{} |
Loading…
Reference in new issue