Skip to content

Commit 539d2db

Browse files
author
Igor Dolzhikov
committed
Merge branch 'hotfix/0.3.2'
2 parents 37ba690 + 6afaac6 commit 539d2db

File tree

7 files changed

+435
-57
lines changed

7 files changed

+435
-57
lines changed

bench_test.go

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
package router
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
)
8+
9+
type route struct {
10+
method string
11+
path string
12+
}
13+
14+
var staticRoutes = []route{
15+
{"GET", "/"},
16+
{"GET", "/cmd.html"},
17+
{"GET", "/code.html"},
18+
{"GET", "/contrib.html"},
19+
{"GET", "/contribute.html"},
20+
{"GET", "/debugging_with_gdb.html"},
21+
{"GET", "/docs.html"},
22+
{"GET", "/effective_go.html"},
23+
{"GET", "/files.log"},
24+
{"GET", "/gccgo_contribute.html"},
25+
{"GET", "/gccgo_install.html"},
26+
{"GET", "/go-logo-black.png"},
27+
{"GET", "/go-logo-blue.png"},
28+
{"GET", "/go-logo-white.png"},
29+
{"GET", "/go1.1.html"},
30+
{"GET", "/go1.2.html"},
31+
{"GET", "/go1.html"},
32+
{"GET", "/go1compat.html"},
33+
{"GET", "/go_faq.html"},
34+
{"GET", "/go_mem.html"},
35+
{"GET", "/go_spec.html"},
36+
{"GET", "/help.html"},
37+
{"GET", "/ie.css"},
38+
{"GET", "/install-source.html"},
39+
{"GET", "/install.html"},
40+
{"GET", "/logo-153x55.png"},
41+
{"GET", "/Makefile"},
42+
{"GET", "/root.html"},
43+
{"GET", "/share.png"},
44+
{"GET", "/sieve.gif"},
45+
{"GET", "/tos.html"},
46+
{"GET", "/articles/"},
47+
{"GET", "/articles/go_command.html"},
48+
{"GET", "/articles/index.html"},
49+
{"GET", "/articles/wiki/"},
50+
{"GET", "/articles/wiki/edit.html"},
51+
{"GET", "/articles/wiki/final-noclosure.go"},
52+
{"GET", "/articles/wiki/final-noerror.go"},
53+
{"GET", "/articles/wiki/final-parsetemplate.go"},
54+
{"GET", "/articles/wiki/final-template.go"},
55+
{"GET", "/articles/wiki/final.go"},
56+
{"GET", "/articles/wiki/get.go"},
57+
{"GET", "/articles/wiki/http-sample.go"},
58+
{"GET", "/articles/wiki/index.html"},
59+
{"GET", "/articles/wiki/Makefile"},
60+
{"GET", "/articles/wiki/notemplate.go"},
61+
{"GET", "/articles/wiki/part1-noerror.go"},
62+
{"GET", "/articles/wiki/part1.go"},
63+
{"GET", "/articles/wiki/part2.go"},
64+
{"GET", "/articles/wiki/part3-errorhandling.go"},
65+
{"GET", "/articles/wiki/part3.go"},
66+
{"GET", "/articles/wiki/test.bash"},
67+
{"GET", "/articles/wiki/test_edit.good"},
68+
{"GET", "/articles/wiki/test_Test.txt.good"},
69+
{"GET", "/articles/wiki/test_view.good"},
70+
{"GET", "/articles/wiki/view.html"},
71+
{"GET", "/codewalk/"},
72+
{"GET", "/codewalk/codewalk.css"},
73+
{"GET", "/codewalk/codewalk.js"},
74+
{"GET", "/codewalk/codewalk.xml"},
75+
{"GET", "/codewalk/functions.xml"},
76+
{"GET", "/codewalk/markov.go"},
77+
{"GET", "/codewalk/markov.xml"},
78+
{"GET", "/codewalk/pig.go"},
79+
{"GET", "/codewalk/popout.png"},
80+
{"GET", "/codewalk/run"},
81+
{"GET", "/codewalk/sharemem.xml"},
82+
{"GET", "/codewalk/urlpoll.go"},
83+
{"GET", "/devel/"},
84+
{"GET", "/devel/release.html"},
85+
{"GET", "/devel/weekly.html"},
86+
{"GET", "/gopher/"},
87+
{"GET", "/gopher/appenginegopher.jpg"},
88+
{"GET", "/gopher/appenginegophercolor.jpg"},
89+
{"GET", "/gopher/appenginelogo.gif"},
90+
{"GET", "/gopher/bumper.png"},
91+
{"GET", "/gopher/bumper192x108.png"},
92+
{"GET", "/gopher/bumper320x180.png"},
93+
{"GET", "/gopher/bumper480x270.png"},
94+
{"GET", "/gopher/bumper640x360.png"},
95+
{"GET", "/gopher/doc.png"},
96+
{"GET", "/gopher/frontpage.png"},
97+
{"GET", "/gopher/gopherbw.png"},
98+
{"GET", "/gopher/gophercolor.png"},
99+
{"GET", "/gopher/gophercolor16x16.png"},
100+
{"GET", "/gopher/help.png"},
101+
{"GET", "/gopher/pkg.png"},
102+
{"GET", "/gopher/project.png"},
103+
{"GET", "/gopher/ref.png"},
104+
{"GET", "/gopher/run.png"},
105+
{"GET", "/gopher/talks.png"},
106+
{"GET", "/gopher/pencil/"},
107+
{"GET", "/gopher/pencil/gopherhat.jpg"},
108+
{"GET", "/gopher/pencil/gopherhelmet.jpg"},
109+
{"GET", "/gopher/pencil/gophermega.jpg"},
110+
{"GET", "/gopher/pencil/gopherrunning.jpg"},
111+
{"GET", "/gopher/pencil/gopherswim.jpg"},
112+
{"GET", "/gopher/pencil/gopherswrench.jpg"},
113+
{"GET", "/play/"},
114+
{"GET", "/play/fib.go"},
115+
{"GET", "/play/hello.go"},
116+
{"GET", "/play/life.go"},
117+
{"GET", "/play/peano.go"},
118+
{"GET", "/play/pi.go"},
119+
{"GET", "/play/sieve.go"},
120+
{"GET", "/play/solitaire.go"},
121+
{"GET", "/play/tree.go"},
122+
{"GET", "/progs/"},
123+
{"GET", "/progs/cgo1.go"},
124+
{"GET", "/progs/cgo2.go"},
125+
{"GET", "/progs/cgo3.go"},
126+
{"GET", "/progs/cgo4.go"},
127+
{"GET", "/progs/defer.go"},
128+
{"GET", "/progs/defer.out"},
129+
{"GET", "/progs/defer2.go"},
130+
{"GET", "/progs/defer2.out"},
131+
{"GET", "/progs/eff_bytesize.go"},
132+
{"GET", "/progs/eff_bytesize.out"},
133+
{"GET", "/progs/eff_qr.go"},
134+
{"GET", "/progs/eff_sequence.go"},
135+
{"GET", "/progs/eff_sequence.out"},
136+
{"GET", "/progs/eff_unused1.go"},
137+
{"GET", "/progs/eff_unused2.go"},
138+
{"GET", "/progs/error.go"},
139+
{"GET", "/progs/error2.go"},
140+
{"GET", "/progs/error3.go"},
141+
{"GET", "/progs/error4.go"},
142+
{"GET", "/progs/go1.go"},
143+
{"GET", "/progs/gobs1.go"},
144+
{"GET", "/progs/gobs2.go"},
145+
{"GET", "/progs/image_draw.go"},
146+
{"GET", "/progs/image_package1.go"},
147+
{"GET", "/progs/image_package1.out"},
148+
{"GET", "/progs/image_package2.go"},
149+
{"GET", "/progs/image_package2.out"},
150+
{"GET", "/progs/image_package3.go"},
151+
{"GET", "/progs/image_package3.out"},
152+
{"GET", "/progs/image_package4.go"},
153+
{"GET", "/progs/image_package4.out"},
154+
{"GET", "/progs/image_package5.go"},
155+
{"GET", "/progs/image_package5.out"},
156+
{"GET", "/progs/image_package6.go"},
157+
{"GET", "/progs/image_package6.out"},
158+
{"GET", "/progs/interface.go"},
159+
{"GET", "/progs/interface2.go"},
160+
{"GET", "/progs/interface2.out"},
161+
{"GET", "/progs/json1.go"},
162+
{"GET", "/progs/json2.go"},
163+
{"GET", "/progs/json2.out"},
164+
{"GET", "/progs/json3.go"},
165+
{"GET", "/progs/json4.go"},
166+
{"GET", "/progs/json5.go"},
167+
{"GET", "/progs/run"},
168+
{"GET", "/progs/slices.go"},
169+
{"GET", "/progs/timeout1.go"},
170+
{"GET", "/progs/timeout2.go"},
171+
{"GET", "/progs/update.bash"},
172+
}
173+
174+
func init() {
175+
176+
staticRouter = loadRouter(staticRoutes)
177+
}
178+
179+
var (
180+
staticRouter http.Handler
181+
)
182+
183+
func routerHandle(_ *Control) {}
184+
185+
func loadRouter(routes []route) http.Handler {
186+
r := New()
187+
for _, route := range routes {
188+
r.Handle(route.method, route.path, routerHandle)
189+
}
190+
return r
191+
}
192+
193+
func benchRequest(b *testing.B, router http.Handler, r *http.Request) {
194+
w := httptest.NewRecorder()
195+
u := r.URL
196+
rq := u.RawQuery
197+
r.RequestURI = u.RequestURI()
198+
199+
b.ReportAllocs()
200+
b.ResetTimer()
201+
202+
for i := 0; i < b.N; i++ {
203+
u.RawQuery = rq
204+
router.ServeHTTP(w, r)
205+
}
206+
}
207+
208+
func benchRoutes(b *testing.B, router http.Handler, routes []route) {
209+
w := httptest.NewRecorder()
210+
r, _ := http.NewRequest("GET", "/", nil)
211+
u := r.URL
212+
rq := u.RawQuery
213+
214+
b.ReportAllocs()
215+
b.ResetTimer()
216+
217+
for i := 0; i < b.N; i++ {
218+
for _, route := range routes {
219+
r.Method = route.method
220+
r.RequestURI = route.path
221+
u.Path = route.path
222+
u.RawQuery = rq
223+
router.ServeHTTP(w, r)
224+
}
225+
}
226+
}
227+
228+
func BenchmarkRouter_StaticAll(b *testing.B) {
229+
benchRoutes(b, staticRouter, staticRoutes)
230+
}
231+
232+
func BenchmarkRouter_Param(b *testing.B) {
233+
router := New()
234+
router.Handle("GET", "/user/:name", routerHandle)
235+
236+
r, _ := http.NewRequest("GET", "/user/jhon", nil)
237+
benchRequest(b, router, r)
238+
}
239+
240+
const fiveColon = "/:a/:b/:c/:d/:e"
241+
const fiveRoute = "/a/b/c/d/e"
242+
243+
func BenchmarkRouter_Param5(b *testing.B) {
244+
router := New()
245+
router.Handle("GET", fiveColon, routerHandle)
246+
247+
r, _ := http.NewRequest("GET", fiveRoute, nil)
248+
benchRequest(b, router, r)
249+
}
250+
251+
const twentyColon = "/:a/:b/:c/:d/:e/:f/:g/:h/:i/:j/:k/:l/:m/:n/:o/:p/:q/:r/:s/:t"
252+
const twentyRoute = "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t"
253+
254+
func BenchmarkRouter_Param20(b *testing.B) {
255+
router := New()
256+
router.Handle("GET", twentyColon, routerHandle)
257+
258+
r, _ := http.NewRequest("GET", twentyRoute, nil)
259+
benchRequest(b, router, r)
260+
}
261+
262+
func BenchmarkRouter_ParamWrite(b *testing.B) {
263+
router := New()
264+
router.Handle("GET", "/user/:name", routerHandle)
265+
266+
r, _ := http.NewRequest("GET", "/user/jhon", nil)
267+
benchRequest(b, router, r)
268+
}

