diff options
author | Niall Sheridan <nsheridan@gmail.com> | 2018-08-23 22:51:21 +0100 |
---|---|---|
committer | Niall Sheridan <nsheridan@gmail.com> | 2018-08-24 13:45:03 +0100 |
commit | f8e3dea19012ccf05965d10255789eec33c2ebcf (patch) | |
tree | 8522ceada8bc7270648f29615b89550db910cb6c /vendor/github.com/gorilla | |
parent | 91f9bc722152146466523861162b85195f99875b (diff) |
Update deps
Diffstat (limited to 'vendor/github.com/gorilla')
-rw-r--r-- | vendor/github.com/gorilla/csrf/README.md | 49 | ||||
-rw-r--r-- | vendor/github.com/gorilla/csrf/doc.go | 5 | ||||
-rw-r--r-- | vendor/github.com/gorilla/handlers/cors.go | 2 | ||||
-rw-r--r-- | vendor/github.com/gorilla/handlers/handlers.go | 225 | ||||
-rw-r--r-- | vendor/github.com/gorilla/handlers/logging.go | 252 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/route.go | 2 |
6 files changed, 285 insertions, 250 deletions
diff --git a/vendor/github.com/gorilla/csrf/README.md b/vendor/github.com/gorilla/csrf/README.md index 75e8525..6bcca60 100644 --- a/vendor/github.com/gorilla/csrf/README.md +++ b/vendor/github.com/gorilla/csrf/README.md @@ -1,26 +1,27 @@ # gorilla/csrf + [![GoDoc](https://godoc.org/github.com/gorilla/csrf?status.svg)](https://godoc.org/github.com/gorilla/csrf) [![Build Status](https://travis-ci.org/gorilla/csrf.svg?branch=master)](https://travis-ci.org/gorilla/csrf) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/csrf/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/csrf?badge) gorilla/csrf is a HTTP middleware library that provides [cross-site request forgery](http://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/) (CSRF) - protection. It includes: +protection. It includes: -* The `csrf.Protect` middleware/handler provides CSRF protection on routes +- The `csrf.Protect` middleware/handler provides CSRF protection on routes attached to a router or a sub-router. -* A `csrf.Token` function that provides the token to pass into your response, +- A `csrf.Token` function that provides the token to pass into your response, whether that be a HTML form or a JSON response body. -* ... and a `csrf.TemplateField` helper that you can pass into your `html/template` +- ... and a `csrf.TemplateField` helper that you can pass into your `html/template` templates to replace a `{{ .csrfField }}` template tag with a hidden input field. gorilla/csrf is designed to work with any Go web framework, including: -* The [Gorilla](http://www.gorillatoolkit.org/) toolkit -* Go's built-in [net/http](http://golang.org/pkg/net/http/) package -* [Goji](https://goji.io) - see the [tailored fork](https://github.com/goji/csrf) -* [Gin](https://github.com/gin-gonic/gin) -* [Echo](https://github.com/labstack/echo) -* ... and any other router/framework that rallies around Go's `http.Handler` interface. +- The [Gorilla](http://www.gorillatoolkit.org/) toolkit +- Go's built-in [net/http](http://golang.org/pkg/net/http/) package +- [Goji](https://goji.io) - see the [tailored fork](https://github.com/goji/csrf) +- [Gin](https://github.com/gin-gonic/gin) +- [Echo](https://github.com/labstack/echo) +- ... and any other router/framework that rallies around Go's `http.Handler` interface. gorilla/csrf is also compatible with middleware 'helper' libraries like [Alice](https://github.com/justinas/alice) and [Negroni](https://github.com/codegangsta/negroni). @@ -28,16 +29,17 @@ gorilla/csrf is also compatible with middleware 'helper' libraries like ## Install With a properly configured Go toolchain: + ```sh go get github.com/gorilla/csrf ``` ## Examples -* [HTML Forms](#html-forms) -* [JavaScript Apps](#javascript-applications) -* [Google App Engine](#google-app-engine) -* [Setting Options](#setting-options) +- [HTML Forms](#html-forms) +- [JavaScript Apps](#javascript-applications) +- [Google App Engine](#google-app-engine) +- [Setting Options](#setting-options) gorilla/csrf is easy to use: add the middleware to your router with the below: @@ -77,7 +79,10 @@ func main() { r := mux.NewRouter() r.HandleFunc("/signup", ShowSignupForm) // All POST requests without a valid token will return HTTP 403 Forbidden. - r.HandleFunc("/signup/post", SubmitSignupForm) + // We should also ensure that our mutating (non-idempotent) handler only + // matches on POST requests. We can check that here, at the router level, or + // within the handler itself via r.Method. + r.HandleFunc("/signup/post", SubmitSignupForm).Methods("POST") // Add the middleware to your router by wrapping it. http.ListenAndServe(":8000", @@ -207,22 +212,22 @@ added, open an issue. Getting CSRF protection right is important, so here's some background: -* This library generates unique-per-request (masked) tokens as a mitigation +- This library generates unique-per-request (masked) tokens as a mitigation against the [BREACH attack](http://breachattack.com/). -* The 'base' (unmasked) token is stored in the session, which means that +- The 'base' (unmasked) token is stored in the session, which means that multiple browser tabs won't cause a user problems as their per-request token is compared with the base token. -* Operates on a "whitelist only" approach where safe (non-mutating) HTTP methods - (GET, HEAD, OPTIONS, TRACE) are the *only* methods where token validation is not +- Operates on a "whitelist only" approach where safe (non-mutating) HTTP methods + (GET, HEAD, OPTIONS, TRACE) are the _only_ methods where token validation is not enforced. -* The design is based on the battle-tested +- The design is based on the battle-tested [Django](https://docs.djangoproject.com/en/1.8/ref/csrf/) and [Ruby on Rails](http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html) approaches. -* Cookies are authenticated and based on the [securecookie](https://github.com/gorilla/securecookie) +- Cookies are authenticated and based on the [securecookie](https://github.com/gorilla/securecookie) library. They're also Secure (issued over HTTPS only) and are HttpOnly by default, because sane defaults are important. -* Go's `crypto/rand` library is used to generate the 32 byte (256 bit) tokens +- Go's `crypto/rand` library is used to generate the 32 byte (256 bit) tokens and the one-time-pad used for masking them. This library does not seek to be adventurous. diff --git a/vendor/github.com/gorilla/csrf/doc.go b/vendor/github.com/gorilla/csrf/doc.go index 3046cdc..503c948 100644 --- a/vendor/github.com/gorilla/csrf/doc.go +++ b/vendor/github.com/gorilla/csrf/doc.go @@ -71,7 +71,10 @@ in order to protect malicious POST requests being made: r := mux.NewRouter() r.HandleFunc("/signup", ShowSignupForm) // All POST requests without a valid token will return HTTP 403 Forbidden. - r.HandleFunc("/signup/post", SubmitSignupForm) + // We should also ensure that our mutating (non-idempotent) handler only + // matches on POST requests. We can check that here, at the router level, or + // within the handler itself via r.Method. + r.HandleFunc("/signup/post", SubmitSignupForm).Methods("POST") // Add the middleware to your router by wrapping it. http.ListenAndServe(":8000", diff --git a/vendor/github.com/gorilla/handlers/cors.go b/vendor/github.com/gorilla/handlers/cors.go index 16efb8a..1acf80d 100644 --- a/vendor/github.com/gorilla/handlers/cors.go +++ b/vendor/github.com/gorilla/handlers/cors.go @@ -119,7 +119,7 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { } else { for _, o := range ch.allowedOrigins { // A configuration of * is different than explicitly setting an allowed - // origin. Returning arbitrary origin headers an an access control allow + // origin. Returning arbitrary origin headers in an access control allow // origin header is unsafe and is not required by any use case. if o == corsOriginMatchAll { returnOrigin = "*" diff --git a/vendor/github.com/gorilla/handlers/handlers.go b/vendor/github.com/gorilla/handlers/handlers.go index 75db7f8..d03f2bf 100644 --- a/vendor/github.com/gorilla/handlers/handlers.go +++ b/vendor/github.com/gorilla/handlers/handlers.go @@ -7,15 +7,10 @@ package handlers import ( "bufio" "fmt" - "io" "net" "net/http" - "net/url" "sort" - "strconv" "strings" - "time" - "unicode/utf8" ) // MethodHandler is an http.Handler that dispatches to a handler whose key in the @@ -48,59 +43,6 @@ func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } -// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its -// friends -type loggingHandler struct { - writer io.Writer - handler http.Handler -} - -// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo -// and its friends -type combinedLoggingHandler struct { - writer io.Writer - handler http.Handler -} - -func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - t := time.Now() - logger := makeLogger(w) - url := *req.URL - h.handler.ServeHTTP(logger, req) - writeLog(h.writer, req, url, t, logger.Status(), logger.Size()) -} - -func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - t := time.Now() - logger := makeLogger(w) - url := *req.URL - h.handler.ServeHTTP(logger, req) - writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size()) -} - -func makeLogger(w http.ResponseWriter) loggingResponseWriter { - var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK} - if _, ok := w.(http.Hijacker); ok { - logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}} - } - h, ok1 := logger.(http.Hijacker) - c, ok2 := w.(http.CloseNotifier) - if ok1 && ok2 { - return hijackCloseNotifier{logger, h, c} - } - if ok2 { - return &closeNotifyWriter{logger, c} - } - return logger -} - -type commonLoggingResponseWriter interface { - http.ResponseWriter - http.Flusher - Status() int - Size() int -} - // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP // status code and body size type responseLogger struct { @@ -165,173 +107,6 @@ type hijackCloseNotifier struct { http.CloseNotifier } -const lowerhex = "0123456789abcdef" - -func appendQuoted(buf []byte, s string) []byte { - var runeTmp [utf8.UTFMax]byte - for width := 0; len(s) > 0; s = s[width:] { - r := rune(s[0]) - width = 1 - if r >= utf8.RuneSelf { - r, width = utf8.DecodeRuneInString(s) - } - if width == 1 && r == utf8.RuneError { - buf = append(buf, `\x`...) - buf = append(buf, lowerhex[s[0]>>4]) - buf = append(buf, lowerhex[s[0]&0xF]) - continue - } - if r == rune('"') || r == '\\' { // always backslashed - buf = append(buf, '\\') - buf = append(buf, byte(r)) - continue - } - if strconv.IsPrint(r) { - n := utf8.EncodeRune(runeTmp[:], r) - buf = append(buf, runeTmp[:n]...) - continue - } - switch r { - case '\a': - buf = append(buf, `\a`...) - case '\b': - buf = append(buf, `\b`...) - case '\f': - buf = append(buf, `\f`...) - case '\n': - buf = append(buf, `\n`...) - case '\r': - buf = append(buf, `\r`...) - case '\t': - buf = append(buf, `\t`...) - case '\v': - buf = append(buf, `\v`...) - default: - switch { - case r < ' ': - buf = append(buf, `\x`...) - buf = append(buf, lowerhex[s[0]>>4]) - buf = append(buf, lowerhex[s[0]&0xF]) - case r > utf8.MaxRune: - r = 0xFFFD - fallthrough - case r < 0x10000: - buf = append(buf, `\u`...) - for s := 12; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - default: - buf = append(buf, `\U`...) - for s := 28; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - } - } - } - return buf - -} - -// buildCommonLogLine builds a log entry for req in Apache Common Log Format. -// ts is the timestamp with which the entry should be logged. -// status and size are used to provide the response HTTP status and size. -func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte { - username := "-" - if url.User != nil { - if name := url.User.Username(); name != "" { - username = name - } - } - - host, _, err := net.SplitHostPort(req.RemoteAddr) - - if err != nil { - host = req.RemoteAddr - } - - uri := req.RequestURI - - // Requests using the CONNECT method over HTTP/2.0 must use - // the authority field (aka r.Host) to identify the target. - // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT - if req.ProtoMajor == 2 && req.Method == "CONNECT" { - uri = req.Host - } - if uri == "" { - uri = url.RequestURI() - } - - buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2) - buf = append(buf, host...) - buf = append(buf, " - "...) - buf = append(buf, username...) - buf = append(buf, " ["...) - buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...) - buf = append(buf, `] "`...) - buf = append(buf, req.Method...) - buf = append(buf, " "...) - buf = appendQuoted(buf, uri) - buf = append(buf, " "...) - buf = append(buf, req.Proto...) - buf = append(buf, `" `...) - buf = append(buf, strconv.Itoa(status)...) - buf = append(buf, " "...) - buf = append(buf, strconv.Itoa(size)...) - return buf -} - -// writeLog writes a log entry for req to w in Apache Common Log Format. -// ts is the timestamp with which the entry should be logged. -// status and size are used to provide the response HTTP status and size. -func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) { - buf := buildCommonLogLine(req, url, ts, status, size) - buf = append(buf, '\n') - w.Write(buf) -} - -// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format. -// ts is the timestamp with which the entry should be logged. -// status and size are used to provide the response HTTP status and size. -func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) { - buf := buildCommonLogLine(req, url, ts, status, size) - buf = append(buf, ` "`...) - buf = appendQuoted(buf, req.Referer()) - buf = append(buf, `" "`...) - buf = appendQuoted(buf, req.UserAgent()) - buf = append(buf, '"', '\n') - w.Write(buf) -} - -// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in -// Apache Combined Log Format. -// -// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format. -// -// LoggingHandler always sets the ident field of the log to - -func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler { - return combinedLoggingHandler{out, h} -} - -// LoggingHandler return a http.Handler that wraps h and logs requests to out in -// Apache Common Log Format (CLF). -// -// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format. -// -// LoggingHandler always sets the ident field of the log to - -// -// Example: -// -// r := mux.NewRouter() -// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { -// w.Write([]byte("This is a catch-all route")) -// }) -// loggedRouter := handlers.LoggingHandler(os.Stdout, r) -// http.ListenAndServe(":1123", loggedRouter) -// -func LoggingHandler(out io.Writer, h http.Handler) http.Handler { - return loggingHandler{out, h} -} - // isContentType validates the Content-Type header matches the supplied // contentType. That is, its type and subtype match. func isContentType(h http.Header, contentType string) bool { diff --git a/vendor/github.com/gorilla/handlers/logging.go b/vendor/github.com/gorilla/handlers/logging.go new file mode 100644 index 0000000..cbd182f --- /dev/null +++ b/vendor/github.com/gorilla/handlers/logging.go @@ -0,0 +1,252 @@ +// Copyright 2013 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package handlers + +import ( + "io" + "net" + "net/http" + "net/url" + "strconv" + "time" + "unicode/utf8" +) + +// Logging + +// FormatterParams is the structure any formatter will be handed when time to log comes +type LogFormatterParams struct { + Request *http.Request + URL url.URL + TimeStamp time.Time + StatusCode int + Size int +} + +// LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler +type LogFormatter func(writer io.Writer, params LogFormatterParams) + +// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its +// friends + +type loggingHandler struct { + writer io.Writer + handler http.Handler + formatter LogFormatter +} + +func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + t := time.Now() + logger := makeLogger(w) + url := *req.URL + + h.handler.ServeHTTP(logger, req) + + params := LogFormatterParams{ + Request: req, + URL: url, + TimeStamp: t, + StatusCode: logger.Status(), + Size: logger.Size(), + } + + h.formatter(h.writer, params) +} + +func makeLogger(w http.ResponseWriter) loggingResponseWriter { + var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK} + if _, ok := w.(http.Hijacker); ok { + logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}} + } + h, ok1 := logger.(http.Hijacker) + c, ok2 := w.(http.CloseNotifier) + if ok1 && ok2 { + return hijackCloseNotifier{logger, h, c} + } + if ok2 { + return &closeNotifyWriter{logger, c} + } + return logger +} + +type commonLoggingResponseWriter interface { + http.ResponseWriter + http.Flusher + Status() int + Size() int +} + +const lowerhex = "0123456789abcdef" + +func appendQuoted(buf []byte, s string) []byte { + var runeTmp [utf8.UTFMax]byte + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RuneSelf { + r, width = utf8.DecodeRuneInString(s) + } + if width == 1 && r == utf8.RuneError { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + continue + } + if r == rune('"') || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + continue + } + if strconv.IsPrint(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + continue + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case r < ' ': + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + case r > utf8.MaxRune: + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + default: + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + } + } + } + return buf + +} + +// buildCommonLogLine builds a log entry for req in Apache Common Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte { + username := "-" + if url.User != nil { + if name := url.User.Username(); name != "" { + username = name + } + } + + host, _, err := net.SplitHostPort(req.RemoteAddr) + + if err != nil { + host = req.RemoteAddr + } + + uri := req.RequestURI + + // Requests using the CONNECT method over HTTP/2.0 must use + // the authority field (aka r.Host) to identify the target. + // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT + if req.ProtoMajor == 2 && req.Method == "CONNECT" { + uri = req.Host + } + if uri == "" { + uri = url.RequestURI() + } + + buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2) + buf = append(buf, host...) + buf = append(buf, " - "...) + buf = append(buf, username...) + buf = append(buf, " ["...) + buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...) + buf = append(buf, `] "`...) + buf = append(buf, req.Method...) + buf = append(buf, " "...) + buf = appendQuoted(buf, uri) + buf = append(buf, " "...) + buf = append(buf, req.Proto...) + buf = append(buf, `" `...) + buf = append(buf, strconv.Itoa(status)...) + buf = append(buf, " "...) + buf = append(buf, strconv.Itoa(size)...) + return buf +} + +// writeLog writes a log entry for req to w in Apache Common Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func writeLog(writer io.Writer, params LogFormatterParams) { + buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size) + buf = append(buf, '\n') + writer.Write(buf) +} + +// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func writeCombinedLog(writer io.Writer, params LogFormatterParams) { + buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size) + buf = append(buf, ` "`...) + buf = appendQuoted(buf, params.Request.Referer()) + buf = append(buf, `" "`...) + buf = appendQuoted(buf, params.Request.UserAgent()) + buf = append(buf, '"', '\n') + writer.Write(buf) +} + +// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in +// Apache Combined Log Format. +// +// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format. +// +// LoggingHandler always sets the ident field of the log to - +func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler { + return loggingHandler{out, h, writeCombinedLog} +} + +// LoggingHandler return a http.Handler that wraps h and logs requests to out in +// Apache Common Log Format (CLF). +// +// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format. +// +// LoggingHandler always sets the ident field of the log to - +// +// Example: +// +// r := mux.NewRouter() +// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("This is a catch-all route")) +// }) +// loggedRouter := handlers.LoggingHandler(os.Stdout, r) +// http.ListenAndServe(":1123", loggedRouter) +// +func LoggingHandler(out io.Writer, h http.Handler) http.Handler { + return loggingHandler{out, h, writeLog} +} + +// CustomLoggingHandler provides a way to supply a custom log formatter +// while taking advantage of the mechanisms in this package +func CustomLoggingHandler(out io.Writer, h http.Handler, f LogFormatter) http.Handler { + return loggingHandler{out, h, f} +} diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go index a591d73..c8bb5c7 100644 --- a/vendor/github.com/gorilla/mux/route.go +++ b/vendor/github.com/gorilla/mux/route.go @@ -137,7 +137,7 @@ func (r *Route) GetHandler() http.Handler { // Name ----------------------------------------------------------------------- // Name sets the name for the route, used to build URLs. -// If the name was registered already it will be overwritten. +// It is an error to call Name more than once on a route. func (r *Route) Name(name string) *Route { if r.name != "" { r.err = fmt.Errorf("mux: route already has name %q, can't set %q", |