go-drip/drip.go
2025-04-24 16:07:48 +00:00

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)
}
}
}