From 5572a1e1f3d21bb19b9714b44dc2f54265f18b6b Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Mon, 9 May 2016 12:22:52 -0700 Subject: [PATCH] update the go-restful dependency. --- Godeps/Godeps.json | 12 +-- .../emicklei/go-restful/container.go | 95 ++++++++++++------- .../emicklei/go-restful/cors_filter.go | 60 ++++++++---- .../examples/msgpack/msgpack_entity.go | 34 +++++++ .../emicklei/go-restful/web_service.go | 6 +- 5 files changed, 145 insertions(+), 62 deletions(-) create mode 100644 vendor/github.com/emicklei/go-restful/examples/msgpack/msgpack_entity.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 7c16e912b3c..a7cdf576432 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -720,18 +720,18 @@ }, { "ImportPath": "github.com/emicklei/go-restful", - "Comment": "v1.2-34-g496d495", - "Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3" + "Comment": "v1.2-54-g7c47e25", + "Rev": "7c47e2558a0bbbaba9ecab06bc6681e73028a28a" }, { "ImportPath": "github.com/emicklei/go-restful/log", - "Comment": "v1.2-34-g496d495", - "Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3" + "Comment": "v1.2-54-g7c47e25", + "Rev": "7c47e2558a0bbbaba9ecab06bc6681e73028a28a" }, { "ImportPath": "github.com/emicklei/go-restful/swagger", - "Comment": "v1.2-34-g496d495", - "Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3" + "Comment": "v1.2-54-g7c47e25", + "Rev": "7c47e2558a0bbbaba9ecab06bc6681e73028a28a" }, { "ImportPath": "github.com/evanphx/json-patch", diff --git a/vendor/github.com/emicklei/go-restful/container.go b/vendor/github.com/emicklei/go-restful/container.go index 59f34abea9c..62ded27c71b 100644 --- a/vendor/github.com/emicklei/go-restful/container.go +++ b/vendor/github.com/emicklei/go-restful/container.go @@ -6,6 +6,7 @@ package restful import ( "bytes" + "errors" "fmt" "net/http" "os" @@ -83,34 +84,16 @@ func (c *Container) EnableContentEncoding(enabled bool) { c.contentEncodingEnabled = enabled } -// Add a WebService to the Container. It will detect duplicate root paths and panic in that case. +// Add a WebService to the Container. It will detect duplicate root paths and exit in that case. func (c *Container) Add(service *WebService) *Container { c.webServicesLock.Lock() defer c.webServicesLock.Unlock() - // If registered on root then no additional specific mapping is needed - if !c.isRegisteredOnRoot { - pattern := c.fixedPrefixPath(service.RootPath()) - // check if root path registration is needed - if "/" == pattern || "" == pattern { - c.ServeMux.HandleFunc("/", c.dispatch) - c.isRegisteredOnRoot = true - } else { - // detect if registration already exists - alreadyMapped := false - for _, each := range c.webServices { - if each.RootPath() == service.RootPath() { - alreadyMapped = true - break - } - } - if !alreadyMapped { - c.ServeMux.HandleFunc(pattern, c.dispatch) - if !strings.HasSuffix(pattern, "/") { - c.ServeMux.HandleFunc(pattern+"/", c.dispatch) - } - } - } + + // if rootPath was not set then lazy initialize it + if len(service.rootPath) == 0 { + service.Path("/") } + // cannot have duplicate root paths for _, each := range c.webServices { if each.RootPath() == service.RootPath() { @@ -118,24 +101,64 @@ func (c *Container) Add(service *WebService) *Container { os.Exit(1) } } - // if rootPath was not set then lazy initialize it - if len(service.rootPath) == 0 { - service.Path("/") + + // If not registered on root then add specific mapping + if !c.isRegisteredOnRoot { + c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux) } c.webServices = append(c.webServices, service) return c } -func (c *Container) Remove(ws *WebService) error { - c.webServicesLock.Lock() - defer c.webServicesLock.Unlock() - newServices := []*WebService{} - for ix := range c.webServices { - if c.webServices[ix].rootPath != ws.rootPath { - newServices = append(newServices, c.webServices[ix]) +// addHandler may set a new HandleFunc for the serveMux +// this function must run inside the critical region protected by the webServicesLock. +// returns true if the function was registered on root ("/") +func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool { + pattern := fixedPrefixPath(service.RootPath()) + // check if root path registration is needed + if "/" == pattern || "" == pattern { + serveMux.HandleFunc("/", c.dispatch) + return true + } + // detect if registration already exists + alreadyMapped := false + for _, each := range c.webServices { + if each.RootPath() == service.RootPath() { + alreadyMapped = true + break } } - c.webServices = newServices + if !alreadyMapped { + serveMux.HandleFunc(pattern, c.dispatch) + if !strings.HasSuffix(pattern, "/") { + serveMux.HandleFunc(pattern+"/", c.dispatch) + } + } + return false +} + +func (c *Container) Remove(ws *WebService) error { + if c.ServeMux == http.DefaultServeMux { + errMsg := fmt.Sprintf("[restful] cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws) + log.Printf(errMsg) + return errors.New(errMsg) + } + c.webServicesLock.Lock() + defer c.webServicesLock.Unlock() + // build a new ServeMux and re-register all WebServices + newServeMux := http.NewServeMux() + newServices := []*WebService{} + newIsRegisteredOnRoot := false + for _, each := range c.webServices { + if each.rootPath != ws.rootPath { + // If not registered on root then add specific mapping + if !newIsRegisteredOnRoot { + newIsRegisteredOnRoot = c.addHandler(each, newServeMux) + } + newServices = append(newServices, each) + } + } + c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot return nil } @@ -251,7 +274,7 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R } // fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {} -func (c Container) fixedPrefixPath(pathspec string) string { +func fixedPrefixPath(pathspec string) string { varBegin := strings.Index(pathspec, "{") if -1 == varBegin { return pathspec diff --git a/vendor/github.com/emicklei/go-restful/cors_filter.go b/vendor/github.com/emicklei/go-restful/cors_filter.go index cb7fc1934f2..1efeef072d0 100644 --- a/vendor/github.com/emicklei/go-restful/cors_filter.go +++ b/vendor/github.com/emicklei/go-restful/cors_filter.go @@ -5,6 +5,7 @@ package restful // that can be found in the LICENSE file. import ( + "regexp" "strconv" "strings" ) @@ -19,11 +20,13 @@ import ( type CrossOriginResourceSharing struct { ExposeHeaders []string // list of Header names AllowedHeaders []string // list of Header names - AllowedDomains []string // list of allowed values for Http Origin. If empty all are allowed. + AllowedDomains []string // list of allowed values for Http Origin. An allowed value can be a regular expression to support subdomain matching. If empty all are allowed. AllowedMethods []string MaxAge int // number of seconds before requiring new Options request CookiesAllowed bool Container *Container + + allowedOriginPatterns []*regexp.Regexp // internal field for origin regexp check. } // Filter is a filter function that implements the CORS flow as documented on http://enable-cors.org/server.html @@ -37,21 +40,12 @@ func (c CrossOriginResourceSharing) Filter(req *Request, resp *Response, chain * chain.ProcessFilter(req, resp) return } - if len(c.AllowedDomains) > 0 { // if provided then origin must be included - included := false - for _, each := range c.AllowedDomains { - if each == origin { - included = true - break - } - } - if !included { - if trace { - traceLogger.Printf("HTTP Origin:%s is not part of %v", origin, c.AllowedDomains) - } - chain.ProcessFilter(req, resp) - return + if !c.isOriginAllowed(origin) { // check whether this origin is allowed + if trace { + traceLogger.Printf("HTTP Origin:%s is not part of %v, neither matches any part of %v", origin, c.AllowedDomains, c.allowedOriginPatterns) } + chain.ProcessFilter(req, resp) + return } if req.Request.Method != "OPTIONS" { c.doActualRequest(req, resp) @@ -128,13 +122,32 @@ func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool { if len(c.AllowedDomains) == 0 { return true } + allowed := false - for _, each := range c.AllowedDomains { - if each == origin { + for _, domain := range c.AllowedDomains { + if domain == origin { allowed = true break } } + + if !allowed { + if len(c.allowedOriginPatterns) == 0 { + // compile allowed domains to allowed origin patterns + allowedOriginRegexps, err := compileRegexps(c.AllowedDomains) + if err != nil { + return false + } + c.allowedOriginPatterns = allowedOriginRegexps + } + + for _, pattern := range c.allowedOriginPatterns { + if allowed = pattern.MatchString(origin); allowed { + break + } + } + } + return allowed } @@ -174,3 +187,16 @@ func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header str } return false } + +// Take a list of strings and compile them into a list of regular expressions. +func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { + regexps := []*regexp.Regexp{} + for _, regexpStr := range regexpStrings { + r, err := regexp.Compile(regexpStr) + if err != nil { + return regexps, err + } + regexps = append(regexps, r) + } + return regexps, nil +} diff --git a/vendor/github.com/emicklei/go-restful/examples/msgpack/msgpack_entity.go b/vendor/github.com/emicklei/go-restful/examples/msgpack/msgpack_entity.go new file mode 100644 index 00000000000..330e45896e4 --- /dev/null +++ b/vendor/github.com/emicklei/go-restful/examples/msgpack/msgpack_entity.go @@ -0,0 +1,34 @@ +package restPack + +import ( + restful "github.com/emicklei/go-restful" + "gopkg.in/vmihailenco/msgpack.v2" +) + +const MIME_MSGPACK = "application/x-msgpack" // Accept or Content-Type used in Consumes() and/or Produces() + +// NewEntityAccessorMPack returns a new EntityReaderWriter for accessing MessagePack content. +// This package is not initialized with such an accessor using the MIME_MSGPACK contentType. +func NewEntityAccessorMsgPack() restful.EntityReaderWriter { + return entityMsgPackAccess{} +} + +// entityOctetAccess is a EntityReaderWriter for Octet encoding +type entityMsgPackAccess struct { +} + +// Read unmarshalls the value from byte slice and using msgpack to unmarshal +func (e entityMsgPackAccess) Read(req *restful.Request, v interface{}) error { + return msgpack.NewDecoder(req.Request.Body).Decode(v) +} + +// Write marshals the value to byte slice and set the Content-Type Header. +func (e entityMsgPackAccess) Write(resp *restful.Response, status int, v interface{}) error { + if v == nil { + resp.WriteHeader(status) + // do not write a nil representation + return nil + } + resp.WriteHeader(status) + return msgpack.NewEncoder(resp).Encode(v) +} diff --git a/vendor/github.com/emicklei/go-restful/web_service.go b/vendor/github.com/emicklei/go-restful/web_service.go index 70708e4bd0e..24fc5328b14 100644 --- a/vendor/github.com/emicklei/go-restful/web_service.go +++ b/vendor/github.com/emicklei/go-restful/web_service.go @@ -36,9 +36,6 @@ func (w *WebService) SetDynamicRoutes(enable bool) { // compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it. func (w *WebService) compilePathExpression() { - if len(w.rootPath) == 0 { - w.Path("/") // lazy initialize path - } compiled, err := newPathExpression(w.rootPath) if err != nil { log.Printf("[restful] invalid path:%s because:%v", w.rootPath, err) @@ -60,6 +57,9 @@ func (w WebService) Version() string { return w.apiVersion } // All Routes will be relative to this path. func (w *WebService) Path(root string) *WebService { w.rootPath = root + if len(w.rootPath) == 0 { + w.rootPath = "/" + } w.compilePathExpression() return w }