update the go-restful dependency.

This commit is contained in:
Brendan Burns 2016-05-09 12:22:52 -07:00
parent 835a2577f8
commit 5572a1e1f3
5 changed files with 145 additions and 62 deletions

12
Godeps/Godeps.json generated
View File

@ -720,18 +720,18 @@
}, },
{ {
"ImportPath": "github.com/emicklei/go-restful", "ImportPath": "github.com/emicklei/go-restful",
"Comment": "v1.2-34-g496d495", "Comment": "v1.2-54-g7c47e25",
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3" "Rev": "7c47e2558a0bbbaba9ecab06bc6681e73028a28a"
}, },
{ {
"ImportPath": "github.com/emicklei/go-restful/log", "ImportPath": "github.com/emicklei/go-restful/log",
"Comment": "v1.2-34-g496d495", "Comment": "v1.2-54-g7c47e25",
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3" "Rev": "7c47e2558a0bbbaba9ecab06bc6681e73028a28a"
}, },
{ {
"ImportPath": "github.com/emicklei/go-restful/swagger", "ImportPath": "github.com/emicklei/go-restful/swagger",
"Comment": "v1.2-34-g496d495", "Comment": "v1.2-54-g7c47e25",
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3" "Rev": "7c47e2558a0bbbaba9ecab06bc6681e73028a28a"
}, },
{ {
"ImportPath": "github.com/evanphx/json-patch", "ImportPath": "github.com/evanphx/json-patch",

View File

@ -6,6 +6,7 @@ package restful
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
@ -83,18 +84,42 @@ func (c *Container) EnableContentEncoding(enabled bool) {
c.contentEncodingEnabled = enabled 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 { func (c *Container) Add(service *WebService) *Container {
c.webServicesLock.Lock() c.webServicesLock.Lock()
defer c.webServicesLock.Unlock() defer c.webServicesLock.Unlock()
// If registered on root then no additional specific mapping is needed
// 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() {
log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
os.Exit(1)
}
}
// If not registered on root then add specific mapping
if !c.isRegisteredOnRoot { if !c.isRegisteredOnRoot {
pattern := c.fixedPrefixPath(service.RootPath()) c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux)
}
c.webServices = append(c.webServices, service)
return c
}
// 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 // check if root path registration is needed
if "/" == pattern || "" == pattern { if "/" == pattern || "" == pattern {
c.ServeMux.HandleFunc("/", c.dispatch) serveMux.HandleFunc("/", c.dispatch)
c.isRegisteredOnRoot = true return true
} else { }
// detect if registration already exists // detect if registration already exists
alreadyMapped := false alreadyMapped := false
for _, each := range c.webServices { for _, each := range c.webServices {
@ -104,38 +129,36 @@ func (c *Container) Add(service *WebService) *Container {
} }
} }
if !alreadyMapped { if !alreadyMapped {
c.ServeMux.HandleFunc(pattern, c.dispatch) serveMux.HandleFunc(pattern, c.dispatch)
if !strings.HasSuffix(pattern, "/") { if !strings.HasSuffix(pattern, "/") {
c.ServeMux.HandleFunc(pattern+"/", c.dispatch) serveMux.HandleFunc(pattern+"/", c.dispatch)
} }
} }
} return false
}
// cannot have duplicate root paths
for _, each := range c.webServices {
if each.RootPath() == service.RootPath() {
log.Printf("[restful] WebService with duplicate root path detected:['%v']", each)
os.Exit(1)
}
}
// if rootPath was not set then lazy initialize it
if len(service.rootPath) == 0 {
service.Path("/")
}
c.webServices = append(c.webServices, service)
return c
} }
func (c *Container) Remove(ws *WebService) error { 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() c.webServicesLock.Lock()
defer c.webServicesLock.Unlock() defer c.webServicesLock.Unlock()
// build a new ServeMux and re-register all WebServices
newServeMux := http.NewServeMux()
newServices := []*WebService{} newServices := []*WebService{}
for ix := range c.webServices { newIsRegisteredOnRoot := false
if c.webServices[ix].rootPath != ws.rootPath { for _, each := range c.webServices {
newServices = append(newServices, c.webServices[ix]) 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 = newServices c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot
return nil 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 {} // 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, "{") varBegin := strings.Index(pathspec, "{")
if -1 == varBegin { if -1 == varBegin {
return pathspec return pathspec

View File

@ -5,6 +5,7 @@ package restful
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
import ( import (
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
@ -19,11 +20,13 @@ import (
type CrossOriginResourceSharing struct { type CrossOriginResourceSharing struct {
ExposeHeaders []string // list of Header names ExposeHeaders []string // list of Header names
AllowedHeaders []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 AllowedMethods []string
MaxAge int // number of seconds before requiring new Options request MaxAge int // number of seconds before requiring new Options request
CookiesAllowed bool CookiesAllowed bool
Container *Container 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 // Filter is a filter function that implements the CORS flow as documented on http://enable-cors.org/server.html
@ -37,22 +40,13 @@ func (c CrossOriginResourceSharing) Filter(req *Request, resp *Response, chain *
chain.ProcessFilter(req, resp) chain.ProcessFilter(req, resp)
return return
} }
if len(c.AllowedDomains) > 0 { // if provided then origin must be included if !c.isOriginAllowed(origin) { // check whether this origin is allowed
included := false
for _, each := range c.AllowedDomains {
if each == origin {
included = true
break
}
}
if !included {
if trace { if trace {
traceLogger.Printf("HTTP Origin:%s is not part of %v", origin, c.AllowedDomains) 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) chain.ProcessFilter(req, resp)
return return
} }
}
if req.Request.Method != "OPTIONS" { if req.Request.Method != "OPTIONS" {
c.doActualRequest(req, resp) c.doActualRequest(req, resp)
chain.ProcessFilter(req, resp) chain.ProcessFilter(req, resp)
@ -128,13 +122,32 @@ func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool {
if len(c.AllowedDomains) == 0 { if len(c.AllowedDomains) == 0 {
return true return true
} }
allowed := false allowed := false
for _, each := range c.AllowedDomains { for _, domain := range c.AllowedDomains {
if each == origin { if domain == origin {
allowed = true allowed = true
break 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 return allowed
} }
@ -174,3 +187,16 @@ func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header str
} }
return false 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
}

View File

@ -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)
}

View File

@ -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. // compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
func (w *WebService) compilePathExpression() { func (w *WebService) compilePathExpression() {
if len(w.rootPath) == 0 {
w.Path("/") // lazy initialize path
}
compiled, err := newPathExpression(w.rootPath) compiled, err := newPathExpression(w.rootPath)
if err != nil { if err != nil {
log.Printf("[restful] invalid path:%s because:%v", w.rootPath, err) 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. // All Routes will be relative to this path.
func (w *WebService) Path(root string) *WebService { func (w *WebService) Path(root string) *WebService {
w.rootPath = root w.rootPath = root
if len(w.rootPath) == 0 {
w.rootPath = "/"
}
w.compilePathExpression() w.compilePathExpression()
return w return w
} }