瀏覽代碼

Update gorilla/mux, gorilla/context, and kr/pty deps

Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
Tianon Gravi 11 年之前
父節點
當前提交
d98af1236c
共有 30 個文件被更改,包括 659 次插入136 次删除
  1. 3 3
      hack/vendor.sh
  2. 7 0
      vendor/src/github.com/gorilla/context/.travis.yml
  3. 1 0
      vendor/src/github.com/gorilla/context/README.md
  4. 32 10
      vendor/src/github.com/gorilla/context/context.go
  5. 95 0
      vendor/src/github.com/gorilla/context/context_test.go
  6. 1 1
      vendor/src/github.com/gorilla/context/doc.go
  7. 7 0
      vendor/src/github.com/gorilla/mux/.travis.yml
  8. 1 0
      vendor/src/github.com/gorilla/mux/README.md
  9. 1 1
      vendor/src/github.com/gorilla/mux/doc.go
  10. 25 7
      vendor/src/github.com/gorilla/mux/mux.go
  11. 200 25
      vendor/src/github.com/gorilla/mux/mux_test.go
  12. 4 4
      vendor/src/github.com/gorilla/mux/old_test.go
  13. 10 7
      vendor/src/github.com/gorilla/mux/regexp.go
  14. 12 4
      vendor/src/github.com/gorilla/mux/route.go
  15. 11 0
      vendor/src/github.com/kr/pty/ioctl.go
  16. 39 0
      vendor/src/github.com/kr/pty/ioctl_bsd.go
  17. 42 0
      vendor/src/github.com/kr/pty/ioctl_linux.go
  18. 19 0
      vendor/src/github.com/kr/pty/mktypes.bash
  19. 8 17
      vendor/src/github.com/kr/pty/pty_darwin.go
  20. 40 20
      vendor/src/github.com/kr/pty/pty_freebsd.go
  21. 9 21
      vendor/src/github.com/kr/pty/pty_linux.go
  22. 0 16
      vendor/src/github.com/kr/pty/pty_unsupported.go
  23. 10 0
      vendor/src/github.com/kr/pty/types.go
  24. 15 0
      vendor/src/github.com/kr/pty/types_freebsd.go
  25. 9 0
      vendor/src/github.com/kr/pty/ztypes_386.go
  26. 9 0
      vendor/src/github.com/kr/pty/ztypes_amd64.go
  27. 9 0
      vendor/src/github.com/kr/pty/ztypes_arm.go
  28. 13 0
      vendor/src/github.com/kr/pty/ztypes_freebsd_386.go
  29. 14 0
      vendor/src/github.com/kr/pty/ztypes_freebsd_amd64.go
  30. 13 0
      vendor/src/github.com/kr/pty/ztypes_freebsd_arm.go

+ 3 - 3
hack/vendor.sh

@@ -39,11 +39,11 @@ clone() {
 	echo done
 }
 
-clone git github.com/kr/pty 98c7b80083
+clone git github.com/kr/pty 67e2db24c8
 
-clone git github.com/gorilla/context 708054d61e5
+clone git github.com/gorilla/context b06ed15e1c
 
-clone git github.com/gorilla/mux 9b36453141c
+clone git github.com/gorilla/mux 136d54f81f
 
 clone git github.com/syndtr/gocapability 3c85049eae
 

+ 7 - 0
vendor/src/github.com/gorilla/context/.travis.yml

@@ -0,0 +1,7 @@
+language: go
+
+go:
+  - 1.0
+  - 1.1
+  - 1.2
+  - tip

+ 1 - 0
vendor/src/github.com/gorilla/context/README.md

@@ -1,5 +1,6 @@
 context
 =======
