Skip to content

Commit 161ec6b

Browse files
author
Olivier Poitrey
committed
Add some more xmux doc
1 parent 9a538f0 commit 161ec6b

File tree

2 files changed

+185
-50
lines changed

2 files changed

+185
-50
lines changed

README.md

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func main() {
7575

7676
### Using xmux
7777

78-
Xhandler comes with an optional context aware muxer forked from [httprouter](https://github.com/julienschmidt/httprouter):
78+
Xhandler comes with an optional context aware [muxer](https://github.com/rs/xhandler/tree/master/xmux) forked from [httprouter](https://github.com/julienschmidt/httprouter):
7979

8080
```go
8181
package main
@@ -113,53 +113,7 @@ func main() {
113113
}
114114
```
115115

116-
#### Benchmarks
117-
118-
Using [Julien Schmidt](https://github.com/julienschmidt) excellent [HTTP routing benchmark](https://github.com/julienschmidt/go-http-routing-benchmark), we can see that xhandler's muxer is pretty close to `httprouter` as it is a fork of it. The small overhead is due to the `net/context` allocation used to store route parameters. It still outperform other routers, thanks to amazing `httprouter`'s radix tree based matcher.
119-
120-
```
121-
BenchmarkXhandler_APIStatic-8 50000000 39.6 ns/op 0 B/op 0 allocs/op
122-
BenchmarkChi_APIStatic-8 3000000 439 ns/op 144 B/op 5 allocs/op
123-
BenchmarkGoji_APIStatic-8 5000000 272 ns/op 0 B/op 0 allocs/op
124-
BenchmarkHTTPRouter_APIStatic-8 50000000 37.3 ns/op 0 B/op 0 allocs/op
125-
126-
BenchmarkXhandler_APIParam-8 5000000 328 ns/op 160 B/op 4 allocs/op
127-
BenchmarkChi_APIParam-8 2000000 675 ns/op 432 B/op 6 allocs/op
128-
BenchmarkGoji_APIParam-8 2000000 692 ns/op 336 B/op 2 allocs/op
129-
BenchmarkHTTPRouter_APIParam-8 10000000 166 ns/op 64 B/op 1 allocs/op
130-
131-
BenchmarkXhandler_API2Params-8 5000000 362 ns/op 160 B/op 4 allocs/op
132-
BenchmarkChi_API2Params-8 2000000 814 ns/op 432 B/op 6 allocs/op
133-
BenchmarkGoji_API2Params-8 2000000 680 ns/op 336 B/op 2 allocs/op
134-
BenchmarkHTTPRouter_API2Params-8 10000000 183 ns/op 64 B/op 1 allocs/op
135-
136-
BenchmarkXhandler_APIAll-8 200000 6473 ns/op 2176 B/op 64 allocs/op
137-
BenchmarkChi_APIAll-8 100000 17261 ns/op 8352 B/op 146 allocs/op
138-
BenchmarkGoji_APIAll-8 100000 15052 ns/op 5377 B/op 32 allocs/op
139-
BenchmarkHTTPRouter_APIAll-8 500000 3716 ns/op 640 B/op 16 allocs/op
140-
141-
BenchmarkXhandler_Param1-8 5000000 271 ns/op 128 B/op 4 allocs/op
142-
BenchmarkChi_Param1-8 2000000 620 ns/op 432 B/op 6 allocs/op
143-
BenchmarkGoji_Param1-8 3000000 522 ns/op 336 B/op 2 allocs/op
144-
BenchmarkHTTPRouter_Param1-8 20000000 112 ns/op 32 B/op 1 allocs/op
145-
146-
BenchmarkXhandler_Param5-8 3000000 414 ns/op 256 B/op 4 allocs/op
147-
BenchmarkChi_Param5-8 1000000 1204 ns/op 432 B/op 6 allocs/op
148-
BenchmarkGoji_Param5-8 2000000 847 ns/op 336 B/op 2 allocs/op
149-
BenchmarkHTTPRouter_Param5-8 5000000 247 ns/op 160 B/op 1 allocs/op
150-
151-
BenchmarkXhandler_Param20-8 2000000 747 ns/op 736 B/op 4 allocs/op
152-
BenchmarkChi_Param20-8 2000000 746 ns/op 736 B/op 4 allocs/op
153-
BenchmarkGoji_Param20-8 500000 2439 ns/op 1247 B/op 2 allocs/op
154-
BenchmarkHTTPRouter_Param20-8 3000000 585 ns/op 640 B/op 1 allocs/op
155-
156-
BenchmarkXhandler_ParamWrite-8 5000000 404 ns/op 144 B/op 5 allocs/op
157-
BenchmarkChi_ParamWrite-8 3000000 407 ns/op 144 B/op 5 allocs/op
158-
BenchmarkGoji_ParamWrite-8 2000000 594 ns/op 336 B/op 2 allocs/op
159-
BenchmarkHTTPRouter_ParamWrite-8 10000000 166 ns/op 32 B/op 1 allocs/op
160-
```
161-
162-
You can run the benchmark using `go test -bench=.` in `xhandler`'s root.
116+
See [xmux](https://github.com/rs/xhandler/tree/master/xmux) for more examples.
163117

164118
## Context Aware Middleware
165119

@@ -177,5 +131,3 @@ Feel free to put up a PR linking your middleware if you have built one:
177131
## Licenses
178132

179133
All source code is licensed under the [MIT License](https://raw.github.com/rs/xhandler/master/LICENSE).
180-
181-
Muxer is forked from [httprouter](https://github.com/julienschmidt/httprouter) with [BSD License](https://github.com/julienschmidt/httprouter/blob/master/LICENSE).

xmux/README.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# Xmux
2+
3+
[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/xhandler/xmux) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/xhandler/master/LICENSE) [![Build Status](https://travis-ci.org/rs/xhandler.svg?branch=master)](https://travis-ci.org/rs/xhandler) [![Coverage](http://gocover.io/_badge/github.com/rs/xhandler/xmux)](http://gocover.io/github.com/rs/xhandler/xmux)
4+
5+
Xmux is a lightweight high performance HTTP request muxer on top [xhandler](https://github.com/rs/xhandler). Xmux gets its speed from the fork of the amazing [httprouter](https://github.com/julienschmidt/httprouter). Route parameters are stored in `net/context` instead of being passed as an additional parameter.
6+
7+
In contrast to the [default mux](http://golang.org/pkg/net/http/#ServeMux) of Go's `net/http` package, this muxer supports variables in the routing pattern and matches against the request method. It also scales better.
8+
9+
The muxer is optimized for high performance and a small memory footprint. It scales well even with very long paths and a large number of routes. A compressing dynamic trie (radix tree) structure is used for efficient matching.
10+
11+
## Features
12+
13+
**Only explicit matches:** With other muxers, like [http.ServeMux](http://golang.org/pkg/net/http/#ServeMux), a requested URL path could match multiple patterns. Therefore they have some awkward pattern priority rules, like *longest match* or *first registered, first matched*. By design of this router, a request can only match exactly one or no route. As a result, there are also no unintended matches, which makes it great for SEO and improves the user experience.
14+
15+
**Stop caring about trailing slashes:** Choose the URL style you like, the muxer automatically redirects the client if a trailing slash is missing or if there is one extra. Of course it only does so, if the new path has a handler. If you don't like it, you can [turn off this behavior](http://godoc.org/github.com/rs/xhandler/xmux#Mux.RedirectTrailingSlash).
16+
17+
**Path auto-correction:** Besides detecting the missing or additional trailing slash at no extra cost, the muxer can also fix wrong cases and remove superfluous path elements (like `../` or `//`). Is [CAPTAIN CAPS LOCK](http://www.urbandictionary.com/define.php?term=Captain+Caps+Lock) one of your users? Xmux can help him by making a case-insensitive look-up and redirecting him to the correct URL.
18+
19+
**Parameters in your routing pattern:** Stop parsing the requested URL path, just give the path segment a name and the router delivers the dynamic value to you. Because of the design of the router, path parameters are very cheap.
20+
21+
**Zero Garbage:** The matching and dispatching process generates zero bytes of garbage. In fact, the only heap allocations that are made, is by building the slice of the key-value pairs for path parameters and the `net/context` instance to store them in the context. If the request path contains no parameters, not a single heap allocation is necessary.
22+
23+
**No more server crashes:** You can set a [Panic handler](http://godoc.org/github.com/rs/xhandler/xmux#Mux.PanicHandler) to deal with panics occurring during handling a HTTP request. The router then recovers and lets the `PanicHandler` log what happened and deliver a nice error page.
24+
25+
Of course you can also set **custom [NotFound](http://godoc.org/github.com/rs/xhandler/xmux#Mux.NotFound) and [MethodNotAllowed](http://godoc.org/github.com/rs/xhandler/xmux#Mux.MethodNotAllowed) handlers**.
26+
27+
## Usage
28+
29+
This is just a quick introduction, view the [GoDoc](http://godoc.org/github.com/rs/xhandler/xmux) for details.
30+
31+
Let's start with a trivial example:
32+
```go
33+
package main
34+
35+
import (
36+
"fmt"
37+
"log"
38+
"net/http"
39+
40+
"github.com/rs/xhandler"
41+
"github.com/rs/xhandler/xmux"
42+
"golang.org/x/net/context"
43+
)
44+
45+
func Index(ctx context.Context, w http.ResponseWriter, r *http.Request) {
46+
fmt.Fprint(w, "Welcome!\n")
47+
}
48+
49+
func Hello(ctx context.Context, w http.ResponseWriter, r *http.Request) {
50+
fmt.Fprintf(w, "hello, %s!\n", xmux.Params(ctx).Get("name"))
51+
}
52+
53+
func main() {
54+
mux := xmux.New()
55+
mux.GET("/", Index)
56+
mux.GET("/hello/:name", Hello)
57+
58+
log.Fatal(http.ListenAndServe(":8080", xhandler.New(context.Background(), mux)))
59+
}
60+
```
61+
62+
You may also chain middleware using `xhandler.Chain`:
63+
64+
```go
65+
package main
66+
67+
import (
68+
"fmt"
69+
"log"
70+
"net/http"
71+
"time"
72+
73+
"github.com/rs/xhandler"
74+
"github.com/rs/xhandler/xmux"
75+
"golang.org/x/net/context"
76+
)
77+
78+
func main() {
79+
c := xhandler.Chain{}
80+
81+
// Append a context-aware middleware handler
82+
c.UseC(xhandler.CloseHandler)
83+
84+
// Another context-aware middleware handler
85+
c.UseC(xhandler.TimeoutHandler(2 * time.Second))
86+
87+
mux := xmux.New()
88+
89+
// Use c.Handler to terminate the chain with your final handler
90+
mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
91+
fmt.Fprintf(w, "Welcome %s!", xmux.Params(ctx).Get("name"))
92+
}))
93+
94+
if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil {
95+
log.Fatal(err)
96+
}
97+
}
98+
```
99+
100+
### Named parameters
101+
102+
As you can see, `:name` is a *named parameter*. The values are accessible via `xmux.Params(ctx)`, which returns `xmux.ParamHolder`.
103+
You can get the value of a parameter by its name using `Get(name)` method:
104+
105+
Named parameters only match a single path segment:
106+
107+
```
108+
Pattern: /user/:user
109+
110+
/user/gordon match
111+
/user/you match
112+
/user/gordon/profile no match
113+
/user/ no match
114+
```
115+
116+
**Note:** Since this muxer has only explicit matches, you can not register static routes and parameters for the same path segment. For example you can not register the patterns `/user/new` and `/user/:user` for the same request method at the same time. The routing of different request methods is independent from each other.
117+
118+
### Catch-All parameters
119+
120+
The second type are *catch-all* parameters and have the form `*name`. Like the name suggests, they match everything. Therefore they must always be at the **end** of the pattern:
121+
122+
```
123+
Pattern: /src/*filepath
124+
125+
/src/ match
126+
/src/somefile.go match
127+
/src/subdir/somefile.go match
128+
```
129+
130+
## Benchmarks
131+
132+
Thanks to [Julien Schmidt](https://github.com/julienschmidt) excellent [HTTP routing benchmark](https://github.com/julienschmidt/go-http-routing-benchmark), we can see that xhandler's muxer is pretty close to `httprouter` as it is a fork of it. The small overhead is due to the `net/context` allocation used to store route parameters. It still outperform other routers, thanks to amazing `httprouter`'s radix tree based matcher.
133+
134+
```
135+
BenchmarkXhandler_APIStatic-8 50000000 39.6 ns/op 0 B/op 0 allocs/op
136+
BenchmarkChi_APIStatic-8 3000000 439 ns/op 144 B/op 5 allocs/op
137+
BenchmarkGoji_APIStatic-8 5000000 272 ns/op 0 B/op 0 allocs/op
138+
BenchmarkHTTPRouter_APIStatic-8 50000000 37.3 ns/op 0 B/op 0 allocs/op
139+
140+
BenchmarkXhandler_APIParam-8 5000000 328 ns/op 160 B/op 4 allocs/op
141+
BenchmarkChi_APIParam-8 2000000 675 ns/op 432 B/op 6 allocs/op
142+
BenchmarkGoji_APIParam-8 2000000 692 ns/op 336 B/op 2 allocs/op
143+
BenchmarkHTTPRouter_APIParam-8 10000000 166 ns/op 64 B/op 1 allocs/op
144+
145+
BenchmarkXhandler_API2Params-8 5000000 362 ns/op 160 B/op 4 allocs/op
146+
BenchmarkChi_API2Params-8 2000000 814 ns/op 432 B/op 6 allocs/op
147+
BenchmarkGoji_API2Params-8 2000000 680 ns/op 336 B/op 2 allocs/op
148+
BenchmarkHTTPRouter_API2Params-8 10000000 183 ns/op 64 B/op 1 allocs/op
149+
150+
BenchmarkXhandler_APIAll-8 200000 6473 ns/op 2176 B/op 64 allocs/op
151+
BenchmarkChi_APIAll-8 100000 17261 ns/op 8352 B/op 146 allocs/op
152+
BenchmarkGoji_APIAll-8 100000 15052 ns/op 5377 B/op 32 allocs/op
153+
BenchmarkHTTPRouter_APIAll-8 500000 3716 ns/op 640 B/op 16 allocs/op
154+
155+
BenchmarkXhandler_Param1-8 5000000 271 ns/op 128 B/op 4 allocs/op
156+
BenchmarkChi_Param1-8 2000000 620 ns/op 432 B/op 6 allocs/op
157+
BenchmarkGoji_Param1-8 3000000 522 ns/op 336 B/op 2 allocs/op
158+
BenchmarkHTTPRouter_Param1-8 20000000 112 ns/op 32 B/op 1 allocs/op
159+
160+
BenchmarkXhandler_Param5-8 3000000 414 ns/op 256 B/op 4 allocs/op
161+
BenchmarkChi_Param5-8 1000000 1204 ns/op 432 B/op 6 allocs/op
162+
BenchmarkGoji_Param5-8 2000000 847 ns/op 336 B/op 2 allocs/op
163+
BenchmarkHTTPRouter_Param5-8 5000000 247 ns/op 160 B/op 1 allocs/op
164+
165+
BenchmarkXhandler_Param20-8 2000000 747 ns/op 736 B/op 4 allocs/op
166+
BenchmarkChi_Param20-8 2000000 746 ns/op 736 B/op 4 allocs/op
167+
BenchmarkGoji_Param20-8 500000 2439 ns/op 1247 B/op 2 allocs/op
168+
BenchmarkHTTPRouter_Param20-8 3000000 585 ns/op 640 B/op 1 allocs/op
169+
170+
BenchmarkXhandler_ParamWrite-8 5000000 404 ns/op 144 B/op 5 allocs/op
171+
BenchmarkChi_ParamWrite-8 3000000 407 ns/op 144 B/op 5 allocs/op
172+
BenchmarkGoji_ParamWrite-8 2000000 594 ns/op 336 B/op 2 allocs/op
173+
BenchmarkHTTPRouter_ParamWrite-8 10000000 166 ns/op 32 B/op 1 allocs/op
174+
```
175+
176+
You can run this benchmark by using `go test -bench=.` in `xmux`'s root.
177+
178+
179+
## Licenses
180+
181+
All source code is licensed under the [MIT License](https://raw.github.com/rs/xhandler/master/LICENSE).
182+
183+
Xmux is forked from [httprouter](https://github.com/julienschmidt/httprouter) with [BSD License](https://github.com/julienschmidt/httprouter/blob/master/LICENSE).

0 commit comments

Comments
 (0)