-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
129 lines (106 loc) · 3.36 KB
/
router.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright (c) 2024, Roel Schut. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package serv
import (
"net/http"
)
// RouteHandler handles routes.
type RouteHandler interface {
HandleRoute(route Route)
}
// RoutesRegisterer registers routes to a [RouteHandler].
type RoutesRegisterer interface {
RegisterRoutes(rh RouteHandler)
}
// RoutesRegistererFunc registers routes to a [RouteHandler].
type RoutesRegistererFunc func(rh RouteHandler)
func (fn RoutesRegistererFunc) RegisterRoutes(rh RouteHandler) { fn(rh) }
// RegisterRoutes registers [Route]s to a [RouteHandler].
func RegisterRoutes(rh RouteHandler, routes ...Route) {
for _, r := range routes {
rh.HandleRoute(r)
}
}
var _ http.Handler = (*Route)(nil)
// Route is a [http.Handler] which represents a route that can be registered to
// a [RouteHandler].
type Route struct {
// Name of the route.
Name string
// Method used to handle the route.
Method string
// Pattern to access the route.
Pattern string
// Handler is the [http.Handler] that handles the route.
Handler http.Handler
}
func (r Route) ServeHTTP(wri http.ResponseWriter, req *http.Request) {
if r.Name == "" {
r.Handler.ServeHTTP(wri, req)
return
}
AddHandlerName(r.Name, r.Handler).ServeHTTP(wri, req)
}
// Router is a [http.Handler] that can handle routes.
type Router interface {
RouteHandler
http.Handler
}
var (
_ Router = (*ServeMux)(nil)
_ Option = (*ServeMux)(nil)
)
type serveMux = http.ServeMux
// ServeMux uses an internal embedded [http.ServeMux] to handle routes. It
// implements the [Router] interface on top of that.
// See [http.ServeMux] for additional information about pattern syntax,
// compatibility etc.
type ServeMux struct {
*serveMux
notFound http.Handler
}
// NewServeMux creates a new [ServeMux] and is ready to be used.
func NewServeMux() *ServeMux {
return &ServeMux{serveMux: http.NewServeMux()}
}
var defaultServeMux = ServeMux{serveMux: http.DefaultServeMux}
// DefaultServeMux returns a [ServeMux] containing [http.DefaultServeMux].
func DefaultServeMux() *ServeMux { return &defaultServeMux }
// HandleRoute registers a route to the [ServeMux] using its internal
// [http.ServeMux.Handle].
func (mux *ServeMux) HandleRoute(route Route) {
mux.serveMux.Handle(route.Method+" "+route.Pattern, route)
}
// WithNotFoundHandler sets a [http.Handler] which is called when there is no
// matching pattern. If not set, [ServeMux] will use the internal
// [http.ServeMux]'s default not found handler, which is [http.NotFound].
func (mux *ServeMux) WithNotFoundHandler(h http.Handler) *ServeMux {
mux.notFound = h
return mux
}
func (mux *ServeMux) ServeHTTP(wri http.ResponseWriter, req *http.Request) {
if mux.notFound == nil {
mux.serveMux.ServeHTTP(wri, req)
return
}
// below code is taken from the http.ServeMux.ServeHTTP method
if req.RequestURI == "*" {
if req.ProtoAtLeast(1, 1) {
wri.Header().Set("Connection", "close")
}
wri.WriteHeader(http.StatusBadRequest)
return
}
if h, pattern := mux.serveMux.Handler(req); pattern != "" {
// a known limitation is that the pattern and matches are not set in the
// http.Request, thus http.Request.PathValue won't work as expected
h.ServeHTTP(wri, req)
} else {
mux.notFound.ServeHTTP(wri, req)
}
}
func (mux *ServeMux) apply(srv *Server) error {
srv.Handler = mux
return nil
}