+[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
 
 gorilla/context is a general purpose registry for global request variables.
 

+ 32 - 10
vendor/src/github.com/gorilla/context/context.go

@@ -11,7 +11,7 @@ import (
 )
 
 var (
-	mutex sync.Mutex
+	mutex sync.RWMutex
 	data  = make(map[*http.Request]map[interface{}]interface{})
 	datat = make(map[*http.Request]int64)
 )
@@ -19,42 +19,64 @@ var (
 // Set stores a value for a given key in a given request.
 func Set(r *http.Request, key, val interface{}) {
 	mutex.Lock()
-	defer mutex.Unlock()
 	if data[r] == nil {
 		data[r] = make(map[interface{}]interface{})
 		datat[r] = time.Now().Unix()
 	}
 	data[r][key] = val
+	mutex.Unlock()
 }
 
 // Get returns a value stored for a given key in a given request.
 func Get(r *http.Request, key interface{}) interface{} {
-	mutex.Lock()
-	defer mutex.Unlock()
+	mutex.RLock()
 	if data[r] != nil {
+		mutex.RUnlock()
 		return data[r][key]
 	}
+	mutex.RUnlock()
 	return nil
 }
 
 // GetOk returns stored value and presence state like multi-value return of map access.
 func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
-	mutex.Lock()
-	defer mutex.Unlock()
+	mutex.RLock()
 	if _, ok := data[r]; ok {
 		value, ok := data[r][key]
+		mutex.RUnlock()
 		return value, ok
 	}
+	mutex.RUnlock()
 	return nil, false
 }
 
+// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
+func GetAll(r *http.Request) map[interface{}]interface{} {
+	mutex.RLock()
+	if context, ok := data[r]; ok {
+		mutex.RUnlock()
+		return context
+	}
+	mutex.RUnlock()
+	return nil
+}
+
+// GetAllOk returns all stored values for the request as a map. It returns not
+// ok if the request was never registered.
+func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
+	mutex.RLock()
+	context, ok := data[r]
+	mutex.RUnlock()
+	return context, ok
+}
+
 // Delete removes a value stored for a given key in a given request.
 func Delete(r *http.Request, key interface{}) {
 	mutex.Lock()
-	defer mutex.Unlock()
 	if data[r] != nil {
 		delete(data[r], key)
 	}
+	mutex.Unlock()
 }
 
 // Clear removes all values stored for a given request.
