Skip to content

Commit

Permalink
Merge pull request #1009 from Fenny/master
Browse files Browse the repository at this point in the history
🧹 housekeeping
  • Loading branch information
Fenny authored Nov 14, 2020
2 parents 6ffc89f + 581cac0 commit f881582
Show file tree
Hide file tree
Showing 38 changed files with 2,298 additions and 806 deletions.
4 changes: 0 additions & 4 deletions .github/CODEOWNERS

This file was deleted.

10 changes: 8 additions & 2 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
)

// Version of current fiber package
const Version = "2.1.4"
const Version = "2.2.0"

// Handler defines a function to serve HTTP requests.
type Handler = func(*Ctx) error
Expand Down Expand Up @@ -258,7 +258,7 @@ type Config struct {
// Default: false
ReduceMemoryUsage bool `json:"reduce_memory_usage"`

// FEATURE: v2.2.x
// FEATURE: v2.3.x
// The router executes the same handler by default if StrictRouting or CaseSensitive is disabled.
// Enabling RedirectFixedPath will change this behaviour into a client redirect to the original route path.
// Using the status code 301 for GET requests and 308 for all other request methods.
Expand Down Expand Up @@ -286,6 +286,12 @@ type Static struct {
// Optional. Default value "index.html".
Index string `json:"index"`

// Expiration duration for inactive file handlers.
// Use a negative time.Duration to disable it.
//
// Optional. Default value 10 * time.Second.
CacheDuration time.Duration `json:"cache_duration"`

// The value for the Cache-Control HTTP-header
// that is set on the file response. MaxAge is defined in seconds.
//
Expand Down
27 changes: 27 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,33 @@ func Test_Ctx_MultipartForm(t *testing.T) {
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
}

// go test -v -run=^$ -bench=Benchmark_Ctx_MultipartForm -benchmem -count=4
func Benchmark_Ctx_MultipartForm(b *testing.B) {
app := New()

app.Post("/", func(c *Ctx) error {
_, _ = c.MultipartForm()
return nil
})

c := &fasthttp.RequestCtx{}

body := []byte("--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--")
c.Request.SetBody(body)
c.Request.Header.SetContentType(MIMEMultipartForm + `;boundary="b"`)
c.Request.Header.SetContentLength(len(body))

h := app.Handler()

b.ReportAllocs()
b.ResetTimer()

for n := 0; n < b.N; n++ {
h(c)
}

}

// go test -run Test_Ctx_OriginalURL
func Test_Ctx_OriginalURL(t *testing.T) {
t.Parallel()
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/gofiber/fiber/v2
go 1.14

require (
github.com/klauspost/compress v1.11.0 // indirect
github.com/valyala/fasthttp v1.17.0
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDa
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.17.0 h1:P8/koH4aSnJ4xbd0cUUFEGQs3jQqIxoDDyRQrUiAkqg=
Expand Down
97 changes: 91 additions & 6 deletions helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,76 @@ import (
"crypto/tls"
"fmt"
"net"
"strings"
"testing"
"time"

"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/fasthttp"
)

// go test -v -run=^$ -bench=Benchmark_Utils_RemoveNewLines -benchmem -count=4
func Benchmark_Utils_RemoveNewLines(b *testing.B) {
// go test -v -run=^$ -bench=Benchmark_RemoveNewLines -benchmem -count=4
func Benchmark_RemoveNewLines(b *testing.B) {
withNL := "foo\r\nSet-Cookie:%20SESSIONID=MaliciousValue\r\n"
withoutNL := "foo Set-Cookie:%20SESSIONID=MaliciousValue "
expected := utils.SafeString(withoutNL)
var res string

b.Run("withNewlines", func(b *testing.B) {
b.Run("withoutNL", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
res = removeNewLines(withNL)
res = removeNewLines(withoutNL)
}
utils.AssertEqual(b, expected, res)
})
b.Run("withoutNewlines", func(b *testing.B) {
b.Run("withNL", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
res = removeNewLines(withoutNL)
res = removeNewLines(withNL)
}
utils.AssertEqual(b, expected, res)
})
}

// go test -v -run=RemoveNewLines_Bytes -count=3
func Test_RemoveNewLines_Bytes(t *testing.T) {
app := New()
t.Run("Not Status OK", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
c.SendString("Hello, World!")
c.Status(201)
setETag(c, false)
utils.AssertEqual(t, "", string(c.Response().Header.Peek(HeaderETag)))
})

t.Run("No Body", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
setETag(c, false)
utils.AssertEqual(t, "", string(c.Response().Header.Peek(HeaderETag)))
})

t.Run("Has HeaderIfNoneMatch", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
c.SendString("Hello, World!")
c.Request().Header.Set(HeaderIfNoneMatch, `"13-1831710635"`)
setETag(c, false)
utils.AssertEqual(t, 304, c.Response().StatusCode())
utils.AssertEqual(t, "", string(c.Response().Header.Peek(HeaderETag)))
utils.AssertEqual(t, "", string(c.Response().Body()))
})

t.Run("No HeaderIfNoneMatch", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
c.SendString("Hello, World!")
setETag(c, false)
utils.AssertEqual(t, `"13-1831710635"`, string(c.Response().Header.Peek(HeaderETag)))
})
}

// go test -v -run=Test_Utils_ -count=3
Expand Down Expand Up @@ -320,3 +359,49 @@ func Test_Utils_lnMetadata(t *testing.T) {
utils.AssertEqual(t, true, config != nil)
})
}

