From 712b585b9d3b5c86a43242ad291333c924b396ac Mon Sep 17 00:00:00 2001 From: ReneWerner87 Date: Mon, 14 Sep 2020 10:23:22 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=93=20Code=20separation=20for=20utils?= =?UTF-8?q?=20(=F0=9F=A7=B9clean=20code)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fenny Co-authored-by: Kiyon --- internal/utils/assertions.go | 62 ++++ internal/utils/assertions_test.go | 13 + internal/utils/bytes.go | 70 +++++ internal/utils/bytes_test.go | 155 +++++++++ internal/utils/common.go | 66 ++++ internal/utils/common_test.go | 47 +++ internal/utils/convert.go | 32 ++ internal/utils/convert_test.go | 81 +++++ internal/utils/http.go | 212 +++++++++++++ internal/utils/http_test.go | 72 +++++ internal/utils/strings.go | 62 ++++ internal/utils/strings_test.go | 149 +++++++++ internal/utils/utils.go | 505 ------------------------------ internal/utils/utils_test.go | 502 ----------------------------- path.go | 18 +- path_test.go | 12 + 16 files changed, 1050 insertions(+), 1008 deletions(-) create mode 100644 internal/utils/assertions.go create mode 100644 internal/utils/assertions_test.go create mode 100644 internal/utils/bytes.go create mode 100644 internal/utils/bytes_test.go create mode 100644 internal/utils/common.go create mode 100644 internal/utils/common_test.go create mode 100644 internal/utils/convert.go create mode 100644 internal/utils/convert_test.go create mode 100644 internal/utils/http.go create mode 100644 internal/utils/http_test.go create mode 100644 internal/utils/strings.go create mode 100644 internal/utils/strings_test.go delete mode 100644 internal/utils/utils.go delete mode 100644 internal/utils/utils_test.go diff --git a/internal/utils/assertions.go b/internal/utils/assertions.go new file mode 100644 index 00000000..a107a465 --- /dev/null +++ b/internal/utils/assertions.go @@ -0,0 +1,62 @@ +// ⚡️ 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 utils + +import ( + "bytes" + "fmt" + "log" + "path/filepath" + "reflect" + "runtime" + "testing" + "text/tabwriter" +) + +// AssertEqual checks if values are equal +func AssertEqual(t testing.TB, expected interface{}, actual interface{}, description ...string) { + if reflect.DeepEqual(expected, actual) { + return + } + var aType = "" + var bType = "" + if reflect.ValueOf(expected).IsValid() { + aType = reflect.TypeOf(expected).Name() + } + if reflect.ValueOf(actual).IsValid() { + bType = reflect.TypeOf(actual).Name() + } + + testName := "AssertEqual" + if t != nil { + testName = t.Name() + } + + _, file, line, _ := runtime.Caller(1) + + var buf bytes.Buffer + w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) + fmt.Fprintf(w, "\nTest:\t%s", testName) + fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) + fmt.Fprintf(w, "\nError:\tNot equal") + fmt.Fprintf(w, "\nExpect:\t%v\t[%s]", expected, aType) + fmt.Fprintf(w, "\nResult:\t%v\t[%s]", actual, bType) + + if len(description) > 0 { + fmt.Fprintf(w, "\nDescription:\t%s", description[0]) + } + + result := "" + if err := w.Flush(); err != nil { + result = err.Error() + } else { + result = buf.String() + } + if t != nil { + t.Fatal(result) + } else { + log.Fatal(result) + } +} diff --git a/internal/utils/assertions_test.go b/internal/utils/assertions_test.go new file mode 100644 index 00000000..99585b5c --- /dev/null +++ b/internal/utils/assertions_test.go @@ -0,0 +1,13 @@ +// ⚡️ 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 utils + +import "testing" + +func Test_Utils_AssertEqual(t *testing.T) { + t.Parallel() + AssertEqual(nil, []string{}, []string{}) + AssertEqual(t, []string{}, []string{}) +} diff --git a/internal/utils/bytes.go b/internal/utils/bytes.go new file mode 100644 index 00000000..d331ac67 --- /dev/null +++ b/internal/utils/bytes.go @@ -0,0 +1,70 @@ +// ⚡️ 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 utils + +// ToLowerBytes is the equivalent of bytes.ToLower +func ToLowerBytes(b []byte) []byte { + for i := 0; i < len(b); i++ { + b[i] = toLowerTable[b[i]] + } + return b +} + +// ToUpperBytes is the equivalent of bytes.ToUpper +func ToUpperBytes(b []byte) []byte { + for i := 0; i < len(b); i++ { + b[i] = toUpperTable[b[i]] + } + return b +} + +// TrimRightBytes is the equivalent of bytes.TrimRight +func TrimRightBytes(b []byte, cutset byte) []byte { + lenStr := len(b) + for lenStr > 0 && b[lenStr-1] == cutset { + lenStr-- + } + return b[:lenStr] +} + +// TrimLeftBytes is the equivalent of bytes.TrimLeft +func TrimLeftBytes(b []byte, cutset byte) []byte { + lenStr, start := len(b), 0 + for start < lenStr && b[start] == cutset { + start++ + } + return b[start:] +} + +// TrimBytes is the equivalent of bytes.Trim +func TrimBytes(b []byte, cutset byte) []byte { + i, j := 0, len(b)-1 + for ; i < j; i++ { + if b[i] != cutset { + break + } + } + for ; i < j; j-- { + if b[j] != cutset { + break + } + } + + return b[i : j+1] +} + +// EqualFold the equivalent of bytes.EqualFold +func EqualsFold(b, s []byte) (equals bool) { + n := len(b) + equals = n == len(s) + if equals { + for i := 0; i < n; i++ { + if equals = b[i]|0x20 == s[i]|0x20; !equals { + break + } + } + } + return +} diff --git a/internal/utils/bytes_test.go b/internal/utils/bytes_test.go new file mode 100644 index 00000000..b01c6384 --- /dev/null +++ b/internal/utils/bytes_test.go @@ -0,0 +1,155 @@ +// ⚡️ 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 utils + +import ( + "bytes" + "testing" +) + +func Test_Utils_ToLowerBytes(t *testing.T) { + t.Parallel() + res := ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*")) + AssertEqual(t, true, bytes.Equal([]byte("/my/name/is/:param/*"), res)) + res = ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*")) + AssertEqual(t, true, bytes.Equal([]byte("/my1/name/is/:param/*"), res)) + res = ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*")) + AssertEqual(t, true, bytes.Equal([]byte("/my2/name/is/:param/*"), res)) + res = ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*")) + AssertEqual(t, true, bytes.Equal([]byte("/my3/name/is/:param/*"), res)) + res = ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*")) + AssertEqual(t, true, bytes.Equal([]byte("/my4/name/is/:param/*"), res)) +} + +func Benchmark_ToLowerBytes(b *testing.B) { + var path = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") + var res []byte + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToLowerBytes(path) + } + AssertEqual(b, bytes.EqualFold(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.ToLower(path) + } + AssertEqual(b, bytes.EqualFold(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) + }) +} + +func Test_Utils_TrimRightBytes(t *testing.T) { + t.Parallel() + res := TrimRightBytes([]byte("/test//////"), '/') + AssertEqual(t, []byte("/test"), res) + + res = TrimRightBytes([]byte("/test"), '/') + AssertEqual(t, []byte("/test"), res) +} + +func Benchmark_TrimRightBytes(b *testing.B) { + var res []byte + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = TrimRightBytes([]byte("foobar "), ' ') + } + AssertEqual(b, []byte("foobar"), res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.TrimRight([]byte("foobar "), " ") + } + AssertEqual(b, []byte("foobar"), res) + }) +} + +func Test_Utils_TrimLeftBytes(t *testing.T) { + t.Parallel() + res := TrimLeftBytes([]byte("////test/"), '/') + AssertEqual(t, []byte("test/"), res) + + res = TrimLeftBytes([]byte("test/"), '/') + AssertEqual(t, []byte("test/"), res) +} +func Benchmark_TrimLeftBytes(b *testing.B) { + var res []byte + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = TrimLeftBytes([]byte(" foobar"), ' ') + } + AssertEqual(b, []byte("foobar"), res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.TrimLeft([]byte(" foobar"), " ") + } + AssertEqual(b, []byte("foobar"), res) + }) +} +func Test_Utils_TrimBytes(t *testing.T) { + t.Parallel() + res := TrimBytes([]byte(" test "), ' ') + AssertEqual(t, []byte("test"), res) + + res = TrimBytes([]byte("test"), ' ') + AssertEqual(t, []byte("test"), res) + + res = TrimBytes([]byte(".test"), '.') + AssertEqual(t, []byte("test"), res) +} +func Benchmark_TrimBytes(b *testing.B) { + var res []byte + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = TrimBytes([]byte(" foobar "), ' ') + } + AssertEqual(b, []byte("foobar"), res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.Trim([]byte(" foobar "), " ") + } + AssertEqual(b, []byte("foobar"), res) + }) +} + +func Benchmark_EqualFolds(b *testing.B) { + var left = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") + var right = []byte("/RePos/goFiber/Fiber/issues/187643/COMMENTS") + var res bool + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = EqualsFold(left, right) + } + AssertEqual(b, true, res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.EqualFold(left, right) + } + AssertEqual(b, true, res) + }) +} + +func Test_Utils_EqualsFold(t *testing.T) { + t.Parallel() + res := EqualsFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) + AssertEqual(t, true, res) + res = EqualsFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) + AssertEqual(t, true, res) + res = EqualsFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) + AssertEqual(t, false, res) + res = EqualsFold([]byte("/dddddd"), []byte("eeeeee")) + AssertEqual(t, false, res) + res = EqualsFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) + AssertEqual(t, true, res) + res = EqualsFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) + AssertEqual(t, true, res) +} diff --git a/internal/utils/common.go b/internal/utils/common.go new file mode 100644 index 00000000..fabf4786 --- /dev/null +++ b/internal/utils/common.go @@ -0,0 +1,66 @@ +// ⚡️ 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 utils + +import ( + "crypto/rand" + "encoding/binary" + "encoding/hex" + "reflect" + "runtime" + "sync/atomic" +) + +const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" +const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + +// Copyright © 2014, Roger Peppe +// github.com/rogpeppe/fastuuid +// All rights reserved. + +var uuidSeed [24]byte +var uuidCounter uint64 + +func UUID() string { + // Setup seed & counter once + if uuidCounter <= 0 { + if _, err := rand.Read(uuidSeed[:]); err != nil { + return "00000000-0000-0000-0000-000000000000" + } + uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) + } + // first 8 bytes differ, taking a slice of the first 16 bytes + x := atomic.AddUint64(&uuidCounter, 1) + uuid := uuidSeed + binary.LittleEndian.PutUint64(uuid[:8], x) + uuid[6], uuid[9] = uuid[9], uuid[6] + + // RFC4122 v4 + uuid[6] = (uuid[6] & 0x0f) | 0x40 + uuid[8] = uuid[8]&0x3f | 0x80 + + // create UUID representation of the first 128 bits + b := make([]byte, 36) + hex.Encode(b[0:8], uuid[0:4]) + b[8] = '-' + hex.Encode(b[9:13], uuid[4:6]) + b[13] = '-' + hex.Encode(b[14:18], uuid[6:8]) + b[18] = '-' + hex.Encode(b[19:23], uuid[8:10]) + b[23] = '-' + hex.Encode(b[24:], uuid[10:16]) + + return GetString(b) +} + +// Returns function name +func FunctionName(fn interface{}) string { + t := reflect.ValueOf(fn).Type() + if t.Kind() == reflect.Func { + return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() + } + return t.String() +} diff --git a/internal/utils/common_test.go b/internal/utils/common_test.go new file mode 100644 index 00000000..c649cdad --- /dev/null +++ b/internal/utils/common_test.go @@ -0,0 +1,47 @@ +// ⚡️ 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 utils + +import ( + "crypto/rand" + "fmt" + "testing" +) + +func Test_Utils_FunctionName(t *testing.T) { + t.Parallel() + AssertEqual(t, "github.com/gofiber/fiber/v2/internal/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) + + AssertEqual(t, "github.com/gofiber/fiber/v2/internal/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) + + var dummyint = 20 + AssertEqual(t, "int", FunctionName(dummyint)) +} + +func Test_Utils_UUID(t *testing.T) { + t.Parallel() + res := UUID() + AssertEqual(t, 36, len(res)) +} + +// go test -v -run=^$ -bench=Benchmark_UUID -benchmem -count=2 + +func Benchmark_UUID(b *testing.B) { + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = UUID() + } + AssertEqual(b, 36, len(res)) + }) + b.Run("default", func(b *testing.B) { + rnd := make([]byte, 16) + _, _ = rand.Read(rnd) + for n := 0; n < b.N; n++ { + res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) + } + AssertEqual(b, 36, len(res)) + }) +} diff --git a/internal/utils/convert.go b/internal/utils/convert.go new file mode 100644 index 00000000..5589bb71 --- /dev/null +++ b/internal/utils/convert.go @@ -0,0 +1,32 @@ +// ⚡️ 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 utils + +import ( + "reflect" + "unsafe" +) + +// #nosec G103 +// GetString returns a string pointer without allocation +func GetString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// #nosec G103 +// GetBytes returns a byte pointer without allocation +func GetBytes(s string) (bs []byte) { + sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) + bh.Data = sh.Data + bh.Len = sh.Len + bh.Cap = sh.Len + return +} + +// ImmutableString copies a string to make it immutable +func ImmutableString(s string) string { + return string(GetBytes(s)) +} diff --git a/internal/utils/convert_test.go b/internal/utils/convert_test.go new file mode 100644 index 00000000..0fdc57bc --- /dev/null +++ b/internal/utils/convert_test.go @@ -0,0 +1,81 @@ +// ⚡️ 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 utils + +import "testing" + +func Test_Utils_GetString(t *testing.T) { + t.Parallel() + res := GetString([]byte("Hello, World!")) + AssertEqual(t, "Hello, World!", res) +} + +// go test -v -run=^$ -bench=GetString -benchmem -count=2 + +func Benchmark_GetString(b *testing.B) { + var hello = []byte("Hello, World!") + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = GetString(hello) + } + AssertEqual(b, "Hello, World!", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = string(hello) + } + AssertEqual(b, "Hello, World!", res) + }) +} + +func Test_Utils_GetBytes(t *testing.T) { + t.Parallel() + res := GetBytes("Hello, World!") + AssertEqual(t, []byte("Hello, World!"), res) +} + +// go test -v -run=^$ -bench=GetBytes -benchmem -count=4 + +func Benchmark_GetBytes(b *testing.B) { + var hello = "Hello, World!" + var res []byte + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = GetBytes(hello) + } + AssertEqual(b, []byte("Hello, World!"), res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = []byte(hello) + } + AssertEqual(b, []byte("Hello, World!"), res) + }) +} + +func Test_Utils_ImmutableString(t *testing.T) { + t.Parallel() + res := ImmutableString("Hello, World!") + AssertEqual(t, "Hello, World!", res) +} + +func Test_Utils_GetMIME(t *testing.T) { + t.Parallel() + res := GetMIME(".json") + AssertEqual(t, "application/json", res) + + res = GetMIME(".xml") + AssertEqual(t, "application/xml", res) + + res = GetMIME("xml") + AssertEqual(t, "application/xml", res) + + res = GetMIME("unknown") + AssertEqual(t, MIMEOctetStream, res) + // empty case + res = GetMIME("") + AssertEqual(t, "", res) +} diff --git a/internal/utils/http.go b/internal/utils/http.go new file mode 100644 index 00000000..4584f3c7 --- /dev/null +++ b/internal/utils/http.go @@ -0,0 +1,212 @@ +// ⚡️ 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 utils + +const MIMEOctetStream = "application/octet-stream" + +// GetMIME returns the content-type of a file extension +func GetMIME(extension string) (mime string) { + if len(extension) == 0 { + return mime + } + if extension[0] == '.' { + mime = mimeExtensions[extension[1:]] + } else { + mime = mimeExtensions[extension] + } + if len(mime) == 0 { + return MIMEOctetStream + } + return mime +} + +// limits for HTTP statuscodes +const ( + statusMessageMin = 100 + statusMessageMax = 511 +) + +// StatusMessage returns the correct message for the provided HTTP statuscode +func StatusMessage(status int) string { + if status < statusMessageMin || status > statusMessageMax { + return "" + } + return statusMessage[status] +} + +// HTTP status codes were copied from net/http. +var statusMessage = []string{ + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Early Hints", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", + 421: "Misdirected Request", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 510: "Not Extended", + 511: "Network Authentication Required", +} + +// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types +var mimeExtensions = map[string]string{ + "html": "text/html", + "htm": "text/html", + "shtml": "text/html", + "css": "text/css", + "gif": "image/gif", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "xml": "application/xml", + "js": "application/javascript", + "atom": "application/atom+xml", + "rss": "application/rss+xml", + "mml": "text/mathml", + "txt": "text/plain", + "jad": "text/vnd.sun.j2me.app-descriptor", + "wml": "text/vnd.wap.wml", + "htc": "text/x-component", + "png": "image/png", + "svg": "image/svg+xml", + "svgz": "image/svg+xml", + "tif": "image/tiff", + "tiff": "image/tiff", + "wbmp": "image/vnd.wap.wbmp", + "webp": "image/webp", + "ico": "image/x-icon", + "jng": "image/x-jng", + "bmp": "image/x-ms-bmp", + "woff": "font/woff", + "woff2": "font/woff2", + "jar": "application/java-archive", + "war": "application/java-archive", + "ear": "application/java-archive", + "json": "application/json", + "hqx": "application/mac-binhex40", + "doc": "application/msword", + "pdf": "application/pdf", + "ps": "application/postscript", + "eps": "application/postscript", + "ai": "application/postscript", + "rtf": "application/rtf", + "m3u8": "application/vnd.apple.mpegurl", + "kml": "application/vnd.google-earth.kml+xml", + "kmz": "application/vnd.google-earth.kmz", + "xls": "application/vnd.ms-excel", + "eot": "application/vnd.ms-fontobject", + "ppt": "application/vnd.ms-powerpoint", + "odg": "application/vnd.oasis.opendocument.graphics", + "odp": "application/vnd.oasis.opendocument.presentation", + "ods": "application/vnd.oasis.opendocument.spreadsheet", + "odt": "application/vnd.oasis.opendocument.text", + "wmlc": "application/vnd.wap.wmlc", + "7z": "application/x-7z-compressed", + "cco": "application/x-cocoa", + "jardiff": "application/x-java-archive-diff", + "jnlp": "application/x-java-jnlp-file", + "run": "application/x-makeself", + "pl": "application/x-perl", + "pm": "application/x-perl", + "prc": "application/x-pilot", + "pdb": "application/x-pilot", + "rar": "application/x-rar-compressed", + "rpm": "application/x-redhat-package-manager", + "sea": "application/x-sea", + "swf": "application/x-shockwave-flash", + "sit": "application/x-stuffit", + "tcl": "application/x-tcl", + "tk": "application/x-tcl", + "der": "application/x-x509-ca-cert", + "pem": "application/x-x509-ca-cert", + "crt": "application/x-x509-ca-cert", + "xpi": "application/x-xpinstall", + "xhtml": "application/xhtml+xml", + "xspf": "application/xspf+xml", + "zip": "application/zip", + "bin": "application/octet-stream", + "exe": "application/octet-stream", + "dll": "application/octet-stream", + "deb": "application/octet-stream", + "dmg": "application/octet-stream", + "iso": "application/octet-stream", + "img": "application/octet-stream", + "msi": "application/octet-stream", + "msp": "application/octet-stream", + "msm": "application/octet-stream", + "mid": "audio/midi", + "midi": "audio/midi", + "kar": "audio/midi", + "mp3": "audio/mpeg", + "ogg": "audio/ogg", + "m4a": "audio/x-m4a", + "ra": "audio/x-realaudio", + "3gpp": "video/3gpp", + "3gp": "video/3gpp", + "ts": "video/mp2t", + "mp4": "video/mp4", + "mpeg": "video/mpeg", + "mpg": "video/mpeg", + "mov": "video/quicktime", + "webm": "video/webm", + "flv": "video/x-flv", + "m4v": "video/x-m4v", + "mng": "video/x-mng", + "asx": "video/x-ms-asf", + "asf": "video/x-ms-asf", + "wmv": "video/x-ms-wmv", + "avi": "video/x-msvideo", +} diff --git a/internal/utils/http_test.go b/internal/utils/http_test.go new file mode 100644 index 00000000..d6f454d7 --- /dev/null +++ b/internal/utils/http_test.go @@ -0,0 +1,72 @@ +// ⚡️ 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 utils + +import ( + "mime" + "net/http" + "testing" +) + +// go test -v -run=^$ -bench=Benchmark_GetMIME -benchmem -count=2 +func Benchmark_GetMIME(b *testing.B) { + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = GetMIME(".xml") + res = GetMIME(".txt") + res = GetMIME(".png") + res = GetMIME(".exe") + res = GetMIME(".json") + } + AssertEqual(b, "application/json", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = mime.TypeByExtension(".xml") + res = mime.TypeByExtension(".txt") + res = mime.TypeByExtension(".png") + res = mime.TypeByExtension(".exe") + res = mime.TypeByExtension(".json") + } + AssertEqual(b, "application/json", res) + }) +} + +func Test_Utils_StatusMessage(t *testing.T) { + t.Parallel() + res := StatusMessage(204) + AssertEqual(t, "No Content", res) + + res = StatusMessage(404) + AssertEqual(t, "Not Found", res) + + res = StatusMessage(426) + AssertEqual(t, "Upgrade Required", res) + + res = StatusMessage(511) + AssertEqual(t, "Network Authentication Required", res) + + res = StatusMessage(1337) + AssertEqual(t, "", res) + + res = StatusMessage(-1) + AssertEqual(t, "", res) + + res = StatusMessage(0) + AssertEqual(t, "", res) + + res = StatusMessage(600) + AssertEqual(t, "", res) +} + +// go test -v -run=^$ -bench=Benchmark_StatusMessage -benchmem -count=4 +func Benchmark_StatusMessage(b *testing.B) { + var res string + for n := 0; n < b.N; n++ { + res = StatusMessage(http.StatusNotExtended) + } + AssertEqual(b, "Not Extended", res) +} diff --git a/internal/utils/strings.go b/internal/utils/strings.go new file mode 100644 index 00000000..6657089a --- /dev/null +++ b/internal/utils/strings.go @@ -0,0 +1,62 @@ +// ⚡️ 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 utils + +// ToLower is the equivalent of strings.ToLower +func ToLower(b string) string { + var res = make([]byte, len(b)) + copy(res, b) + for i := 0; i < len(res); i++ { + res[i] = toLowerTable[res[i]] + } + + return GetString(res) +} + +// ToUpper is the equivalent of strings.ToUpper +func ToUpper(b string) string { + var res = make([]byte, len(b)) + copy(res, b) + for i := 0; i < len(res); i++ { + res[i] = toUpperTable[res[i]] + } + + return GetString(res) +} + +// TrimLeft is the equivalent of strings.TrimLeft +func TrimLeft(s string, cutset byte) string { + lenStr, start := len(s), 0 + for start < lenStr && s[start] == cutset { + start++ + } + return s[start:] +} + +// Trim is the equivalent of strings.Trim +func Trim(s string, cutset byte) string { + i, j := 0, len(s)-1 + for ; i < j; i++ { + if s[i] != cutset { + break + } + } + for ; i < j; j-- { + if s[j] != cutset { + break + } + } + + return s[i : j+1] +} + +// TrimRight is the equivalent of strings.TrimRight +func TrimRight(s string, cutset byte) string { + lenStr := len(s) + for lenStr > 0 && s[lenStr-1] == cutset { + lenStr-- + } + return s[:lenStr] +} diff --git a/internal/utils/strings_test.go b/internal/utils/strings_test.go new file mode 100644 index 00000000..c65142b4 --- /dev/null +++ b/internal/utils/strings_test.go @@ -0,0 +1,149 @@ +// ⚡️ 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 utils + +import ( + "strings" + "testing" +) + +func Test_Utils_ToUpper(t *testing.T) { + t.Parallel() + res := ToUpper("/my/name/is/:param/*") + AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res) +} + +func Benchmark_ToUpper(b *testing.B) { + var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" + var res string + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToUpper(path) + } + AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.ToUpper(path) + } + AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) + }) +} + +func Test_Utils_ToLower(t *testing.T) { + t.Parallel() + res := ToLower("/MY/NAME/IS/:PARAM/*") + AssertEqual(t, "/my/name/is/:param/*", res) + res = ToLower("/MY1/NAME/IS/:PARAM/*") + AssertEqual(t, "/my1/name/is/:param/*", res) + res = ToLower("/MY2/NAME/IS/:PARAM/*") + AssertEqual(t, "/my2/name/is/:param/*", res) + res = ToLower("/MY3/NAME/IS/:PARAM/*") + AssertEqual(t, "/my3/name/is/:param/*", res) + res = ToLower("/MY4/NAME/IS/:PARAM/*") + AssertEqual(t, "/my4/name/is/:param/*", res) +} + +func Benchmark_ToLower(b *testing.B) { + var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToLower(path) + } + AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.ToLower(path) + } + AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) + }) +} + +func Test_Utils_TrimRight(t *testing.T) { + t.Parallel() + res := TrimRight("/test//////", '/') + AssertEqual(t, "/test", res) + + res = TrimRight("/test", '/') + AssertEqual(t, "/test", res) +} +func Benchmark_TrimRight(b *testing.B) { + var res string + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = TrimRight("foobar ", ' ') + } + AssertEqual(b, "foobar", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.TrimRight("foobar ", " ") + } + AssertEqual(b, "foobar", res) + }) +} + +func Test_Utils_TrimLeft(t *testing.T) { + t.Parallel() + res := TrimLeft("////test/", '/') + AssertEqual(t, "test/", res) + + res = TrimLeft("test/", '/') + AssertEqual(t, "test/", res) +} +func Benchmark_TrimLeft(b *testing.B) { + var res string + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = TrimLeft(" foobar", ' ') + } + AssertEqual(b, "foobar", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.TrimLeft(" foobar", " ") + } + AssertEqual(b, "foobar", res) + }) +} +func Test_Utils_Trim(t *testing.T) { + t.Parallel() + res := Trim(" test ", ' ') + AssertEqual(t, "test", res) + + res = Trim("test", ' ') + AssertEqual(t, "test", res) + + res = Trim(".test", '.') + AssertEqual(t, "test", res) +} + +func Benchmark_Trim(b *testing.B) { + var res string + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = Trim(" foobar ", ' ') + } + AssertEqual(b, "foobar", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.Trim(" foobar ", " ") + } + AssertEqual(b, "foobar", res) + }) + b.Run("default.trimspace", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.TrimSpace(" foobar ") + } + AssertEqual(b, "foobar", res) + }) +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go deleted file mode 100644 index 09f240ed..00000000 --- a/internal/utils/utils.go +++ /dev/null @@ -1,505 +0,0 @@ -// ⚡️ 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 utils - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "encoding/hex" - "fmt" - "log" - "path/filepath" - "reflect" - "runtime" - "strings" - "sync/atomic" - "testing" - "text/tabwriter" - "unsafe" -) - -const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" -const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - -// Copyright © 2014, Roger Peppe -// github.com/rogpeppe/fastuuid -// All rights reserved. - -var uuidSeed [24]byte -var uuidCounter uint64 - -func UUID() string { - // Setup seed & counter once - if uuidCounter <= 0 { - if _, err := rand.Read(uuidSeed[:]); err != nil { - return "00000000-0000-0000-0000-000000000000" - } - uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) - } - // first 8 bytes differ, taking a slice of the first 16 bytes - x := atomic.AddUint64(&uuidCounter, 1) - uuid := uuidSeed - binary.LittleEndian.PutUint64(uuid[:8], x) - uuid[6], uuid[9] = uuid[9], uuid[6] - - // RFC4122 v4 - uuid[6] = (uuid[6] & 0x0f) | 0x40 - uuid[8] = uuid[8]&0x3f | 0x80 - - // create UUID representation of the first 128 bits - b := make([]byte, 36) - hex.Encode(b[0:8], uuid[0:4]) - b[8] = '-' - hex.Encode(b[9:13], uuid[4:6]) - b[13] = '-' - hex.Encode(b[14:18], uuid[6:8]) - b[18] = '-' - hex.Encode(b[19:23], uuid[8:10]) - b[23] = '-' - hex.Encode(b[24:], uuid[10:16]) - - return GetString(b) -} - -// Returns function name -func FunctionName(fn interface{}) string { - t := reflect.ValueOf(fn).Type() - if t.Kind() == reflect.Func { - return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() - } - return t.String() -} - -// ToLower is the equivalent of strings.ToLower -func ToLower(b string) string { - var res = make([]byte, len(b)) - copy(res, b) - for i := 0; i < len(res); i++ { - res[i] = toLowerTable[res[i]] - } - - return GetString(res) -} - -// ToLowerBytes is the equivalent of bytes.ToLower -func ToLowerBytes(b []byte) []byte { - for i := 0; i < len(b); i++ { - b[i] = toLowerTable[b[i]] - } - return b -} - -// ToUpper is the equivalent of strings.ToUpper -func ToUpper(b string) string { - var res = make([]byte, len(b)) - copy(res, b) - for i := 0; i < len(res); i++ { - res[i] = toUpperTable[res[i]] - } - - return GetString(res) -} - -// ToUpperBytes is the equivalent of bytes.ToUpper -func ToUpperBytes(b []byte) []byte { - for i := 0; i < len(b); i++ { - b[i] = toUpperTable[b[i]] - } - return b -} - -// TrimRight is the equivalent of strings.TrimRight -func TrimRight(s string, cutset byte) string { - lenStr := len(s) - for lenStr > 0 && s[lenStr-1] == cutset { - lenStr-- - } - return s[:lenStr] -} - -// TrimRightBytes is the equivalent of bytes.TrimRight -func TrimRightBytes(b []byte, cutset byte) []byte { - lenStr := len(b) - for lenStr > 0 && b[lenStr-1] == cutset { - lenStr-- - } - return b[:lenStr] -} - -// TrimLeft is the equivalent of strings.TrimLeft -func TrimLeft(s string, cutset byte) string { - lenStr, start := len(s), 0 - for start < lenStr && s[start] == cutset { - start++ - } - return s[start:] -} - -// TrimLeftBytes is the equivalent of bytes.TrimLeft -func TrimLeftBytes(b []byte, cutset byte) []byte { - lenStr, start := len(b), 0 - for start < lenStr && b[start] == cutset { - start++ - } - return b[start:] -} - -// Trim is the equivalent of strings.Trim -func Trim(s string, cutset byte) string { - i, j := 0, len(s)-1 - for ; i < j; i++ { - if s[i] != cutset { - break - } - } - for ; i < j; j-- { - if s[j] != cutset { - break - } - } - - return s[i : j+1] -} - -// TrimBytes is the equivalent of bytes.Trim -func TrimBytes(b []byte, cutset byte) []byte { - i, j := 0, len(b)-1 - for ; i < j; i++ { - if b[i] != cutset { - break - } - } - for ; i < j; j-- { - if b[j] != cutset { - break - } - } - - return b[i : j+1] -} - -// EqualFold the equivalent of bytes.EqualFold -func EqualsFold(b, s []byte) (equals bool) { - n := len(b) - equals = n == len(s) - if equals { - for i := 0; i < n; i++ { - if equals = b[i]|0x20 == s[i]|0x20; !equals { - break - } - } - } - return -} - -// #nosec G103 -// GetString returns a string pointer without allocation -func GetString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -// #nosec G103 -// GetBytes returns a byte pointer without allocation -func GetBytes(s string) (bs []byte) { - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) - bh.Data = sh.Data - bh.Len = sh.Len - bh.Cap = sh.Len - return -} - -// ImmutableString copies a string to make it immutable -func ImmutableString(s string) string { - return string(GetBytes(s)) -} - -// AssertEqual checks if values are equal -func AssertEqual(t testing.TB, expected interface{}, actual interface{}, description ...string) { - if reflect.DeepEqual(expected, actual) { - return - } - var aType = "" - var bType = "" - if reflect.ValueOf(expected).IsValid() { - aType = reflect.TypeOf(expected).Name() - } - if reflect.ValueOf(actual).IsValid() { - bType = reflect.TypeOf(actual).Name() - } - - testName := "AssertEqual" - if t != nil { - testName = t.Name() - } - - _, file, line, _ := runtime.Caller(1) - - var buf bytes.Buffer - w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) - fmt.Fprintf(w, "\nTest:\t%s", testName) - fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) - fmt.Fprintf(w, "\nError:\tNot equal") - fmt.Fprintf(w, "\nExpect:\t%v\t[%s]", expected, aType) - fmt.Fprintf(w, "\nResult:\t%v\t[%s]", actual, bType) - - if len(description) > 0 { - fmt.Fprintf(w, "\nDescription:\t%s", description[0]) - } - - result := "" - if err := w.Flush(); err != nil { - result = err.Error() - } else { - result = buf.String() - } - if t != nil { - t.Fatal(result) - } else { - log.Fatal(result) - } -} - -const MIMEOctetStream = "application/octet-stream" - -// GetMIME returns the content-type of a file extension -func GetMIME(extension string) (mime string) { - if len(extension) == 0 { - return mime - } - if extension[0] == '.' { - mime = mimeExtensions[extension[1:]] - } else { - mime = mimeExtensions[extension] - } - if len(mime) == 0 { - return MIMEOctetStream - } - return mime -} - -// GetTrimmedParam trims the ':' & '?' from a string -func GetTrimmedParam(param string) string { - start := 0 - end := len(param) - - if param[start] != ':' { // is not a param - return param - } - start++ - if param[end-1] == '?' { // is ? - end-- - } - - return param[start:end] -} - -// GetCharPos ... -func GetCharPos(s string, char byte, matchCount int) int { - if matchCount == 0 { - matchCount = 1 - } - endPos, pos := 0, -2 - for matchCount > 0 && pos != -1 { - if pos > -1 { - s = s[pos+1:] - endPos++ - } - pos = strings.IndexByte(s, char) - endPos += pos - matchCount-- - } - return endPos -} - -// limits for HTTP statuscodes -const ( - statusMessageMin = 100 - statusMessageMax = 511 -) - -// StatusMessage returns the correct message for the provided HTTP statuscode -func StatusMessage(status int) string { - if status < statusMessageMin || status > statusMessageMax { - return "" - } - return statusMessage[status] -} - -// HTTP status codes were copied from net/http. -var statusMessage = []string{ - 100: "Continue", - 101: "Switching Protocols", - 102: "Processing", - 103: "Early Hints", - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non-Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 207: "Multi-Status", - 208: "Already Reported", - 226: "IM Used", - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Found", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 306: "Switch Proxy", - 307: "Temporary Redirect", - 308: "Permanent Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 418: "I'm a teapot", - 421: "Misdirected Request", - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 426: "Upgrade Required", - 428: "Precondition Required", - 429: "Too Many Requests", - 431: "Request Header Fields Too Large", - 451: "Unavailable For Legal Reasons", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported", - 506: "Variant Also Negotiates", - 507: "Insufficient Storage", - 508: "Loop Detected", - 510: "Not Extended", - 511: "Network Authentication Required", -} - -// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types -var mimeExtensions = map[string]string{ - "html": "text/html", - "htm": "text/html", - "shtml": "text/html", - "css": "text/css", - "gif": "image/gif", - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "xml": "application/xml", - "js": "application/javascript", - "atom": "application/atom+xml", - "rss": "application/rss+xml", - "mml": "text/mathml", - "txt": "text/plain", - "jad": "text/vnd.sun.j2me.app-descriptor", - "wml": "text/vnd.wap.wml", - "htc": "text/x-component", - "png": "image/png", - "svg": "image/svg+xml", - "svgz": "image/svg+xml", - "tif": "image/tiff", - "tiff": "image/tiff", - "wbmp": "image/vnd.wap.wbmp", - "webp": "image/webp", - "ico": "image/x-icon", - "jng": "image/x-jng", - "bmp": "image/x-ms-bmp", - "woff": "font/woff", - "woff2": "font/woff2", - "jar": "application/java-archive", - "war": "application/java-archive", - "ear": "application/java-archive", - "json": "application/json", - "hqx": "application/mac-binhex40", - "doc": "application/msword", - "pdf": "application/pdf", - "ps": "application/postscript", - "eps": "application/postscript", - "ai": "application/postscript", - "rtf": "application/rtf", - "m3u8": "application/vnd.apple.mpegurl", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "xls": "application/vnd.ms-excel", - "eot": "application/vnd.ms-fontobject", - "ppt": "application/vnd.ms-powerpoint", - "odg": "application/vnd.oasis.opendocument.graphics", - "odp": "application/vnd.oasis.opendocument.presentation", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "odt": "application/vnd.oasis.opendocument.text", - "wmlc": "application/vnd.wap.wmlc", - "7z": "application/x-7z-compressed", - "cco": "application/x-cocoa", - "jardiff": "application/x-java-archive-diff", - "jnlp": "application/x-java-jnlp-file", - "run": "application/x-makeself", - "pl": "application/x-perl", - "pm": "application/x-perl", - "prc": "application/x-pilot", - "pdb": "application/x-pilot", - "rar": "application/x-rar-compressed", - "rpm": "application/x-redhat-package-manager", - "sea": "application/x-sea", - "swf": "application/x-shockwave-flash", - "sit": "application/x-stuffit", - "tcl": "application/x-tcl", - "tk": "application/x-tcl", - "der": "application/x-x509-ca-cert", - "pem": "application/x-x509-ca-cert", - "crt": "application/x-x509-ca-cert", - "xpi": "application/x-xpinstall", - "xhtml": "application/xhtml+xml", - "xspf": "application/xspf+xml", - "zip": "application/zip", - "bin": "application/octet-stream", - "exe": "application/octet-stream", - "dll": "application/octet-stream", - "deb": "application/octet-stream", - "dmg": "application/octet-stream", - "iso": "application/octet-stream", - "img": "application/octet-stream", - "msi": "application/octet-stream", - "msp": "application/octet-stream", - "msm": "application/octet-stream", - "mid": "audio/midi", - "midi": "audio/midi", - "kar": "audio/midi", - "mp3": "audio/mpeg", - "ogg": "audio/ogg", - "m4a": "audio/x-m4a", - "ra": "audio/x-realaudio", - "3gpp": "video/3gpp", - "3gp": "video/3gpp", - "ts": "video/mp2t", - "mp4": "video/mp4", - "mpeg": "video/mpeg", - "mpg": "video/mpeg", - "mov": "video/quicktime", - "webm": "video/webm", - "flv": "video/x-flv", - "m4v": "video/x-m4v", - "mng": "video/x-mng", - "asx": "video/x-ms-asf", - "asf": "video/x-ms-asf", - "wmv": "video/x-ms-wmv", - "avi": "video/x-msvideo", -} diff --git a/internal/utils/utils_test.go b/internal/utils/utils_test.go deleted file mode 100644 index 494521a5..00000000 --- a/internal/utils/utils_test.go +++ /dev/null @@ -1,502 +0,0 @@ -// ⚡️ 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 utils - -import ( - "bytes" - "crypto/rand" - "fmt" - "mime" - "net/http" - "strings" - "testing" -) - -func Test_Utils_FunctionName(t *testing.T) { - t.Parallel() - AssertEqual(t, "github.com/gofiber/fiber/v2/internal/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) - - AssertEqual(t, "github.com/gofiber/fiber/v2/internal/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) - - var dummyint = 20 - AssertEqual(t, "int", FunctionName(dummyint)) -} - -func Test_Utils_UUID(t *testing.T) { - t.Parallel() - res := UUID() - AssertEqual(t, 36, len(res)) -} - -// go test -v -run=^$ -bench=Benchmark_UUID -benchmem -count=2 - -func Benchmark_UUID(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = UUID() - } - AssertEqual(b, 36, len(res)) - }) - b.Run("default", func(b *testing.B) { - rnd := make([]byte, 16) - _, _ = rand.Read(rnd) - for n := 0; n < b.N; n++ { - res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) - } - AssertEqual(b, 36, len(res)) - }) -} - -func Test_Utils_ToUpper(t *testing.T) { - t.Parallel() - res := ToUpper("/my/name/is/:param/*") - AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res) -} - -func Benchmark_ToUpper(b *testing.B) { - var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToUpper(path) - } - AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.ToUpper(path) - } - AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) - }) -} - -func Test_Utils_ToLower(t *testing.T) { - t.Parallel() - res := ToLower("/MY/NAME/IS/:PARAM/*") - AssertEqual(t, "/my/name/is/:param/*", res) - res = ToLower("/MY1/NAME/IS/:PARAM/*") - AssertEqual(t, "/my1/name/is/:param/*", res) - res = ToLower("/MY2/NAME/IS/:PARAM/*") - AssertEqual(t, "/my2/name/is/:param/*", res) - res = ToLower("/MY3/NAME/IS/:PARAM/*") - AssertEqual(t, "/my3/name/is/:param/*", res) - res = ToLower("/MY4/NAME/IS/:PARAM/*") - AssertEqual(t, "/my4/name/is/:param/*", res) -} - -func Benchmark_ToLower(b *testing.B) { - var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToLower(path) - } - AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.ToLower(path) - } - AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) - }) -} - -func Test_Utils_ToLowerBytes(t *testing.T) { - t.Parallel() - res := ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my1/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my2/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my3/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my4/name/is/:param/*"), res)) -} - -func Benchmark_ToLowerBytes(b *testing.B) { - var path = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToLowerBytes(path) - } - AssertEqual(b, bytes.EqualFold(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.ToLower(path) - } - AssertEqual(b, bytes.EqualFold(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) - }) -} - -func Benchmark_EqualFolds(b *testing.B) { - var left = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") - var right = []byte("/RePos/goFiber/Fiber/issues/187643/COMMENTS") - var res bool - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = EqualsFold(left, right) - } - AssertEqual(b, true, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.EqualFold(left, right) - } - AssertEqual(b, true, res) - }) -} - -func Test_Utils_EqualsFold(t *testing.T) { - t.Parallel() - res := EqualsFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualsFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, true, res) - res = EqualsFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) - AssertEqual(t, false, res) - res = EqualsFold([]byte("/dddddd"), []byte("eeeeee")) - AssertEqual(t, false, res) - res = EqualsFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualsFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) - AssertEqual(t, true, res) -} - -func Test_Utils_TrimRight(t *testing.T) { - t.Parallel() - res := TrimRight("/test//////", '/') - AssertEqual(t, "/test", res) - - res = TrimRight("/test", '/') - AssertEqual(t, "/test", res) -} -func Benchmark_TrimRight(b *testing.B) { - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimRight("foobar ", ' ') - } - AssertEqual(b, "foobar", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.TrimRight("foobar ", " ") - } - AssertEqual(b, "foobar", res) - }) -} - -func Test_Utils_TrimLeft(t *testing.T) { - t.Parallel() - res := TrimLeft("////test/", '/') - AssertEqual(t, "test/", res) - - res = TrimLeft("test/", '/') - AssertEqual(t, "test/", res) -} -func Benchmark_TrimLeft(b *testing.B) { - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimLeft(" foobar", ' ') - } - AssertEqual(b, "foobar", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.TrimLeft(" foobar", " ") - } - AssertEqual(b, "foobar", res) - }) -} -func Test_Utils_Trim(t *testing.T) { - t.Parallel() - res := Trim(" test ", ' ') - AssertEqual(t, "test", res) - - res = Trim("test", ' ') - AssertEqual(t, "test", res) - - res = Trim(".test", '.') - AssertEqual(t, "test", res) -} - -func Benchmark_Trim(b *testing.B) { - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = Trim(" foobar ", ' ') - } - AssertEqual(b, "foobar", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.Trim(" foobar ", " ") - } - AssertEqual(b, "foobar", res) - }) - b.Run("default.trimspace", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.TrimSpace(" foobar ") - } - AssertEqual(b, "foobar", res) - }) -} - -func Test_Utils_TrimRightBytes(t *testing.T) { - t.Parallel() - res := TrimRightBytes([]byte("/test//////"), '/') - AssertEqual(t, []byte("/test"), res) - - res = TrimRightBytes([]byte("/test"), '/') - AssertEqual(t, []byte("/test"), res) -} - -func Benchmark_TrimRightBytes(b *testing.B) { - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimRightBytes([]byte("foobar "), ' ') - } - AssertEqual(b, []byte("foobar"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.TrimRight([]byte("foobar "), " ") - } - AssertEqual(b, []byte("foobar"), res) - }) -} - -func Test_Utils_TrimLeftBytes(t *testing.T) { - t.Parallel() - res := TrimLeftBytes([]byte("////test/"), '/') - AssertEqual(t, []byte("test/"), res) - - res = TrimLeftBytes([]byte("test/"), '/') - AssertEqual(t, []byte("test/"), res) -} -func Benchmark_TrimLeftBytes(b *testing.B) { - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimLeftBytes([]byte(" foobar"), ' ') - } - AssertEqual(b, []byte("foobar"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.TrimLeft([]byte(" foobar"), " ") - } - AssertEqual(b, []byte("foobar"), res) - }) -} -func Test_Utils_TrimBytes(t *testing.T) { - t.Parallel() - res := TrimBytes([]byte(" test "), ' ') - AssertEqual(t, []byte("test"), res) - - res = TrimBytes([]byte("test"), ' ') - AssertEqual(t, []byte("test"), res) - - res = TrimBytes([]byte(".test"), '.') - AssertEqual(t, []byte("test"), res) -} -func Benchmark_TrimBytes(b *testing.B) { - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimBytes([]byte(" foobar "), ' ') - } - AssertEqual(b, []byte("foobar"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.Trim([]byte(" foobar "), " ") - } - AssertEqual(b, []byte("foobar"), res) - }) -} - -func Test_Utils_GetCharPos(t *testing.T) { - t.Parallel() - res := GetCharPos("/foo/bar/foobar/test", '/', 3) - AssertEqual(t, 8, res) - res = GetCharPos("foo/bar/foobar/test", '/', 3) - AssertEqual(t, 14, res) - res = GetCharPos("foo/bar/foobar/test", '/', 1) - AssertEqual(t, 3, res) - res = GetCharPos("foo/bar/foobar/test", 'f', 2) - AssertEqual(t, 8, res) - res = GetCharPos("foo/bar/foobar/test", 'f', 0) - AssertEqual(t, 0, res) -} - -func Test_Utils_GetTrimmedParam(t *testing.T) { - t.Parallel() - res := GetTrimmedParam("*") - AssertEqual(t, "*", res) - res = GetTrimmedParam(":param") - AssertEqual(t, "param", res) - res = GetTrimmedParam(":param1?") - AssertEqual(t, "param1", res) - res = GetTrimmedParam("noParam") - AssertEqual(t, "noParam", res) -} - -func Test_Utils_GetString(t *testing.T) { - t.Parallel() - res := GetString([]byte("Hello, World!")) - AssertEqual(t, "Hello, World!", res) -} - -// go test -v -run=^$ -bench=GetString -benchmem -count=2 - -func Benchmark_GetString(b *testing.B) { - var hello = []byte("Hello, World!") - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetString(hello) - } - AssertEqual(b, "Hello, World!", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = string(hello) - } - AssertEqual(b, "Hello, World!", res) - }) -} - -func Test_Utils_GetBytes(t *testing.T) { - t.Parallel() - res := GetBytes("Hello, World!") - AssertEqual(t, []byte("Hello, World!"), res) -} - -// go test -v -run=^$ -bench=GetBytes -benchmem -count=4 - -func Benchmark_GetBytes(b *testing.B) { - var hello = "Hello, World!" - var res []byte - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetBytes(hello) - } - AssertEqual(b, []byte("Hello, World!"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = []byte(hello) - } - AssertEqual(b, []byte("Hello, World!"), res) - }) -} - -func Test_Utils_ImmutableString(t *testing.T) { - t.Parallel() - res := ImmutableString("Hello, World!") - AssertEqual(t, "Hello, World!", res) -} - -func Test_Utils_GetMIME(t *testing.T) { - t.Parallel() - res := GetMIME(".json") - AssertEqual(t, "application/json", res) - - res = GetMIME(".xml") - AssertEqual(t, "application/xml", res) - - res = GetMIME("xml") - AssertEqual(t, "application/xml", res) - - res = GetMIME("unknown") - AssertEqual(t, MIMEOctetStream, res) - // empty case - res = GetMIME("") - AssertEqual(t, "", res) -} - -// go test -v -run=^$ -bench=Benchmark_GetMIME -benchmem -count=2 -func Benchmark_GetMIME(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetMIME(".xml") - res = GetMIME(".txt") - res = GetMIME(".png") - res = GetMIME(".exe") - res = GetMIME(".json") - } - AssertEqual(b, "application/json", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = mime.TypeByExtension(".xml") - res = mime.TypeByExtension(".txt") - res = mime.TypeByExtension(".png") - res = mime.TypeByExtension(".exe") - res = mime.TypeByExtension(".json") - } - AssertEqual(b, "application/json", res) - }) -} - -func Test_Utils_StatusMessage(t *testing.T) { - t.Parallel() - res := StatusMessage(204) - AssertEqual(t, "No Content", res) - - res = StatusMessage(404) - AssertEqual(t, "Not Found", res) - - res = StatusMessage(426) - AssertEqual(t, "Upgrade Required", res) - - res = StatusMessage(511) - AssertEqual(t, "Network Authentication Required", res) - - res = StatusMessage(1337) - AssertEqual(t, "", res) - - res = StatusMessage(-1) - AssertEqual(t, "", res) - - res = StatusMessage(0) - AssertEqual(t, "", res) - - res = StatusMessage(600) - AssertEqual(t, "", res) -} - -// go test -v -run=^$ -bench=Benchmark_StatusMessage -benchmem -count=4 -func Benchmark_StatusMessage(b *testing.B) { - var res string - for n := 0; n < b.N; n++ { - res = StatusMessage(http.StatusNotExtended) - } - AssertEqual(b, "Not Extended", res) -} - -func Test_Utils_AssertEqual(t *testing.T) { - t.Parallel() - AssertEqual(nil, []string{}, []string{}) - AssertEqual(t, []string{}, []string{}) -} diff --git a/path.go b/path.go index c32680c7..5f62e69a 100644 --- a/path.go +++ b/path.go @@ -186,7 +186,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r // cut params part processedPart := pattern[0 : parameterEndPosition+1] - paramName := utils.GetTrimmedParam(processedPart) + paramName := GetTrimmedParam(processedPart) // add access iterator to wildcard and plus if isWildCard { routeParser.wildCardCount++ @@ -318,3 +318,19 @@ func findGreedyParamLen(s string, searchCount int, segment *routeSegment) int { return len(s) } + +// GetTrimmedParam trims the ':' & '?' from a string +func GetTrimmedParam(param string) string { + start := 0 + end := len(param) + + if param[start] != ':' { // is not a param + return param + } + start++ + if param[end-1] == '?' { // is ? + end-- + } + + return param[start:end] +} diff --git a/path_test.go b/path_test.go index 664c2fcf..c8750247 100644 --- a/path_test.go +++ b/path_test.go @@ -386,6 +386,18 @@ func Test_Path_matchParams(t *testing.T) { }) } +func Test_Utils_GetTrimmedParam(t *testing.T) { + t.Parallel() + res := GetTrimmedParam("*") + utils.AssertEqual(t, "*", res) + res = GetTrimmedParam(":param") + utils.AssertEqual(t, "param", res) + res = GetTrimmedParam(":param1?") + utils.AssertEqual(t, "param1", res) + res = GetTrimmedParam("noParam") + utils.AssertEqual(t, "noParam", res) +} + // go test -race -run Test_Path_matchParams func Benchmark_Path_matchParams(t *testing.B) { type testparams struct {