Skip to content

Commit

Permalink
Refactor parameters API
Browse files Browse the repository at this point in the history
  • Loading branch information
Olivier Poitrey committed Dec 13, 2015
1 parent 0f8b338 commit 9a538f0
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 67 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func main() {

// Use c.Handler to terminate the chain with your final handler
mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome %s!", xmux.URLParams(ctx).Get("name"))
fmt.Fprintf(w, "Welcome %s!", xmux.Params(ctx).Get("name"))
}))

if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil {
Expand Down
38 changes: 20 additions & 18 deletions xmux/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// }
//
// func Hello(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// fmt.Fprintf(w, "hello, %s!\n", xmux.URLParams(ctx).Get("name"))
// fmt.Fprintf(w, "hello, %s!\n", xmux.Params(ctx).Get("name"))
// }
//
// func main() {
Expand Down Expand Up @@ -65,8 +65,8 @@
// /files no match, but the router would redirect
//
// The value of parameters is saved as aParams type saved into the context.
// Parameters can be retrieved by name using xhandler.URLParams(ctx).Get(name) method:
// user := xhandler.URLParams(ctx).Get("user") // defined by :user or *user
// Parameters can be retrieved by name using xhandler.Params(ctx).Get(name) method:
// user := xhandler.Params(ctx).Get("user") // defined by :user or *user
package xmux

import (
Expand Down Expand Up @@ -125,17 +125,19 @@ type Mux struct {
PanicHandler func(context.Context, http.ResponseWriter, *http.Request, interface{})
}

// Params holds URL parameters.
type Params struct {
params []struct {
key string
value string
}
// ParamHolder holds URL parameters.
type ParamHolder struct {
params []param
}

type param struct {
key string
value string
}

// Get returns the value of the first Param which key matches the given name.
// If no matching Param is found, an empty string is returned.
func (ps Params) Get(name string) string {
func (ps ParamHolder) Get(name string) string {
for i := range ps.params {
if ps.params[i].key == name {
return ps.params[i].value
Expand All @@ -146,20 +148,20 @@ func (ps Params) Get(name string) string {

type key int

const paramKey key = iota
const paramsKey key = iota

var emptyParams = Params{}
var emptyParams = ParamHolder{}

func newParamContext(ctx context.Context, p Params) context.Context {
return context.WithValue(ctx, paramKey, p)
func newParamContext(ctx context.Context, p ParamHolder) context.Context {
return context.WithValue(ctx, paramsKey, p)
}

// URLParams returns URL parameters stored in context
func URLParams(ctx context.Context) Params {
// Params returns URL parameters stored in context
func Params(ctx context.Context) ParamHolder {
if ctx == nil {
return emptyParams
}
if p, ok := ctx.Value(paramKey).(Params); ok {
if p, ok := ctx.Value(paramsKey).(ParamHolder); ok {
return p
}
return emptyParams
Expand Down Expand Up @@ -246,7 +248,7 @@ func (mux *Mux) recv(ctx context.Context, w http.ResponseWriter, r *http.Request
// If the path was found, it returns the handle function and the path parameter
// values. Otherwise the third return value indicates whether a redirection to
// the same path with an extra / without the trailing slash should be performed.
func (mux *Mux) Lookup(method, path string) (xhandler.HandlerC, Params, bool) {
func (mux *Mux) Lookup(method, path string) (xhandler.HandlerC, ParamHolder, bool) {
if root := mux.trees[method]; root != nil {
return root.getValue(path)
}
Expand Down
2 changes: 1 addition & 1 deletion xmux/mux_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type route struct {
var httpHandlerC = xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {})

var xhandlerWrite = xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
io.WriteString(w, URLParams(ctx).Get("name"))
io.WriteString(w, Params(ctx).Get("name"))
})

func loadXhandler(routes []route) xhandler.HandlerC {
Expand Down
2 changes: 1 addition & 1 deletion xmux/mux_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func ExampleMux() {

// Use c.Handler to terminate the chain with your final handler
mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome %s!", xmux.URLParams(ctx).Get("name"))
fmt.Fprintf(w, "Welcome %s!", xmux.Params(ctx).Get("name"))
}))

if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil {
Expand Down
36 changes: 11 additions & 25 deletions xmux/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@ func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
func (m *mockResponseWriter) WriteHeader(int) {}

func TestParams(t *testing.T) {
ps := Params{
params: []struct {
key string
value string
}{
ps := ParamHolder{
params: []param{
{"param1", "value1"},
{"param2", "value2"},
{"param3", "value3"},
Expand All @@ -46,31 +43,23 @@ func TestParams(t *testing.T) {
}

func TestParamsDup(t *testing.T) {
ps := Params{
params: []struct {
key string
value string
}{
ps := ParamHolder{
params: []param{
{"param", "value1"},
{"param", "value2"},
},
}
assert.Equal(t, "value1", ps.Get("param"))
}

func TestURLParams(t *testing.T) {
ps := Params{
params: []struct {
key string
value string
}{
{"param1", "value1"},
},
func TestCtxParams(t *testing.T) {
ps := ParamHolder{
params: []param{{"param1", "value1"}},
}
ctx := newParamContext(context.TODO(), ps)
assert.Equal(t, "value1", URLParams(ctx).Get("param1"))
assert.Equal(t, emptyParams, URLParams(context.TODO()))
assert.Equal(t, emptyParams, URLParams(nil))
assert.Equal(t, "value1", Params(ctx).Get("param1"))
assert.Equal(t, emptyParams, Params(context.TODO()))
assert.Equal(t, emptyParams, Params(nil))
}

func TestMux(t *testing.T) {
Expand All @@ -79,10 +68,7 @@ func TestMux(t *testing.T) {
routed := false
mux.Handle("GET", "/user/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
routed = true
assert.Equal(t, Params{params: []struct {
key string
value string
}{{"name", "gopher"}}}, URLParams(ctx))
assert.Equal(t, ParamHolder{params: []param{{"name", "gopher"}}}, Params(ctx))
}))

w := new(mockResponseWriter)
Expand Down
32 changes: 13 additions & 19 deletions xmux/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type nodeType uint8
const (
static nodeType = iota // default
root
param
parameter
catchAll
)

Expand Down Expand Up @@ -161,7 +161,7 @@ func (n *node) addRoute(path string, handler xhandler.HandlerC) {
c := path[0]

// slash after param
if n.nType == param && c == '/' && len(n.children) == 1 {
if n.nType == parameter && c == '/' && len(n.children) == 1 {
n = n.children[0]
n.priority++
continue walk
Expand Down Expand Up @@ -247,7 +247,7 @@ func (n *node) insertChild(numParams uint8, path, fullPath string, handler xhand
}

child := &node{
nType: param,
nType: parameter,
maxParams: numParams,
}
n.children = []*node{child}
Expand Down Expand Up @@ -322,13 +322,13 @@ func (n *node) insertChild(numParams uint8, path, fullPath string, handler xhand
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
// made if a handler exists with an extra (without the) trailing slash for the
// given path.
func (n *node) getValue(path string) (handler xhandler.HandlerC, p Params, tsr bool) {
func (n *node) getValue(path string) (handler xhandler.HandlerC, p ParamHolder, tsr bool) {
walk: // Outer loop for walking the tree
for {
if len(path) > len(n.path) {
if path[:len(n.path)] == n.path {
path = path[len(n.path):]
// If this node does not have a wildcard (param or catchAll)
// If this node does not have a wildcard (parameter or catchAll)
// child, we can just look up the next child node and continue
// to walk down the tree
if !n.wildChild {
Expand All @@ -351,8 +351,8 @@ walk: // Outer loop for walking the tree
// handle wildcard child
n = n.children[0]
switch n.nType {
case param:
// find param end (either '/' or path end)
case parameter:
// find parameter end (either '/' or path end)
end := 0
for end < len(path) && path[end] != '/' {
end++
Expand All @@ -361,10 +361,7 @@ walk: // Outer loop for walking the tree
// save param value
if p.params == nil {
// lazy allocation
p.params = make([]struct {
key string
value string
}, 0, n.maxParams)
p.params = make([]param, 0, n.maxParams)
}
i := len(p.params)
p.params = p.params[:i+1] // expand slice within preallocated capacity
Expand Down Expand Up @@ -405,10 +402,7 @@ walk: // Outer loop for walking the tree
// save param value
if p.params == nil {
// lazy allocation
p.params = make([]struct {
key string
value string
}, 0, n.maxParams)
p.params = make([]param, 0, n.maxParams)
}
i := len(p.params)
p.params = p.params[:i+1] // expand slice within preallocated capacity
Expand Down Expand Up @@ -469,7 +463,7 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa
ciPath = append(ciPath, n.path...)

if len(path) > 0 {
// If this node does not have a wildcard (param or catchAll) child,
// If this node does not have a wildcard (parameter or catchAll) child,
// we can just look up the next child node and continue to walk down
// the tree
if !n.wildChild {
Expand All @@ -493,14 +487,14 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa

n = n.children[0]
switch n.nType {
case param:
// find param end (either '/' or path end)
case parameter:
// find parameter end (either '/' or path end)
k := 0
for k < len(path) && path[k] != '/' {
k++
}

// add param value to case insensitive path
// add parameter value to case insensitive path
ciPath = append(ciPath, path[:k]...)

// we need to go deeper!
Expand Down
4 changes: 2 additions & 2 deletions xmux/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type testRequests []struct {
path string
nilHandler bool
route string
ps Params
ps ParamHolder
}

func checkRequests(t *testing.T, tree *node, requests testRequests) {
Expand Down Expand Up @@ -158,7 +158,7 @@ func TestTreeAddAndGet(t *testing.T) {
checkMaxParams(t, tree)
}

func newParams(kv ...string) (ps Params) {
func newParams(kv ...string) (ps ParamHolder) {
for i, l := 0, len(kv); i < l; i += 2 {
ps.params = append(ps.params, struct {
key string
Expand Down

0 comments on commit 9a538f0

Please sign in to comment.