Skip to content

Commit e12e5f4

Browse files
committed
Merge branch 'release-0.2.0'
2 parents 12e2f88 + 0208449 commit e12e5f4

6 files changed

Lines changed: 218 additions & 18 deletions

File tree

README.md

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A simple, compact and fast router package for use with Go services to process HT
88

99
### Examples
1010

11-
Simplest example (serve static route):
11+
- Simplest example (serve static route):
1212
```go
1313
package main
1414

@@ -29,7 +29,7 @@ func main() {
2929
}
3030
```
3131

32-
Check it:
32+
- Check it:
3333
```sh
3434
curl -i http://localhost:8888/hello/
3535

@@ -41,7 +41,7 @@ Content-Length: 11
4141
Hello world
4242
```
4343

44-
Serve dynamic route with parameter:
44+
- Serve dynamic route with parameter:
4545
```go
4646
func main() {
4747
r := router.New()
@@ -54,7 +54,7 @@ func main() {
5454
}
5555
```
5656

57-
Check it:
57+
- Check it:
5858
```sh
5959
curl -i http://localhost:8888/hello/John
6060

@@ -66,7 +66,7 @@ Content-Length: 10
6666
Hello John
6767
```
6868

69-
Checks JSON Content-Type automatically:
69+
- Checks JSON Content-Type automatically:
7070
```go
7171
func main() {
7272
r := router.New()
@@ -85,7 +85,7 @@ func main() {
8585
}
8686
```
8787

88-
Check it:
88+
- Check it:
8989
```sh
9090
curl -i http://localhost:8888/api/v1/settings/database/testdb
9191

@@ -103,6 +103,47 @@ Content-Length: 102
103103
}
104104
```
105105

106+
- Use timer to calculate the elapsed time of request handling:
107+
```go
108+
func main() {
109+
r := router.New()
110+
r.GET("/api/v1/settings/database/:db", func(c *router.Control) {
111+
data := map[string]map[string]string{
112+
"Database settings": {
113+
"database": c.Get(":db"),
114+
"host": "localhost",
115+
"port": "3306",
116+
},
117+
}
118+
c.UseTimer()
119+
c.Code(200).Body(data)
120+
})
121+
// Listen and serve on 0.0.0.0:8888
122+
r.Listen(":8888")
123+
}
124+
```
125+
126+
- Check it:
127+
```sh
128+
curl -i http://localhost:8888/api/v1/settings/database/testdb
129+
130+
HTTP/1.1 200 OK
131+
Content-Type: application/json
132+
Date: Sun, 17 Aug 2014 13:26:05 GMT
133+
Content-Length: 143
134+
135+
{
136+
"took": 422,
137+
"data": {
138+
"Database settings": {
139+
"database": "testdb",
140+
"host": "localhost",
141+
"port": "3306"
142+
}
143+
}
144+
}
145+
```
146+
106147
## Author
107148

