Skip to content

Commit

Permalink
Merge pull request #8 from volmedo/panic-recovery
Browse files Browse the repository at this point in the history
Add a basic panic recovery middleware to send 500 Internal Server Error responses to clients when something goes wrong.
  • Loading branch information
volmedo authored Jun 7, 2019
2 parents 30b99fa + 0dc781b commit 8c9d6d7
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 60 deletions.
32 changes: 0 additions & 32 deletions cmd/server/limiter.go

This file was deleted.

8 changes: 5 additions & 3 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (

func main() {
var dbHost, dbUser, dbPass, dbName, migrationsPath string
var port, rps, dbPort int
var port, dbPort int
var rps int64
flag.IntVar(&port, "port", 8080, "Port where the server is listening for connections.")
flag.IntVar(&rps, "rps", 100, "Rate limit expressed in requests per second (per client)")
flag.Int64Var(&rps, "rps", 100, "Rate limit expressed in requests per second (per client)")

flag.StringVar(&dbHost, "dbhost", "localhost", "Address of the server that hosts the DB")
flag.IntVar(&dbPort, "dbport", 5432, "Port where the DB server is listening for connections")
Expand Down Expand Up @@ -55,10 +56,11 @@ func main() {
}

apiHandler, prometheusHandler := newMeasuredHandler(apiHandler)
apiHandler, err = newRateLimitedHandler(int64(rps), apiHandler)
apiHandler, err = newRateLimitedHandler(rps, apiHandler)
if err != nil {
log.Panicf("Error creating rate limiter middleware: %v", err)
}
apiHandler = newRecoverableHandler(apiHandler)

mux := http.NewServeMux()
mux.Handle("/metrics", prometheusHandler)
Expand Down
25 changes: 0 additions & 25 deletions cmd/server/metrics.go

This file was deleted.

59 changes: 59 additions & 0 deletions cmd/server/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package main

import (
"fmt"
"net/http"
"time"

"github.com/prometheus/client_golang/prometheus/promhttp"
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
"github.com/slok/go-http-metrics/middleware"
"github.com/ulule/limiter/v3"
"github.com/ulule/limiter/v3/drivers/middleware/stdlib"
"github.com/ulule/limiter/v3/drivers/store/memory"
"github.com/unrolled/recovery"
)

// newMeasuredHandler creates a middleware that take essential metrics about
// the handler being measured, such as number of requests, duration of each request,
// concurrent or in-flight requests and response size.
// This function returns two handlers, the handler being measured and a Prometheus
// handler that exposes the metrics being collected
func newMeasuredHandler(handler http.Handler) (measuredH http.Handler, metricsH http.Handler) {
recorder := metrics.NewRecorder(metrics.Config{
Prefix: "pAPI",
})
mdlw := middleware.New(middleware.Config{
Recorder: recorder,
})

return mdlw.Handler("", handler), promhttp.Handler()
}

// newRateLimitedHandler creates a new middleware based on ulule/limiter package that
// limits the request rate that is sent to the specified handler.
// The returned rate-limited handler will allow up to rps requests per second to
// handler. When the rate exceeds the limit, a "429 Too Many Requests" response will be
// sent back without invoking the wrapped handler.
func newRateLimitedHandler(rps int64, handler http.Handler) (http.Handler, error) {
if rps <= 0 {
return nil, fmt.Errorf("rps cannot be negative (rps = %d)", rps)
}

store := memory.NewStore()
rate := limiter.Rate{
Period: time.Second,
Limit: rps,
}
instance := limiter.New(store, rate)
middleware := stdlib.NewMiddleware(instance)

return middleware.Handler(handler), nil
}

// newRecoveredHandler adds a basic panic recovery middleware so that clients
// get a 500 Internal Server Error when something goes wrong
func newRecoverableHandler(handler http.Handler) http.Handler {
rec := recovery.New()
return rec.Handler(handler)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ require (
github.com/prometheus/client_golang v0.9.3
github.com/slok/go-http-metrics v0.4.0
github.com/ulule/limiter/v3 v3.2.0
github.com/unrolled/recovery v0.0.0-20170109144926-b19e1efea904
)
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ github.com/slok/go-http-metrics v0.4.0/go.mod h1:ZRJk+3AdSpQ0IUFseoCHaLE0Tpel+3n
github.com/slok/go-prometheus-middleware v0.4.0/go.mod h1:dEbMQSF4RMuY7tG5XJVTxsHnqVs/AJqTJtpOFk7x/yo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
Expand All @@ -259,6 +260,8 @@ github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJ
github.com/ugorji/go/codec v0.0.0-20180831062425-e253f1f20942/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulule/limiter/v3 v3.2.0 h1:LVG8PirlwDZDVFHzWEqt5K2CTBrCVUKSpIhJ4yDHE7Y=
github.com/ulule/limiter/v3 v3.2.0/go.mod h1:hgLFsUPxhPqrgqqLhtdhiwfI1PXAhq//DIrbANjAX5o=
github.com/unrolled/recovery v0.0.0-20170109144926-b19e1efea904 h1:e9zUexNIJo01P0VOfb0KiiE50AWfN1YzDq3kuvuf7nk=
github.com/unrolled/recovery v0.0.0-20170109144926-b19e1efea904/go.mod h1:KpJfrwoP/I2+S0o3Ywl0+HYSSDzkNw8UBSiMDvRFeSs=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
Expand Down

0 comments on commit 8c9d6d7

Please sign in to comment.