Skip to content

Commit 34faadc

Browse files
committed
feat(middleware): add basic middleware to the project
Fixes: #3
1 parent a004ba4 commit 34faadc

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

middleware.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2023-2024 Flavio Garcia
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package httpok
16+
17+
import (
18+
"net/http"
19+
)
20+
21+
// Middleware represents a function that can wrap a http.Handler with
22+
// additional functionality. It takes a http.Handler and returns a new
23+
// http.Handler that includes the middleware's behavior.
24+
type Middleware func(http.Handler) http.Handler
25+
26+
// Chain creates a chain of HTTP middleware functions to wrap around a
27+
// http.Handler.
28+
// It applies each middleware in the order they are provided, allowing for
29+
// layered processing of HTTP requests and responses.
30+
func Chain(next http.Handler, ms ...Middleware) http.Handler {
31+
for i := len(ms) - 1; i >= 0; i-- {
32+
m := ms[i]
33+
next = m(next)
34+
}
35+
return next
36+
}

middleware_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2023-2024 Flavio Garcia
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package httpok
16+
17+
import (
18+
"net/http"
19+
"testing"
20+
21+
"github.com/candango/httpok/testrunner"
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
type PlainHandler struct {
26+
http.Handler
27+
}
28+
29+
func (h *PlainHandler) GetSomething(w http.ResponseWriter, r *http.Request) {
30+
w.Write([]byte("Something"))
31+
}
32+
33+
func (h *PlainHandler) GetSomethingElse(w http.ResponseWriter, r *http.Request) {
34+
w.Write([]byte("Something else"))
35+
}
36+
37+
func NewPlainServeMux() http.Handler {
38+
plain := &PlainHandler{}
39+
h := http.NewServeMux()
40+
h.HandleFunc("/something", plain.GetSomething)
41+
h.HandleFunc("/something_else", plain.GetSomethingElse)
42+
return h
43+
}
44+
45+
func TestChainMiddlewareServer(t *testing.T) {
46+
plain := NewPlainServeMux()
47+
48+
runner := testrunner.NewHttpTestRunner(t).WithHandler(plain)
49+
50+
t.Run("Plain runner", func(t *testing.T) {
51+
res, err := runner.WithPath("/something").Get()
52+
if err != nil {
53+
t.Error(err)
54+
}
55+
assert.Equal(t, "200 OK", res.Status)
56+
assert.Equal(t, "Something", testrunner.BodyAsString(t, res))
57+
58+
res, err = runner.WithPath("/something_else").Get()
59+
if err != nil {
60+
t.Error(err)
61+
}
62+
assert.Equal(t, "200 OK", res.Status)
63+
assert.Equal(t, "Something else", testrunner.BodyAsString(t, res))
64+
})
65+
66+
changeSomething := func(next http.Handler) http.Handler {
67+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
68+
if r.URL.String() == "/something" {
69+
w.Write([]byte("First Middleware with "))
70+
}
71+
next.ServeHTTP(w, r)
72+
})
73+
}
74+
75+
blockSomethingElse := func(next http.Handler) http.Handler {
76+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77+
if r.URL.String() == "/something_else" {
78+
http.Error(w, "Not allowed", http.StatusMethodNotAllowed)
79+
return
80+
}
81+
next.ServeHTTP(w, r)
82+
})
83+
}
84+
85+
chain := Chain(plain, changeSomething, blockSomethingElse)
86+
runner = testrunner.NewHttpTestRunner(t).WithHandler(chain)
87+
88+
t.Run("Chained runner", func(t *testing.T) {
89+
res, err := runner.WithPath("/something").Get()
90+
if err != nil {
91+
t.Error(err)
92+
}
93+
assert.Equal(t, "200 OK", res.Status)
94+
assert.Equal(t, "First Middleware with Something", testrunner.BodyAsString(t, res))
95+
96+
res, err = runner.WithPath("/something_else").Get()
97+
if err != nil {
98+
t.Error(err)
99+
}
100+
assert.Equal(t, "405 Method Not Allowed", res.Status)
101+
assert.Equal(t, "Not allowed\n", testrunner.BodyAsString(t, res))
102+
})
103+
}

0 commit comments

Comments
 (0)