145 lines
3.8 KiB
Go
145 lines
3.8 KiB
Go
package drip
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/gorilla/websocket"
|
|
"github.com/julienschmidt/httprouter"
|
|
)
|
|
|
|
type Drip struct {
|
|
router *httprouter.Router
|
|
errorHandler ErrorHandler
|
|
middlewares []Middleware
|
|
logger *log.Logger
|
|
websocketManager *WebsocketManager
|
|
}
|
|
|
|
func New() *Drip {
|
|
return &Drip{
|
|
router: httprouter.New(),
|
|
errorHandler: defaultErrorHandler,
|
|
middlewares: []Middleware{},
|
|
logger: log.New(os.Stdout, "[Drip] ", log.LstdFlags),
|
|
websocketManager: &WebsocketManager{
|
|
conns: make(map[string]map[*websocket.Conn]bool),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *Drip) Start(listenAddr string) error {
|
|
browsableUrl := listenAddr
|
|
if strings.HasPrefix(browsableUrl, ":") {
|
|
browsableUrl = fmt.Sprintf("http://localhost%s", browsableUrl)
|
|
}
|
|
|
|
d.logger.Printf("Starting app at %s\n", browsableUrl)
|
|
return http.ListenAndServe(listenAddr, d.router)
|
|
}
|
|
|
|
func (d *Drip) StartTLS(listenAddr, certFile, keyFile string) error {
|
|
browsableUrl := listenAddr
|
|
if strings.HasPrefix(browsableUrl, ":") {
|
|
browsableUrl = fmt.Sprintf("http://localhost%s", browsableUrl)
|
|
}
|
|
|
|
d.logger.Printf("Starting app at %s\n", browsableUrl)
|
|
return http.ListenAndServeTLS(listenAddr, certFile, keyFile, d.router)
|
|
}
|
|
|
|
func (d *Drip) SetErrorHandler(errorHandler ErrorHandler) {
|
|
d.errorHandler = errorHandler
|
|
}
|
|
|
|
func (d *Drip) Group(prefix string) *Group {
|
|
return &Group{
|
|
prefix: prefix,
|
|
drip: d,
|
|
}
|
|
}
|
|
|
|
func (d *Drip) Websocket(path string, middlewares ...Middleware) {
|
|
d.register(http.MethodGet, path, d.websocketManager.createHandler(), middlewares...)
|
|
}
|
|
|
|
func (d *Drip) Static(prefix, root string, middlewares ...Middleware) {
|
|
fileServer := http.FileServer(http.Dir(root))
|
|
wrappedHandler := d.createHandlerChain(func(ctx *Context) error {
|
|
http.StripPrefix(prefix, fileServer).ServeHTTP(ctx.response, ctx.request)
|
|
return nil
|
|
}, middlewares)
|
|
|
|
d.router.Handle(http.MethodGet, prefix+"/*filepath", wrappedHandler)
|
|
}
|
|
|
|
func (d *Drip) Use(middlewares ...Middleware) {
|
|
d.middlewares = append(d.middlewares, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) GET(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodGet, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) POST(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodPost, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) PUT(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodPut, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) DELETE(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodDelete, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) PATCH(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodPatch, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) HEAD(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodHead, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) OPTIONS(path string, h Handler, middlewares ...Middleware) {
|
|
d.register(http.MethodOptions, path, h, middlewares...)
|
|
}
|
|
|
|
func (d *Drip) register(method string, path string, h Handler, middlewares ...Middleware) {
|
|
handlers := slices.Clone(middlewares)
|
|
|
|
finalHandler := d.createHandlerChain(h, handlers)
|
|
d.router.Handle(method, path, finalHandler)
|
|
}
|
|
|
|
func (d *Drip) createHandlerChain(h Handler, middlewares []Middleware) httprouter.Handle {
|
|
return func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
|
ctx := &Context{
|
|
response: rw,
|
|
request: r,
|
|
params: params,
|
|
ctx: context.Background(),
|
|
}
|
|
|
|
finalHandler := h
|
|
|
|
for i := len(middlewares) - 1; i >= 0; i-- {
|
|
finalHandler = middlewares[i](finalHandler)
|
|
}
|
|
|
|
for i := len(d.middlewares) - 1; i >= 0; i-- {
|
|
finalHandler = d.middlewares[i](finalHandler)
|
|
}
|
|
|
|
if err := finalHandler(ctx); err != nil {
|
|
d.logger.Printf("Error in request %s %s: %v\n", r.Method, r.URL.Path, err)
|
|
d.errorHandler(err, ctx)
|
|
}
|
|
}
|
|
}
|