108149
[Igor Dolzhikov](https://github.com/takama)

control.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,34 @@ package router
77
import (
88
"encoding/json"
99
"net/http"
10+
"time"
1011
)
1112

1213
const (
14+
// MIMEJSON - "Content-type" for JSON
1315
MIMEJSON = "application/json"
16+
// MIMETEXT - "Content-type" for TEXT
1417
MIMETEXT = "text/plain"
1518
)
1619

1720
// Control allows us to pass variables between middleware,
1821
// assign Http codes and render a Body.
1922
type Control struct {
23+
24+
// Request is an adapter which allows the usage of a http.Request as standard request
2025
Request *http.Request
21-
Writer http.ResponseWriter
22-
code int
23-
Params []Param
26+
27+
// Writer is an adapter which allows the usage of a http.ResponseWriter as standard writer
28+
Writer http.ResponseWriter
29+
30+
// Code of HTTP status
31+
code int
32+
33+
// Params is set of parameters
34+
Params []Param
35+
36+
// timer used to calculate a elapsed time for handler and writing it in a response
37+
timer time.Time
2438
}
2539

2640
// Param is a URL parameter which represents as key and value.
@@ -29,6 +43,12 @@ type Param struct {
2943
Value string
3044
}
3145

46+
// Header used if non-zero Control.timer initialized by UserTimer() method
47+
type Header struct {
48+
Took time.Duration `json:"took"`
49+
Data interface{} `json:"data"`
50+
}
51+
3252
// Get returns the first value associated with the given name.
3353
// If there are no values associated with the key, an empty string is returned.
3454
func (c *Control) Get(name string) string {
@@ -54,19 +74,28 @@ func (c *Control) Code(code int) *Control {
5474
return c
5575
}
5676

77+
// UseTimer allow caalculate elapsed time of request handling
78+
func (c *Control) UseTimer() {
79+
c.timer = time.Now()
80+
}
81+
5782
// Body renders the given data into the response body
5883
func (c *Control) Body(data interface{}) {
5984
var content []byte
6085
if str, ok := data.(string); ok {
61-
c.Writer.Header().Set("Content-type", MIMETEXT)
86+
c.Writer.Header().Add("Content-type", MIMETEXT)
6287
content = []byte(str)
6388
} else {
89+
if !c.timer.IsZero() {
90+
took := time.Now()
91+
data = &Header{Took: took.Sub(c.timer), Data: data}
92+
}
6493
jsn, err := json.MarshalIndent(data, "", " ")
6594
if err != nil {
6695
c.Writer.WriteHeader(http.StatusInternalServerError)
6796
return
6897
}
69-
c.Writer.Header().Set("Content-type", MIMEJSON)
98+
c.Writer.Header().Add("Content-type", MIMEJSON)
7099
content = jsn
71100
}
72101
if c.code > 0 {

control_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"testing"
1010
)
1111

12-
var parameters []Param = []Param{
12+
var parameters = []Param{
1313
{"name", "John"},
1414
{"age", "32"},
1515
{"gender", "M"},

example/example.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func main() {
1818
"port": "3306",
1919
},
2020
}
21+
c.UseTimer()
2122
c.Code(200).Body(data)
2223
})
2324
// Listen and serve on 0.0.0.0:8888

router.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// license that can be found in the LICENSE file.
44

55
/*
6-
Package router 0.1.1 provides fast HTTP request router.
6+
Package router 0.2.0 provides fast HTTP request router.
77
88
The router matches incoming requests by the request method and the path.
99
If a handle is registered for this path and method, the router delegates the
@@ -71,15 +71,26 @@ package router
7171
import (
7272
"log"
7373
"net/http"
74+
"strings"
7475
)
7576

7677
// Router represents a multiplexer for HTTP requests.
7778
type Router struct {
79+
// List of handlers which accociated with known http methods (GET, POST ...)
7880
handlers map[string]*parser
81+
7982
// NotFound is called when unknown HTTP method or a handler not found.
8083
// If it is not set, http.NotFound is used.
8184
// Please overwrite this if need your own NotFound handler.
8285
NotFound http.HandlerFunc
86+
87+
// PanicHandler is called when panic happen.
88+
// The handler prevents your server from crashing and should be used to return
89+
// http status code http.StatusInternalServerError (500)
90+
PanicHandler func(c *Control)
91+
92+
// Logger activates logging for each requests
93+
Logger bool
8394
}
8495

8596
// Handle type is aliased to type of handler function.
@@ -158,6 +169,19 @@ func (r *Router) Listen(hostPort string) {
158169

159170
// ServeHTTP implements http.Handler interface.
160171
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
172+
defer func() {
173+
if recovery := recover(); recovery != nil {
174+
c := &Control{Request: req, Writer: w}
175+
if r.PanicHandler != nil {
176+
r.PanicHandler(c)
177+
} else {
178+
log.Println("Recovered in handler:", req.Method, req.URL.Path)
179+
}
180+
}
181+
}()
182+
if r.Logger {
183+
log.Println(req.Method, req.URL.Path)
184+
}
161185
if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
162186
c := &Control{Request: req, Writer: w}
163187
if len(params) > 0 {
@@ -166,11 +190,22 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
166190
handle(c)
167191
return
168192
}
193+
allowed := make([]string, 0, len(r.handlers))
194+
for method, parser := range r.handlers {
195+
if _, _, ok := parser.get(req.URL.Path); ok {
196+
allowed = append(allowed, method)
197+
}
198+
}
169199

170-
if r.NotFound != nil {
171-
r.NotFound(w, req)
172-
} else {
173-
http.NotFound(w, req)
200+
if len(allowed) == 0 {
201+
if r.NotFound != nil {
202+
r.NotFound(w, req)
203+
} else {
204+
http.NotFound(w, req)
205+
}
206+
return
174207
}
175-
return
208+
209+
w.Header().Add("Allow", strings.Join(allowed, ", "))
210+
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
176211
}

0 commit comments

Comments
 (0)