Files
hamlogger/internal/router/router.go
Олег Бородин ada2a49a64 initial import of sources
2024-06-18 10:15:22 +02:00

158 lines
3.3 KiB
Go

package router
import (
"fmt"
"net/http"
"regexp"
)
type HandlerFunc func(ctx *Context)
func (handlerFunc HandlerFunc) ServeHTTP(ctx *Context) {
handlerFunc(ctx)
}
type Handler interface {
ServeHTTP(ctx *Context)
}
type Router struct {
routingHandler *RoutingHandler
headHandler Handler
}
func NewRouter() *Router {
rh := NewRoutingHandler()
return &Router{
headHandler: rh,
routingHandler: rh,
}
}
func (rt *Router) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
ctx := &Context{
Writer: writer,
Req: req,
PathMap: make(map[string]string),
StatusCode: http.StatusOK,
}
rt.headHandler.ServeHTTP(ctx)
}
func (rout *Router) Use(mw func(next Handler) Handler) {
rout.headHandler = mw(rout.headHandler)
}
func (rout *Router) AddRoute(method, path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute(method, path, handlerFunc)
}
func (rout *Router) Get(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("GET", path, handlerFunc)
}
func (rout *Router) Head(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("HEAD", path, handlerFunc)
}
func (rout *Router) Post(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("POST", path, handlerFunc)
}
func (rout *Router) Put(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("PUT", path, handlerFunc)
}
func (rout *Router) Patch(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("PATCH", path, handlerFunc)
}
func (rout *Router) Delete(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("DELETE", path, handlerFunc)
}
func (rout *Router) Option(path string, handlerFunc HandlerFunc) {
rout.routingHandler.AddRoute("OPTION", path, handlerFunc)
}
func (rout *Router) NotFound(handlerFunc HandlerFunc) {
rout.routingHandler.notFound = handlerFunc
}
func (rout *Router) ListRoutes() []*Route {
return rout.routingHandler.Routes
}
type RoutingHandler struct {
Routes []*Route
notFound Handler
}
func NewRoutingHandler() *RoutingHandler {
notFound := HandlerFunc(func(ctx *Context) {
http.NotFound(ctx.Writer, ctx.Req)
})
return &RoutingHandler{
notFound: notFound,
}
}
const globalRoutePattern = `^%s$`
func (hand *RoutingHandler) AddRoute(method, path string, handlerFunc HandlerFunc) error {
var err error
path = pathComp(path)
path = fmt.Sprintf(globalRoutePattern, path)
re, err := regexp.Compile(path)
if err != nil {
return err
}
route := &Route{
Method: method,
Path: path,
Regexp: re,
Handler: handlerFunc,
}
hand.Routes = append(hand.Routes, route)
return err
}
func (hand *RoutingHandler) ServeHTTP(ctx *Context) {
handler := hand.notFound
for _, route := range hand.Routes {
match := route.Match(ctx.Req)
if !match {
continue
}
pathMatch := route.Regexp.FindStringSubmatch(ctx.Req.URL.Path)
subNames := route.Regexp.SubexpNames()
for i, val := range pathMatch {
key := subNames[i]
if key != "" {
ctx.PathMap[key] = val
}
}
handler = route.Handler
break
}
handler.ServeHTTP(ctx)
}
type Route struct {
Method string
Path string
Regexp *regexp.Regexp
Handler HandlerFunc
}
func (route *Route) Match(req *http.Request) bool {
if req.Method != route.Method {
return false
}
match := route.Regexp.MatchString(req.URL.Path)
if !match {
return false
}
return true
}