dangogh closed pull request #2952: Change Traffic Ops to 501 Not Implemented on
unknown version reqs, and client to return a helpful message
URL: https://github.com/apache/trafficcontrol/pull/2952
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/traffic_ops/client/session.go b/traffic_ops/client/session.go
index 967c775bf..4faf0b299 100644
--- a/traffic_ops/client/session.go
+++ b/traffic_ops/client/session.go
@@ -254,6 +254,11 @@ func (to *Session) ErrUnlessOK(resp *http.Response,
remoteAddr net.Addr, err err
}
defer resp.Body.Close()
+
+ if resp.StatusCode == http.StatusNotImplemented {
+ return nil, remoteAddr, errors.New("Traffic Ops Server returned
'Not Implemented', this client is probably newer than Traffic Ops, and you
probably need to either upgrade Traffic Ops, or use a client whose version
matches your Traffic Ops version.")
+ }
+
body, readErr := ioutil.ReadAll(resp.Body)
if readErr != nil {
return nil, remoteAddr, readErr
diff --git a/traffic_ops/traffic_ops_golang/routing.go
b/traffic_ops/traffic_ops_golang/routing.go
index a6a75ce51..fc94cd65f 100644
--- a/traffic_ops/traffic_ops_golang/routing.go
+++ b/traffic_ops/traffic_ops_golang/routing.go
@@ -105,7 +105,8 @@ type PathHandler struct {
}
// CreateRouteMap returns a map of methods to a slice of paths and handlers;
wrapping the handlers in the appropriate middleware. Uses Semantic Versioning:
routes are added to every subsequent minor version, but not subsequent major
versions. For example, a 1.2 route is added to 1.3 but not 2.1. Also truncates
'2.0' to '2', creating succinct major versions.
-func CreateRouteMap(rs []Route, rawRoutes []RawRoute, authBase AuthBase,
reqTimeOutSeconds int) map[string][]PathHandler {
+// Returns the map of routes, and a map of API versions served.
+func CreateRouteMap(rs []Route, rawRoutes []RawRoute, authBase AuthBase,
reqTimeOutSeconds int) (map[string][]PathHandler, map[float64]struct{}) {
// TODO strong types for method, path
versions := getSortedRouteVersions(rs)
requestTimeout := time.Second * time.Duration(60)
@@ -132,7 +133,13 @@ func CreateRouteMap(rs []Route, rawRoutes []RawRoute,
authBase AuthBase, reqTime
m[r.Method] = append(m[r.Method], PathHandler{Path: r.Path,
Handler: use(r.Handler, middlewares)})
log.Infof("adding raw route %v %v\n", r.Method, r.Path)
}
- return m
+
+ versionSet := map[float64]struct{}{}
+ for _, version := range versions {
+ versionSet[version] = struct{}{}
+ }
+
+ return m, versionSet
}
func getRouteMiddleware(middlewares []Middleware, authBase AuthBase,
authenticated bool, privLevel int, requestTimeout time.Duration) []Middleware {
@@ -174,6 +181,7 @@ func CompileRoutes(routes map[string][]PathHandler)
map[string][]CompiledRoute {
// Handler - generic handler func used by the Handlers hooking into the routes
func Handler(
routes map[string][]CompiledRoute,
+ versions map[float64]struct{},
catchall http.Handler,
db *sqlx.DB,
cfg *config.Config,
@@ -227,9 +235,39 @@ func Handler(
compiledRoute.Handler(w, r)
return
}
+
+ if IsRequestAPIAndUnknownVersion(r, versions) {
+ h := wrapAccessLog(cfg.Secrets[0], NotImplementedHandler())
+ h.ServeHTTP(w, r)
+ return
+ }
+
catchall.ServeHTTP(w, r)
}
+// IsRequestAPIAndUnknownVersion returns true if the request starts with
`/api` and is a version not in the list of versions.
+func IsRequestAPIAndUnknownVersion(req *http.Request, versions
map[float64]struct{}) bool {
+ pathParts := strings.Split(req.URL.Path, "/")
+ if len(pathParts) < 2 {
+ return false // path doesn't start with `/api`, so it's not an
api request
+ }
+ if strings.ToLower(pathParts[1]) != "api" {
+ return false // path doesn't start with `/api`, so it's not an
api request
+ }
+ if len(pathParts) < 3 {
+ return true // path starts with `/api` but not
`/api/{version}`, so it's an api request, and an unknown/nonexistent version.
+ }
+
+ version, err := strconv.ParseFloat(pathParts[2], 64)
+ if err != nil {
+ return true // path starts with `/api`, and version isn't a
number, so it's an unknown/nonexistent version
+ }
+ if _, versionExists := versions[version]; versionExists {
+ return false // path starts with `/api` and version exists, so
it's API but a known version
+ }
+ return true // path starts with `/api`, and version is unknown
+}
+
// RegisterRoutes - parses the routes and registers the handlers with the Go
Router
func RegisterRoutes(d ServerData) error {
routeSlice, rawRoutes, catchall, err := Routes(d)
@@ -238,11 +276,11 @@ func RegisterRoutes(d ServerData) error {
}
authBase := AuthBase{secret: d.Config.Secrets[0], override: nil} //we
know d.Config.Secrets is a slice of at least one or start up would fail.
- routes := CreateRouteMap(routeSlice, rawRoutes, authBase,
d.RequestTimeout)
+ routes, versions := CreateRouteMap(routeSlice, rawRoutes, authBase,
d.RequestTimeout)
compiledRoutes := CompileRoutes(routes)
getReqID := nextReqIDGetter()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- Handler(compiledRoutes, catchall, d.DB, &d.Config, getReqID,
d.Plugins, w, r)
+ Handler(compiledRoutes, versions, catchall, d.DB, &d.Config,
getReqID, d.Plugins, w, r)
})
return nil
}
diff --git a/traffic_ops/traffic_ops_golang/wrappers.go
b/traffic_ops/traffic_ops_golang/wrappers.go
index 6d3b0d98d..e30a35c04 100644
--- a/traffic_ops/traffic_ops_golang/wrappers.go
+++ b/traffic_ops/traffic_ops_golang/wrappers.go
@@ -275,3 +275,11 @@ func (i *BodyInterceptor) RealWrite(b []byte) (int, error)
{
func (i *BodyInterceptor) Body() []byte {
return i.body
}
+
+func NotImplementedHandler() http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set(tc.ContentType, tc.ApplicationJson)
+ w.WriteHeader(http.StatusNotImplemented)
+ w.Write([]byte(`{"alerts":[{"level":"error","text":"The
requested api version is not implemented by this server. If you are using a
newer client with an older server, you will need to use an older client version
or upgrade your server."}]}`))
+ })
+}
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services