Skip to content

Commit d00007b

Browse files
committed
fix dupe options handler
1 parent 0df3714 commit d00007b

File tree

6 files changed

+129
-5
lines changed

6 files changed

+129
-5
lines changed

endpoints.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (e Endpoints) Append(mid ...MiddleWare) {
1818
// Prepend add middleware to the front of the chain for the endpoint group
1919
// but after the global middleware
2020
func (e Endpoints) Prepend(mid ...MiddleWare) {
21-
mw := make([]MiddleWare, 0, 0)
21+
mw := make([]MiddleWare, 0)
2222
mw = append(mw, mid...)
2323
for i := 0; i < len(e); i++ {
2424
e[i].MiddlewareHandlers = append(mw, e[i].MiddlewareHandlers...)

examples/simple.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ func main() {
2020
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, Authorization")
2121
w.WriteHeader(http.StatusOK)
2222
})
23+
2324
app.AddEndpoint(http.MethodGet, "/hello", a, w, w2)
25+
app.AddGlobalMiddleware(w2)
2426

2527
app.Run(&http.Server{
2628
Addr: ":8080",

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
module github.com/dbubel/intake/v2
22

33
go 1.23
4+
5+
require github.com/stretchr/testify v1.10.0
6+
7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

intake.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@ import (
1010
"time"
1111
)
1212

13-
// TODO: OPTIONS handler
1413
type MiddleWare func(http.HandlerFunc) http.HandlerFunc
1514

1615
type Intake struct {
1716
Mux *http.ServeMux
1817
PanicHandler func(http.ResponseWriter, *http.Request, interface{})
1918
GlobalMiddleware []MiddleWare
2019
OptionsHandlerFunc http.HandlerFunc
20+
// Track paths that already have OPTIONS handlers
21+
optionsPaths map[string]bool
2122
}
2223

23-
2424
func New() *Intake {
2525
return &Intake{
26-
GlobalMiddleware: make([]MiddleWare, 0, 0),
26+
GlobalMiddleware: make([]MiddleWare, 0),
2727
Mux: http.NewServeMux(),
28+
optionsPaths: make(map[string]bool),
2829
}
2930
}
3031

@@ -43,6 +44,9 @@ func (a *Intake) AddEndpoints(e ...Endpoints) {
4344

4445
func (a *Intake) OptionsHandler(h http.HandlerFunc) {
4546
a.OptionsHandlerFunc = h
47+
// When setting a new OPTIONS handler, we need to clear existing paths
48+
// in case the handler has changed
49+
a.optionsPaths = make(map[string]bool)
4650
}
4751

4852
func (a *Intake) AddEndpoint(verb string, path string, finalHandler http.HandlerFunc, middleware ...MiddleWare) {
@@ -58,9 +62,13 @@ func (a *Intake) AddEndpoint(verb string, path string, finalHandler http.Handler
5862
a.Mux.HandleFunc(fmt.Sprintf("%s %s", verb, path), func(w http.ResponseWriter, r *http.Request) {
5963
finalHandler(w, r)
6064
})
61-
if a.OptionsHandlerFunc != nil {
65+
66+
// Only add OPTIONS handler if we have one and haven't already added it for this path
67+
if a.OptionsHandlerFunc != nil && !a.optionsPaths[path] {
6268
a.Mux.HandleFunc(fmt.Sprintf("%s %s", http.MethodOptions, path), a.OptionsHandlerFunc)
69+
a.optionsPaths[path] = true
6370
}
71+
6472
fmt.Printf("added route %s %s\n", verb, path)
6573
}
6674

intake_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,97 @@
11
package intake
2+
3+
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
type testPayload struct {
14+
Msg string `json:"msg"`
15+
}
16+
17+
func TestIntake(t *testing.T) {
18+
payload := testPayload{Msg: "test response"}
19+
var app = New()
20+
21+
testHandler := func(w http.ResponseWriter, r *http.Request) {
22+
json.NewEncoder(w).Encode(payload)
23+
}
24+
25+
t.Run("test single endpoint", func(t *testing.T) {
26+
app.AddEndpoint(http.MethodGet, "/test", testHandler)
27+
28+
r := httptest.NewRequest(http.MethodGet, "/test", nil)
29+
w := httptest.NewRecorder()
30+
app.Mux.ServeHTTP(w, r)
31+
32+
require.Equal(t, http.StatusOK, w.Code)
33+
resp, err := ioutil.ReadAll(w.Body)
34+
require.NoError(t, err)
35+
36+
var res testPayload
37+
require.NoError(t, json.Unmarshal(resp, &res))
38+
require.Equal(t, "test response", res.Msg)
39+
})
40+
41+
t.Run("test options handler", func(t *testing.T) {
42+
optionsHandler := func(w http.ResponseWriter, r *http.Request) {
43+
w.Header().Set("Allow", "GET, POST, OPTIONS")
44+
w.WriteHeader(http.StatusOK)
45+
}
46+
47+
app.OptionsHandler(optionsHandler)
48+
app.AddEndpoint(http.MethodGet, "/test-options", testHandler)
49+
app.AddEndpoint(http.MethodPost, "/test-options", testHandler)
50+
51+
r := httptest.NewRequest(http.MethodOptions, "/test-options", nil)
52+
w := httptest.NewRecorder()
53+
app.Mux.ServeHTTP(w, r)
54+
55+
require.Equal(t, http.StatusOK, w.Code)
56+
require.Equal(t, "GET, POST, OPTIONS", w.Header().Get("Allow"))
57+
})
58+
59+
t.Run("test middleware execution", func(t *testing.T) {
60+
middlewareCalled := false
61+
middleware := func(next http.HandlerFunc) http.HandlerFunc {
62+
return func(w http.ResponseWriter, r *http.Request) {
63+
middlewareCalled = true
64+
next(w, r)
65+
}
66+
}
67+
68+
app.AddGlobalMiddleware(middleware)
69+
app.AddEndpoint(http.MethodGet, "/test-middleware", testHandler)
70+
71+
r := httptest.NewRequest(http.MethodGet, "/test-middleware", nil)
72+
w := httptest.NewRecorder()
73+
app.Mux.ServeHTTP(w, r)
74+
75+
require.True(t, middlewareCalled)
76+
require.Equal(t, http.StatusOK, w.Code)
77+
})
78+
79+
t.Run("test duplicate options handler", func(t *testing.T) {
80+
optionsCallCount := 0
81+
optionsHandler := func(w http.ResponseWriter, r *http.Request) {
82+
optionsCallCount++
83+
w.WriteHeader(http.StatusOK)
84+
}
85+
86+
app.OptionsHandler(optionsHandler)
87+
app.AddEndpoint(http.MethodGet, "/test-duplicate", testHandler)
88+
app.AddEndpoint(http.MethodPost, "/test-duplicate", testHandler)
89+
90+
r := httptest.NewRequest(http.MethodOptions, "/test-duplicate", nil)
91+
w := httptest.NewRecorder()
92+
app.Mux.ServeHTTP(w, r)
93+
94+
require.Equal(t, 1, optionsCallCount)
95+
require.Equal(t, http.StatusOK, w.Code)
96+
})
97+
}

0 commit comments

Comments
 (0)