// go test -v -run=^$ -bench=Benchmark_SlashRecognition -benchmem -count=4
func Benchmark_SlashRecognition(b *testing.B) {
search := "wtf/1234"
var result bool
b.Run("indexBytes", func(b *testing.B) {
result = false
for i := 0; i < b.N; i++ {
if strings.IndexByte(search, slashDelimiter) != -1 {
result = true
}
}
utils.AssertEqual(b, true, result)
})
b.Run("forEach", func(b *testing.B) {
result = false
c := int32(slashDelimiter)
for i := 0; i < b.N; i++ {
for _, b := range search {
if b == c {
result = true
break
}
}
}
utils.AssertEqual(b, true, result)
})
b.Run("IndexRune", func(b *testing.B) {
result = false
c := int32(slashDelimiter)
for i := 0; i < b.N; i++ {
result = IndexRune(search, c)

}
utils.AssertEqual(b, true, result)
})
}

func IndexRune(str string, needle int32) bool {
for _, b := range str {
if b == needle {
return true
}
}
return false
}
33 changes: 33 additions & 0 deletions internal/storage/memory/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package memory

import "time"

// Config defines the config for storage.
type Config struct {
// Time before deleting expired keys
//
// Default is 10 * time.Second
GCInterval time.Duration
}

// ConfigDefault is the default config
var ConfigDefault = Config{
GCInterval: 10 * time.Second,
}

// configDefault is a helper function to set default values
func configDefault(config ...Config) Config {
// Return default config if nothing provided
if len(config) < 1 {
return ConfigDefault
}

// Override default config
cfg := config[0]

// Set default values
if int(cfg.GCInterval.Seconds()) <= 0 {
cfg.GCInterval = ConfigDefault.GCInterval
}
return cfg
}
122 changes: 122 additions & 0 deletions internal/storage/memory/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package memory

import (
"errors"
"sync"
"time"
)

// Storage interface that is implemented by storage providers
type Storage struct {
mux sync.RWMutex
db map[string]entry
gcInterval time.Duration
done chan struct{}
}

// Common storage errors
var ErrNotExist = errors.New("key does not exist")

type entry struct {
data []byte
expiry int64
}

// New creates a new memory storage
func New(config ...Config) *Storage {
// Set default config
cfg := configDefault(config...)

// Create storage
store := &Storage{
db: make(map[string]entry),
gcInterval: cfg.GCInterval,
done: make(chan struct{}),
}

// Start garbage collector
go store.gc()

return store
}

// Get value by key
func (s *Storage) Get(key string) ([]byte, error) {
if len(key) <= 0 {
return nil, ErrNotExist
}
s.mux.RLock()
v, ok := s.db[key]
s.mux.RUnlock()
if !ok || v.expiry != 0 && v.expiry <= time.Now().Unix() {
return nil, ErrNotExist
}

return v.data, nil
}

// Set key with value
// Set key with value
func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
// Ain't Nobody Got Time For That
if len(key) <= 0 || len(val) <= 0 {
return nil
}

var expire int64
if exp != 0 {
expire = time.Now().Add(exp).Unix()
}

s.mux.Lock()
s.db[key] = entry{val, expire}
s.mux.Unlock()
return nil
}

// Delete key by key
func (s *Storage) Delete(key string) error {
// Ain't Nobody Got Time For That
if len(key) <= 0 {
return nil
}
s.mux.Lock()
delete(s.db, key)
s.mux.Unlock()
return nil
}

// Reset all keys
func (s *Storage) Reset() error {
s.mux.Lock()
s.db = make(map[string]entry)
s.mux.Unlock()
return nil
}

// Close the memory storage
func (s *Storage) Close() error {
s.done <- struct{}{}
return nil
}

func (s *Storage) gc() {
ticker := time.NewTicker(s.gcInterval)
defer ticker.Stop()

for {
select {
case <-s.done:
return
case t := <-ticker.C:
now := t.Unix()
s.mux.Lock()
for id, v := range s.db {
if v.expiry != 0 && v.expiry < now {
delete(s.db, id)
}
}
s.mux.Unlock()
}
}
}
Loading

0 comments on commit f881582

Please sign in to comment.