Skip to content

Commit

Permalink
Merge pull request #8 from savsgio/develop
Browse files Browse the repository at this point in the history
- Added more types of response
- Refactor of project structure
- Fixes and improvements
  • Loading branch information
Sergio Andrés Virviescas Santana authored Jul 18, 2018
2 parents f227e45 + 7d7b20a commit b8064b1
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 59 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Atreugo
[![Go Report Card](https://goreportcard.com/badge/github.com/savsgio/atreugo)](https://goreportcard.com/report/github.com/savsgio/atreugo)
[![GoDoc](https://godoc.org/github.com/savsgio/atreugo?status.svg)](https://godoc.org/github.com/savsgio/atreugo)

Micro-framework to make simple the use of routing and middlewares of fasthttp.
Micro-framework to make simple the use of routing and middlewares in fasthttp.

Is based on [erikdubbelboer's fasthttp fork](https://github.com/erikdubbelboer/fasthttp) that it more active than [valyala's fasthttp](https://github.com/valyala/fasthttp)

Expand Down Expand Up @@ -67,11 +67,11 @@ func main() {

// Views
server.Path("GET", "/", func(ctx *fasthttp.RequestCtx) error {
return atreugo.HttpResponse(ctx, []byte("<h1>Atreugo Micro-Framework</h1>"))
return atreugo.HTTPResponse(ctx, []byte("<h1>Atreugo Micro-Framework</h1>"))
})

server.Path("GET", "/jsonPage", func(ctx *fasthttp.RequestCtx) error {
return atreugo.JsonResponse(ctx, atreugo.Json{"Atreugo": true})
return atreugo.JSONResponse(ctx, atreugo.JSON{"Atreugo": true})
})

// Start server
Expand Down
42 changes: 7 additions & 35 deletions atreugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,7 @@ import (
"github.com/thehowl/fasthttprouter"
)

// Config config for Atreugo
type Config struct {
Host string
Port int
LogLevel string
Compress bool
TLSEnable bool
CertKey string
CertFile string
GracefulEnable bool
}

// Atreugo struct for make up a server
type Atreugo struct {
server *fasthttp.Server
router *fasthttprouter.Router
middlewares []Middleware
log *logger.Logger
cfg *Config
}

// View must process incoming requests.
type View func(ctx *fasthttp.RequestCtx) error

// Middleware must process all incoming requests before defined views.
type Middleware func(ctx *fasthttp.RequestCtx) (int, error)

// AllowedHTTPMethods all http methods that Atruego supports
var AllowedHTTPMethods = []string{"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"}
var allowedHTTPMethods = []string{"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"}

// New create a new instance of Atreugo Server
func New(cfg *Config) *Atreugo {
Expand Down Expand Up @@ -71,32 +43,32 @@ func New(cfg *Config) *Atreugo {
return server
}

func (s *Atreugo) viewHandler(viewFn View) fasthttp.RequestHandler {
func (s *Atreugo) handler(viewFn View) fasthttp.RequestHandler {
return fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) {
s.log.Debugf("%s %s", ctx.Method(), ctx.URI())

for _, middlewareFn := range s.middlewares {
if statusCode, err := middlewareFn(ctx); err != nil {
s.log.Errorf("Msg: %v | RequestUri: %s", err, ctx.URI().String())

JSONResponse(ctx, JSON{"Error": err.Error()}, statusCode)
ctx.Error(err.Error(), statusCode)
return
}
}

if err := viewFn(ctx); err != nil {
s.log.Error(err)
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
}
})
}

func (s *Atreugo) getListener(addr string) net.Listener {
network := "tcp4"
ln, err := reuseport.Listen(network, addr)
if err == nil {
return ln
}
s.log.Errorf("Error in reuseport listener %s", err)
s.log.Warningf("Error in reuseport listener %s", err)

s.log.Infof("Trying with net listener")
ln, err = net.Listen(network, addr)
Expand Down Expand Up @@ -149,11 +121,11 @@ func (s *Atreugo) Static(rootStaticDirPath string) {

// Path add the views to serve
func (s *Atreugo) Path(httpMethod string, url string, viewFn View) {
if !include(AllowedHTTPMethods, httpMethod) {
if !include(allowedHTTPMethods, httpMethod) {
panic("Invalid http method '" + httpMethod + "' for the url " + url)
}

s.router.Handle(httpMethod, url, s.viewHandler(viewFn))
s.router.Handle(httpMethod, url, s.handler(viewFn))
}

// UseMiddleware register middleware functions that viewHandler will use
Expand Down
3 changes: 3 additions & 0 deletions consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package atreugo

const network = "tcp4"
4 changes: 2 additions & 2 deletions examples/jwt_auth_middleware_sever/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func validateToken(requestToken string) (*jwt.Token, *userCredential, error) {
// checkTokenMiddleware middleware to check jwt token authoritation
func checkTokenMiddleware(ctx *fasthttp.RequestCtx) (int, error) {
// Avoid middleware when you are going to login view
if atreugo.B2S(ctx.Path()) == "/login" {
if string(ctx.Path()) == "/login" {
return fasthttp.StatusOK, nil
}

Expand All @@ -70,7 +70,7 @@ func checkTokenMiddleware(ctx *fasthttp.RequestCtx) (int, error) {
return fasthttp.StatusForbidden, errors.New("login required")
}

token, _, err := validateToken(atreugo.B2S(jwtCookie))
token, _, err := validateToken(string(jwtCookie))

if !token.Valid {
return fasthttp.StatusForbidden, errors.New("your session is expired, login again please")
Expand Down
77 changes: 60 additions & 17 deletions response.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package atreugo

import (
"bufio"
"encoding/json"
"fmt"
"os"

"github.com/erikdubbelboer/fasthttp"
)

// JSON is a map whose key is a string and whose value an interface
type JSON map[string]interface{}

// JSONResponse return http response with content-type application/json
func JSONResponse(ctx *fasthttp.RequestCtx, response interface{}, statusCode ...int) error {
ctx.SetContentType("application/json")
func newResponse(ctx *fasthttp.RequestCtx, contentType string, statusCode ...int) {
ctx.SetContentType(contentType)

if len(statusCode) > 0 {
ctx.SetStatusCode(statusCode[0])
Expand All @@ -20,25 +19,69 @@ func JSONResponse(ctx *fasthttp.RequestCtx, response interface{}, statusCode ...
}

ctx.ResetBody()
return json.NewEncoder(ctx).Encode(response)
}

// HTTPResponse return http response with content-type text/html; charset=utf-8
func HTTPResponse(ctx *fasthttp.RequestCtx, response []byte, statusCode ...int) error {
ctx.SetContentType("text/html; charset=utf-8")
// JSONResponse return response with body in json format
func JSONResponse(ctx *fasthttp.RequestCtx, body interface{}, statusCode ...int) error {
newResponse(ctx, "application/json", statusCode...)

if len(statusCode) > 0 {
ctx.SetStatusCode(statusCode[0])
} else {
ctx.SetStatusCode(fasthttp.StatusOK)
}
return json.NewEncoder(ctx).Encode(body)
}

ctx.ResetBody()
// 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...)

_, 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...)

_, 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...)

_, err := ctx.Write(response)
_, err := ctx.Write(body)
return err
}

// FileResponse return a streaming response with file data.
func FileResponse(ctx *fasthttp.RequestCtx, fileName, filePath string, mimeType string) error {
f := atreugoPools.acquireFile()
defer atreugoPools.putFile(f)

reader := atreugoPools.acquireBufioReader()
defer atreugoPools.putBufioReader(reader)

var err error

ctx.Response.Header.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
ctx.SetStatusCode(fasthttp.StatusOK)
ctx.SetContentType(mimeType)

f, err = os.Open(filePath)
if err != nil {
return err
}

info, err := f.Stat()
if err != nil {
return err
}

reader = bufio.NewReaderSize(f, int64ToInt(info.Size()))
ctx.SetBodyStream(reader, int64ToInt(info.Size()))

return nil
}

// RedirectResponse redirect request to an especific url
func RedirectResponse(ctx *fasthttp.RequestCtx, url string, statusCode int) error {
ctx.ResetBody()
Expand Down
48 changes: 48 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package atreugo

import (
"sync"

"github.com/erikdubbelboer/fasthttp"
"github.com/savsgio/go-logger"
"github.com/thehowl/fasthttprouter"
)

// private

type pools struct {
filePool sync.Pool
readerPool sync.Pool
}

// public

// Config config for Atreugo
type Config struct {
Host string
Port int
LogLevel string
Compress bool
TLSEnable bool
CertKey string
CertFile string
GracefulEnable bool
}

// Atreugo struct for make up a server
type Atreugo struct {
server *fasthttp.Server
router *fasthttprouter.Router
middlewares []Middleware
log *logger.Logger
cfg *Config
}

// View must process incoming requests.
type View func(ctx *fasthttp.RequestCtx) error

// Middleware must process all incoming requests before defined views.
type Middleware func(ctx *fasthttp.RequestCtx) (int, error)

// JSON is a map whose key is a string and whose value an interface
type JSON map[string]interface{}
43 changes: 41 additions & 2 deletions utils.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,59 @@
package atreugo

import (
"bufio"
"os"
"sync"
"unsafe"
)

var atreugoPools = &pools{
filePool: sync.Pool{
New: func() interface{} {
return new(os.File)
},
},
readerPool: sync.Pool{
New: func() interface{} {
return new(bufio.Reader)
},
},
}

func (p *pools) acquireFile() *os.File {
return p.filePool.Get().(*os.File)
}

func (p *pools) acquireBufioReader() *bufio.Reader {
return p.readerPool.Get().(*bufio.Reader)
}

func (p *pools) putFile(f *os.File) {
f = nil
p.filePool.Put(f)
}

func (p *pools) putBufioReader(br *bufio.Reader) {
br = nil
p.readerPool.Put(br)
}

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 {
// 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 {
Expand Down

0 comments on commit b8064b1

Please sign in to comment.