@@ -63,8 +85,8 @@ func Delete(r *http.Request, key interface{}) {
 // variables at the end of a request lifetime. See ClearHandler().
 func Clear(r *http.Request) {
 	mutex.Lock()
-	defer mutex.Unlock()
 	clear(r)
+	mutex.Unlock()
 }
 
 // clear is Clear without the lock.
@@ -84,7 +106,6 @@ func clear(r *http.Request) {
 // periodically until the problem is fixed.
 func Purge(maxAge int) int {
 	mutex.Lock()
-	defer mutex.Unlock()
 	count := 0
 	if maxAge <= 0 {
 		count = len(data)
@@ -92,13 +113,14 @@ func Purge(maxAge int) int {
 		datat = make(map[*http.Request]int64)
 	} else {
 		min := time.Now().Unix() - int64(maxAge)
-		for r, _ := range data {
+		for r := range data {
 			if datat[r] < min {
 				clear(r)
 				count++
 			}
 		}
 	}
+	mutex.Unlock()
 	return count
 }
 

+ 95 - 0
vendor/src/github.com/gorilla/context/context_test.go

@@ -24,6 +24,7 @@ func TestContext(t *testing.T) {
 	}
 
 	r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
+	emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
 
 	// Get()
 	assertEqual(Get(r, key1), nil)
@@ -51,6 +52,26 @@ func TestContext(t *testing.T) {
 	assertEqual(value, nil)
 	assertEqual(ok, true)
 
+	// GetAll()
+	values := GetAll(r)
+	assertEqual(len(values), 3)
+
+	// GetAll() for empty request
+	values = GetAll(emptyR)
+	if values != nil {
+		t.Error("GetAll didn't return nil value for invalid request")
+	}
+
+	// GetAllOk()
+	values, ok = GetAllOk(r)
+	assertEqual(len(values), 3)
+	assertEqual(ok, true)
+
+	// GetAllOk() for empty request
+	values, ok = GetAllOk(emptyR)
+	assertEqual(value, nil)
+	assertEqual(ok, false)
+
 	// Delete()
 	Delete(r, key1)
 	assertEqual(Get(r, key1), nil)
@@ -64,3 +85,77 @@ func TestContext(t *testing.T) {
 	Clear(r)
 	assertEqual(len(data), 0)
 }
+
+func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
+	<-wait
+	for i := 0; i < iterations; i++ {
+		Get(r, key)
+	}
+	done <- struct{}{}
+
+}
+
+func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
+	<-wait
+	for i := 0; i < iterations; i++ {
+		Get(r, key)
+	}
+	done <- struct{}{}
+
+}
+
+func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
+
+	b.StopTimer()
+	r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
+	done := make(chan struct{})
+	b.StartTimer()
+
+	for i := 0; i < b.N; i++ {
+		wait := make(chan struct{})
+
+		for i := 0; i < numReaders; i++ {
+			go parallelReader(r, "test", iterations, wait, done)
+		}
+
+		for i := 0; i < numWriters; i++ {
+			go parallelWriter(r, "test", "123", iterations, wait, done)
+		}
+
+		close(wait)
+
+		for i := 0; i < numReaders+numWriters; i++ {
+			<-done
+		}
+
+	}
+
+}
+
+func BenchmarkMutexSameReadWrite1(b *testing.B) {
+	benchmarkMutex(b, 1, 1, 32)
+}
+func BenchmarkMutexSameReadWrite2(b *testing.B) {
+	benchmarkMutex(b, 2, 2, 32)
+}
+func BenchmarkMutexSameReadWrite4(b *testing.B) {
+	benchmarkMutex(b, 4, 4, 32)
+}
+func BenchmarkMutex1(b *testing.B) {
+	benchmarkMutex(b, 2, 8, 32)
+}
+func BenchmarkMutex2(b *testing.B) {
+	benchmarkMutex(b, 16, 4, 64)
+}
+func BenchmarkMutex3(b *testing.B) {
+	benchmarkMutex(b, 1, 2, 128)
+}
+func BenchmarkMutex4(b *testing.B) {
+	benchmarkMutex(b, 128, 32, 256)
+}
+func BenchmarkMutex5(b *testing.B) {
+	benchmarkMutex(b, 1024, 2048, 64)
+}
+func BenchmarkMutex6(b *testing.B) {
+	benchmarkMutex(b, 2048, 1024, 512)
+}

+ 1 - 1
vendor/src/github.com/gorilla/context/doc.go

@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 /*
-Package gorilla/context stores values shared during a request lifetime.
+Package context stores values shared during a request lifetime.
 
 For example, a router can set variables extracted from the URL and later
 application handlers can access those values, or it can be used to store

+ 7 - 0
vendor/src/github.com/gorilla/mux/.travis.yml

@@ -0,0 +1,7 @@
+language: go
+
+go:
+  - 1.0
+  - 1.1
+  - 1.2
+  - tip

+ 1 - 0
vendor/src/github.com/gorilla/mux/README.md

@@ -1,5 +1,6 @@
 mux
 ===
+[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
 
 gorilla/mux is a powerful URL router and dispatcher.
 

+ 1 - 1
vendor/src/github.com/gorilla/mux/doc.go

@@ -134,7 +134,7 @@ the inner routes use it as base for their paths:
 	// "/products/{key}/"
 	s.HandleFunc("/{key}/", ProductHandler)
 	// "/products/{key}/details"
-	s.HandleFunc("/{key}/details"), ProductDetailsHandler)
+	s.HandleFunc("/{key}/details", ProductDetailsHandler)
 
 Now let's see how to build registered URLs.
 

+ 25 - 7
vendor/src/github.com/gorilla/mux/mux.go

@@ -14,7 +14,7 @@ import (
 
 // NewRouter returns a new router instance.
 func NewRouter() *Router {
-	return &Router{namedRoutes: make(map[string]*Route)}
+	return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
 }
 
 // Router registers routes to be matched and dispatches a handler.
@@ -46,6 +46,8 @@ type Router struct {
 	namedRoutes map[string]*Route
 	// See Router.StrictSlash(). This defines the flag for new routes.
 	strictSlash bool
+	// If true, do not clear the request context after handling the request
+	KeepContext bool
 }
 
 // Match matches registered routes against the request.
@@ -65,6 +67,14 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
 func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 	// Clean path to canonical form and redirect.
 	if p := cleanPath(req.URL.Path); p != req.URL.Path {
+
+		// Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
+		// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
+		// http://code.google.com/p/go/issues/detail?id=5252
+		url := *req.URL
+		url.Path = p
+		p = url.String()
+
 		w.Header().Set("Location", p)
 		w.WriteHeader(http.StatusMovedPermanently)
 		return
@@ -82,7 +92,9 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 		}
 		handler = r.NotFoundHandler
 	}
-	defer context.Clear(req)
+	if !r.KeepContext {
+		defer context.Clear(req)
+	}
 	handler.ServeHTTP(w, req)
 }
 
@@ -97,14 +109,20 @@ func (r *Router) GetRoute(name string) *Route {
 	return r.getNamedRoutes()[name]
 }
 
-// StrictSlash defines the slash behavior for new routes.
+// StrictSlash defines the trailing slash behavior for new routes. The initial
+// value is false.
 //
 // When true, if the route path is "/path/", accessing "/path" will redirect
-// to the former and vice versa.
+// to the former and vice versa. In other words, your application will always
+// see the path as specified in the route.
+//
+// When false, if the route path is "/path", accessing "/path/" will not match
+// this route and vice versa.
 //
-// Special case: when a route sets a path prefix, strict slash is
-// automatically set to false for that route because the redirect behavior
-// can't be determined for prefixes.
+// Special case: when a route sets a path prefix using the PathPrefix() method,
+// strict slash is ignored for that route because the redirect behavior can't
+// be determined from a prefix alone. However, any subrouters created from that
+// route inherit the original StrictSlash setting.
 func (r *Router) StrictSlash(value bool) *Router {
 	r.strictSlash = value
 	return r

+ 200 - 25
vendor/src/github.com/gorilla/mux/mux_test.go

@@ -8,16 +8,19 @@ import (
 	"fmt"
 	"net/http"
 	"testing"
+
+	"github.com/gorilla/context"
 )
 
 type routeTest struct {
-	title       string            // title of the test
-	route       *Route            // the route being tested
-	request     *http.Request     // a request to test the route
-	vars        map[string]string // the expected vars of the match
-	host        string            // the expected host of the match
-	path        string            // the expected path of the match
-	shouldMatch bool              // whether the request is expected to match the route at all
+	title          string            // title of the test
+	route          *Route            // the route being tested
+	request        *http.Request     // a request to test the route
+	vars           map[string]string // the expected vars of the match
+	host           string            // the expected host of the match
+	path           string            // the expected path of the match
+	shouldMatch    bool              // whether the request is expected to match the route at all
+	shouldRedirect bool              // whether the request should result in a redirect
 }
 
 func TestHost(t *testing.T) {
@@ -149,6 +152,33 @@ func TestPath(t *testing.T) {
 			path:        "/111/222/333",
 			shouldMatch: true,
 		},
+		{
+			title:       "Path route, match with trailing slash in request and path",
+			route:       new(Route).Path("/111/"),
+			request:     newRequest("GET", "http://localhost/111/"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111/",
+			shouldMatch: true,
+		},
+		{
+			title:       "Path route, do not match with trailing slash in path",
+			route:       new(Route).Path("/111/"),
+			request:     newRequest("GET", "http://localhost/111"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111",
+			shouldMatch: false,
+		},
+		{
+			title:       "Path route, do not match with trailing slash in request",
+			route:       new(Route).Path("/111"),
+			request:     newRequest("GET", "http://localhost/111/"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/111/",
+			shouldMatch: false,
+		},
 		{
 			title:       "Path route, wrong path in request in request URL",
 			route:       new(Route).Path("/111/222/333"),
@@ -212,6 +242,15 @@ func TestPathPrefix(t *testing.T) {
 			path:        "/111",
 			shouldMatch: true,
 		},
+		{
+			title:       "PathPrefix route, match substring",
+			route:       new(Route).PathPrefix("/1"),
+			request:     newRequest("GET", "http://localhost/111/222/333"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "/1",
+			shouldMatch: true,
+		},
 		{
 			title:       "PathPrefix route, URL prefix in request does not match",
 			route:       new(Route).PathPrefix("/111"),
@@ -414,6 +453,15 @@ func TestQueries(t *testing.T) {
 			path:        "",
 			shouldMatch: true,
 		},
+		{
+			title:       "Queries route, match with a query string",
+			route:       new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
+			request:     newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
+			vars:        map[string]string{},
+			host:        "",
+			path:        "",
+			shouldMatch: true,
+		},
 		{
 			title:       "Queries route, bad query",
 			route:       new(Route).Queries("foo", "bar", "baz", "ding"),
@@ -568,26 +616,74 @@ func TestNamedRoutes(t *testing.T) {
 }
 
 func TestStrictSlash(t *testing.T) {
-	var r *Router
-	var req *http.Request
-	var route *Route
-	var match *RouteMatch
-	var matched bool
-
-	// StrictSlash should be ignored for path prefix.
-	// So we register a route ending in slash but it doesn't attempt to add
-	// the slash for a path not ending in slash.
-	r = NewRouter()
+	r := NewRouter()
 	r.StrictSlash(true)
-	route = r.NewRoute().PathPrefix("/static/")
-	req, _ = http.NewRequest("GET", "http://localhost/static/logo.png", nil)
-	match = new(RouteMatch)
-	matched = r.Match(req, match)
-	if !matched {
-		t.Errorf("Should match request %q -- %v", req.URL.Path, getRouteTemplate(route))
+
+	tests := []routeTest{
+		{
+			title:          "Redirect path without slash",
+			route:          r.NewRoute().Path("/111/"),
+			request:        newRequest("GET", "http://localhost/111"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111/",
+			shouldMatch:    true,
+			shouldRedirect: true,
+		},
+		{
+			title:          "Do not redirect path with slash",
+			route:          r.NewRoute().Path("/111/"),
+			request:        newRequest("GET", "http://localhost/111/"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111/",
+			shouldMatch:    true,
+			shouldRedirect: false,
+		},
+		{
+			title:          "Redirect path with slash",
+			route:          r.NewRoute().Path("/111"),
+			request:        newRequest("GET", "http://localhost/111/"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111",
+			shouldMatch:    true,
+			shouldRedirect: true,
+		},
+		{
+			title:          "Do not redirect path without slash",
+			route:          r.NewRoute().Path("/111"),
+			request:        newRequest("GET", "http://localhost/111"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/111",
+			shouldMatch:    true,
+			shouldRedirect: false,
+		},
+		{
+			title:          "Propagate StrictSlash to subrouters",
+			route:          r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
+			request:        newRequest("GET", "http://localhost/static/images"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/static/images/",
+			shouldMatch:    true,
+			shouldRedirect: true,
+		},
+		{
+			title:          "Ignore StrictSlash for path prefix",
+			route:          r.NewRoute().PathPrefix("/static/"),
+			request:        newRequest("GET", "http://localhost/static/logo.png"),
+			vars:           map[string]string{},
+			host:           "",
+			path:           "/static/",
+			shouldMatch:    true,
+			shouldRedirect: false,
+		},
 	}
-	if match.Handler != nil {
-		t.Errorf("Should not redirect")
+
+	for _, test := range tests {
+		testRoute(t, test)
 	}
 }
 
@@ -616,6 +712,7 @@ func testRoute(t *testing.T, test routeTest) {
 	host := test.host
 	path := test.path
 	url := test.host + test.path
+	shouldRedirect := test.shouldRedirect
 
 	var match RouteMatch
 	ok := route.Match(request, &match)
@@ -653,6 +750,84 @@ func testRoute(t *testing.T, test routeTest) {
 				return
 			}
 		}
+		if shouldRedirect && match.Handler == nil {
+			t.Errorf("(%v) Did not redirect", test.title)
+			return
+		}
+		if !shouldRedirect && match.Handler != nil {
+			t.Errorf("(%v) Unexpected redirect", test.title)
+			return
+		}
+	}
+}
+
+// Tests that the context is cleared or not cleared properly depending on
+// the configuration of the router
+func TestKeepContext(t *testing.T) {
+	func1 := func(w http.ResponseWriter, r *http.Request) {}
+
+	r := NewRouter()
+	r.HandleFunc("/", func1).Name("func1")
+
+	req, _ := http.NewRequest("GET", "http://localhost/", nil)
+	context.Set(req, "t", 1)
+
+	res := new(http.ResponseWriter)
+	r.ServeHTTP(*res, req)
+
+	if _, ok := context.GetOk(req, "t"); ok {
+		t.Error("Context should have been cleared at end of request")
+	}
+
+	r.KeepContext = true
+
+	req, _ = http.NewRequest("GET", "http://localhost/", nil)
+	context.Set(req, "t", 1)
+
+	r.ServeHTTP(*res, req)
+	if _, ok := context.GetOk(req, "t"); !ok {
+		t.Error("Context should NOT have been cleared at end of request")
+	}
+
+}
+
+type TestA301ResponseWriter struct {
+	hh     http.Header
+	status int
+}
+
+func (ho TestA301ResponseWriter) Header() http.Header {
+	return http.Header(ho.hh)
+}
+
+func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
+	return 0, nil
+}
+
+func (ho TestA301ResponseWriter) WriteHeader(code int) {
+	ho.status = code
+}
+
+func Test301Redirect(t *testing.T) {
+	m := make(http.Header)
+
+	func1 := func(w http.ResponseWriter, r *http.Request) {}
+	func2 := func(w http.ResponseWriter, r *http.Request) {}
+
+	r := NewRouter()
+	r.HandleFunc("/api/", func2).Name("func2")
+	r.HandleFunc("/", func1).Name("func1")
+
+	req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
+
+	res := TestA301ResponseWriter{
+		hh:     m,
+		status: 0,
+	}
+	r.ServeHTTP(&res, req)
+
+	if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
+		t.Errorf("Should have complete URL with query string")
 	}
 }
 

+ 4 - 4
vendor/src/github.com/gorilla/mux/old_test.go

@@ -96,8 +96,8 @@ func TestRouteMatchers(t *testing.T) {
 		method = "GET"
 		headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
 		resultVars = map[bool]map[string]string{
-			true:  map[string]string{"var1": "www", "var2": "product", "var3": "42"},
-			false: map[string]string{},
+			true:  {"var1": "www", "var2": "product", "var3": "42"},
+			false: {},
 		}
 	}
 
@@ -110,8 +110,8 @@ func TestRouteMatchers(t *testing.T) {
 		method = "POST"
 		headers = map[string]string{"Content-Type": "application/json"}
 		resultVars = map[bool]map[string]string{
-			true:  map[string]string{"var4": "google", "var5": "product", "var6": "42"},
-			false: map[string]string{},
+			true:  {"var4": "google", "var5": "product", "var6": "42"},
+			false: {},
 		}
 	}
 

+ 10 - 7
vendor/src/github.com/gorilla/mux/regexp.go

@@ -98,12 +98,13 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, strictSlash bool) (*rout
 	}
 	// Done!
 	return &routeRegexp{
-		template:  template,
-		matchHost: matchHost,
-		regexp:    reg,
-		reverse:   reverse.String(),
-		varsN:     varsN,
-		varsR:     varsR,
+		template:    template,
+		matchHost:   matchHost,
+		strictSlash: strictSlash,
+		regexp:      reg,
+		reverse:     reverse.String(),
+		varsN:       varsN,
+		varsR:       varsR,
 	}, nil
 }
 
@@ -114,6 +115,8 @@ type routeRegexp struct {
 	template string
 	// True for host match, false for path match.
 	matchHost bool
+	// The strictSlash value defined on the route, but disabled if PathPrefix was used.
+	strictSlash bool
 	// Expanded regexp.
 	regexp *regexp.Regexp
 	// Reverse template.
@@ -216,7 +219,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
 				m.Vars[v] = pathVars[k+1]
 			}
 			// Check if we should redirect.
-			if r.strictSlash {
+			if v.path.strictSlash {
 				p1 := strings.HasSuffix(req.URL.Path, "/")
 				p2 := strings.HasSuffix(v.path.template, "/")
 				if p1 != p2 {

+ 12 - 4
vendor/src/github.com/gorilla/mux/route.go

@@ -259,7 +259,8 @@ func (r *Route) Methods(methods ...string) *Route {
 // Path -----------------------------------------------------------------------
 
 // Path adds a matcher for the URL path.
-// It accepts a template with zero or more URL variables enclosed by {}.
+// It accepts a template with zero or more URL variables enclosed by {}. The
+// template must start with a "/".
 // Variables can define an optional regexp pattern to me matched:
 //
 // - {name} matches anything until the next slash.
@@ -283,9 +284,16 @@ func (r *Route) Path(tpl string) *Route {
 
 // PathPrefix -----------------------------------------------------------------
 
-// PathPrefix adds a matcher for the URL path prefix.
+// PathPrefix adds a matcher for the URL path prefix. This matches if the given
+// template is a prefix of the full URL path. See Route.Path() for details on
+// the tpl argument.
+//
+// Note that it does not treat slashes specially ("/foobar/" will be matched by
+// the prefix "/foo") so you may want to use a trailing slash here.
+//
+// Also note that the setting of Router.StrictSlash() has no effect on routes
+// with a PathPrefix matcher.
 func (r *Route) PathPrefix(tpl string) *Route {
-	r.strictSlash = false
 	r.err = r.addRegexpMatcher(tpl, false, true)
 	return r
 }
@@ -328,7 +336,7 @@ func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
 }
 
 // Schemes adds a matcher for URL schemes.
-// It accepts a sequence schemes to be matched, e.g.: "http", "https".
+// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
 func (r *Route) Schemes(schemes ...string) *Route {
 	for k, v := range schemes {
 		schemes[k] = strings.ToLower(v)

+ 11 - 0
vendor/src/github.com/kr/pty/ioctl.go

@@ -0,0 +1,11 @@
+package pty
+
+import "syscall"
+
+func ioctl(fd, cmd, ptr uintptr) error {
+	_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
+	if e != 0 {
+		return e
+	}
+	return nil
+}

+ 39 - 0
vendor/src/github.com/kr/pty/ioctl_bsd.go

@@ -0,0 +1,39 @@
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package pty
+
+// from <sys/ioccom.h>
+const (
+	_IOC_VOID    uintptr = 0x20000000
+	_IOC_OUT     uintptr = 0x40000000
+	_IOC_IN      uintptr = 0x80000000
+	_IOC_IN_OUT  uintptr = _IOC_OUT | _IOC_IN
+	_IOC_DIRMASK         = _IOC_VOID | _IOC_OUT | _IOC_IN
+
+	_IOC_PARAM_SHIFT = 13
+	_IOC_PARAM_MASK  = (1 << _IOC_PARAM_SHIFT) - 1
+)
+
+func _IOC_PARM_LEN(ioctl uintptr) uintptr {
+	return (ioctl >> 16) & _IOC_PARAM_MASK
+}
+
+func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
+}
+
+func _IO(group byte, ioctl_num uintptr) uintptr {
+	return _IOC(_IOC_VOID, group, ioctl_num, 0)
+}
+
+func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return _IOC(_IOC_OUT, group, ioctl_num, param_len)
+}
+
+func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return _IOC(_IOC_IN, group, ioctl_num, param_len)
+}
+
+func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+	return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
+}

+ 42 - 0
vendor/src/github.com/kr/pty/ioctl_linux.go

@@ -0,0 +1,42 @@
+package pty
+
+// from <asm-generic/ioctl.h>
+const (
+	_IOC_NRBITS   = 8
+	_IOC_TYPEBITS = 8
+
+	_IOC_SIZEBITS = 14
+	_IOC_DIRBITS  = 2
+
+	_IOC_NRSHIFT   = 0
+	_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
+	_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
+	_IOC_DIRSHIFT  = _IOC_SIZESHIFT + _IOC_SIZEBITS
+
+	_IOC_NONE  uint = 0
+	_IOC_WRITE uint = 1
+	_IOC_READ  uint = 2
+)
+
+func _IOC(dir uint, ioctl_type byte, nr byte, size uintptr) uintptr {
+	return (uintptr(dir)<<_IOC_DIRSHIFT |
+		uintptr(ioctl_type)<<_IOC_TYPESHIFT |
+		uintptr(nr)<<_IOC_NRSHIFT |
+		size<<_IOC_SIZESHIFT)
+}
+
+func _IO(ioctl_type byte, nr byte) uintptr {
+	return _IOC(_IOC_NONE, ioctl_type, nr, 0)
+}
+
+func _IOR(ioctl_type byte, nr byte, size uintptr) uintptr {
+	return _IOC(_IOC_READ, ioctl_type, nr, size)
+}
+
+func _IOW(ioctl_type byte, nr byte, size uintptr) uintptr {
+	return _IOC(_IOC_WRITE, ioctl_type, nr, size)
+}
+
+func _IOWR(ioctl_type byte, nr byte, size uintptr) uintptr {
+	return _IOC(_IOC_READ|_IOC_WRITE, ioctl_type, nr, size)
+}

+ 19 - 0
vendor/src/github.com/kr/pty/mktypes.bash

@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+GOOSARCH="${GOOS}_${GOARCH}"
+case "$GOOSARCH" in
+_* | *_ | _)
+	echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
+	exit 1
+	;;
+esac
+
+GODEFS="go tool cgo -godefs"
+
+$GODEFS types.go |gofmt > ztypes_$GOARCH.go
+
+case $GOOS in
+freebsd)
+	$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
+	;;
+esac

+ 8 - 17
vendor/src/github.com/kr/pty/pty_darwin.go

@@ -7,9 +7,6 @@ import (
 	"unsafe"
 )
 
-// see ioccom.h
-const sys_IOCPARM_MASK = 0x1fff
-
 func open() (pty, tty *os.File, err error) {
 	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
 	if err != nil {
@@ -39,9 +36,13 @@ func open() (pty, tty *os.File, err error) {
 }
 
 func ptsname(f *os.File) (string, error) {
-	var n [(syscall.TIOCPTYGNAME >> 16) & sys_IOCPARM_MASK]byte
+	n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
+
+	err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
+	if err != nil {
+		return "", err
+	}
 
-	ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n)))
 	for i, c := range n {
 		if c == 0 {
 			return string(n[:i]), nil
@@ -51,19 +52,9 @@ func ptsname(f *os.File) (string, error) {
 }
 
 func grantpt(f *os.File) error {
-	var u int
-	return ioctl(f.Fd(), syscall.TIOCPTYGRANT, uintptr(unsafe.Pointer(&u)))
+	return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
 }
 
 func unlockpt(f *os.File) error {
-	var u int
-	return ioctl(f.Fd(), syscall.TIOCPTYUNLK, uintptr(unsafe.Pointer(&u)))
-}
-
-func ioctl(fd, cmd, ptr uintptr) error {
-	_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
-	if e != 0 {
-		return syscall.ENOTTY
-	}
-	return nil
+	return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
 }

+ 40 - 20
vendor/src/github.com/kr/pty/pty_freebsd.go

@@ -1,53 +1,73 @@
 package pty
 
 import (
+	"errors"
 	"os"
-	"strconv"
 	"syscall"
 	"unsafe"
 )
 
-const (
-	sys_TIOCGPTN   = 0x4004740F
-	sys_TIOCSPTLCK = 0x40045431
-)
+func posix_openpt(oflag int) (fd int, err error) {
+	r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = e1
+	}
+	return
+}
 
 func open() (pty, tty *os.File, err error) {
-	p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+	fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC)
 	if err != nil {
 		return nil, nil, err
 	}
 
+	p := os.NewFile(uintptr(fd), "/dev/pts")
 	sname, err := ptsname(p)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+	t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
 	if err != nil {
 		return nil, nil, err
 	}
 	return p, t, nil
 }
 
+func isptmaster(fd uintptr) (bool, error) {
+	err := ioctl(fd, syscall.TIOCPTMASTER, 0)
+	return err == nil, err
+}
+
+var (
+	emptyFiodgnameArg fiodgnameArg
+	ioctl_FIODGNAME   = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+)
+
 func ptsname(f *os.File) (string, error) {
-	var n int
-	err := ioctl(f.Fd(), sys_TIOCGPTN, &n)
+	master, err := isptmaster(f.Fd())
 	if err != nil {
 		return "", err
 	}
-	return "/dev/pts/" + strconv.Itoa(n), nil
-}
+	if !master {
+		return "", syscall.EINVAL
+	}
 
-func ioctl(fd uintptr, cmd uintptr, data *int) error {
-	_, _, e := syscall.Syscall(
-		syscall.SYS_IOCTL,
-		fd,
-		cmd,
-		uintptr(unsafe.Pointer(data)),
+	const n = _C_SPECNAMELEN + 1
+	var (
+		buf = make([]byte, n)
+		arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
 	)
-	if e != 0 {
-		return syscall.ENOTTY
+	err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg)))
+	if err != nil {
+		return "", err
+	}
+
+	for i, c := range buf {
+		if c == 0 {
+			return string(buf[:i]), nil
+		}
 	}
-	return nil
+	return "", errors.New("FIODGNAME string not NUL-terminated")
 }

+ 9 - 21
vendor/src/github.com/kr/pty/pty_linux.go

@@ -7,9 +7,9 @@ import (
 	"unsafe"
 )
 
-const (
-	sys_TIOCGPTN   = 0x80045430
-	sys_TIOCSPTLCK = 0x40045431
+var (
+	ioctl_TIOCGPTN   = _IOR('T', 0x30, unsafe.Sizeof(_C_uint(0))) /* Get Pty Number (of pty-mux device) */
+	ioctl_TIOCSPTLCK = _IOW('T', 0x31, unsafe.Sizeof(_C_int(0)))  /* Lock/unlock Pty */
 )
 
 func open() (pty, tty *os.File, err error) {
@@ -36,28 +36,16 @@ func open() (pty, tty *os.File, err error) {
 }
 
 func ptsname(f *os.File) (string, error) {
-	var n int
-	err := ioctl(f.Fd(), sys_TIOCGPTN, &n)
+	var n _C_uint
+	err := ioctl(f.Fd(), ioctl_TIOCGPTN, uintptr(unsafe.Pointer(&n)))
 	if err != nil {
 		return "", err
 	}
-	return "/dev/pts/" + strconv.Itoa(n), nil
+	return "/dev/pts/" + strconv.Itoa(int(n)), nil
 }
 
 func unlockpt(f *os.File) error {
-	var u int
-	return ioctl(f.Fd(), sys_TIOCSPTLCK, &u)
-}
-
-func ioctl(fd uintptr, cmd uintptr, data *int) error {
-	_, _, e := syscall.Syscall(
-		syscall.SYS_IOCTL,
-		fd,
-		cmd,
-		uintptr(unsafe.Pointer(data)),
-	)
-	if e != 0 {
-		return syscall.ENOTTY
-	}
-	return nil
+	var u _C_int
+	// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
+	return ioctl(f.Fd(), ioctl_TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
 }

+ 0 - 16
vendor/src/github.com/kr/pty/pty_unsupported.go

@@ -9,19 +9,3 @@ import (
 func open() (pty, tty *os.File, err error) {
 	return nil, nil, ErrUnsupported
 }
-
-func ptsname(f *os.File) (string, error) {
-	return "", ErrUnsupported
-}
-
-func grantpt(f *os.File) error {
-	return ErrUnsupported
-}
-
-func unlockpt(f *os.File) error {
-	return ErrUnsupported
-}
-
-func ioctl(fd, cmd, ptr uintptr) error {
-	return ErrUnsupported
-}

+ 10 - 0
vendor/src/github.com/kr/pty/types.go

@@ -0,0 +1,10 @@
+// +build ignore
+
+package pty
+
+import "C"
+
+type (
+	_C_int  C.int
+	_C_uint C.uint
+)

+ 15 - 0
vendor/src/github.com/kr/pty/types_freebsd.go

@@ -0,0 +1,15 @@
+// +build ignore
+
+package pty
+
+/*
+#include <sys/param.h>
+#include <sys/filio.h>
+*/
+import "C"
+
+const (
+	_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
+)
+
+type fiodgnameArg C.struct_fiodgname_arg

+ 9 - 0
vendor/src/github.com/kr/pty/ztypes_386.go

@@ -0,0 +1,9 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)

+ 9 - 0
vendor/src/github.com/kr/pty/ztypes_amd64.go

@@ -0,0 +1,9 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)

+ 9 - 0
vendor/src/github.com/kr/pty/ztypes_arm.go

@@ -0,0 +1,9 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+	_C_int  int32
+	_C_uint uint32
+)

+ 13 - 0
vendor/src/github.com/kr/pty/ztypes_freebsd_386.go

@@ -0,0 +1,13 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len int32
+	Buf *byte
+}

+ 14 - 0
vendor/src/github.com/kr/pty/ztypes_freebsd_amd64.go

@@ -0,0 +1,14 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len       int32
+	Pad_cgo_0 [4]byte
+	Buf       *byte
+}

+ 13 - 0
vendor/src/github.com/kr/pty/ztypes_freebsd_arm.go

@@ -0,0 +1,13 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+	_C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+	Len int32
+	Buf *byte
+}