mirror of https://github.com/gofiber/fiber.git
✨ feature: customizable colors (#1977)
* ✨ feature: customizable colors * ✨ feature: customizable colors * ✨ feature: customizable colorspull/1987/head
parent
ff1e0109a3
commit
4103f9463d
425
app.go
425
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()
|
||||
}
|
||||
|
|
164
app_test.go
164
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/*"))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue