Skip to content

Commit fd46d2f

Browse files
committed
group use with options, add contextParams pointer
add forest custom hostMatcher
1 parent 5d02d1f commit fd46d2f

File tree

12 files changed

+280
-149
lines changed

12 files changed

+280
-149
lines changed

README.org

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
return c.HTML(http.StatusOK, "<h1>Hello Forest</h1>")
1818
})
1919

20-
v1 := r.Group("/v1")
20+
v1 := r.Group(forest.WithPrefix("/api"))
2121
{
2222
v1.GET("/posts/{title}", func(c forest.Context) error {
2323
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
@@ -35,14 +35,14 @@
3535
})
3636
}
3737

38-
v2 := forest.NewHost("v2.localhost:8000")
38+
v2 := forest.NewGroup(forest.WithHost("v2.localhost:8000"))
3939
{
4040
v2.GET("/posts/{title}", func(c forest.Context) error {
4141
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
4242
})
4343
}
4444

45-
r.Mount("", v2)
45+
r.Mount(v2)
4646
r.Start("127.0.0.1:8000")
4747
}
4848
#+end_src
@@ -122,8 +122,8 @@
122122
*** Named Route
123123
#+begin_src go
124124
r := forest.New()
125-
g1 := r.Group("/api").Named("g1")
126-
g2 := g1.Group("/v1").Named("g2")
125+
g1 := r.Group(forest.WithPrefix("/api"), forest.WithName("g1"))
126+
g2 := g1.Group(forest.WithPrefix("/v1"), forest.WithName("g2"))
127127
r1 := api.GET("/posts").Named("list_posts", "some description")
128128
r2 := api.DELETE("/posts/:pk").Named("delete_post", "delete post with pk param")
129129
// result
@@ -184,7 +184,7 @@
184184
// with root
185185
router.Use(MyMiddleware)
186186
// with group
187-
group := router.Group("/api/v1", MyMiddleware)
187+
group := router.Group(forest.WithPrefix("/api/v1"), forest.WithMiddlewares(MyMiddleware))
188188
// with special handler
189189
group.GET("/", MyMiddleware, func(c forest.Context) error {
190190
return nil
@@ -201,13 +201,13 @@
201201
...
202202
})
203203

204-
group := router.Group("/api/v1")
204+
group := router.Group(forest.WithPrefix("/api/v1"))
205205
group.GET("/posts", func(c forest.Context) error {
206206
// c.Logger() == Logger1
207207
...
208208
})
209209