control.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014 Igor Dolzhikov. All rights reserved.
1+
// Copyright 2015 Igor Dolzhikov. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

@@ -12,6 +12,7 @@ import (
1212
"time"
1313
)
1414

15+
// Default content types
1516
const (
1617
// MIMEJSON - "Content-type" for JSON
1718
MIMEJSON = "application/json"
@@ -29,6 +30,9 @@ type Control struct {
2930
// Writer is an adapter which allows the usage of a http.ResponseWriter as standard writer
3031
Writer http.ResponseWriter
3132

33+
// User content type
34+
ContentType string
35+
3236
// Code of HTTP status
3337
code int
3438

@@ -93,9 +97,14 @@ func (c *Control) UseTimer() {
9397
// Body renders the given data into the response body
9498
func (c *Control) Body(data interface{}) {
9599
var content []byte
100+
96101
if str, ok := data.(string); ok {
97-
c.Writer.Header().Add("Content-type", MIMETEXT)
98102
content = []byte(str)
103+
if c.ContentType != "" {
104+
c.Writer.Header().Add("Content-type", c.ContentType)
105+
} else {
106+
c.Writer.Header().Add("Content-type", MIMETEXT)
107+
}
99108
} else {
100109
if !c.timer.IsZero() {
101110
took := time.Now()

control_test.go

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
// Copyright 2014 Igor Dolzhikov. All rights reserved.
2-
// Use of this source code is governed by a BSD-style
3-
// license that can be found in the LICENSE file.
4-
51
package router
62

73
import (
84
"net/http"
5+
"net/http/httptest"
96
"testing"
107
)
118

@@ -15,24 +12,6 @@ var parameters = []Param{
1512
{"gender", "M"},
1613
}
1714

18-
type testResponseWriter struct {
19-
code int
20-
data []byte
21-
}
22-
23-
func (trw *testResponseWriter) Header() http.Header {
24-
return http.Header{}
25-
}
26-
27-
func (trw *testResponseWriter) Write(data []byte) (int, error) {
28-
trw.data = data
29-
return len(data), nil
30-
}
31-
32-
func (trw *testResponseWriter) WriteHeader(code int) {
33-
trw.code = code
34-
}
35-
3615
func TestControlSetGet(t *testing.T) {
3716

3817
c := new(Control)
@@ -58,20 +37,34 @@ func TestControlCode(t *testing.T) {
5837
}
5938

6039
func TestControlBody(t *testing.T) {
61-
trw := new(testResponseWriter)
6240
req, err := http.NewRequest("GET", "hello/:name", nil)
6341
if err != nil {
6442
t.Error("Error creting new request")
6543
}
6644
c := new(Control)
45+
trw := httptest.NewRecorder()
6746
c.Writer, c.Request = trw, req
6847
c.Body("Hello")
69-
if string(trw.data) != "Hello" {
70-
t.Error("Expected", "Hello", "got", string(trw.data))
48+
if trw.Body.String() != "Hello" {
49+
t.Error("Expected", "Hello", "got", trw.Body.String())
50+
}
51+
trw = httptest.NewRecorder()
52+
c.Writer = trw
53+
c.Body(123)
54+
if trw.Body.String() != "123" {
55+
t.Error("Expected", "123", "got", trw.Body.String())
56+
}
57+
trw = httptest.NewRecorder()
58+
c.Writer = trw
59+
c.Body(123.1)
60+
if trw.Body.String() != "123.1" {
61+
t.Error("Expected", "123.1", "got", trw.Body.String())
7162
}
63+
trw = httptest.NewRecorder()
64+
c.Writer = trw
7265
c.Body(parameters)
73-
if string(trw.data) != testJSONData {
74-
t.Error("Expected", testJSONData, "got", string(trw.data))
66+
if trw.Body.String() != testJSONData {
67+
t.Error("Expected", testJSONData, "got", trw.Body.String())
7568
}
7669
}
7770

0 commit comments

Comments
 (0)