Add back old middlewares

This commit is contained in:
Morgan Bazalgette 2017-02-02 15:13:17 +01:00
parent 736c904f65
commit 490d13e333
5 changed files with 116 additions and 135 deletions

View File

@ -3,6 +3,7 @@ package app
import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"unsafe"
@ -114,3 +115,17 @@ func mkjson(c *fasthttp.RequestCtx, data interface{}) {
func b2s(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// s2b converts string to a byte slice without memory allocation.
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func s2b(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}

View File

@ -1,104 +0,0 @@
package app
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
raven "github.com/getsentry/raven-go"
"github.com/gin-gonic/gin"
)
// Recovery is a better sentry logger.
func Recovery(client *raven.Client, onlyCrashes bool) gin.HandlerFunc {
return func(c *gin.Context) {
var requestBody []byte
defer func() {
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple"})
tokenRaw, ex := c.Get("token")
var token string
if ex {
token = tokenRaw.(string)
}
ravenHTTP := raven.NewHttp(c.Request)
if len(requestBody) != 0 {
ravenHTTP.Data = string(requestBody)
}
ravenUser := &raven.User{
Username: token,
IP: c.Request.RemoteAddr,
}
flags := map[string]string{
"endpoint": c.Request.RequestURI,
"token": token,
}
if rval := recover(); rval != nil {
var err error
switch rval := rval.(type) {
case string:
err = errors.New(rval)
case error:
err = rval
default:
err = fmt.Errorf("%v - %#v", rval, rval)
}
fmt.Println(err)
client.CaptureError(
err,
flags,
st,
ravenHTTP,
ravenUser,
)
c.AbortWithStatus(http.StatusInternalServerError)
}
if !onlyCrashes {
for _, item := range c.Errors {
var err = error(item)
if item.Type == gin.ErrorTypePrivate {
err = item.Err
}
fmt.Println(err)
client.CaptureError(
err,
flags,
ravenHTTP,
ravenUser,
)
}
}
}()
if c.Request.Method == "POST" && c.Request.URL.Path != "/tokens" &&
c.Request.URL.Path != "/tokens/new" {
var err error
requestBody, err = ioutil.ReadAll(c.Request.Body)
if err != nil {
c.Error(err)
}
c.Request.Body = fakeBody{
r: bytes.NewReader(requestBody),
orig: c.Request.Body,
}
}
c.Next()
}
}
type fakeBody struct {
r io.Reader
orig io.ReadCloser
}
func (f fakeBody) Read(p []byte) (int, error) { return f.r.Read(p) }
func (f fakeBody) Close() error { return f.orig.Close() }

View File

@ -1,6 +1,11 @@
package app
import (
"bytes"
"errors"
"fmt"
"time"
"github.com/buaazp/fasthttprouter"
"github.com/jmoiron/sqlx"
"github.com/valyala/fasthttp"
@ -12,14 +17,85 @@ type router struct {
}
func (r router) Method(path string, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) {
r.r.GET(path, Method(f, privilegesNeeded...))
r.r.GET(path, wrap(Method(f, privilegesNeeded...)))
}
func (r router) POSTMethod(path string, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) {
r.r.POST(path, Method(f, privilegesNeeded...))
r.r.POST(path, wrap(Method(f, privilegesNeeded...)))
}
func (r router) Peppy(path string, a func(c *fasthttp.RequestCtx, db *sqlx.DB)) {
r.r.GET(path, PeppyMethod(a))
r.r.GET(path, wrap(PeppyMethod(a)))
}
func (r router) GET(path string, handle fasthttp.RequestHandler) {
r.r.GET(path, handle)
r.r.GET(path, wrap(handle))
}
const (
// \x1b is escape code for ESC
// <ESC>[<n>m is escape sequence for a certain colour
// no IP is written out because of the hundreds of possible ways to pass IPs
// to a request when using a reverse proxy
// this is partly inspired from gin, though made even more simplistic.
fmtString = "%s | %15s |\x1b[%sm %3d \x1b[0m %-7s %s\n"
// a kind of human readable RFC3339
timeFormat = "2006-01-02 15:04:05"
// color reference
// http://misc.flogisoft.com/bash/tip_colors_and_formatting
colorOk = "42" // green
colorError = "41" // red
)
// wrap returns a function that wraps around handle, providing middleware
// functionality to apply to all API calls, which is to say:
// - logging
// - panic recovery (reporting to sentry)
// - gzipping
func wrap(handle fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(c *fasthttp.RequestCtx) {
start := time.Now()
defer func() {
if rval := recover(); rval != nil {
var err error
switch rval := rval.(type) {
case string:
err = errors.New(rval)
case error:
err = rval
default:
err = fmt.Errorf("%v - %#v", rval, rval)
}
common.Err(c, err)
c.SetStatusCode(500)
c.SetBodyString(`{ "code": 500, "message": "something really bad happened" }`)
}
// switch color to colorError if statusCode is in [500;600)
color := colorOk
statusCode := c.Response.StatusCode()
if statusCode >= 500 && statusCode < 600 {
color = colorError
}
if bytes.Contains(c.Request.Header.Peek("Accept-Encoding"), s2b("gzip")) {
c.Response.Header.Add("Content-Encoding", "gzip")
c.Response.Header.Add("Vary", "Accept-Encoding")
b := c.Response.Body()
c.Response.ResetBody()
fasthttp.WriteGzip(c.Response.BodyWriter(), b)
}
// print stuff
fmt.Printf(
fmtString,
time.Now().Format(timeFormat),
time.Since(start).String(),
color,
statusCode,
c.Method(),
c.Path(),
)
}()
handle(c)
}
}

View File

@ -82,7 +82,8 @@ var randomSentences = [...]string{
}
func surpriseMe() string {
return randomSentences[rn.Intn(len(randomSentences))] + " " + kaomojis[rn.Intn(len(kaomojis))]
n := int(time.Now().UnixNano())
return randomSentences[n%len(randomSentences)] + " " + kaomojis[n%len(kaomojis)]
}
type pingResponse struct {

View File

@ -47,36 +47,30 @@ func (md MethodData) ClientIP() string {
// Err logs an error. If RavenClient is set, it will use the client to report
// the error to sentry, otherwise it will just write the error to stdout.
func (md MethodData) Err(err error) {
if RavenClient == nil {
fmt.Println("ERROR!!!!")
fmt.Println(err)
return
}
// Create stacktrace
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple", "git.zxq.co/ripple"})
// Generate tags for error
tags := map[string]string{
"endpoint": b2s(md.Ctx.RequestURI()),
"token": md.User.Value,
}
RavenClient.CaptureError(
err,
tags,
st,
generateRavenHTTP(md.Ctx),
&raven.User{
user := &raven.User{
ID: strconv.Itoa(md.User.UserID),
Username: md.User.Value,
IP: md.Ctx.RemoteAddr().String(),
},
)
}
// Generate tags for error
tags := map[string]string{
"endpoint": string(md.Ctx.RequestURI()),
"token": md.User.Value,
}
_err(err, tags, user, md.Ctx)
}
// Err for peppy API calls
func Err(c *fasthttp.RequestCtx, err error) {
// Generate tags for error
tags := map[string]string{
"endpoint": string(c.RequestURI()),
}
_err(err, tags, nil, c)
}
func _err(err error, tags map[string]string, user *raven.User, c *fasthttp.RequestCtx) {
if RavenClient == nil {
fmt.Println("ERROR!!!!")
fmt.Println(err)
@ -86,16 +80,15 @@ func Err(c *fasthttp.RequestCtx, err error) {
// Create stacktrace
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple", "git.zxq.co/ripple"})
// Generate tags for error
tags := map[string]string{
"endpoint": b2s(c.RequestURI()),
ifaces := []raven.Interface{st, generateRavenHTTP(c)}
if user != nil {
ifaces = append(ifaces, user)
}
RavenClient.CaptureError(
err,
tags,
st,
generateRavenHTTP(c),
ifaces...,
)
}