210-
group := router.Group("/api/v2")
210+
group := router.Group(forest.WithPrefix("/api/v2"))
211211
group.Logger = Logger2
212212
group.GET("/posts", func(c forest.Context) error {
213213
// c.Logger() == Logger2
@@ -229,7 +229,7 @@
229229
router.ErrorHandler = func(err error, c Context) {
230230
c.String(500, err.Error())
231231
}
232-
group := router.Group("/api/v1")
232+
group := router.Group(forest.WithPrefix("/api/v1"))
233233
// group only
234234
group.ErrorHandler = func(err error, c Context) {
235235
c.String(501, err.Error())
@@ -252,6 +252,16 @@
252252
}
253253
#+end_src
254254

255+
*** Custom Host Matcher
256+
#+begin_src go
257+
func matcher(host, dst string) bool {
258+
return host == dst
259+
}
260+
r := forest.New(forest.HostMatch(matcher))
261+
// or use internal matcher
262+
r := forest.New(forest.HostMatch(forest.HostMatcher))
263+
#+end_src
264+
255265
*** Custom URL Param
256266
#+begin_src go
257267
import (
@@ -279,7 +289,7 @@
279289
return 18, true
280290
}
281291

282-
func NewUUIDMatcher(pname string, ptype string) forest.Matcher {
292+
func NewUUIDMatcher(rule string) forest.Matcher {
283293
return &UUIDMatcher{}
284294
}
285295

context.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ type Context interface {
6565
type context struct {
6666
response *Response
6767
request *http.Request
68-
pvalues []string
68+
params *contextParams
6969
storeLock sync.RWMutex
7070
store map[string]interface{}
7171
query url.Values
@@ -108,8 +108,8 @@ func (c *context) Get(key string) interface{} {
108108

109109
func (c *context) Param(name string) string {
110110
for i, p := range c.route.pnames {
111-
if i < len(c.pvalues) && p.name == name {
112-
return c.pvalues[i]
111+
if i < len(c.params.pvalues) && p.name == name {
112+
return c.params.pvalues[i]
113113
}
114114
}
115115
return ""
@@ -121,8 +121,8 @@ func (c *context) Params() map[string]string {
121121
}
122122
params := make(map[string]string)
123123
for i, p := range c.route.pnames {
124-
if i < len(c.pvalues) {
125-
params[p.name] = c.pvalues[i]
124+
if i < len(c.params.pvalues) {
125+
params[p.name] = c.params.pvalues[i]
126126
}
127127
}
128128
return params
@@ -323,8 +323,18 @@ func (c *context) reset(r *http.Request, w http.ResponseWriter) {
323323
c.request = r
324324
c.store = nil
325325
c.index = -1
326-
for i := 0; i < len(c.pvalues); i++ {
327-
c.pvalues[i] = ""
326+
c.params.reset(0)
327+
}
328+
329+
type contextParams struct {
330+
pindex int
331+
pvalues []string
332+
}
333+
334+
func (m *contextParams) reset(pindex int) {
335+
m.pindex = pindex
336+
for ; pindex < len(m.pvalues); pindex++ {
337+
m.pvalues[pindex] = ""
328338
}
329339
}
330340

examples/helloworld/main.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,40 @@ import (
99

1010
func main() {
1111
r := forest.New(forest.Debug())
12-
r.Use(middleware.Recover())
12+
1313
r.Use(middleware.Logger())
14-
r.GET("/", func(c forest.Context) error {
15-
return c.HTML(http.StatusOK, "<h1>Hello Forest</h1>")
14+
r.Use(middleware.Recover())
15+
16+
r.NotFound(func(c forest.Context) error {
17+
return c.JSON(444, forest.H{"message": "not found"})
1618
})
1719

18-
v1 := r.Group("/v1")
20+
// r.Use(middleware.BasicAuth(nil))
21+
r.GET("/*", func(c forest.Context) error {
22+
return c.HTML(http.StatusOK, "<h1>Hello Forest</h1>")
23+
24+
})
25+
v1 := r.Group(forest.WithPrefix("/v1"), forest.WithName("v1"))
1926
{
2027
v1.GET("/posts/{title}", func(c forest.Context) error {
2128
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
22-
})
29+
}).Named("vv")
2330
v1.GET("/posts/{titleId:int}", func(c forest.Context) error {
2431
return c.JSON(http.StatusOK, forest.H{"title": c.Param("titleId")})
2532
})
2633
}
2734

28-
v2 := r.Host("v2.localhost:8000", "")
35+
v2 := r.Group(forest.WithHost("v2.localhost:8000"))
2936
{
3037
v2.GET("/posts/{title:^test-\\w+}", func(c forest.Context) error {
3138
return c.JSON(http.StatusOK, forest.H{"title": c.Param("title")})
3239
})
3340
}
3441

3542
v3 := newGroup()
36-
r.MountGroup("/v3", v3)
43+
r.MountGroup(v3, forest.WithPrefix("/v3"))
44+
45+
v2.Mount(v3, forest.WithPrefix("/v3"))
3746

3847
r.Start("127.0.0.1:8000")
3948
}

examples/pprof/main.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ func profile() *forest.Forest {
1717
return r
1818
}
1919

20+
func profile1() *forest.Group {
21+
r := forest.NewGroup()
22+
r.GET("/pprof/", forest.WrapHandlerFunc(pprof.Index))
23+
r.GET("/pprof/*", forest.WrapHandler(http.DefaultServeMux))
24+
r.POST("/pprof/symbol", forest.WrapHandlerFunc(pprof.Symbol))
25+
return r
26+
}
27+
2028
func main() {
2129
r := forest.New(forest.Debug())
2230
r.Use(middleware.Recover())
@@ -25,6 +33,7 @@ func main() {
2533
return c.HTML(http.StatusOK, "<h1>PPROF</h1>")
2634

2735
})
28-
r.Mount("/debug", profile())
36+
r.Mount(profile(), forest.WithPrefix("/debug"))
37+
r.MountGroup(profile1(), forest.WithPrefix("/debug1"))
2938
r.Start("127.0.0.1:8000")
3039
}

forest.go

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"reflect"
99
"runtime"
1010
"sort"
11+
"strings"
1112
"sync"
1213
)
1314

@@ -26,6 +27,7 @@ type (
2627
methodNotAllowedRoute *Route
2728
maxParam int
2829
debug bool
30+
hostMatch func(string, string) bool
2931
Server *http.Server
3032
}
3133
HandlerFunc func(Context) error
@@ -86,14 +88,32 @@ func debugPrint(msg string, args ...interface{}) {
8688
fmt.Fprint(os.Stdout, sprintf(msg, args...))
8789
}
8890

89-
type Option func(e *Forest)
91+
type Option func(*Forest)
92+
93+
func (opt Option) Group() GroupOption {
94+
return func(g *Group) {
95+
opt(g.forest)
96+
}
97+
}
9098

9199
func Debug() Option {
92100
return func(e *Forest) {
93101
e.debug = true
94102
}
95103
}
96104

105+
func HostMatch(matcher func(string, string) bool) Option {
106+
return func(e *Forest) {
107+
e.hostMatch = matcher
108+
}
109+
}
110+
111+
func Middlewares(handlers ...HandlerFunc) Option {
112+
return func(e *Forest) {
113+
e.middlewares = handlers
114+
}
115+
}
116+
97117
func New(opts ...Option) *Forest {
98118
e := &Forest{
99119
node: &node{},
@@ -116,6 +136,18 @@ func New(opts ...Option) *Forest {
116136
return e
117137
}
118138

139+
func HostMatcher(host, dst string) bool {
140+
c := len(host) - len(dst)
141+
if c < 0 {
142+
return false
143+
}
144+
index := strings.IndexRune(dst, '*')
145+
if index < 0 {
146+
return false
147+
}
148+
return dst[:index] == host[:index] && dst[index+1:] == host[c+index+1:]
149+
}
150+
119151
func WrapHandler(h http.Handler) HandlerFunc {
120152
return func(c Context) error {
121153
h.ServeHTTP(c.Response(), c.Request())
@@ -165,22 +197,32 @@ func (e *Forest) addRoute(host, method, path string) *Route {
165197
return route
166198
}
167199

168-
func (e *Forest) findRoute(host, method, path string, pvalues []string) *Route {
169-
root := e.node
170-
if host != "" {
171-
if h, ok := e.nodes[host]; ok {
172-
root = h
200+
func (e *Forest) findHost(host string) *node {
201+
if host != "" && len(e.nodes) > 0 {
202+
if node, ok := e.nodes[host]; ok {
203+
return node
204+
}
205+
if e.hostMatch == nil {
206+
return e.node
207+
}
208+
for h, node := range e.nodes {
209+
if e.hostMatch(host, h) {
210+
return node
211+
}
173212
}
174213
}
175-
n := root.find(path, 0, pvalues)
176-
if n == nil || n.routes == nil {
177-
return e.notFoundRoute
178-
}
179-
if len(n.routes) == 0 {
214+
return e.node
215+
}
216+
217+
func (e *Forest) findRoute(host, method, path string, params *contextParams) *Route {
218+
n := e.findHost(host).find(path, params)
219+
if n == nil || n.routes == nil || len(n.routes) == 0 {
180220
return e.notFoundRoute
181221
}
182-
if result := n.routes.find(method); result != nil {
183-
return result
222+
for _, route := range n.routes {
223+
if route.Method() == method {
224+
return route
225+
}
184226
}
185227
return e.methodNotAllowedRoute
186228
}
@@ -237,17 +279,19 @@ func (e *Forest) Use(middlewares ...HandlerFunc) *Forest {
237279
return e
238280
}
239281

240-
func (e *Forest) Mount(prefix string, child *Forest) {
241-
e.rootGroup.Mount(prefix, child.rootGroup)
282+
func (e *Forest) Mount(child *Forest, opts ...GroupOption) {
283+
e.rootGroup.Mount(child.rootGroup, opts...)
242284
}
243285

244-
func (e *Forest) MountGroup(prefix string, child *Group) {
245-
e.rootGroup.Mount(prefix, child)
286+
func (e *Forest) MountGroup(child *Group, opts ...GroupOption) {
287+
e.rootGroup.Mount(child, opts...)
246288
}
247289

248290
func (e *Forest) NewContext(w http.ResponseWriter, r *http.Request) *context {
249291
c := &context{
250-
pvalues: make([]string, e.maxParam),
292+
params: &contextParams{
293+
pvalues: make([]string, e.maxParam),
294+
},
251295
response: NewResponse(w),
252296
}
253297
return c
@@ -263,7 +307,7 @@ func (e *Forest) ServeHTTP(w http.ResponseWriter, r *http.Request) {
263307
path = r.URL.Path
264308
}
265309
// pass []string is faster than *context than *([]string)
266-
c.route = e.findRoute(r.Host, r.Method, path, c.pvalues)
310+
c.route = e.findRoute(r.Host, r.Method, path, c.params)
267311
c.Next()
268312
}
269313

0 commit comments

Comments
 (0)