git.iko.soy/typeable/oauth2-proxy
on master help sign in
Add HealthCheck middleware
commitca416a2ebbacad164dcea3243f4e3d6e24ecf3f8 authorJoel Speed <joel.speed@hotmail.co.uk> dateSun 14 Jun 2020, 16:42 (6 years ago) parent43f214c
5 files changed+180
go.mod+1 go.sum+3 pkg/middleware/healthcheck.go+48 pkg/middleware/healthcheck_test.go+112 pkg/middleware/middleware_suite_test.go+16
go.mod+1
@@ -12,6 +12,7 @@ require (
1212 github.com/dgrijalva/jwt-go v3.2.0+incompatible
1313 github.com/fsnotify/fsnotify v1.4.9
1414 github.com/go-redis/redis/v7 v7.2.0
15+ github.com/justinas/alice v1.2.0
1516 github.com/kr/pretty v0.2.0 // indirect
1617 github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
1718 github.com/mitchellh/mapstructure v1.1.2
go.sum+3
@@ -102,6 +102,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
102102github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
103103github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
104104github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
105+github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
106+github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
105107github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
106108github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
107109github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -129,6 +131,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
129131github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
130132github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
131133github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
134+github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ=
132135github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
133136github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
134137github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
pkg/middleware/healthcheck.gonew file+48
@@ -0,0 +1,48 @@
1+package middleware
2+
3+import (
4+ "fmt"
5+ "net/http"
6+
7+ "github.com/justinas/alice"
8+)
9+
10+func NewHealthCheck(paths, userAgents []string) alice.Constructor {
11+ return func(next http.Handler) http.Handler {
12+ return healthCheck(paths, userAgents, next)
13+ }
14+}
15+
16+func healthCheck(paths, userAgents []string, next http.Handler) http.Handler {
17+ // Use a map as a set to check health check paths
18+ pathSet := make(map[string]struct{})
19+ for _, path := range paths {
20+ pathSet[path] = struct{}{}
21+ }
22+
23+ // Use a map as a set to check health check paths
24+ userAgentSet := make(map[string]struct{})
25+ for _, userAgent := range userAgents {
26+ userAgentSet[userAgent] = struct{}{}
27+ }
28+
29+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
30+ if isHealthCheckRequest(pathSet, userAgentSet, req) {
31+ rw.WriteHeader(http.StatusOK)
32+ fmt.Fprintf(rw, "OK")
33+ return
34+ }
35+
36+ next.ServeHTTP(rw, req)
37+ })
38+}
39+
40+func isHealthCheckRequest(paths, userAgents map[string]struct{}, req *http.Request) bool {
41+ if _, ok := paths[req.URL.EscapedPath()]; ok {
42+ return true
43+ }
44+ if _, ok := userAgents[req.Header.Get("User-Agent")]; ok {
45+ return true
46+ }
47+ return false
48+}
pkg/middleware/healthcheck_test.gonew file+112
@@ -0,0 +1,112 @@
1+package middleware
2+
3+import (
4+ "net/http"
5+ "net/http/httptest"
6+
7+ . "github.com/onsi/ginkgo"
8+ . "github.com/onsi/ginkgo/extensions/table"
9+ . "github.com/onsi/gomega"
10+)
11+
12+var _ = Describe("HealthCheck suite", func() {
13+ type requestTableInput struct {
14+ healthCheckPaths []string
15+ healthCheckUserAgents []string
16+ requestString string
17+ headers map[string]string
18+ expectedStatus int
19+ expectedBody string
20+ }
21+
22+ DescribeTable("when serving a request",
23+ func(in *requestTableInput) {
24+ req := httptest.NewRequest("", in.requestString, nil)
25+ for k, v := range in.headers {
26+ req.Header.Add(k, v)
27+ }
28+
29+ rw := httptest.NewRecorder()
30+
31+ handler := NewHealthCheck(in.healthCheckPaths, in.healthCheckUserAgents)(http.NotFoundHandler())
32+ handler.ServeHTTP(rw, req)
33+
34+ Expect(rw.Code).To(Equal(in.expectedStatus))
35+ Expect(rw.Body.String()).To(Equal(in.expectedBody))
36+ },
37+ Entry("when requesting the healthcheck path", &requestTableInput{
38+ healthCheckPaths: []string{"/ping"},
39+ healthCheckUserAgents: []string{"hc/1.0"},
40+ requestString: "http://example.com/ping",
41+ headers: map[string]string{},
42+ expectedStatus: 200,
43+ expectedBody: "OK",
44+ }),
45+ Entry("when requesting a different path", &requestTableInput{
46+ healthCheckPaths: []string{"/ping"},
47+ healthCheckUserAgents: []string{"hc/1.0"},
48+ requestString: "http://example.com/different",
49+ headers: map[string]string{},
50+ expectedStatus: 404,
51+ expectedBody: "404 page not found\n",
52+ }),
53+ Entry("with a request from the health check user agent", &requestTableInput{
54+ healthCheckPaths: []string{"/ping"},
55+ healthCheckUserAgents: []string{"hc/1.0"},
56+ requestString: "http://example.com/abc",
57+ headers: map[string]string{
58+ "User-Agent": "hc/1.0",
59+ },
60+ expectedStatus: 200,
61+ expectedBody: "OK",
62+ }),
63+ Entry("with a request from a different user agent", &requestTableInput{
64+ healthCheckPaths: []string{"/ping"},
65+ healthCheckUserAgents: []string{"hc/1.0"},
66+ requestString: "http://example.com/abc",
67+ headers: map[string]string{
68+ "User-Agent": "different",
69+ },
70+ expectedStatus: 404,
71+ expectedBody: "404 page not found\n",
72+ }),
73+ Entry("with multiple paths, request one of the healthcheck paths", &requestTableInput{
74+ healthCheckPaths: []string{"/ping", "/liveness_check", "/readiness_check"},
75+ healthCheckUserAgents: []string{"hc/1.0"},
76+ requestString: "http://example.com/readiness_check",
77+ headers: map[string]string{},
78+ expectedStatus: 200,
79+ expectedBody: "OK",
80+ }),
81+ Entry("with multiple paths, request none of the healthcheck paths", &requestTableInput{
82+ healthCheckPaths: []string{"/ping", "/liveness_check", "/readiness_check"},
83+ healthCheckUserAgents: []string{"hc/1.0"},
84+ requestString: "http://example.com/readiness",
85+ headers: map[string]string{
86+ "User-Agent": "user",
87+ },
88+ expectedStatus: 404,
89+ expectedBody: "404 page not found\n",
90+ }),
91+ Entry("with multiple user agents, request from a health check user agent", &requestTableInput{
92+ healthCheckPaths: []string{"/ping"},
93+ healthCheckUserAgents: []string{"hc/1.0", "GoogleHC/1.0"},
94+ requestString: "http://example.com/abc",
95+ headers: map[string]string{
96+ "User-Agent": "GoogleHC/1.0",
97+ },
98+ expectedStatus: 200,
99+ expectedBody: "OK",
100+ }),
101+ Entry("with multiple user agents, request from none of the health check user agents", &requestTableInput{
102+ healthCheckPaths: []string{"/ping"},
103+ healthCheckUserAgents: []string{"hc/1.0", "GoogleHC/1.0"},
104+ requestString: "http://example.com/abc",
105+ headers: map[string]string{
106+ "User-Agent": "user",
107+ },
108+ expectedStatus: 404,
109+ expectedBody: "404 page not found\n",
110+ }),
111+ )
112+})
pkg/middleware/middleware_suite_test.gonew file+16
@@ -0,0 +1,16 @@
1+package middleware
2+
3+import (
4+ "testing"
5+
6+ "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
7+ . "github.com/onsi/ginkgo"
8+ . "github.com/onsi/gomega"
9+)
10+
11+func TestMiddlewareSuite(t *testing.T) {
12+ logger.SetOutput(GinkgoWriter)
13+
14+ RegisterFailHandler(Fail)
15+ RunSpecs(t, "Middleware")
16+}