From 4103f9463dce7fe658a4eaf360142426b9070615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 1 Aug 2022 09:24:37 +0300 Subject: [PATCH] :sparkles: feature: customizable colors (#1977) * :sparkles: feature: customizable colors * :sparkles: feature: customizable colors * :sparkles: feature: customizable colors --- app.go | 425 +------------------------------ app_test.go | 164 ------------ color.go | 99 +++++++ listen.go | 416 ++++++++++++++++++++++++++++++ listen_test.go | 181 +++++++++++++ middleware/logger/logger.go | 44 ++-- middleware/logger/logger_test.go | 5 +- middleware/logger/utils.go | 28 +- 8 files changed, 739 insertions(+), 623 deletions(-) create mode 100644 color.go create mode 100644 listen.go create mode 100644 listen_test.go diff --git a/app.go b/app.go index fd9a3138..f3b48889 100644 --- a/app.go +++ b/app.go @@ -10,30 +10,20 @@ package fiber import ( "bufio" "bytes" - "crypto/tls" - "crypto/x509" "errors" "fmt" - "io/ioutil" "net" "net/http" "net/http/httputil" - "os" - "path/filepath" "reflect" - "runtime" - "sort" "strconv" "strings" "sync" "sync/atomic" - "text/tabwriter" "time" "encoding/json" - "github.com/gofiber/fiber/v2/internal/colorable" - "github.com/gofiber/fiber/v2/internal/isatty" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -371,6 +361,11 @@ type Config struct { // If set to true, will print all routes with their method, path and handler. // Default: false EnablePrintRoutes bool `json:"enable_print_routes"` + + // You can define custom color scheme. They'll be used for startup message, route list and some middlewares. + // + // Optional. Default: DefaultColors + ColorScheme Colors `json:"color_scheme"` } // Static defines configuration options when defining static assets. @@ -523,6 +518,9 @@ func New(config ...Config) *App { app.handleTrustedProxy(ipAddress) } + // Override colors + app.config.ColorScheme = defaultColors(app.config.ColorScheme) + // Init appList app.appList[""] = app @@ -758,154 +756,6 @@ func NewError(code int, message ...string) *Error { return err } -// Listener can be used to pass a custom listener. -func (app *App) Listener(ln net.Listener) error { - // Prefork is supported for custom listeners - if app.config.Prefork { - addr, tlsConfig := lnMetadata(app.config.Network, ln) - return app.prefork(app.config.Network, addr, tlsConfig) - } - // prepare the server for the start - app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") - } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - // Start listening - return app.server.Serve(ln) -} - -// Listen serves HTTP requests from the given addr. -// -// app.Listen(":8080") -// app.Listen("127.0.0.1:8080") -func (app *App) Listen(addr string) error { - // Start prefork - if app.config.Prefork { - return app.prefork(app.config.Network, addr, nil) - } - // Setup listener - ln, err := net.Listen(app.config.Network, addr) - if err != nil { - return err - } - // prepare the server for the start - app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), false, "") - } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - // Start listening - return app.server.Serve(ln) -} - -// ListenTLS serves HTTPS requests from the given addr. -// certFile and keyFile are the paths to TLS certificate and key file: -// app.ListenTLS(":8080", "./cert.pem", "./cert.key") -func (app *App) ListenTLS(addr, certFile, keyFile string) error { - // Check for valid cert/key path - if len(certFile) == 0 || len(keyFile) == 0 { - return errors.New("tls: provide a valid cert or key path") - } - // Prefork is supported - if app.config.Prefork { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) - } - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{ - cert, - }, - } - return app.prefork(app.config.Network, addr, config) - } - // Setup listener - ln, err := net.Listen(app.config.Network, addr) - if err != nil { - return err - } - // prepare the server for the start - app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), true, "") - } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - // Start listening - return app.server.ServeTLS(ln, certFile, keyFile) -} - -// ListenMutualTLS serves HTTPS requests from the given addr. -// certFile, keyFile and clientCertFile are the paths to TLS certificate and key file: -// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") -func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error { - // Check for valid cert/key path - if len(certFile) == 0 || len(keyFile) == 0 { - return errors.New("tls: provide a valid cert or key path") - } - - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) - } - - clientCACert, err := ioutil.ReadFile(filepath.Clean(clientCertFile)) - if err != nil { - return err - } - clientCertPool := x509.NewCertPool() - clientCertPool.AppendCertsFromPEM(clientCACert) - - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: clientCertPool, - Certificates: []tls.Certificate{ - cert, - }, - } - - // Prefork is supported - if app.config.Prefork { - return app.prefork(app.config.Network, addr, config) - } - - // Setup listener - ln, err := tls.Listen(app.config.Network, addr, config) - if err != nil { - return err - } - - // prepare the server for the start - app.startupProcess() - - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), true, "") - } - - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - - // Start listening - return app.server.Serve(ln) -} - // Config returns the app config as value ( read-only ). func (app *App) Config() Config { return app.config @@ -1137,262 +987,3 @@ func (app *App) startupProcess() *App { app.mutex.Unlock() return app } - -// startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string) { - // ignore child processes - if IsChild() { - return - } - - const ( - cBlack = "\u001b[90m" - // cRed = "\u001b[91m" - cCyan = "\u001b[96m" - // cGreen = "\u001b[92m" - // cYellow = "\u001b[93m" - // cBlue = "\u001b[94m" - // cMagenta = "\u001b[95m" - // cWhite = "\u001b[97m" - cReset = "\u001b[0m" - ) - - value := func(s string, width int) string { - pad := width - len(s) - str := "" - for i := 0; i < pad; i++ { - str += "." - } - if s == "Disabled" { - str += " " + s - } else { - str += fmt.Sprintf(" %s%s%s", cCyan, s, cBlack) - } - return str - } - - center := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) - str := fmt.Sprintf("%"+pad+"s", " ") - str += s - str += fmt.Sprintf("%"+pad+"s", " ") - if len(str) < width { - str += " " - } - return str - } - - centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) - str := fmt.Sprintf("%"+pad+"s", " ") - str += fmt.Sprintf("%s%s%s", cCyan, s, cBlack) - str += fmt.Sprintf("%"+pad+"s", " ") - if len(str)-10 < width { - str += " " - } - return str - } - - pad := func(s string, width int) (str string) { - toAdd := width - len(s) - str += s - for i := 0; i < toAdd; i++ { - str += " " - } - return - } - - host, port := parseAddr(addr) - if host == "" { - if app.config.Network == NetworkTCP6 { - host = "[::1]" - } else { - host = "0.0.0.0" - } - } - - scheme := "http" - if tls { - scheme = "https" - } - - isPrefork := "Disabled" - if app.config.Prefork { - isPrefork = "Enabled" - } - - procs := strconv.Itoa(runtime.GOMAXPROCS(0)) - if !app.config.Prefork { - procs = "1" - } - - mainLogo := cBlack + " ┌───────────────────────────────────────────────────┐\n" - if app.config.AppName != "" { - mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" - } - mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" - - if host == "0.0.0.0" { - mainLogo += - " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + - " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" - } else { - mainLogo += - " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" - } - - mainLogo += fmt.Sprintf( - " │ │\n"+ - " │ Handlers %s Processes %s │\n"+ - " │ Prefork .%s PID ....%s │\n"+ - " └───────────────────────────────────────────────────┘"+ - cReset, - value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), - value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), - ) - - var childPidsLogo string - if app.config.Prefork { - var childPidsTemplate string - childPidsTemplate += "%s" - childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s" - childPidsTemplate += " └───────────────────────────────────────────────────┘" - childPidsTemplate += "%s" - - newLine := " │ %s%s%s │" - - // Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs - var pidSlice []string - for _, v := range strings.Split(pids, ",") { - if v != "" { - pidSlice = append(pidSlice, v) - } - } - - var lines []string - thisLine := "Child PIDs ... " - var itemsOnThisLine []string - - addLine := func() { - lines = append(lines, - fmt.Sprintf( - newLine, - cBlack, - thisLine+cCyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), - cBlack, - ), - ) - } - - for _, pid := range pidSlice { - if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { - addLine() - thisLine = "" - itemsOnThisLine = []string{pid} - } else { - itemsOnThisLine = append(itemsOnThisLine, pid) - } - } - - // Add left over items to their own line - if len(itemsOnThisLine) != 0 { - addLine() - } - - // Form logo - childPidsLogo = fmt.Sprintf(childPidsTemplate, - cBlack, - strings.Join(lines, "\n")+"\n", - cReset, - ) - } - - // Combine both the child PID logo and the main Fiber logo - - // Pad the shorter logo to the length of the longer one - splitMainLogo := strings.Split(mainLogo, "\n") - splitChildPidsLogo := strings.Split(childPidsLogo, "\n") - - mainLen := len(splitMainLogo) - childLen := len(splitChildPidsLogo) - - if mainLen > childLen { - diff := mainLen - childLen - for i := 0; i < diff; i++ { - splitChildPidsLogo = append(splitChildPidsLogo, "") - } - } else { - diff := childLen - mainLen - for i := 0; i < diff; i++ { - splitMainLogo = append(splitMainLogo, "") - } - } - - // Combine the two logos, line by line - output := "\n" - for i := range splitMainLogo { - output += cBlack + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n" - } - - out := colorable.NewColorableStdout() - if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { - out = colorable.NewNonColorable(os.Stdout) - } - - _, _ = fmt.Fprintln(out, output) -} - -// printRoutesMessage print all routes with method, path, name and handlers -// in a format of table, like this: -// method | path | name | handlers -// GET | / | routeName | github.com/gofiber/fiber/v2.emptyHandler -// HEAD | / | | github.com/gofiber/fiber/v2.emptyHandler -func (app *App) printRoutesMessage() { - // ignore child processes - if IsChild() { - return - } - - const ( - // cBlack = "\u001b[90m" - // cRed = "\u001b[91m" - cCyan = "\u001b[96m" - cGreen = "\u001b[92m" - cYellow = "\u001b[93m" - cBlue = "\u001b[94m" - // cMagenta = "\u001b[95m" - cWhite = "\u001b[97m" - // cReset = "\u001b[0m" - ) - var routes []RouteMessage - for _, routeStack := range app.stack { - for _, route := range routeStack { - var newRoute = RouteMessage{} - newRoute.name = route.Name - newRoute.method = route.Method - newRoute.path = route.Path - for _, handler := range route.Handlers { - newRoute.handlers += runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name() + " " - } - routes = append(routes, newRoute) - } - } - - out := colorable.NewColorableStdout() - if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { - out = colorable.NewNonColorable(os.Stdout) - } - - w := tabwriter.NewWriter(out, 1, 1, 1, ' ', 0) - // Sort routes by path - sort.Slice(routes, func(i, j int) bool { - return routes[i].path < routes[j].path - }) - _, _ = fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\n", cBlue, cWhite, cGreen, cWhite, cCyan, cWhite, cYellow) - _, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", cBlue, cWhite, cGreen, cWhite, cCyan, cWhite, cYellow) - for _, route := range routes { - _, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", cBlue, route.method, cWhite, cGreen, route.path, cWhite, cCyan, route.name, cWhite, cYellow, route.handlers) - } - - _ = w.Flush() -} diff --git a/app_test.go b/app_test.go index bbc816b1..f64c32fb 100644 --- a/app_test.go +++ b/app_test.go @@ -6,7 +6,6 @@ package fiber import ( "bytes" - "crypto/tls" "errors" "fmt" "io" @@ -26,7 +25,6 @@ import ( "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" - "github.com/valyala/fasthttp/fasthttputil" ) var testEmptyHandler = func(c *Ctx) error { @@ -1122,131 +1120,6 @@ func Test_App_Next_Method(t *testing.T) { utils.AssertEqual(t, 404, resp.StatusCode, "Status code") } -// go test -run Test_App_Listen -func Test_App_Listen(t *testing.T) { - app := New(Config{DisableStartupMessage: true}) - - utils.AssertEqual(t, false, app.Listen(":99999") == nil) - - go func() { - time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.Listen(":4003")) -} - -// go test -run Test_App_Listen_Prefork -func Test_App_Listen_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - utils.AssertEqual(t, nil, app.Listen(":99999")) -} - -// go test -run Test_App_ListenTLS -func Test_App_ListenTLS(t *testing.T) { - app := New() - - // invalid port - utils.AssertEqual(t, false, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) - // missing perm/cert file - utils.AssertEqual(t, false, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) - - go func() { - time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) -} - -// go test -run Test_App_ListenTLS_Prefork -func Test_App_ListenTLS_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - // invalid key file content - utils.AssertEqual(t, false, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) - - utils.AssertEqual(t, nil, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) -} - -// go test -run Test_App_ListenMutualTLS -func Test_App_ListenMutualTLS(t *testing.T) { - app := New() - - // invalid port - utils.AssertEqual(t, false, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) - // missing perm/cert file - utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) - - go func() { - time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) -} - -// go test -run Test_App_ListenMutualTLS_Prefork -func Test_App_ListenMutualTLS_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - // invalid key file content - utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) - - utils.AssertEqual(t, nil, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) -} - -// go test -run Test_App_Listener -func Test_App_Listener(t *testing.T) { - app := New() - - go func() { - time.Sleep(500 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) -} - -// go test -run Test_App_Listener_Prefork -func Test_App_Listener_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) -} - -func Test_App_Listener_TLS_Listener(t *testing.T) { - // Create tls certificate - cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") - if err != nil { - utils.AssertEqual(t, nil, err) - } - config := &tls.Config{Certificates: []tls.Certificate{cer}} - - ln, err := tls.Listen(NetworkTCP4, ":0", config) - utils.AssertEqual(t, nil, err) - - app := New() - - go func() { - time.Sleep(time.Millisecond * 500) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.Listener(ln)) -} - // go test -v -run=^$ -bench=Benchmark_AcquireCtx -benchmem -count=4 func Benchmark_AcquireCtx(b *testing.B) { app := New() @@ -1710,40 +1583,3 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") } - -func emptyHandler(c *Ctx) error { - return nil -} -func Test_App_print_Route(t *testing.T) { - app := New(Config{EnablePrintRoutes: true}) - app.Get("/", emptyHandler).Name("routeName") - printRoutesMessage := captureOutput(func() { - app.printRoutesMessage() - }) - fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) -} - -func Test_App_print_Route_with_group(t *testing.T) { - app := New(Config{EnablePrintRoutes: true}) - app.Get("/", emptyHandler) - v1 := app.Group("v1") - v1.Get("/test", emptyHandler).Name("v1") - v1.Post("/test/fiber", emptyHandler) - v1.Put("/test/fiber/*", emptyHandler) - printRoutesMessage := captureOutput(func() { - app.printRoutesMessage() - }) - fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) -} diff --git a/color.go b/color.go new file mode 100644 index 00000000..f982b379 --- /dev/null +++ b/color.go @@ -0,0 +1,99 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +// Colors is a struct to define custom colors for Fiber app and middlewares. +type Colors struct { + // Black color. + // + // Optional. Default: "\u001b[90m" + Black string + + // Red color. + // + // Optional. Default: "\u001b[91m" + Red string + + // Green color. + // + // Optional. Default: "\u001b[92m" + Green string + + // Yellow color. + // + // Optional. Default: "\u001b[93m" + Yellow string + + // Blue color. + // + // Optional. Default: "\u001b[94m" + Blue string + + // Magenta color. + // + // Optional. Default: "\u001b[95m" + Magenta string + + // Cyan color. + // + // Optional. Default: "\u001b[96m" + Cyan string + + // White color. + // + // Optional. Default: "\u001b[97m" + White string + + // Reset color. + // + // Optional. Default: "\u001b[0m" + Reset string +} + +// Default color codes +var DefaultColors = Colors{ + Black: "\u001b[90m", + Red: "\u001b[91m", + Green: "\u001b[92m", + Yellow: "\u001b[93m", + Blue: "\u001b[94m", + Magenta: "\u001b[95m", + Cyan: "\u001b[96m", + White: "\u001b[97m", + Reset: "\u001b[0m", +} + +// defaultColors is a function to override default colors to config +func defaultColors(colors Colors) Colors { + if colors.Red == "" { + colors.Red = DefaultColors.Red + } + + if colors.Green == "" { + colors.Green = DefaultColors.Green + } + + if colors.Yellow == "" { + colors.Yellow = DefaultColors.Yellow + } + + if colors.Blue == "" { + colors.Blue = DefaultColors.Blue + } + + if colors.Magenta == "" { + colors.Magenta = DefaultColors.Magenta + } + + if colors.Cyan == "" { + colors.Cyan = DefaultColors.Cyan + } + + if colors.Reset == "" { + colors.Reset = DefaultColors.Reset + } + + return colors +} diff --git a/listen.go b/listen.go new file mode 100644 index 00000000..8f92007b --- /dev/null +++ b/listen.go @@ -0,0 +1,416 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "text/tabwriter" + + "github.com/gofiber/fiber/v2/internal/colorable" + "github.com/gofiber/fiber/v2/internal/isatty" +) + +// Listener can be used to pass a custom listener. +func (app *App) Listener(ln net.Listener) error { + // Prefork is supported for custom listeners + if app.config.Prefork { + addr, tlsConfig := lnMetadata(app.config.Network, ln) + return app.prefork(app.config.Network, addr, tlsConfig) + } + // prepare the server for the start + app.startupProcess() + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") + } + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + // Start listening + return app.server.Serve(ln) +} + +// Listen serves HTTP requests from the given addr. +// +// app.Listen(":8080") +// app.Listen("127.0.0.1:8080") +func (app *App) Listen(addr string) error { + // Start prefork + if app.config.Prefork { + return app.prefork(app.config.Network, addr, nil) + } + // Setup listener + ln, err := net.Listen(app.config.Network, addr) + if err != nil { + return err + } + // prepare the server for the start + app.startupProcess() + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), false, "") + } + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + // Start listening + return app.server.Serve(ln) +} + +// ListenTLS serves HTTPS requests from the given addr. +// certFile and keyFile are the paths to TLS certificate and key file: +// app.ListenTLS(":8080", "./cert.pem", "./cert.key") +func (app *App) ListenTLS(addr, certFile, keyFile string) error { + // Check for valid cert/key path + if len(certFile) == 0 || len(keyFile) == 0 { + return errors.New("tls: provide a valid cert or key path") + } + // Prefork is supported + if app.config.Prefork { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + } + config := &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + cert, + }, + } + return app.prefork(app.config.Network, addr, config) + } + // Setup listener + ln, err := net.Listen(app.config.Network, addr) + if err != nil { + return err + } + // prepare the server for the start + app.startupProcess() + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), true, "") + } + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + // Start listening + return app.server.ServeTLS(ln, certFile, keyFile) +} + +// ListenMutualTLS serves HTTPS requests from the given addr. +// certFile, keyFile and clientCertFile are the paths to TLS certificate and key file: +// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") +func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error { + // Check for valid cert/key path + if len(certFile) == 0 || len(keyFile) == 0 { + return errors.New("tls: provide a valid cert or key path") + } + + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + } + + clientCACert, err := ioutil.ReadFile(filepath.Clean(clientCertFile)) + if err != nil { + return err + } + clientCertPool := x509.NewCertPool() + clientCertPool.AppendCertsFromPEM(clientCACert) + + config := &tls.Config{ + MinVersion: tls.VersionTLS12, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: clientCertPool, + Certificates: []tls.Certificate{ + cert, + }, + } + + // Prefork is supported + if app.config.Prefork { + return app.prefork(app.config.Network, addr, config) + } + + // Setup listener + ln, err := tls.Listen(app.config.Network, addr, config) + if err != nil { + return err + } + + // prepare the server for the start + app.startupProcess() + + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), true, "") + } + + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + + // Start listening + return app.server.Serve(ln) +} + +// startupMessage prepares the startup message with the handler number, port, address and other information +func (app *App) startupMessage(addr string, tls bool, pids string) { + // ignore child processes + if IsChild() { + return + } + + // Alias colors + colors := app.config.ColorScheme + + value := func(s string, width int) string { + pad := width - len(s) + str := "" + for i := 0; i < pad; i++ { + str += "." + } + if s == "Disabled" { + str += " " + s + } else { + str += fmt.Sprintf(" %s%s%s", colors.Cyan, s, colors.Black) + } + return str + } + + center := func(s string, width int) string { + pad := strconv.Itoa((width - len(s)) / 2) + str := fmt.Sprintf("%"+pad+"s", " ") + str += s + str += fmt.Sprintf("%"+pad+"s", " ") + if len(str) < width { + str += " " + } + return str + } + + centerValue := func(s string, width int) string { + pad := strconv.Itoa((width - len(s)) / 2) + str := fmt.Sprintf("%"+pad+"s", " ") + str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) + str += fmt.Sprintf("%"+pad+"s", " ") + if len(str)-10 < width { + str += " " + } + return str + } + + pad := func(s string, width int) (str string) { + toAdd := width - len(s) + str += s + for i := 0; i < toAdd; i++ { + str += " " + } + return + } + + host, port := parseAddr(addr) + if host == "" { + if app.config.Network == NetworkTCP6 { + host = "[::1]" + } else { + host = "0.0.0.0" + } + } + + scheme := "http" + if tls { + scheme = "https" + } + + isPrefork := "Disabled" + if app.config.Prefork { + isPrefork = "Enabled" + } + + procs := strconv.Itoa(runtime.GOMAXPROCS(0)) + if !app.config.Prefork { + procs = "1" + } + + mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n" + if app.config.AppName != "" { + mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" + } + mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" + + if host == "0.0.0.0" { + mainLogo += + " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + + " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" + } else { + mainLogo += + " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" + } + + mainLogo += fmt.Sprintf( + " │ │\n"+ + " │ Handlers %s Processes %s │\n"+ + " │ Prefork .%s PID ....%s │\n"+ + " └───────────────────────────────────────────────────┘"+ + colors.Reset, + value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), + value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), + ) + + var childPidsLogo string + if app.config.Prefork { + var childPidsTemplate string + childPidsTemplate += "%s" + childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s" + childPidsTemplate += " └───────────────────────────────────────────────────┘" + childPidsTemplate += "%s" + + newLine := " │ %s%s%s │" + + // Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs + var pidSlice []string + for _, v := range strings.Split(pids, ",") { + if v != "" { + pidSlice = append(pidSlice, v) + } + } + + var lines []string + thisLine := "Child PIDs ... " + var itemsOnThisLine []string + + addLine := func() { + lines = append(lines, + fmt.Sprintf( + newLine, + colors.Black, + thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), + colors.Black, + ), + ) + } + + for _, pid := range pidSlice { + if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { + addLine() + thisLine = "" + itemsOnThisLine = []string{pid} + } else { + itemsOnThisLine = append(itemsOnThisLine, pid) + } + } + + // Add left over items to their own line + if len(itemsOnThisLine) != 0 { + addLine() + } + + // Form logo + childPidsLogo = fmt.Sprintf(childPidsTemplate, + colors.Black, + strings.Join(lines, "\n")+"\n", + colors.Reset, + ) + } + + // Combine both the child PID logo and the main Fiber logo + + // Pad the shorter logo to the length of the longer one + splitMainLogo := strings.Split(mainLogo, "\n") + splitChildPidsLogo := strings.Split(childPidsLogo, "\n") + + mainLen := len(splitMainLogo) + childLen := len(splitChildPidsLogo) + + if mainLen > childLen { + diff := mainLen - childLen + for i := 0; i < diff; i++ { + splitChildPidsLogo = append(splitChildPidsLogo, "") + } + } else { + diff := childLen - mainLen + for i := 0; i < diff; i++ { + splitMainLogo = append(splitMainLogo, "") + } + } + + // Combine the two logos, line by line + output := "\n" + for i := range splitMainLogo { + output += colors.Black + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n" + } + + out := colorable.NewColorableStdout() + if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { + out = colorable.NewNonColorable(os.Stdout) + } + + _, _ = fmt.Fprintln(out, output) +} + +// printRoutesMessage print all routes with method, path, name and handlers +// in a format of table, like this: +// method | path | name | handlers +// GET | / | routeName | github.com/gofiber/fiber/v2.emptyHandler +// HEAD | / | | github.com/gofiber/fiber/v2.emptyHandler +func (app *App) printRoutesMessage() { + // ignore child processes + if IsChild() { + return + } + + // Alias colors + colors := app.config.ColorScheme + + var routes []RouteMessage + for _, routeStack := range app.stack { + for _, route := range routeStack { + var newRoute = RouteMessage{} + newRoute.name = route.Name + newRoute.method = route.Method + newRoute.path = route.Path + for _, handler := range route.Handlers { + newRoute.handlers += runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name() + " " + } + routes = append(routes, newRoute) + } + } + + out := colorable.NewColorableStdout() + if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { + out = colorable.NewNonColorable(os.Stdout) + } + + w := tabwriter.NewWriter(out, 1, 1, 1, ' ', 0) + // Sort routes by path + sort.Slice(routes, func(i, j int) bool { + return routes[i].path < routes[j].path + }) + + _, _ = fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow) + _, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow) + for _, route := range routes { + _, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", colors.Blue, route.method, colors.White, colors.Green, route.path, colors.White, colors.Cyan, route.name, colors.White, colors.Yellow, route.handlers) + } + + _ = w.Flush() +} diff --git a/listen_test.go b/listen_test.go new file mode 100644 index 00000000..5eb035f5 --- /dev/null +++ b/listen_test.go @@ -0,0 +1,181 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "crypto/tls" + "fmt" + "strings" + "testing" + "time" + + "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp/fasthttputil" +) + +// go test -run Test_App_Listen +func Test_App_Listen(t *testing.T) { + app := New(Config{DisableStartupMessage: true}) + + utils.AssertEqual(t, false, app.Listen(":99999") == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.Listen(":4003")) +} + +// go test -run Test_App_Listen_Prefork +func Test_App_Listen_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + utils.AssertEqual(t, nil, app.Listen(":99999")) +} + +// go test -run Test_App_ListenTLS +func Test_App_ListenTLS(t *testing.T) { + app := New() + + // invalid port + utils.AssertEqual(t, false, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) + // missing perm/cert file + utils.AssertEqual(t, false, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) +} + +// go test -run Test_App_ListenTLS_Prefork +func Test_App_ListenTLS_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + // invalid key file content + utils.AssertEqual(t, false, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) + + utils.AssertEqual(t, nil, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) +} + +// go test -run Test_App_ListenMutualTLS +func Test_App_ListenMutualTLS(t *testing.T) { + app := New() + + // invalid port + utils.AssertEqual(t, false, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) + // missing perm/cert file + utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) +} + +// go test -run Test_App_ListenMutualTLS_Prefork +func Test_App_ListenMutualTLS_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + // invalid key file content + utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) + + utils.AssertEqual(t, nil, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) +} + +// go test -run Test_App_Listener +func Test_App_Listener(t *testing.T) { + app := New() + + go func() { + time.Sleep(500 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + ln := fasthttputil.NewInmemoryListener() + utils.AssertEqual(t, nil, app.Listener(ln)) +} + +// go test -run Test_App_Listener_Prefork +func Test_App_Listener_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + ln := fasthttputil.NewInmemoryListener() + utils.AssertEqual(t, nil, app.Listener(ln)) +} + +func Test_App_Listener_TLS_Listener(t *testing.T) { + // Create tls certificate + cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") + if err != nil { + utils.AssertEqual(t, nil, err) + } + config := &tls.Config{Certificates: []tls.Certificate{cer}} + + ln, err := tls.Listen(NetworkTCP4, ":0", config) + utils.AssertEqual(t, nil, err) + + app := New() + + go func() { + time.Sleep(time.Millisecond * 500) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.Listener(ln)) +} + +func Test_App_print_Route(t *testing.T) { + app := New(Config{EnablePrintRoutes: true}) + app.Get("/", emptyHandler).Name("routeName") + printRoutesMessage := captureOutput(func() { + app.printRoutesMessage() + }) + fmt.Println(printRoutesMessage) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) +} + +func Test_App_print_Route_with_group(t *testing.T) { + app := New(Config{EnablePrintRoutes: true}) + app.Get("/", emptyHandler) + + v1 := app.Group("v1") + v1.Get("/test", emptyHandler).Name("v1") + v1.Post("/test/fiber", emptyHandler) + v1.Put("/test/fiber/*", emptyHandler) + + printRoutesMessage := captureOutput(func() { + app.printRoutesMessage() + }) + + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) +} + +func emptyHandler(c *Ctx) error { + return nil +} diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 6cbffa1e..776b0501 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -61,19 +61,6 @@ const ( TagReset = "reset" ) -// Color values -const ( - cBlack = "\u001b[90m" - cRed = "\u001b[91m" - cGreen = "\u001b[92m" - cYellow = "\u001b[93m" - cBlue = "\u001b[94m" - cMagenta = "\u001b[95m" - cCyan = "\u001b[96m" - cWhite = "\u001b[97m" - cReset = "\u001b[0m" -) - // New creates a new middleware handler func New(config ...Config) fiber.Handler { // Set default config @@ -133,6 +120,9 @@ func New(config ...Config) fiber.Handler { return c.Next() } + // Alias colors + colors := c.App().Config().ColorScheme + // Set error handler once once.Do(func() { // get longested possible path @@ -179,16 +169,16 @@ func New(config ...Config) fiber.Handler { // Format error if exist formatErr := "" if chainErr != nil { - formatErr = cRed + " | " + chainErr.Error() + cReset + formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset } // Format log to buffer _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", timestamp.Load().(string), - statusColor(c.Response().StatusCode()), c.Response().StatusCode(), cReset, + statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, stop.Sub(start).Round(time.Millisecond), c.IP(), - methodColor(c.Method()), c.Method(), cReset, + methodColor(c.Method(), colors), c.Method(), colors.Reset, c.Path(), formatErr, )) @@ -240,7 +230,7 @@ func New(config ...Config) fiber.Handler { return buf.WriteString(c.Route().Path) case TagStatus: if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode()), c.Response().StatusCode(), cReset)) + return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) } return appendInt(buf, c.Response().StatusCode()) case TagResBody: @@ -255,27 +245,27 @@ func New(config ...Config) fiber.Handler { return buf.WriteString(c.Request().URI().QueryArgs().String()) case TagMethod: if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method()), c.Method(), cReset)) + return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) } return buf.WriteString(c.Method()) case TagBlack: - return buf.WriteString(cBlack) + return buf.WriteString(colors.Black) case TagRed: - return buf.WriteString(cRed) + return buf.WriteString(colors.Red) case TagGreen: - return buf.WriteString(cGreen) + return buf.WriteString(colors.Green) case TagYellow: - return buf.WriteString(cYellow) + return buf.WriteString(colors.Yellow) case TagBlue: - return buf.WriteString(cBlue) + return buf.WriteString(colors.Blue) case TagMagenta: - return buf.WriteString(cMagenta) + return buf.WriteString(colors.Magenta) case TagCyan: - return buf.WriteString(cCyan) + return buf.WriteString(colors.Cyan) case TagWhite: - return buf.WriteString(cWhite) + return buf.WriteString(colors.White) case TagReset: - return buf.WriteString(cReset) + return buf.WriteString(colors.Reset) case TagError: if chainErr != nil { return buf.WriteString(chainErr.Error()) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 0e0ba1d6..95f053a0 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -144,11 +144,14 @@ func Test_Logger_All(t *testing.T) { Output: buf, })) + // Alias colors + colors := app.Config().ColorScheme + resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) - expected := fmt.Sprintf("%dHost=example.comhttp0.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), cBlack, cRed, cGreen, cYellow, cBlue, cMagenta, cCyan, cWhite, cReset) + expected := fmt.Sprintf("%dHost=example.comhttp0.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), colors.Black, colors.Red, colors.Green, colors.Yellow, colors.Blue, colors.Magenta, colors.Cyan, colors.White, colors.Reset) utils.AssertEqual(t, expected, buf.String()) } diff --git a/middleware/logger/utils.go b/middleware/logger/utils.go index f1616bde..2af456d4 100644 --- a/middleware/logger/utils.go +++ b/middleware/logger/utils.go @@ -4,36 +4,36 @@ import ( "github.com/gofiber/fiber/v2" ) -func methodColor(method string) string { +func methodColor(method string, colors fiber.Colors) string { switch method { case fiber.MethodGet: - return cCyan + return colors.Cyan case fiber.MethodPost: - return cGreen + return colors.Green case fiber.MethodPut: - return cYellow + return colors.Yellow case fiber.MethodDelete: - return cRed + return colors.Red case fiber.MethodPatch: - return cWhite + return colors.White case fiber.MethodHead: - return cMagenta + return colors.Magenta case fiber.MethodOptions: - return cBlue + return colors.Blue default: - return cReset + return colors.Reset } } -func statusColor(code int) string { +func statusColor(code int, colors fiber.Colors) string { switch { case code >= fiber.StatusOK && code < fiber.StatusMultipleChoices: - return cGreen + return colors.Green case code >= fiber.StatusMultipleChoices && code < fiber.StatusBadRequest: - return cBlue + return colors.Blue case code >= fiber.StatusBadRequest && code < fiber.StatusInternalServerError: - return cYellow + return colors.Yellow default: - return cRed + return colors.Red } }