func (rtr *Router) Add(routes ...*Route) {
	hmap := httpHandlers(routes)
	rtr.optHandlers = append(rtr.optHandlers, hmap[http.MethodOptions]...)
	rtr.headHandlers = append(rtr.headHandlers, hmap[http.MethodHead]...)
	rtr.getHandlers = append(rtr.getHandlers, hmap[http.MethodGet]...)
	rtr.postHandlers = append(rtr.postHandlers, hmap[http.MethodPost]...)
	rtr.putHandlers = append(rtr.putHandlers, hmap[http.MethodPut]...)
	rtr.patchHandlers = append(rtr.patchHandlers, hmap[http.MethodPatch]...)
	rtr.deleteHandlers = append(rtr.deleteHandlers, hmap[http.MethodDelete]...)
	all := rtr.allHandlers
	if all == nil {
		all = map[string][]*Route{}
	}
	for _, key := range supportedHTTPMethods {
		newlist, hasKey := hmap[key]
		if !hasKey {
			continue
		}
		if all[key] == nil {
			all[key] = make([]*Route, 0, len(hmap))
		}
		all[key] = append(all[key], newlist...)
	}
	rtr.allHandlers = all
}
Add is a convenience method used to add a new route to an already initialized router
Important: `.Use` should be used only after all routes are added
package webgo
import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"net"
	"net/http"
	"sync"
)
const (
	urlchars                  = `([^/]+)`
	urlwildcard               = `(.*)[^/]`
	urlwildcardWithTrailslash = `(.*)[/]?`
	trailingSlash             = `[/]?`
	errDuplicateKey           = `Error: Duplicate URI keys found`
)
var (
	validHTTPMethods = []string{
		http.MethodOptions,
		http.MethodHead,
		http.MethodGet,
		http.MethodPost,
		http.MethodPut,
		http.MethodPatch,
		http.MethodDelete,
	}
	ctxPool = &sync.Pool{
		New: func() interface{} {
			return new(ContextPayload)
		},
	}
	crwPool = &sync.Pool{
		New: func() interface{} {
			return new(customResponseWriter)
		},
	}
)
type customResponseWriter struct {
	http.ResponseWriter
	statusCode    int
	written       bool
	headerWritten bool
}
func (crw *customResponseWriter) WriteHeader(code int) {
	if crw.headerWritten {
		return
	}
	crw.headerWritten = true
	crw.statusCode = code
	crw.ResponseWriter.WriteHeader(code)
}
func (crw *customResponseWriter) Write(body []byte) (int, error) {
	crw.WriteHeader(crw.statusCode)
	crw.written = true
	return crw.ResponseWriter.Write(body)
}
func (crw *customResponseWriter) Flush() {
	if rw, ok := crw.ResponseWriter.(http.Flusher); ok {
		rw.Flush()
	}
}
func (crw *customResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hj, ok := crw.ResponseWriter.(http.Hijacker); ok {
		return hj.Hijack()
	}
	return nil, nil, errors.New("unable to create hijacker")
}
func (crw *customResponseWriter) CloseNotify() <-chan bool {
	if n, ok := crw.ResponseWriter.(http.CloseNotifier); ok {
		return n.CloseNotify()
	}
	return nil
}
func (crw *customResponseWriter) reset() {
	crw.statusCode = 0
	crw.written = false
	crw.headerWritten = false
	crw.ResponseWriter = nil
}
type Middleware func(http.ResponseWriter, *http.Request, http.HandlerFunc)
func discoverRoute(path string, routes []*Route) *Route {
	for _, route := range routes {
		if ok, _ := route.matchPath(path); ok {
			return route
		}
	}
	return nil
}
type Router struct {
	optHandlers    []*Route
	headHandlers   []*Route
	getHandlers    []*Route
	postHandlers   []*Route
	putHandlers    []*Route
	patchHandlers  []*Route
	deleteHandlers []*Route
	allHandlers    map[string][]*Route
	NotFound http.HandlerFunc
	NotImplemented http.HandlerFunc
	config *Config
	httpServer *http.Server
	httpsServer *http.Server
}
func (rtr *Router) methodRoutes(method string) (routes []*Route) {
	switch method {
	case http.MethodOptions:
		return rtr.optHandlers
	case http.MethodHead:
		return rtr.headHandlers
	case http.MethodGet:
		return rtr.getHandlers
	case http.MethodPost:
		return rtr.postHandlers
	case http.MethodPut:
		return rtr.putHandlers
	case http.MethodPatch:
		return rtr.patchHandlers
	case http.MethodDelete:
		return rtr.deleteHandlers
	}
	return nil
}
func (rtr *Router) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	crw := newCRW(rw, http.StatusOK)
	routes := rtr.methodRoutes(r.Method)
	if routes == nil {
		crw.statusCode = http.StatusNotImplemented
		rtr.NotImplemented(crw, r)
		releaseCRW(crw)
		return
	}
	path := r.URL.EscapedPath()
	route := discoverRoute(path, routes)
	if route == nil {
		crw.statusCode = http.StatusNotFound
		rtr.NotFound(crw, r)
		releaseCRW(crw)
		return
	}
	ctxPayload := newContext()
	ctxPayload.path = path
	ctxPayload.Route = route
	*r = *r.WithContext(
		context.WithValue(
			r.Context(),
			wgoCtxKey,
			ctxPayload,
		),
	)
	route.serve(crw, r)
	releasePoolResources(crw, ctxPayload)
}
func (rtr *Router) Use(mm ...Middleware) {
	for _, handlers := range rtr.allHandlers {
		for idx := range handlers {
			route := handlers[idx]
			if route.skipMiddleware {
				continue
			}
			route.use(mm...)
		}
	}
}
func (rtr *Router) UseOnSpecialHandlers(mm ...Middleware) {
	for idx := range mm {
		m := mm[idx]
		nf := rtr.NotFound
		rtr.NotFound = func(rw http.ResponseWriter, req *http.Request) {
			m(rw, req, nf)
		}
		ni := rtr.NotImplemented
		rtr.NotImplemented = func(rw http.ResponseWriter, req *http.Request) {
			m(rw, req, ni)
		}
	}
}