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) Router() *httprouter.Router { return 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) } } }