Skip to content

Commit 4f61136

Browse files
committed
Add Goji support
Signed-off-by: Xabier Larrakoetxea <[email protected]>
1 parent 1dab114 commit 4f61136

File tree

9 files changed

+249
-9
lines changed

9 files changed

+249
-9
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- New middleware helper for the Goji framework.
8+
59
## [0.7.0] - 2020-06-02
610

711
Breaking change: The library has been refactored to be more flexible when adding new framework/libraries.

Readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ The middleware is mainly focused to be compatible with Go std library using http
4141
- [go-restful][gorestful-example]
4242
- [Gin][gin-example]
4343
- [Echo][echo-example]
44+
- [Goji][goji-example]
4445

4546
## Getting Started
4647

@@ -212,5 +213,6 @@ This Option is used to unregister the Recorder views before are being registered
212213
[gorestful-example]: examples/gorestful
213214
[gin-example]: examples/gin
214215
[echo-example]: examples/echo
216+
[goji-example]: examples/goji
215217
[prometheus-recorder]: metrics/prometheus
216218
[opencensus-recorder]: metrics/opencensus

examples/goji/main.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
10+
"github.com/prometheus/client_golang/prometheus/promhttp"
11+
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
12+
"github.com/slok/go-http-metrics/middleware"
13+
gojimiddleware "github.com/slok/go-http-metrics/middleware/goji"
14+
"goji.io"
15+
"goji.io/pat"
16+
)
17+
18+
const (
19+
srvAddr = ":8080"
20+
metricsAddr = ":8081"
21+
)
22+
23+
func main() {
24+
// Create our middleware.
25+
mdlw := middleware.New(middleware.Config{
26+
Recorder: metrics.NewRecorder(metrics.Config{}),
27+
})
28+
29+
// Create our router with the metrics middleware.
30+
mux := goji.NewMux()
31+
mux.Use(gojimiddleware.Handler("", mdlw))
32+
33+
mux.HandleFunc(pat.Get("/"), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
34+
w.WriteHeader(http.StatusOK)
35+
_, _ = w.Write([]byte("Hello world"))
36+
}))
37+
38+
// Serve our handler.
39+
go func() {
40+
log.Printf("server listening at %s", srvAddr)
41+
if err := http.ListenAndServe(srvAddr, mux); err != nil {
42+
log.Panicf("error while serving: %s", err)
43+
}
44+
}()
45+
46+
// Serve our metrics.
47+
go func() {
48+
log.Printf("metrics listening at %s", metricsAddr)
49+
if err := http.ListenAndServe(metricsAddr, promhttp.Handler()); err != nil {
50+
log.Panicf("error while serving metrics: %s", err)
51+
}
52+
}()
53+
54+
// Wait until some signal is captured.
55+
sigC := make(chan os.Signal, 1)
56+
signal.Notify(sigC, syscall.SIGTERM, syscall.SIGINT)
57+
<-sigC
58+
}

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/stretchr/testify v1.6.0
1111
github.com/urfave/negroni v1.0.0
1212
go.opencensus.io v0.22.3
13+
goji.io v2.0.2+incompatible
1314
)
1415

15-
go 1.13
16+
go 1.14

go.sum

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
22
contrib.go.opencensus.io/exporter/prometheus v0.1.0 h1:SByaIoWwNgMdPSgl5sMqM2KDE5H/ukPWBRo314xiDvg=
33
contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A=
4+
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
45
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
56
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
67
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -161,6 +162,8 @@ go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
161162
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
162163
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
163164
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
165+
goji.io v2.0.2+incompatible h1:uIssv/elbKRLznFUy3Xj4+2Mz/qKhek/9aZQDUMae7c=
166+
goji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk=
164167
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
165168
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
166169
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=

