From eb97910c3657499fcd9c195b80672dffef13adc0 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Sat, 18 Aug 2018 17:16:12 +0200 Subject: [PATCH 1/9] Added wrapper context for extend fasthttp.RequestCtx --- atreugo.go | 28 ++++++++++++++++++++-------- examples/simple_server/main.go | 16 +++++++--------- response.go | 24 ++++++++++++------------ types.go | 9 +++++++-- vars.go | 11 +++++++++++ 5 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 vars.go diff --git a/atreugo.go b/atreugo.go index 12f5e80..e4d5169 100644 --- a/atreugo.go +++ b/atreugo.go @@ -14,8 +14,6 @@ import ( "github.com/thehowl/fasthttprouter" ) -var allowedHTTPMethods = []string{"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"} - // New create a new instance of Atreugo Server func New(cfg *Config) *Atreugo { if cfg.LogLevel == "" { @@ -43,22 +41,36 @@ func New(cfg *Config) *Atreugo { return server } +func acquireRequestCtx(ctx *fasthttp.RequestCtx) *RequestCtx { + actx := requestCtxPool.Get().(*RequestCtx) + actx.RequestCtx = ctx + return actx +} + +func releaseRequestCtx(actx *RequestCtx) { + actx.RequestCtx = nil + requestCtxPool.Put(actx) +} + 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) + + 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) } } } diff --git a/examples/simple_server/main.go b/examples/simple_server/main.go index 30f02a5..de27f56 100644 --- a/examples/simple_server/main.go +++ b/examples/simple_server/main.go @@ -1,8 +1,6 @@ package main import ( - "errors" - "github.com/erikdubbelboer/fasthttp" "github.com/savsgio/atreugo" ) @@ -14,22 +12,22 @@ 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) { + return fasthttp.StatusBadRequest, nil } 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..3ee6f39 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,42 +20,42 @@ 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) @@ -68,7 +68,7 @@ func FileResponse(ctx *fasthttp.RequestCtx, fileName, filePath, mimeType string) } // 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/types.go b/types.go index 6694e39..d0e68c9 100644 --- a/types.go +++ b/types.go @@ -31,11 +31,16 @@ type Atreugo struct { cfg *Config } +// RequestCtx context for atreugo +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/vars.go b/vars.go new file mode 100644 index 0000000..b64dde3 --- /dev/null +++ b/vars.go @@ -0,0 +1,11 @@ +package atreugo + +import "sync" + +var allowedHTTPMethods = []string{"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"} + +var requestCtxPool = sync.Pool{ + New: func() interface{} { + return new(RequestCtx) + }, +} From efe2c1d345e58bc443ac385c8950634f51f2da6b Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 09:01:06 +0200 Subject: [PATCH 2/9] Test fixes and finished context --- atreugo.go | 23 +++++------- atreugo_test.go | 29 +++++++-------- context.go | 28 +++++++++++++++ response_test.go | 91 ++++++++++++++++++++++++++---------------------- types.go | 2 +- vars.go | 11 ------ 6 files changed, 102 insertions(+), 82 deletions(-) create mode 100644 context.go delete mode 100644 vars.go diff --git a/atreugo.go b/atreugo.go index e4d5169..a1b74f1 100644 --- a/atreugo.go +++ b/atreugo.go @@ -14,6 +14,8 @@ import ( "github.com/thehowl/fasthttprouter" ) +var allowedHTTPMethods = []string{"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"} + // New create a new instance of Atreugo Server func New(cfg *Config) *Atreugo { if cfg.LogLevel == "" { @@ -41,23 +43,14 @@ func New(cfg *Config) *Atreugo { return server } -func acquireRequestCtx(ctx *fasthttp.RequestCtx) *RequestCtx { - actx := requestCtxPool.Get().(*RequestCtx) - actx.RequestCtx = ctx - return actx -} - -func releaseRequestCtx(actx *RequestCtx) { - actx.RequestCtx = nil - requestCtxPool.Put(actx) -} - func (s *Atreugo) handler(viewFn View) fasthttp.RequestHandler { return func(ctx *fasthttp.RequestCtx) { actx := acquireRequestCtx(ctx) defer releaseRequestCtx(actx) - s.log.Debugf("%s %s", actx.Method(), actx.URI()) + if s.cfg.LogLevel == logger.DEBUG { + s.log.Debugf("%s %s", actx.Method(), actx.URI()) + } for _, middlewareFn := range s.middlewares { if statusCode, err := middlewareFn(actx); err != nil { @@ -90,12 +83,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) } diff --git a/atreugo_test.go b/atreugo_test.go index 96eccb4..6c2049b 100644 --- a/atreugo_test.go +++ b/atreugo_test.go @@ -78,12 +78,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 +98,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 +121,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 +144,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 }, @@ -306,7 +306,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 { @@ -538,13 +538,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/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 d0e68c9..02e8400 100644 --- a/types.go +++ b/types.go @@ -31,7 +31,7 @@ type Atreugo struct { cfg *Config } -// RequestCtx context for atreugo +// RequestCtx context wrapper for fasthttp.RequestCtx to adds extra funtionality type RequestCtx struct { *fasthttp.RequestCtx } diff --git a/vars.go b/vars.go deleted file mode 100644 index b64dde3..0000000 --- a/vars.go +++ /dev/null @@ -1,11 +0,0 @@ -package atreugo - -import "sync" - -var allowedHTTPMethods = []string{"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"} - -var requestCtxPool = sync.Pool{ - New: func() interface{} { - return new(RequestCtx) - }, -} From 2f5d0f4fddbf44b2ecb349a37351fc1335173500 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 09:39:09 +0200 Subject: [PATCH 3/9] Fix examples --- README.md | 23 ++++++++-------------- examples/jwt_auth_middleware_sever/main.go | 13 ++++++------ examples/simple_server/main.go | 5 ++++- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3cddfc1..bef17f5 100644 --- a/README.md +++ b/README.md @@ -45,38 +45,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/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 de27f56..c548f37 100644 --- a/examples/simple_server/main.go +++ b/examples/simple_server/main.go @@ -1,6 +1,8 @@ package main import ( + "errors" + "github.com/erikdubbelboer/fasthttp" "github.com/savsgio/atreugo" ) @@ -17,7 +19,8 @@ func main() { } fnMiddlewareTwo := func(ctx *atreugo.RequestCtx) (int, error) { - return fasthttp.StatusBadRequest, nil + // Disable this middleware if you don't want to see this error + return fasthttp.StatusBadRequest, errors.New("Error example") } server.UseMiddleware(fnMiddlewareOne, fnMiddlewareTwo) From e26b14eb413dfe50bca062e1e13e3e884e8f7b71 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 09:50:33 +0200 Subject: [PATCH 4/9] Remove unused util functions --- utils.go | 14 -------------- utils_test.go | 38 -------------------------------------- 2 files changed, 52 deletions(-) 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 From d8cf76dd5d9b71db754a3072d44680dafc951d74 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 10:03:04 +0200 Subject: [PATCH 5/9] Added tests of context and fixes --- context_test.go | 38 ++++++++++++++++++++++++++++++++++++++ response.go | 1 - 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 context_test.go 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/response.go b/response.go index 3ee6f39..a802a61 100644 --- a/response.go +++ b/response.go @@ -61,7 +61,6 @@ func (ctx *RequestCtx) FileResponse(fileName, filePath, mimeType string) error { buff.WriteString(fileName) ctx.Response.Header.Set("Content-Disposition", buff.String()) - ctx.SetStatusCode(fasthttp.StatusOK) ctx.SetContentType(mimeType) return nil From 0bbb282d8b038c6e233f40dd33a161af4110bea5 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 10:22:36 +0200 Subject: [PATCH 6/9] Update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index bef17f5..1bc32e1 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ Is based on [erikdubbelboer's fasthttp fork](https://github.com/erikdubbelboer/f - CertFile *(string)*: Path of cert.pem file - GracefulEnable *(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: From f58ebdf2a38acd59e191905fc0ab38fcaf8ff8e4 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 11:10:14 +0200 Subject: [PATCH 7/9] Added SetLogOutput and tests --- atreugo.go | 6 ++++ atreugo_test.go | 82 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/atreugo.go b/atreugo.go index a1b74f1..d6a8028 100644 --- a/atreugo.go +++ b/atreugo.go @@ -2,6 +2,7 @@ package atreugo import ( "fmt" + "io" "net" "os" "os/signal" @@ -141,6 +142,11 @@ 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) diff --git a/atreugo_test.go b/atreugo_test.go index 6c2049b..e4f7763 100644 --- a/atreugo_test.go +++ b/atreugo_test.go @@ -2,6 +2,7 @@ package atreugo import ( "bufio" + "bytes" "errors" "testing" "time" @@ -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 @@ -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") } } From 0a4a04846b8656ae25b8f362523b263712100474 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 11:14:25 +0200 Subject: [PATCH 8/9] Upgrade dependencies --- Gopkg.lock | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) 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 From c2c3d202b931cc25fe828c34de4c539e60615039 Mon Sep 17 00:00:00 2001 From: Sergio Andres Virviescas Santana Date: Mon, 20 Aug 2018 11:45:10 +0200 Subject: [PATCH 9/9] Rename GracefulEnable to GracefulShutdown --- README.md | 2 +- atreugo.go | 2 +- atreugo_test.go | 10 +++++----- types.go | 16 ++++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1bc32e1..822ec3f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ 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 diff --git a/atreugo.go b/atreugo.go index d6a8028..bae54a5 100644 --- a/atreugo.go +++ b/atreugo.go @@ -152,7 +152,7 @@ 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 e4f7763..084313b 100644 --- a/atreugo_test.go +++ b/atreugo_test.go @@ -540,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) diff --git a/types.go b/types.go index 02e8400..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