diff --git a/Gopkg.lock b/Gopkg.lock
index d65ab07..4c5440a 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -2,65 +2,89 @@
[[projects]]
+ digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
+ pruneopts = "UT"
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
branch = "master"
+ digest = "1:977911e40ac577da6af9b6b8c301f17b2406293a5dabf7907cc8fcd3b2d755ce"
name = "github.com/erikdubbelboer/fasthttp"
packages = [
".",
"fasthttputil",
"reuseport",
- "stackless"
+ "stackless",
]
- revision = "9d45869b466e1c7413609aa82b268fe84d17f3f5"
+ pruneopts = "UT"
+ revision = "16e0bf24d475f8dcd37b7241cf08b5a839d69c71"
[[projects]]
+ branch = "master"
+ digest = "1:cdbed6a7b574b13bbabcd8c36c5be56d6444789bab21fcd37d39655eab0b2141"
name = "github.com/klauspost/compress"
packages = [
"flate",
"gzip",
- "zlib"
+ "zlib",
]
- revision = "b939724e787a27c0005cabe3f78e7ed7987ac74f"
- version = "v1.4.0"
+ pruneopts = "UT"
+ revision = "b50017755d442260d792c34c7c43216d9ba7ffc7"
[[projects]]
branch = "master"
+ digest = "1:2d643962fac133904694fffa959bc3c5dcfdcee38c6f5ffdd99a3c93eb9c835c"
name = "github.com/klauspost/cpuid"
packages = ["."]
+ pruneopts = "UT"
revision = "e7e905edc00ea8827e58662220139109efea09db"
[[projects]]
+ digest = "1:0dbfe9f0e50a4c923870a165a770a3bb6843c745d8e81ee702ef2a8c8ebc8282"
name = "github.com/savsgio/go-logger"
packages = ["."]
+ pruneopts = "UT"
revision = "764afe23ced5bf5ef3f462417a939974b6facae3"
version = "v2.0.1"
[[projects]]
+ branch = "master"
+ digest = "1:1c7ddcb896dfc7b1124dc0c5a93135832f7991626c470a9d797fc3799422f40c"
name = "github.com/thehowl/fasthttprouter"
packages = ["."]
+ pruneopts = "UT"
revision = "0e77ecfde28ad422d670671c17415e1d15f0f5be"
- version = "v0.2.1"
[[projects]]
branch = "master"
+ digest = "1:c468422f334a6b46a19448ad59aaffdfc0a36b08fdcc1c749a0b29b6453d7e59"
name = "github.com/valyala/bytebufferpool"
packages = ["."]
+ pruneopts = "UT"
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
[[projects]]
branch = "master"
+ digest = "1:8507835fade46d48a94828b351e1f8fb83df1aa65ba1e67c9493b0a1b47abc73"
name = "github.com/valyala/tcplisten"
packages = ["."]
+ pruneopts = "UT"
revision = "ceec8f93295a060cdb565ec25e4ccf17941dbd55"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "4d51514d15d4f8c4c732e381e449510eb46cb494b0b542e728288ed35d9208e3"
+ input-imports = [
+ "github.com/dgrijalva/jwt-go",
+ "github.com/erikdubbelboer/fasthttp",
+ "github.com/erikdubbelboer/fasthttp/fasthttputil",
+ "github.com/erikdubbelboer/fasthttp/reuseport",
+ "github.com/savsgio/go-logger",
+ "github.com/thehowl/fasthttprouter",
+ "github.com/valyala/bytebufferpool",
+ ]
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/README.md b/README.md
index 3cddfc1..822ec3f 100644
--- a/README.md
+++ b/README.md
@@ -29,8 +29,11 @@ Is based on [erikdubbelboer's fasthttp fork](https://github.com/erikdubbelboer/f
- TLSEnable *(bool)*: Enable HTTPS
- CertKey *(string)*: Path of cert.key file
- CertFile *(string)*: Path of cert.pem file
-- GracefulEnable *(bool)*: Start server with graceful shutdown
+- GracefulShutdown *(bool)*: Start server with graceful shutdown
+## Note:
+`*atreugo.RequestCtx` is equal than `*fasthttp.RequestCtx`, but adding extra funtionality, so you can use
+the same functions of `*fasthttp.RequestCtx`. Don't worry :smile:
## Example:
@@ -45,38 +48,31 @@ import (
)
func main() {
- // Configuration for Atreugo server
config := &atreugo.Config{
Host: "0.0.0.0",
Port: 8000,
}
-
- // New instance of atreugo server with your config
server := atreugo.New(config)
- // Middlewares
- fnMiddlewareOne := func(ctx *fasthttp.RequestCtx) (int, error) {
+ fnMiddlewareOne := func(ctx *atreugo.RequestCtx) (int, error) {
return fasthttp.StatusOK, nil
}
- fnMiddlewareTwo := func(ctx *fasthttp.RequestCtx) (int, error) {
- return fasthttp.StatusBadRequest, errors.New("Error message")
+ fnMiddlewareTwo := func(ctx *atreugo.RequestCtx) (int, error) {
+ // Disable this middleware if you don't want to see this error
+ return fasthttp.StatusBadRequest, errors.New("Error example")
}
- // Register middlewares
server.UseMiddleware(fnMiddlewareOne, fnMiddlewareTwo)
-
- // Views
- server.Path("GET", "/", func(ctx *fasthttp.RequestCtx) error {
- return atreugo.HTTPResponse(ctx, []byte("
Atreugo Micro-Framework
"))
+ server.Path("GET", "/", func(ctx *atreugo.RequestCtx) error {
+ return ctx.HTTPResponse([]byte("Atreugo Micro-Framework
"))
})
- server.Path("GET", "/jsonPage", func(ctx *fasthttp.RequestCtx) error {
- return atreugo.JSONResponse(ctx, atreugo.JSON{"Atreugo": true})
+ server.Path("GET", "/jsonPage", func(ctx *atreugo.RequestCtx) error {
+ return ctx.JSONResponse(atreugo.JSON{"Atreugo": true})
})
- // Start server
err := server.ListenAndServe()
if err != nil {
panic(err)
diff --git a/atreugo.go b/atreugo.go
index 12f5e80..bae54a5 100644
--- a/atreugo.go
+++ b/atreugo.go
@@ -2,6 +2,7 @@ package atreugo
import (
"fmt"
+ "io"
"net"
"os"
"os/signal"
@@ -45,20 +46,25 @@ func New(cfg *Config) *Atreugo {
func (s *Atreugo) handler(viewFn View) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
- s.log.Debugf("%s %s", ctx.Method(), ctx.URI())
+ actx := acquireRequestCtx(ctx)
+ defer releaseRequestCtx(actx)
+
+ if s.cfg.LogLevel == logger.DEBUG {
+ s.log.Debugf("%s %s", actx.Method(), actx.URI())
+ }
for _, middlewareFn := range s.middlewares {
- if statusCode, err := middlewareFn(ctx); err != nil {
- s.log.Errorf("Msg: %v | RequestUri: %s", err, ctx.URI().String())
+ if statusCode, err := middlewareFn(actx); err != nil {
+ s.log.Errorf("Msg: %v | RequestUri: %s", err, actx.URI().String())
- ctx.Error(err.Error(), statusCode)
+ actx.Error(err.Error(), statusCode)
return
}
}
- if err := viewFn(ctx); err != nil {
+ if err := viewFn(actx); err != nil {
s.log.Error(err)
- ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
+ actx.Error(err.Error(), fasthttp.StatusInternalServerError)
}
}
}
@@ -78,12 +84,12 @@ func (s *Atreugo) getListener(addr string) net.Listener {
}
func (s *Atreugo) serve(ln net.Listener) error {
- protocol := "http"
+ schema := "http"
if s.cfg.TLSEnable {
- protocol = "https"
+ schema = "https"
}
- s.log.Infof("Listening on: %s://%s/", protocol, ln.Addr().String())
+ s.log.Infof("Listening on: %s://%s/", schema, ln.Addr().String())
if s.cfg.TLSEnable {
return s.server.ServeTLS(ln, s.cfg.CertFile, s.cfg.CertKey)
}
@@ -136,12 +142,17 @@ func (s *Atreugo) UseMiddleware(fns ...Middleware) {
s.middlewares = append(s.middlewares, fns...)
}
+// SetLogOutput set log output of server
+func (s *Atreugo) SetLogOutput(output io.Writer) {
+ s.log.SetOutput(output)
+}
+
// ListenAndServe start Atreugo server according to the configuration
func (s *Atreugo) ListenAndServe() error {
addr := fmt.Sprintf("%s:%d", s.cfg.Host, s.cfg.Port)
ln := s.getListener(addr)
- if s.cfg.GracefulEnable {
+ if s.cfg.GracefulShutdown {
return s.serveGracefully(ln)
}
diff --git a/atreugo_test.go b/atreugo_test.go
index 96eccb4..084313b 100644
--- a/atreugo_test.go
+++ b/atreugo_test.go
@@ -2,6 +2,7 @@ package atreugo
import (
"bufio"
+ "bytes"
"errors"
"testing"
"time"
@@ -78,12 +79,12 @@ func TestAtreugoServer(t *testing.T) {
{
name: "AllOk",
args: args{
- viewFn: func(ctx *fasthttp.RequestCtx) error {
+ viewFn: func(ctx *RequestCtx) error {
viewCalled = true
return nil
},
middlewareFns: []Middleware{
- func(ctx *fasthttp.RequestCtx) (int, error) {
+ func(ctx *RequestCtx) (int, error) {
middleWareCounter++
return 0, nil
},
@@ -98,15 +99,15 @@ func TestAtreugoServer(t *testing.T) {
{
name: "FirstMiddlewareError",
args: args{
- viewFn: func(ctx *fasthttp.RequestCtx) error {
+ viewFn: func(ctx *RequestCtx) error {
viewCalled = true
return nil
},
middlewareFns: []Middleware{
- func(ctx *fasthttp.RequestCtx) (int, error) {
+ func(ctx *RequestCtx) (int, error) {
return 403, errors.New("Bad request")
},
- func(ctx *fasthttp.RequestCtx) (int, error) {
+ func(ctx *RequestCtx) (int, error) {
middleWareCounter++
return 0, nil
},
@@ -121,16 +122,16 @@ func TestAtreugoServer(t *testing.T) {
{
name: "SecondMiddlewareError",
args: args{
- viewFn: func(ctx *fasthttp.RequestCtx) error {
+ viewFn: func(ctx *RequestCtx) error {
viewCalled = true
return nil
},
middlewareFns: []Middleware{
- func(ctx *fasthttp.RequestCtx) (int, error) {
+ func(ctx *RequestCtx) (int, error) {
middleWareCounter++
return 0, nil
},
- func(ctx *fasthttp.RequestCtx) (int, error) {
+ func(ctx *RequestCtx) (int, error) {
return 403, errors.New("Bad request")
},
},
@@ -144,12 +145,12 @@ func TestAtreugoServer(t *testing.T) {
{
name: "ViewError",
args: args{
- viewFn: func(ctx *fasthttp.RequestCtx) error {
+ viewFn: func(ctx *RequestCtx) error {
viewCalled = true
return errors.New("Fake error")
},
middlewareFns: []Middleware{
- func(ctx *fasthttp.RequestCtx) (int, error) {
+ func(ctx *RequestCtx) (int, error) {
middleWareCounter++
return 0, nil
},
@@ -297,6 +298,38 @@ func TestAtreugo_getListener(t *testing.T) {
}
}
+func TestAtreugo_Static(t *testing.T) {
+ type args struct {
+ path string
+ }
+ type want struct {
+ getPanic bool
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want want
+ }{
+ {
+ name: "Ok",
+ args: args{
+ path: "/tmp",
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ s := New(testAtreugoConfig)
+ s.Static(tt.args.path)
+
+ if s.router.NotFound == nil {
+ t.Error("Static files not configure")
+ }
+ })
+ }
+}
+
func TestAtreugo_Path(t *testing.T) {
type args struct {
method string
@@ -306,7 +339,7 @@ func TestAtreugo_Path(t *testing.T) {
type want struct {
getPanic bool
}
- testViewFn := func(ctx *fasthttp.RequestCtx) error {
+ testViewFn := func(ctx *RequestCtx) error {
return nil
}
tests := []struct {
@@ -421,35 +454,34 @@ func TestAtreugo_Path(t *testing.T) {
}
}
-func TestAtreugo_Static(t *testing.T) {
- type args struct {
- path string
- }
- type want struct {
- getPanic bool
+func TestAtreugo_UseMiddleware(t *testing.T) {
+ middlewareFns := []Middleware{
+ func(ctx *RequestCtx) (int, error) {
+ return 403, errors.New("Bad request")
+ },
+ func(ctx *RequestCtx) (int, error) {
+ return 0, nil
+ },
}
- tests := []struct {
- name string
- args args
- want want
- }{
- {
- name: "Ok",
- args: args{
- path: "/tmp",
- },
- },
+ s := New(testAtreugoConfig)
+ s.UseMiddleware(middlewareFns...)
+
+ if len(s.middlewares) != len(middlewareFns) {
+ t.Errorf("Middlewares are not registered")
}
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- s := New(testAtreugoConfig)
- s.Static(tt.args.path)
- if s.router.NotFound == nil {
- t.Error("Static files not configure")
- }
- })
+}
+
+func TestAtreugo_SetLogOutput(t *testing.T) {
+ s := New(&Config{LogLevel: "info"})
+ output := new(bytes.Buffer)
+
+ s.SetLogOutput(output)
+ s.log.Info("Test")
+
+ if len(output.Bytes()) <= 0 {
+ t.Error("SetLogOutput() log output was not changed")
}
}
@@ -508,11 +540,11 @@ func TestAtreugo_ListenAndServe(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := New(&Config{
- Host: "localhost",
- Port: 8000,
- LogLevel: "error",
- TLSEnable: tt.args.tlsEnable,
- GracefulEnable: tt.args.graceful,
+ Host: "localhost",
+ Port: 8000,
+ LogLevel: "error",
+ TLSEnable: tt.args.tlsEnable,
+ GracefulShutdown: tt.args.graceful,
})
serverCh := make(chan error, 1)
@@ -538,13 +570,14 @@ func TestAtreugo_ListenAndServe(t *testing.T) {
// Benchmarks
func Benchmark_handler(b *testing.B) {
s := New(testAtreugoConfig)
- viewFn := func(ctx *fasthttp.RequestCtx) error {
- return TextResponse(ctx, nil)
+ viewFn := func(ctx *RequestCtx) error {
+ return nil
}
ctx := new(fasthttp.RequestCtx)
+ h := s.handler(viewFn)
b.ResetTimer()
for i := 0; i <= b.N; i++ {
- s.handler(viewFn)(ctx)
+ h(ctx)
}
}
diff --git a/context.go b/context.go
new file mode 100644
index 0000000..54e2e2f
--- /dev/null
+++ b/context.go
@@ -0,0 +1,28 @@
+package atreugo
+
+import (
+ "sync"
+
+ "github.com/erikdubbelboer/fasthttp"
+)
+
+var requestCtxPool = sync.Pool{
+ New: func() interface{} {
+ return new(RequestCtx)
+ },
+}
+
+func (ctx *RequestCtx) reset() {
+ ctx.RequestCtx = nil
+}
+
+func acquireRequestCtx(ctx *fasthttp.RequestCtx) *RequestCtx {
+ actx := requestCtxPool.Get().(*RequestCtx)
+ actx.RequestCtx = ctx
+ return actx
+}
+
+func releaseRequestCtx(actx *RequestCtx) {
+ actx.reset()
+ requestCtxPool.Put(actx)
+}
diff --git a/context_test.go b/context_test.go
new file mode 100644
index 0000000..149a7b9
--- /dev/null
+++ b/context_test.go
@@ -0,0 +1,38 @@
+package atreugo
+
+import (
+ "testing"
+
+ "github.com/erikdubbelboer/fasthttp"
+)
+
+func TestRequestCtx_reset(t *testing.T) {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ actx.reset()
+
+ if actx.RequestCtx != nil {
+ t.Errorf("reset() *fasthttp.RequestCtx = %p, want %v", actx.RequestCtx, nil)
+ }
+}
+
+func Test_acquireRequestCtx(t *testing.T) {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ if actx.RequestCtx != ctx {
+ t.Errorf("acquireRequestCtx() = %p, want %p", actx.RequestCtx, ctx)
+ }
+}
+
+func Test_releaseRequestCtx(t *testing.T) {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ releaseRequestCtx(actx)
+
+ if actx.RequestCtx != nil {
+ t.Errorf("releaseRequestCtx() *fasthttp.RequestCtx = %p, want %v", actx.RequestCtx, nil)
+ }
+}
diff --git a/examples/jwt_auth_middleware_sever/main.go b/examples/jwt_auth_middleware_sever/main.go
index 700b602..5a6bf6a 100644
--- a/examples/jwt_auth_middleware_sever/main.go
+++ b/examples/jwt_auth_middleware_sever/main.go
@@ -58,7 +58,7 @@ func validateToken(requestToken string) (*jwt.Token, *userCredential, error) {
}
// checkTokenMiddleware middleware to check jwt token authorization
-func checkTokenMiddleware(ctx *fasthttp.RequestCtx) (int, error) {
+func checkTokenMiddleware(ctx *atreugo.RequestCtx) (int, error) {
// Avoid middleware when you are going to login view
if string(ctx.Path()) == "/login" {
return fasthttp.StatusOK, nil
@@ -88,13 +88,12 @@ func main() {
server.UseMiddleware(checkTokenMiddleware)
- server.Path("GET", "/", func(ctx *fasthttp.RequestCtx) error {
- return atreugo.HTTPResponse(ctx,
- []byte(fmt.Sprintf(`You are login with JWT
+ server.Path("GET", "/", func(ctx *atreugo.RequestCtx) error {
+ return ctx.HTTPResponse([]byte(fmt.Sprintf(`You are login with JWT
JWT cookie value: %s`, ctx.Request.Header.Cookie("atreugo_jwt"))))
})
- server.Path("GET", "/login", func(ctx *fasthttp.RequestCtx) error {
+ server.Path("GET", "/login", func(ctx *atreugo.RequestCtx) error {
qUser := []byte("savsgio")
qPasswd := []byte("mypasswd")
@@ -105,13 +104,15 @@ func main() {
// Set cookie for domain
cookie := fasthttp.AcquireCookie()
+ defer fasthttp.ReleaseCookie(cookie)
+
cookie.SetKey("atreugo_jwt")
cookie.SetValue(tokenString)
cookie.SetExpire(expireAt)
ctx.Response.Header.SetCookie(cookie)
}
- return atreugo.RedirectResponse(ctx, "/", ctx.Response.StatusCode())
+ return ctx.RedirectResponse("/", ctx.Response.StatusCode())
})
err := server.ListenAndServe()
diff --git a/examples/simple_server/main.go b/examples/simple_server/main.go
index 30f02a5..c548f37 100644
--- a/examples/simple_server/main.go
+++ b/examples/simple_server/main.go
@@ -14,22 +14,23 @@ func main() {
}
server := atreugo.New(config)
- fnMiddlewareOne := func(ctx *fasthttp.RequestCtx) (int, error) {
+ fnMiddlewareOne := func(ctx *atreugo.RequestCtx) (int, error) {
return fasthttp.StatusOK, nil
}
- fnMiddlewareTwo := func(ctx *fasthttp.RequestCtx) (int, error) {
- return fasthttp.StatusBadRequest, errors.New("Error message")
+ fnMiddlewareTwo := func(ctx *atreugo.RequestCtx) (int, error) {
+ // Disable this middleware if you don't want to see this error
+ return fasthttp.StatusBadRequest, errors.New("Error example")
}
server.UseMiddleware(fnMiddlewareOne, fnMiddlewareTwo)
- server.Path("GET", "/", func(ctx *fasthttp.RequestCtx) error {
- return atreugo.HTTPResponse(ctx, []byte("Atreugo Micro-Framework
"))
+ server.Path("GET", "/", func(ctx *atreugo.RequestCtx) error {
+ return ctx.HTTPResponse([]byte("Atreugo Micro-Framework
"))
})
- server.Path("GET", "/jsonPage", func(ctx *fasthttp.RequestCtx) error {
- return atreugo.JSONResponse(ctx, atreugo.JSON{"Atreugo": true})
+ server.Path("GET", "/jsonPage", func(ctx *atreugo.RequestCtx) error {
+ return ctx.JSONResponse(atreugo.JSON{"Atreugo": true})
})
err := server.ListenAndServe()
diff --git a/response.go b/response.go
index 6ddd0dc..a802a61 100644
--- a/response.go
+++ b/response.go
@@ -7,7 +7,7 @@ import (
"github.com/valyala/bytebufferpool"
)
-func newResponse(ctx *fasthttp.RequestCtx, contentType string, statusCode ...int) {
+func (ctx *RequestCtx) newResponse(contentType string, statusCode ...int) {
ctx.SetContentType(contentType)
if len(statusCode) > 0 {
@@ -20,55 +20,54 @@ func newResponse(ctx *fasthttp.RequestCtx, contentType string, statusCode ...int
}
// JSONResponse return response with body in json format
-func JSONResponse(ctx *fasthttp.RequestCtx, body interface{}, statusCode ...int) error {
- newResponse(ctx, "application/json", statusCode...)
+func (ctx *RequestCtx) JSONResponse(body interface{}, statusCode ...int) error {
+ ctx.newResponse("application/json", statusCode...)
return json.NewEncoder(ctx).Encode(body)
}
// HTTPResponse return response with body in html format
-func HTTPResponse(ctx *fasthttp.RequestCtx, body []byte, statusCode ...int) error {
- newResponse(ctx, "text/html; charset=utf-8", statusCode...)
+func (ctx *RequestCtx) HTTPResponse(body []byte, statusCode ...int) error {
+ ctx.newResponse("text/html; charset=utf-8", statusCode...)
_, err := ctx.Write(body)
return err
}
// TextResponse return response with body in text format
-func TextResponse(ctx *fasthttp.RequestCtx, body []byte, statusCode ...int) error {
- newResponse(ctx, "text/plain; charset=utf-8", statusCode...)
+func (ctx *RequestCtx) TextResponse(body []byte, statusCode ...int) error {
+ ctx.newResponse("text/plain; charset=utf-8", statusCode...)
_, err := ctx.Write(body)
return err
}
// RawResponse returns response without encoding the body.
-func RawResponse(ctx *fasthttp.RequestCtx, body []byte, statusCode ...int) error {
- newResponse(ctx, "application/octet-stream", statusCode...)
+func (ctx *RequestCtx) RawResponse(body []byte, statusCode ...int) error {
+ ctx.newResponse("application/octet-stream", statusCode...)
_, err := ctx.Write(body)
return err
}
// FileResponse return a streaming response with file data.
-func FileResponse(ctx *fasthttp.RequestCtx, fileName, filePath, mimeType string) error {
+func (ctx *RequestCtx) FileResponse(fileName, filePath, mimeType string) error {
buff := bytebufferpool.Get()
defer bytebufferpool.Put(buff)
- fasthttp.ServeFile(ctx, filePath)
+ fasthttp.ServeFile(ctx.RequestCtx, filePath)
buff.SetString("attachment; filename=")
buff.WriteString(fileName)
ctx.Response.Header.Set("Content-Disposition", buff.String())
- ctx.SetStatusCode(fasthttp.StatusOK)
ctx.SetContentType(mimeType)
return nil
}
// RedirectResponse redirect request to an especific url
-func RedirectResponse(ctx *fasthttp.RequestCtx, url string, statusCode int) error {
+func (ctx *RequestCtx) RedirectResponse(url string, statusCode int) error {
ctx.ResetBody()
ctx.Redirect(url, statusCode)
diff --git a/response_test.go b/response_test.go
index da97d43..ce389fb 100644
--- a/response_test.go
+++ b/response_test.go
@@ -11,7 +11,6 @@ import (
func Test_newResponse(t *testing.T) {
type args struct {
- ctx *fasthttp.RequestCtx
contentType string
statusCode []int
}
@@ -27,7 +26,6 @@ func Test_newResponse(t *testing.T) {
{
name: "WithStatusCode",
args: args{
- ctx: new(fasthttp.RequestCtx),
contentType: "text/plain",
statusCode: []int{301},
},
@@ -39,7 +37,6 @@ func Test_newResponse(t *testing.T) {
{
name: "WithOutStatusCode",
args: args{
- ctx: new(fasthttp.RequestCtx),
contentType: "text/plain",
statusCode: make([]int, 0),
},
@@ -52,14 +49,17 @@ func Test_newResponse(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- newResponse(tt.args.ctx, tt.args.contentType, tt.args.statusCode...)
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ actx.newResponse(tt.args.contentType, tt.args.statusCode...)
- responseStatusCode := tt.args.ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
- responseContentType := string(tt.args.ctx.Response.Header.ContentType())
+ responseContentType := string(actx.Response.Header.ContentType())
if responseContentType != tt.want.contentType {
t.Errorf("content-type: '%v', want: '%v'", responseContentType, tt.want.contentType)
}
@@ -69,7 +69,6 @@ func Test_newResponse(t *testing.T) {
func TestJSONResponse(t *testing.T) {
type args struct {
- ctx *fasthttp.RequestCtx
body interface{}
statusCode int
contentType string
@@ -87,7 +86,6 @@ func TestJSONResponse(t *testing.T) {
{
name: "Test",
args: args{
- ctx: new(fasthttp.RequestCtx),
body: JSON{"test": true},
statusCode: 200,
},
@@ -101,21 +99,24 @@ func TestJSONResponse(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := JSONResponse(tt.args.ctx, tt.args.body, tt.args.statusCode); err != nil {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ if err := actx.JSONResponse(tt.args.body, tt.args.statusCode); err != nil {
t.Errorf("JSONResponse() error: %v", err)
}
- responseBody := string(bytes.TrimSpace(tt.args.ctx.Response.Body()))
+ responseBody := string(bytes.TrimSpace(actx.Response.Body()))
if responseBody != tt.want.body {
t.Errorf("body: '%v', want: '%v'", responseBody, tt.want.body)
}
- responseStatusCode := tt.args.ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
- responseContentType := string(tt.args.ctx.Response.Header.ContentType())
+ responseContentType := string(actx.Response.Header.ContentType())
if responseContentType != tt.want.contentType {
t.Errorf("content-type: '%v', want: '%v'", responseContentType, tt.want.contentType)
}
@@ -125,7 +126,6 @@ func TestJSONResponse(t *testing.T) {
func TestHTTPResponse(t *testing.T) {
type args struct {
- ctx *fasthttp.RequestCtx
body []byte
statusCode int
contentType string
@@ -143,7 +143,6 @@ func TestHTTPResponse(t *testing.T) {
{
name: "Test",
args: args{
- ctx: new(fasthttp.RequestCtx),
body: []byte("Test
"),
statusCode: 200,
},
@@ -157,21 +156,24 @@ func TestHTTPResponse(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := HTTPResponse(tt.args.ctx, tt.args.body, tt.args.statusCode); err != nil {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ if err := actx.HTTPResponse(tt.args.body, tt.args.statusCode); err != nil {
t.Errorf("HTTPResponse() error: %v", err)
}
- responseBody := string(bytes.TrimSpace(tt.args.ctx.Response.Body()))
+ responseBody := string(bytes.TrimSpace(actx.Response.Body()))
if responseBody != tt.want.body {
t.Errorf("body: '%v', want: '%v'", responseBody, tt.want.body)
}
- responseStatusCode := tt.args.ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
- responseContentType := string(tt.args.ctx.Response.Header.ContentType())
+ responseContentType := string(actx.Response.Header.ContentType())
if responseContentType != tt.want.contentType {
t.Errorf("content-type: '%v', want: '%v'", responseContentType, tt.want.contentType)
}
@@ -181,7 +183,6 @@ func TestHTTPResponse(t *testing.T) {
func TestTextResponse(t *testing.T) {
type args struct {
- ctx *fasthttp.RequestCtx
body []byte
statusCode int
contentType string
@@ -199,7 +200,6 @@ func TestTextResponse(t *testing.T) {
{
name: "Test",
args: args{
- ctx: new(fasthttp.RequestCtx),
body: []byte("Test
"),
statusCode: 200,
},
@@ -213,21 +213,24 @@ func TestTextResponse(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := TextResponse(tt.args.ctx, tt.args.body, tt.args.statusCode); err != nil {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ if err := actx.TextResponse(tt.args.body, tt.args.statusCode); err != nil {
t.Errorf("TextResponse() error: %v", err)
}
- responseBody := string(bytes.TrimSpace(tt.args.ctx.Response.Body()))
+ responseBody := string(bytes.TrimSpace(actx.Response.Body()))
if responseBody != tt.want.body {
t.Errorf("body: '%v', want: '%v'", responseBody, tt.want.body)
}
- responseStatusCode := tt.args.ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
- responseContentType := string(tt.args.ctx.Response.Header.ContentType())
+ responseContentType := string(actx.Response.Header.ContentType())
if responseContentType != tt.want.contentType {
t.Errorf("content-type: '%v', want: '%v'", responseContentType, tt.want.contentType)
}
@@ -237,7 +240,6 @@ func TestTextResponse(t *testing.T) {
func TestRawResponse(t *testing.T) {
type args struct {
- ctx *fasthttp.RequestCtx
body []byte
statusCode int
contentType string
@@ -255,7 +257,6 @@ func TestRawResponse(t *testing.T) {
{
name: "Test",
args: args{
- ctx: new(fasthttp.RequestCtx),
body: []byte("Test
"),
statusCode: 200,
},
@@ -269,21 +270,24 @@ func TestRawResponse(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := RawResponse(tt.args.ctx, tt.args.body, tt.args.statusCode); err != nil {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ if err := actx.RawResponse(tt.args.body, tt.args.statusCode); err != nil {
t.Errorf("RawResponse() error: %v", err)
}
- responseBody := string(bytes.TrimSpace(tt.args.ctx.Response.Body()))
+ responseBody := string(bytes.TrimSpace(actx.Response.Body()))
if responseBody != tt.want.body {
t.Errorf("body: '%v', want: '%v'", responseBody, tt.want.body)
}
- responseStatusCode := tt.args.ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
- responseContentType := string(tt.args.ctx.Response.Header.ContentType())
+ responseContentType := string(actx.Response.Header.ContentType())
if responseContentType != tt.want.contentType {
t.Errorf("content-type: '%v', want: '%v'", responseContentType, tt.want.contentType)
}
@@ -332,25 +336,26 @@ func TestFileResponse(t *testing.T) {
defer os.Remove(tt.args.filePath)
ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
- FileResponse(ctx, tt.args.fileName, tt.args.filePath, tt.args.mimeType)
+ actx.FileResponse(tt.args.fileName, tt.args.filePath, tt.args.mimeType)
- responseBody := string(bytes.TrimSpace(ctx.Response.Body()))
+ responseBody := string(bytes.TrimSpace(actx.Response.Body()))
if responseBody != tt.want.body {
t.Errorf("body: '%v', want: '%v'", responseBody, tt.want.body)
}
- responseStatusCode := ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
- responseContentType := string(ctx.Response.Header.ContentType())
+ responseContentType := string(actx.Response.Header.ContentType())
if responseContentType != tt.want.contentType {
t.Errorf("Header content-type: '%v', want: '%v'", responseContentType, tt.want.contentType)
}
- responseContentDisposition := string(ctx.Response.Header.Peek("Content-Disposition"))
+ responseContentDisposition := string(actx.Response.Header.Peek("Content-Disposition"))
if responseContentDisposition != tt.want.contentDisposition {
t.Errorf("Header content-disposition: '%v', want: '%v'", responseContentDisposition, tt.want.contentDisposition)
}
@@ -360,7 +365,6 @@ func TestFileResponse(t *testing.T) {
func TestRedirectResponse(t *testing.T) {
type args struct {
- ctx *fasthttp.RequestCtx
url string
statusCode int
}
@@ -376,7 +380,6 @@ func TestRedirectResponse(t *testing.T) {
{
name: "Test",
args: args{
- ctx: new(fasthttp.RequestCtx),
url: "http://urltoredirect.es",
statusCode: 301,
},
@@ -388,16 +391,19 @@ func TestRedirectResponse(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := RedirectResponse(tt.args.ctx, tt.args.url, tt.args.statusCode); err != nil {
+ ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+
+ if err := actx.RedirectResponse(tt.args.url, tt.args.statusCode); err != nil {
t.Errorf("RedirectResponse() error: %v", err)
}
- responseLocation := string(tt.args.ctx.Response.Header.Peek("Location"))
+ responseLocation := string(actx.Response.Header.Peek("Location"))
if responseLocation != tt.want.locationURL {
t.Errorf("Header content-disposition: '%v', want: '%v'", responseLocation, tt.want.locationURL)
}
- responseStatusCode := tt.args.ctx.Response.StatusCode()
+ responseStatusCode := actx.Response.StatusCode()
if responseStatusCode != tt.want.statusCode {
t.Errorf("status_code: '%v', want: '%v'", responseStatusCode, tt.want.statusCode)
}
@@ -409,9 +415,12 @@ func TestRedirectResponse(t *testing.T) {
func Benchmark_FileResponse(b *testing.B) {
cwd, _ := os.Getwd()
path := cwd + "/LICENSE"
+
ctx := new(fasthttp.RequestCtx)
+ actx := acquireRequestCtx(ctx)
+ b.ResetTimer()
for i := 0; i <= b.N; i++ {
- FileResponse(ctx, "hola", path, "text/plain")
+ actx.FileResponse("hola", path, "text/plain")
}
}
diff --git a/types.go b/types.go
index 6694e39..3d23eb7 100644
--- a/types.go
+++ b/types.go
@@ -12,14 +12,14 @@ import (
// Config config for Atreugo
type Config struct {
- Host string
- Port int
- LogLevel string
- Compress bool
- TLSEnable bool
- CertKey string
- CertFile string
- GracefulEnable bool
+ Host string
+ Port int
+ LogLevel string
+ Compress bool
+ TLSEnable bool
+ CertKey string
+ CertFile string
+ GracefulShutdown bool
}
// Atreugo struct for make up a server
@@ -31,11 +31,16 @@ type Atreugo struct {
cfg *Config
}
+// RequestCtx context wrapper for fasthttp.RequestCtx to adds extra funtionality
+type RequestCtx struct {
+ *fasthttp.RequestCtx
+}
+
// View must process incoming requests.
-type View func(ctx *fasthttp.RequestCtx) error
+type View func(ctx *RequestCtx) error
// Middleware must process all incoming requests before defined views.
-type Middleware func(ctx *fasthttp.RequestCtx) (int, error)
+type Middleware func(ctx *RequestCtx) (int, error)
// JSON is a map whose key is a string and whose value an interface
type JSON map[string]interface{}
diff --git a/utils.go b/utils.go
index da781e3..cb3442d 100644
--- a/utils.go
+++ b/utils.go
@@ -1,25 +1,11 @@
package atreugo
-import (
- "unsafe"
-)
-
func panicOnError(err error) {
if err != nil {
panic(err)
}
}
-// b2s convert bytes array to string without memory allocation (non safe)
-func b2s(b []byte) string {
- return *(*string)(unsafe.Pointer(&b))
-}
-
-// int64ToInt convert int64 to int without memory allocation (non safe)
-func int64ToInt(i int64) int {
- return *(*int)(unsafe.Pointer(&i))
-}
-
// index returns the first index of the target string `t`, or
// -1 if no match is found.
func indexOf(vs []string, t string) int {
diff --git a/utils_test.go b/utils_test.go
index 64ac010..e6f0df8 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -46,44 +46,6 @@ func Test_panicOnError(t *testing.T) {
}
}
-func Test_b2s(t *testing.T) {
- type args struct {
- b []byte
- }
- tests := struct {
- args args
- want string
- }{
- args: args{
- b: []byte("Test"),
- },
- want: "Test",
- }
-
- if got := b2s(tests.args.b); got != tests.want {
- t.Errorf("b2s(): '%v', want: '%v'", got, tests.want)
- }
-}
-
-func Test_int64ToInt(t *testing.T) {
- type args struct {
- i int64
- }
- tests := struct {
- args args
- want int
- }{
- args: args{
- i: int64(3),
- },
- want: 3,
- }
-
- if got := int64ToInt(tests.args.i); got != tests.want {
- t.Errorf("int64ToInt: '%v', want: '%v'", got, tests.want)
- }
-}
-
func Test_indexOf(t *testing.T) {
type args struct {
vs []string