middleware/goji/example_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package goji_test
2+
3+
import (
4+
"log"
5+
"net/http"
6+
7+
"github.com/prometheus/client_golang/prometheus/promhttp"
8+
"goji.io"
9+
"goji.io/pat"
10+
11+
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
12+
"github.com/slok/go-http-metrics/middleware"
13+
gojimiddleware "github.com/slok/go-http-metrics/middleware/goji"
14+
)
15+
16+
// GojiMiddleware shows how you would create a default middleware factory and use it
17+
// to create a Goji compatible middleware.
18+
func Example_gojiMiddleware() {
19+
// Create our middleware factory with the default settings.
20+
mdlw := middleware.New(middleware.Config{
21+
Recorder: metrics.NewRecorder(metrics.Config{}),
22+
})
23+
24+
// Create our Goji instance.
25+
mux := goji.NewMux()
26+
27+
// Add our middleware.
28+
mux.Use(gojimiddleware.Handler("", mdlw))
29+
30+
// Add our handler.
31+
mux.HandleFunc(pat.Get("/"), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
32+
w.WriteHeader(http.StatusOK)
33+
_, _ = w.Write([]byte("Hello world"))
34+
}))
35+
36+
// Serve metrics from the default prometheus registry.
37+
log.Printf("serving metrics at: %s", ":8081")
38+
go func() {
39+
_ = http.ListenAndServe(":8081", promhttp.Handler())
40+
}()
41+
42+
// Serve our handler.
43+
log.Printf("listening at: %s", ":8080")
44+
if err := http.ListenAndServe(":8080", mux); err != nil {
45+
log.Panicf("error while serving: %s", err)
46+
}
47+
}

middleware/goji/goji.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Package goji is a helper package to get a goji compatible middleware.
2+
package goji
3+
4+
import (
5+
"net/http"
6+
7+
"github.com/slok/go-http-metrics/middleware"
8+
"github.com/slok/go-http-metrics/middleware/std"
9+
)
10+
11+
// Handler returns a Goji measuring middleware.
12+
func Handler(handlerID string, m middleware.Middleware) func(http.Handler) http.Handler {
13+
return func(next http.Handler) http.Handler {
14+
return std.Handler(handlerID, m, next)
15+
}
16+
}

middleware/goji/goji_test.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package goji_test
2+
3+
import (
4+
"io/ioutil"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/mock"
11+
"github.com/stretchr/testify/require"
12+
"goji.io"
13+
"goji.io/pat"
14+
15+
mmetrics "github.com/slok/go-http-metrics/internal/mocks/metrics"
16+
"github.com/slok/go-http-metrics/metrics"
17+
"github.com/slok/go-http-metrics/middleware"
18+
gojimiddleware "github.com/slok/go-http-metrics/middleware/goji"
19+
)
20+
21+
func TestMiddleware(t *testing.T) {
22+
tests := map[string]struct {
23+
handlerID string
24+
config middleware.Config
25+
req func() *http.Request
26+
mock func(m *mmetrics.Recorder)
27+
handler func() http.Handler
28+
expRespCode int
29+
expRespBody string
30+
}{
31+
"A default HTTP middleware should call the recorder to measure.": {
32+
req: func() *http.Request {
33+
return httptest.NewRequest(http.MethodPost, "/test", nil)
34+
},
35+
mock: func(m *mmetrics.Recorder) {
36+
expHTTPReqProps := metrics.HTTPReqProperties{
37+
ID: "/test",
38+
Service: "",
39+
Method: "POST",
40+
Code: "202",
41+
}
42+
m.On("ObserveHTTPRequestDuration", mock.Anything, expHTTPReqProps, mock.Anything).Once()
43+
m.On("ObserveHTTPResponseSize", mock.Anything, expHTTPReqProps, int64(5)).Once()
44+
45+
expHTTPProps := metrics.HTTPProperties{
46+
ID: "/test",
47+
Service: "",
48+
}
49+
m.On("AddInflightRequests", mock.Anything, expHTTPProps, 1).Once()
50+
m.On("AddInflightRequests", mock.Anything, expHTTPProps, -1).Once()
51+
},
52+
handler: func() http.Handler {
53+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
54+
w.WriteHeader(202)
55+
w.Write([]byte("test1")) // nolint: errcheck
56+
})
57+
},
58+
expRespCode: 202,
59+
expRespBody: "test1",
60+
},
61+
}
62+
63+
for name, test := range tests {
64+
t.Run(name, func(t *testing.T) {
65+
assert := assert.New(t)
66+
require := require.New(t)
67+
68+
// Mocks.
69+
mr := &mmetrics.Recorder{}
70+
test.mock(mr)
71+
72+
// Create our instance with the middleware.
73+
mdlw := middleware.New(middleware.Config{Recorder: mr})
74+
mux := goji.NewMux()
75+
req := test.req()
76+
mux.Handle(pat.NewWithMethods(req.URL.Path, req.Method), test.handler())
77+
mux.Use(gojimiddleware.Handler(test.handlerID, mdlw))
78+
79+
// Make the request.
80+
resp := httptest.NewRecorder()
81+
mux.ServeHTTP(resp, req)
82+
83+
// Check.
84+
mr.AssertExpectations(t)
85+
assert.Equal(test.expRespCode, resp.Result().StatusCode)
86+
gotBody, err := ioutil.ReadAll(resp.Result().Body)
87+
require.NoError(err)
88+
assert.Equal(test.expRespBody, string(gotBody))
89+
})
90+
}
91+
}

