chore(SimpleRouter)!: promoted from unstable
This commit is contained in:
224
simplerouter/router.go
Normal file
224
simplerouter/router.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package SimpleRouter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
Type "github.com/lbatuska/goutils/type"
|
||||
)
|
||||
|
||||
func (rg *RouteGroup) StartServer(addr string, config Type.Optional[ServerConfig]) *http.Server {
|
||||
var server *http.Server
|
||||
if config.HasValue() {
|
||||
serverConfig := config.Unwrap()
|
||||
server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: rg,
|
||||
ReadTimeout: serverConfig.ReadTimeout,
|
||||
ReadHeaderTimeout: serverConfig.ReadHeaderTimeout,
|
||||
WriteTimeout: serverConfig.WriteTimeout,
|
||||
}
|
||||
} else {
|
||||
server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: rg,
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
panic(fmt.Sprintf("HTTP server error: %v", err))
|
||||
}
|
||||
}()
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) StartWithGracefulShutdown(addr string, config Type.Optional[ServerConfig]) {
|
||||
var server *http.Server
|
||||
if config.HasValue() {
|
||||
serverConfig := config.Unwrap()
|
||||
server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: rg,
|
||||
ReadTimeout: serverConfig.ReadTimeout,
|
||||
ReadHeaderTimeout: serverConfig.ReadHeaderTimeout,
|
||||
WriteTimeout: serverConfig.WriteTimeout,
|
||||
}
|
||||
} else {
|
||||
server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: rg,
|
||||
}
|
||||
}
|
||||
|
||||
// Start the server in a separate goroutine
|
||||
go func() {
|
||||
fmt.Println("Starting server on", addr)
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
panic(fmt.Sprintf("HTTP server error: %v", err))
|
||||
}
|
||||
}()
|
||||
|
||||
// Set up signal catching for graceful shutdown (Ctrl+C, SIGINT, SIGTERM)
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Block until a signal is received
|
||||
<-sigChan
|
||||
|
||||
fmt.Println("Shutting down server...")
|
||||
|
||||
// Set a timeout for the shutdown process (10 seconds)
|
||||
shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer shutdownRelease()
|
||||
|
||||
// Perform graceful shutdown
|
||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||
panic(fmt.Sprintf("HTTP shutdown error: %v", err))
|
||||
}
|
||||
|
||||
fmt.Println("Graceful shutdown complete.")
|
||||
}
|
||||
|
||||
// http.ListenAndServe takes a Handler interface defined as: ServeHTTP(ResponseWriter, *Request)
|
||||
func (rg *RouteGroup) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rg.mux.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
mwlen := len(*rg.global_middlewares) - 1
|
||||
for i := range *rg.global_middlewares {
|
||||
handler = (*rg.global_middlewares)[mwlen-i](handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// / Handler returns the handler to use for the given request, ... propagate call to http library
|
||||
func (rg *RouteGroup) Handler(r *http.Request) (h http.Handler, pattern string) {
|
||||
return rg.mux.Handler(r)
|
||||
}
|
||||
|
||||
// Creates a new SimpleRouter with the basePath of "/"
|
||||
func SimpleRouter() *RouteGroup {
|
||||
return &RouteGroup{mux: http.NewServeMux(), basePath: "/", global_middlewares: &[]Middleware{}}
|
||||
}
|
||||
|
||||
// Puts middlewares on top of existing ones in order
|
||||
func (rg *RouteGroup) PushMiddleware(first Middleware, others ...Middleware) *RouteGroup {
|
||||
rg.middlewares = append(rg.middlewares, first)
|
||||
// append supposedly keeps the order of elements, so no extra work is needed
|
||||
rg.middlewares = append(rg.middlewares, others...)
|
||||
return rg
|
||||
}
|
||||
|
||||
// Removes the top middleware and returns it to the caller
|
||||
func (rg *RouteGroup) PopMiddleware() Middleware {
|
||||
if len(rg.middlewares) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastIndex := len(rg.middlewares) - 1
|
||||
lastMiddleware := rg.middlewares[lastIndex]
|
||||
rg.middlewares = rg.middlewares[:lastIndex]
|
||||
|
||||
return lastMiddleware
|
||||
}
|
||||
|
||||
// this vs PushMiddleware ? what's the diff?
|
||||
func (rg *RouteGroup) PushGlobalMiddleware(first Middleware, others ...Middleware) *RouteGroup {
|
||||
*rg.global_middlewares = append(*rg.global_middlewares, first)
|
||||
*rg.global_middlewares = append(*rg.global_middlewares, others...)
|
||||
return rg
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) PopGlobalMiddleware() Middleware {
|
||||
if len(*rg.global_middlewares) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastIndex := len(*rg.global_middlewares) - 1
|
||||
lastMiddleware := (*rg.global_middlewares)[lastIndex]
|
||||
*rg.global_middlewares = (*rg.global_middlewares)[:lastIndex]
|
||||
|
||||
return lastMiddleware
|
||||
}
|
||||
|
||||
// applies the array of middlewares on the handler
|
||||
func (rg *RouteGroup) applyMiddlewares(handler http.Handler) http.Handler {
|
||||
mwlen := len(rg.middlewares) - 1 // We need to start from the last element going to 0 for correct ordering
|
||||
for i := range rg.middlewares {
|
||||
handler = rg.middlewares[mwlen-i](handler)
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) registerRoute(method string, path string, handler http.HandlerFunc) {
|
||||
if path == "/" {
|
||||
path = rg.basePath[:len(rg.basePath)-1]
|
||||
} else {
|
||||
path = rg.basePath + path
|
||||
}
|
||||
|
||||
fullPath := method + " " + path
|
||||
|
||||
rg.mux.HandleFunc(fullPath, rg.applyMiddlewares(handler).ServeHTTP)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) SubPath(path string) *RouteGroup {
|
||||
rgc := &RouteGroup{
|
||||
mux: rg.mux,
|
||||
basePath: rg.basePath + path + "/",
|
||||
global_middlewares: rg.global_middlewares,
|
||||
}
|
||||
middlewares := make([]Middleware, len(rg.middlewares))
|
||||
copy(middlewares, rg.middlewares)
|
||||
rgc.middlewares = middlewares
|
||||
return rgc
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) HandleFunc(method string, path string, handler http.HandlerFunc) {
|
||||
rg.registerRoute(method, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) Handle(method string, path string, handler http.Handler) {
|
||||
rg.HandleFunc(method, path, handler.ServeHTTP)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) SubpathHandle(path string, handler http.Handler) {
|
||||
rg.Handle("", path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) GET(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodGet, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) HEAD(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodHead, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) OPTIONS(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodOptions, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) POST(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodPost, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) PUT(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodPut, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) PATCH(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodPatch, path, handler)
|
||||
}
|
||||
|
||||
func (rg *RouteGroup) DELETE(path string, handler http.Handler) {
|
||||
rg.Handle(http.MethodDelete, path, handler)
|
||||
}
|
||||
Reference in New Issue
Block a user