diff options
Diffstat (limited to 'vendor/github.com/emicklei/go-restful/container.go')
-rw-r--r-- | vendor/github.com/emicklei/go-restful/container.go | 126 |
1 files changed, 77 insertions, 49 deletions
diff --git a/vendor/github.com/emicklei/go-restful/container.go b/vendor/github.com/emicklei/go-restful/container.go index 59f34abea..4196180e5 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" @@ -24,24 +25,24 @@ type Container struct { ServeMux *http.ServeMux isRegisteredOnRoot bool containerFilters []FilterFunction - doNotRecover bool // default is false + doNotRecover bool // default is true recoverHandleFunc RecoverHandleFunction serviceErrorHandleFunc ServiceErrorHandleFunction - router RouteSelector // default is a RouterJSR311, CurlyRouter is the faster alternative + router RouteSelector // default is a CurlyRouter (RouterJSR311 is a slower alternative) contentEncodingEnabled bool // default is false } -// NewContainer creates a new Container using a new ServeMux and default router (RouterJSR311) +// NewContainer creates a new Container using a new ServeMux and default router (CurlyRouter) func NewContainer() *Container { return &Container{ webServices: []*WebService{}, ServeMux: http.NewServeMux(), isRegisteredOnRoot: false, containerFilters: []FilterFunction{}, - doNotRecover: false, + doNotRecover: true, recoverHandleFunc: logStackOnRecover, serviceErrorHandleFunc: writeServiceError, - router: RouterJSR311{}, + router: CurlyRouter{}, contentEncodingEnabled: false} } @@ -68,12 +69,12 @@ func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) { // DoNotRecover controls whether panics will be caught to return HTTP 500. // If set to true, Route functions are responsible for handling any error situation. -// Default value is false = recover from panics. This has performance implications. +// Default value is true. func (c *Container) DoNotRecover(doNot bool) { c.doNotRecover = doNot } -// Router changes the default Router (currently RouterJSR311) +// Router changes the default Router (currently CurlyRouter) func (c *Container) Router(aRouter RouteSelector) { c.router = aRouter } @@ -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 } +// 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 + } + } + 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.Print(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{} - for ix := range c.webServices { - if c.webServices[ix].rootPath != ws.rootPath { - newServices = append(newServices, c.webServices[ix]) + 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 = newServices + c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot return nil } @@ -166,6 +189,17 @@ func writeServiceError(err ServiceError, req *Request, resp *Response) { } // Dispatch the incoming Http Request to a matching WebService. +func (c *Container) Dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) { + if httpWriter == nil { + panic("httpWriter cannot be nil") + } + if httpRequest == nil { + panic("httpRequest cannot be nil") + } + c.dispatch(httpWriter, httpRequest) +} + +// Dispatch the incoming Http Request to a matching WebService. func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) { writer := httpWriter @@ -185,12 +219,6 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R } }() } - // Install closing the request body (if any) - defer func() { - if nil != httpRequest.Body { - httpRequest.Body.Close() - } - }() // Detect if compression is needed // assume without compression, test for override @@ -251,7 +279,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 @@ -260,12 +288,12 @@ func (c Container) fixedPrefixPath(pathspec string) string { } // ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server -func (c Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) { +func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) { c.ServeMux.ServeHTTP(httpwriter, httpRequest) } // Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics. -func (c Container) Handle(pattern string, handler http.Handler) { +func (c *Container) Handle(pattern string, handler http.Handler) { c.ServeMux.Handle(pattern, handler) } @@ -295,7 +323,7 @@ func (c *Container) Filter(filter FilterFunction) { } // RegisteredWebServices returns the collections of added WebServices -func (c Container) RegisteredWebServices() []*WebService { +func (c *Container) RegisteredWebServices() []*WebService { c.webServicesLock.RLock() defer c.webServicesLock.RUnlock() result := make([]*WebService, len(c.webServices)) @@ -306,7 +334,7 @@ func (c Container) RegisteredWebServices() []*WebService { } // computeAllowedMethods returns a list of HTTP methods that are valid for a Request -func (c Container) computeAllowedMethods(req *Request) []string { +func (c *Container) computeAllowedMethods(req *Request) []string { // Go through all RegisteredWebServices() and all its Routes to collect the options methods := []string{} requestPath := req.Request.URL.Path |