Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 859d4fa458 | |||
| 894614cd54 | |||
| c34f5b7d0d | |||
| f4a2452a94 | |||
| eda7c79ab6 |
@@ -27,7 +27,7 @@ func main() {
|
||||
r.Use(middleware1, middleware2)
|
||||
|
||||
// let's add a route
|
||||
r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.GET("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("i am route /hello"))
|
||||
})
|
||||
// r.Post(pattern string, h http.HandlerFunc)
|
||||
@@ -37,7 +37,7 @@ func main() {
|
||||
// you can inline middleware(s) to a route
|
||||
r.
|
||||
With(mwInline).
|
||||
Get("/hello-2", func(w http.ResponseWriter, r *http.Request) {
|
||||
GET("/hello-2", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("i am route /hello-2 with my own middleware"))
|
||||
})
|
||||
|
||||
@@ -64,13 +64,13 @@ func main() {
|
||||
// create a group of few routes with their own middlewares
|
||||
r.Group(func(grp *mux.Router) {
|
||||
grp.Use(mwGroup)
|
||||
grp.Get("/group", func(w http.ResponseWriter, r *http.Request) {
|
||||
grp.GET("/group", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("i am route /group"))
|
||||
})
|
||||
})
|
||||
|
||||
// catches all
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("hello there"))
|
||||
})
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ func main() {
|
||||
r.Use(middleware1, middleware2)
|
||||
|
||||
// let's add a route
|
||||
r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.GET("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("i am route /hello"))
|
||||
})
|
||||
// r.Post(pattern string, h http.HandlerFunc)
|
||||
@@ -35,7 +35,7 @@ func main() {
|
||||
// you can inline middleware(s) to a route
|
||||
r.
|
||||
With(mwInline).
|
||||
Get("/hello-2", func(w http.ResponseWriter, r *http.Request) {
|
||||
GET("/hello-2", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("i am route /hello-2 with my own middleware"))
|
||||
})
|
||||
|
||||
@@ -62,13 +62,13 @@ func main() {
|
||||
// create a group of few routes with their own middlewares
|
||||
r.Group(func(grp *mux.Router) {
|
||||
grp.Use(mwGroup)
|
||||
grp.Get("/group", func(w http.ResponseWriter, r *http.Request) {
|
||||
grp.GET("/group", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("i am route /group"))
|
||||
})
|
||||
})
|
||||
|
||||
// catches all
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("hello there"))
|
||||
})
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ type OriginValidator func(string) bool
|
||||
var (
|
||||
defaultCorsOptionStatusCode = http.StatusOK
|
||||
defaultCorsMethods = []string{http.MethodHead, http.MethodGet, http.MethodPost}
|
||||
defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"}
|
||||
defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Type", "Content-Language", "Origin"}
|
||||
// (WebKit/Safari v9 sends the Origin header by default in AJAX requests).
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -10,41 +11,42 @@ import (
|
||||
|
||||
func TestHelmet(t *testing.T) {
|
||||
r := mux.NewRouter()
|
||||
r.Use(Helmet(HelmetOption{}))
|
||||
r.Get("/hello", func(writer http.ResponseWriter, request *http.Request) {
|
||||
_, _ = writer.Write([]byte("hello there"))
|
||||
})
|
||||
|
||||
endpoint := httptest.NewRequest(http.MethodGet, "/hello", nil)
|
||||
srv := httptest.NewServer(r)
|
||||
defer srv.Close()
|
||||
w, _ := testRequest(t, srv, "GET", "/hello", nil)
|
||||
|
||||
// test endpoint registered/reachable
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, endpoint)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Error("not expecting status", w.Code)
|
||||
return
|
||||
}
|
||||
|
||||
// no header test
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, endpoint)
|
||||
csp := w.Header().Get("Content-Security-Policy")
|
||||
csp := w.Header.Get("Content-Security-Policy")
|
||||
// must not have a csp header, technically no header related to helmet but lets test with one.
|
||||
if csp != "" {
|
||||
t.Error("csp header not expected")
|
||||
}
|
||||
|
||||
// introduce helmet middleware
|
||||
r.Use(Helmet(HelmetOption{}))
|
||||
|
||||
// header tests..
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, endpoint)
|
||||
// csp and other headers are expected
|
||||
csp = w.Header().Get("Content-Security-Policy")
|
||||
// fmt.Printf("csp %s", csp)
|
||||
if csp == "" {
|
||||
t.Error("csp header missing")
|
||||
t.Error("csp header is expected")
|
||||
}
|
||||
|
||||
// TODO need more tests
|
||||
}
|
||||
|
||||
func testRequest(t *testing.T, ts *httptest.Server, method, path string, body io.Reader) (*http.Response, string) {
|
||||
req, err := http.NewRequest(method, ts.URL+path, body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
respBody, err := io.ReadAll(io.Reader(resp.Body))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp, string(respBody)
|
||||
}
|
||||
|
||||
54
router.go
54
router.go
@@ -14,9 +14,10 @@ type Router struct {
|
||||
}
|
||||
|
||||
func NewRouter() *Router {
|
||||
return &Router{
|
||||
r := &Router{
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Use will register middleware(s) with router stack
|
||||
@@ -27,48 +28,46 @@ func (r *Router) Use(h ...func(http.Handler) http.Handler) {
|
||||
r.middlewares = append(r.middlewares, h...)
|
||||
}
|
||||
|
||||
// Get method route
|
||||
func (r *Router) Get(pattern string, h http.HandlerFunc) {
|
||||
// GET method route
|
||||
func (r *Router) GET(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodGet, pattern, h)
|
||||
}
|
||||
|
||||
// Head method route
|
||||
func (r *Router) Head(pattern string, h http.HandlerFunc) {
|
||||
// HEAD method route
|
||||
func (r *Router) HEAD(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodHead, pattern, h)
|
||||
}
|
||||
|
||||
// Post method route
|
||||
func (r *Router) Post(pattern string, h http.HandlerFunc) {
|
||||
// POST method route
|
||||
func (r *Router) POST(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodPost, pattern, h)
|
||||
}
|
||||
|
||||
// Put method route
|
||||
func (r *Router) Put(pattern string, h http.HandlerFunc) {
|
||||
// PUT method route
|
||||
func (r *Router) PUT(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodPut, pattern, h)
|
||||
}
|
||||
|
||||
// Patch method route
|
||||
func (r *Router) Patch(pattern string, h http.HandlerFunc) {
|
||||
// PATCH method route
|
||||
func (r *Router) PATCH(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodPatch, pattern, h)
|
||||
}
|
||||
|
||||
// Delete method route
|
||||
func (r *Router) Delete(pattern string, h http.HandlerFunc) {
|
||||
// DELETE method route
|
||||
func (r *Router) DELETE(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodDelete, pattern, h)
|
||||
}
|
||||
|
||||
// Connect method route
|
||||
func (r *Router) Connect(pattern string, h http.HandlerFunc) {
|
||||
// CONNECT method route
|
||||
func (r *Router) CONNECT(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodConnect, pattern, h)
|
||||
}
|
||||
|
||||
// Options method route
|
||||
func (r *Router) Options(pattern string, h http.HandlerFunc) {
|
||||
} // OPTIONS method route
|
||||
func (r *Router) OPTIONS(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodOptions, pattern, h)
|
||||
}
|
||||
|
||||
// Trace method route
|
||||
func (r *Router) Trace(pattern string, h http.HandlerFunc) {
|
||||
// TRACE method route
|
||||
func (r *Router) TRACE(pattern string, h http.HandlerFunc) {
|
||||
r.handlerFunc(http.MethodTrace, pattern, h)
|
||||
}
|
||||
|
||||
@@ -141,18 +140,11 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
panic("mux: method ServeHTTP called on nil")
|
||||
}
|
||||
|
||||
h, pattern := r.mux.Handler(req)
|
||||
if pattern == "" {
|
||||
http.Error(w, "Not Found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// ensure we run all the middlewares
|
||||
h = stack(r.middlewares, h)
|
||||
// serve
|
||||
h.ServeHTTP(w, req)
|
||||
r.mux.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
// TODO: proxy for aws lambda
|
||||
|
||||
// stack middlewares(http handler) in order they are passed (FIFO)
|
||||
func stack(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler {
|
||||
// Return ahead of time if there aren't any middlewares for the chain
|
||||
|
||||
@@ -3,6 +3,7 @@ package mux
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -13,6 +14,21 @@ type ServeCB func(srv *http.Server) error
|
||||
|
||||
// Serve with graceful shutdown
|
||||
func (r *Router) Serve(cb ServeCB) {
|
||||
// catch all options
|
||||
// lets get it thorugh all middlewares
|
||||
r.OPTIONS("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Length", "0")
|
||||
if r.ContentLength != 0 {
|
||||
// Read up to 4KB of OPTIONS body (as mentioned in the
|
||||
// spec as being reserved for future use), but anything
|
||||
// over that is considered a waste of server resources
|
||||
// (or an attack) and we abort and close the connection,
|
||||
// courtesy of MaxBytesReader's EOF behavior.
|
||||
mb := http.MaxBytesReader(w, r.Body, 4<<10)
|
||||
io.Copy(io.Discard, mb)
|
||||
}
|
||||
})
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user