Move to fasthttp for improved performance
This commit is contained in:
29
common/conversions.go
Normal file
29
common/conversions.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// b2s converts byte slice to a string without memory allocation.
|
||||
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
||||
//
|
||||
// Note it may break if string and/or slice header will change
|
||||
// in the future go versions.
|
||||
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))
|
||||
}
|
@@ -2,26 +2,132 @@ package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/DataDog/datadog-go/statsd"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/getsentry/raven-go"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/valyala/fasthttp"
|
||||
"gopkg.in/redis.v5"
|
||||
)
|
||||
|
||||
// RavenClient is the raven client to which report errors happening.
|
||||
// If nil, errors will just be fmt.Println'd
|
||||
var RavenClient *raven.Client
|
||||
|
||||
// MethodData is a struct containing the data passed over to an API method.
|
||||
type MethodData struct {
|
||||
User Token
|
||||
DB *sqlx.DB
|
||||
RequestData RequestData
|
||||
C *gin.Context
|
||||
Doggo *statsd.Client
|
||||
R *redis.Client
|
||||
User Token
|
||||
DB *sqlx.DB
|
||||
Doggo *statsd.Client
|
||||
R *redis.Client
|
||||
Ctx *fasthttp.RequestCtx
|
||||
}
|
||||
|
||||
// Err logs an error into gin.
|
||||
// ClientIP implements a best effort algorithm to return the real client IP, it parses
|
||||
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
|
||||
func (md MethodData) ClientIP() string {
|
||||
clientIP := strings.TrimSpace(string(md.Ctx.Request.Header.Peek("X-Real-Ip")))
|
||||
if len(clientIP) > 0 {
|
||||
return clientIP
|
||||
}
|
||||
clientIP = string(md.Ctx.Request.Header.Peek("X-Forwarded-For"))
|
||||
if index := strings.IndexByte(clientIP, ','); index >= 0 {
|
||||
clientIP = clientIP[0:index]
|
||||
}
|
||||
clientIP = strings.TrimSpace(clientIP)
|
||||
if len(clientIP) > 0 {
|
||||
return clientIP
|
||||
}
|
||||
return md.Ctx.RemoteIP().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) {
|
||||
md.C.Error(err)
|
||||
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{
|
||||
ID: strconv.Itoa(md.User.UserID),
|
||||
Username: md.User.Value,
|
||||
IP: md.Ctx.RemoteAddr().String(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Err for peppy API calls
|
||||
func Err(c *fasthttp.RequestCtx, 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(c.RequestURI()),
|
||||
}
|
||||
|
||||
RavenClient.CaptureError(
|
||||
err,
|
||||
tags,
|
||||
st,
|
||||
generateRavenHTTP(c),
|
||||
)
|
||||
}
|
||||
|
||||
func generateRavenHTTP(ctx *fasthttp.RequestCtx) *raven.Http {
|
||||
// build uri
|
||||
uri := ctx.URI()
|
||||
// safe to use b2s because a new string gets allocated eventually for
|
||||
// concatenation
|
||||
sURI := b2s(uri.Scheme()) + "://" + b2s(uri.Host()) + b2s(uri.Path())
|
||||
|
||||
// build header map
|
||||
// using ctx.Request.Header.Len would mean calling .VisitAll two times
|
||||
// which can be quite expensive since it means iterating over all the
|
||||
// headers, so we give a rough estimate of the number of headers we expect
|
||||
// to have
|
||||
m := make(map[string]string, 16)
|
||||
ctx.Request.Header.VisitAll(func(k, v []byte) {
|
||||
// not using b2s because we mustn't keep references to the underlying
|
||||
// k and v
|
||||
m[string(k)] = string(v)
|
||||
})
|
||||
|
||||
return &raven.Http{
|
||||
URL: sURI,
|
||||
// Not using b2s because raven sending is concurrent and may happen
|
||||
// AFTER the request, meaning that values could potentially be replaced
|
||||
// by new ones.
|
||||
Method: string(ctx.Method()),
|
||||
Query: string(uri.QueryString()),
|
||||
Cookies: string(ctx.Request.Header.Peek("Cookie")),
|
||||
Headers: m,
|
||||
}
|
||||
}
|
||||
|
||||
// ID retrieves the Token's owner user ID.
|
||||
@@ -31,23 +137,16 @@ func (md MethodData) ID() int {
|
||||
|
||||
// Query is shorthand for md.C.Query.
|
||||
func (md MethodData) Query(q string) string {
|
||||
return md.C.Query(q)
|
||||
return b2s(md.Ctx.QueryArgs().Peek(q))
|
||||
}
|
||||
|
||||
// HasQuery returns true if the parameter is encountered in the querystring.
|
||||
// It returns true even if the parameter is "" (the case of ?param&etc=etc)
|
||||
func (md MethodData) HasQuery(q string) bool {
|
||||
_, has := md.C.GetQuery(q)
|
||||
return has
|
||||
return md.Ctx.QueryArgs().Has(q)
|
||||
}
|
||||
|
||||
// RequestData is the body of a request. It is wrapped into this type
|
||||
// to implement the Unmarshal function, which is just a shorthand to
|
||||
// json.Unmarshal.
|
||||
type RequestData []byte
|
||||
|
||||
// Unmarshal json-decodes Requestdata into a value. Basically a
|
||||
// shorthand to json.Unmarshal.
|
||||
func (r RequestData) Unmarshal(into interface{}) error {
|
||||
return json.Unmarshal([]byte(r), into)
|
||||
// Unmarshal unmarshals a request's JSON body into an interface.
|
||||
func (md MethodData) Unmarshal(into interface{}) error {
|
||||
return json.Unmarshal(md.Ctx.PostBody(), into)
|
||||
}
|
||||
|
@@ -19,8 +19,8 @@ func Sort(md MethodData, config SortConfiguration) string {
|
||||
config.Table += "."
|
||||
}
|
||||
var sortBy string
|
||||
for _, s := range md.C.Request.URL.Query()["sort"] {
|
||||
sortParts := strings.Split(strings.ToLower(s), ",")
|
||||
for _, s := range md.Ctx.Request.URI().QueryArgs().PeekMulti("sort") {
|
||||
sortParts := strings.Split(strings.ToLower(b2s(s)), ",")
|
||||
if contains(config.Allowed, sortParts[0]) {
|
||||
if sortBy != "" {
|
||||
sortBy += ", "
|
||||
|
@@ -51,15 +51,15 @@ func (w *WhereClause) And() *WhereClause {
|
||||
// initial is the initial part, e.g. "users.id".
|
||||
// Fields are the possible values.
|
||||
// Sample output: users.id IN ('1', '2', '3')
|
||||
func (w *WhereClause) In(initial string, fields ...string) *WhereClause {
|
||||
func (w *WhereClause) In(initial string, fields ...[]byte) *WhereClause {
|
||||
if len(fields) == 0 {
|
||||
return w
|
||||
}
|
||||
w.addWhere()
|
||||
w.Clause += initial + " IN (" + generateQuestionMarks(len(fields)) + ")"
|
||||
fieldsInterfaced := make([]interface{}, 0, len(fields))
|
||||
for _, i := range fields {
|
||||
fieldsInterfaced = append(fieldsInterfaced, interface{}(i))
|
||||
fieldsInterfaced := make([]interface{}, len(fields))
|
||||
for k, f := range fields {
|
||||
fieldsInterfaced[k] = string(f)
|
||||
}
|
||||
w.Params = append(w.Params, fieldsInterfaced...)
|
||||
return w
|
||||
|
Reference in New Issue
Block a user