test/integration/integration_test.go

+26-8
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ import (
1818
"github.com/stretchr/testify/assert"
1919
"github.com/stretchr/testify/require"
2020
"github.com/urfave/negroni"
21+
"goji.io"
22+
"goji.io/pat"
2123

2224
metricsprometheus "github.com/slok/go-http-metrics/metrics/prometheus"
2325
"github.com/slok/go-http-metrics/middleware"
2426
echomiddleware "github.com/slok/go-http-metrics/middleware/echo"
2527
ginmiddleware "github.com/slok/go-http-metrics/middleware/gin"
28+
gojimiddleware "github.com/slok/go-http-metrics/middleware/goji"
2629
gorestfulmiddleware "github.com/slok/go-http-metrics/middleware/gorestful"
2730
httproutermiddleware "github.com/slok/go-http-metrics/middleware/httprouter"
2831
negronimiddleware "github.com/slok/go-http-metrics/middleware/negroni"
@@ -50,6 +53,7 @@ func TestMiddlewarePrometheus(t *testing.T) {
5053
"Gorestful": {handler: prepareHandlerGorestful},
5154
"Gin": {handler: prepareHandlerGin},
5255
"Echo": {handler: prepareHandlerEcho},
56+
"Goji": {handler: prepareHandlerGoji},
5357
}
5458

5559
for name, test := range tests {
@@ -139,8 +143,7 @@ func prepareHandlerSTD(m middleware.Middleware, hc []handlerConfig) http.Handler
139143

140144
time.Sleep(h.SleepDuration)
141145
w.WriteHeader(h.Code)
142-
// nolint: errcheck
143-
w.Write([]byte(h.ReturnData))
146+
w.Write([]byte(h.ReturnData)) // nolint: errcheck
144147
}))
145148
}
146149

@@ -163,8 +166,7 @@ func prepareHandlerNegroni(m middleware.Middleware, hc []handlerConfig) http.Han
163166

164167
time.Sleep(h.SleepDuration)
165168
w.WriteHeader(h.Code)
166-
// nolint: errcheck
167-
w.Write([]byte(h.ReturnData))
169+
w.Write([]byte(h.ReturnData)) // nolint: errcheck
168170
}))
169171
}
170172

@@ -185,8 +187,7 @@ func prepareHandlerHTTPRouter(m middleware.Middleware, hc []handlerConfig) http.
185187
hr := func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
186188
time.Sleep(h.SleepDuration)
187189
w.WriteHeader(h.Code)
188-
// nolint: errcheck
189-
w.Write([]byte(h.ReturnData))
190+
w.Write([]byte(h.ReturnData)) // nolint: errcheck
190191
}
191192

192193
// Setup middleware on each of the routes.
@@ -208,8 +209,7 @@ func prepareHandlerGorestful(m middleware.Middleware, hc []handlerConfig) http.H
208209
ws.Route(ws.Method(h.Method).Path(h.Path).To(func(_ *gorestful.Request, resp *gorestful.Response) {
209210
time.Sleep(h.SleepDuration)
210211
resp.WriteHeader(h.Code)
211-
// nolint: errcheck
212-
resp.Write([]byte(h.ReturnData))
212+
resp.Write([]byte(h.ReturnData)) // nolint: errcheck
213213
}))
214214
}
215215
c.Add(ws)
@@ -250,3 +250,21 @@ func prepareHandlerEcho(m middleware.Middleware, hc []handlerConfig) http.Handle
250250

251251
return e
252252
}
253+
254+
func prepareHandlerGoji(m middleware.Middleware, hc []handlerConfig) http.Handler {
255+
// Setup server and middleware.
256+
mux := goji.NewMux()
257+
mux.Use(gojimiddleware.Handler("", m))
258+
259+
// Setup handlers.
260+
for _, h := range hc {
261+
h := h
262+
mux.HandleFunc(pat.NewWithMethods(h.Path, h.Method), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
263+
time.Sleep(h.SleepDuration)
264+
w.WriteHeader(h.Code)
265+
w.Write([]byte(h.ReturnData)) // nolint: errcheck
266+
}))
267+
}
268+
269+
return mux
270+
}

0 commit comments

Comments
 (0)