From 6c8bec2c3c86fa3c784f55ff10212b109a0cea92 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Sun, 3 Oct 2021 15:45:08 +0200 Subject: [PATCH] Re-enable API Gateway route handling (/api/gateway/...) API Gateway routes are now bind under /api/gateway. This also removes custom route method validation and handles not-found responses with a chi's NotFound method --- app/servers.go | 3 +++ pkg/apigw/helpers.go | 4 +++- pkg/apigw/route.go | 14 -------------- pkg/apigw/route_test.go | 12 ------------ pkg/apigw/service.go | 43 ++++++++++++++++++++++++++++------------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/app/servers.go b/app/servers.go index 940459ecd..993eeca3f 100644 --- a/app/servers.go +++ b/app/servers.go @@ -112,6 +112,9 @@ func (app *CortezaApp) mountHttpRoutes(r chi.Router) { r.Handle("/docs", http.RedirectHandler(fullpathDocs+"/", http.StatusPermanentRedirect)) r.Handle("/docs*", http.StripPrefix(fullpathDocs, http.FileServer(docs.GetFS()))) + + var fullpathGateway = options.CleanBase(ho.BaseUrl, ho.ApiBaseUrl, "gateway") + r.Handle("/gateway*", http.StripPrefix(fullpathGateway, app.ApigwService)) }) }() diff --git a/pkg/apigw/helpers.go b/pkg/apigw/helpers.go index 62367ff72..ec5e08cfd 100644 --- a/pkg/apigw/helpers.go +++ b/pkg/apigw/helpers.go @@ -13,9 +13,11 @@ const ( func helperDefaultResponse(opt *options.ApigwOpt) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if opt.LogEnabled { + // Say something friendly when logging is enabled http.Error(w, devHelperResponseBody, http.StatusTeapot) } else { - http.Error(w, ``, http.StatusFound) + // Default 404 response + http.Error(w, "", http.StatusNotFound) } } } diff --git a/pkg/apigw/route.go b/pkg/apigw/route.go index c89534b51..76d82f4e5 100644 --- a/pkg/apigw/route.go +++ b/pkg/apigw/route.go @@ -54,12 +54,6 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) { scope.Set("opts", r.opts) scope.Set("payload", body) - if err := r.validate(req); err != nil { - r.log.Debug("error validating request on route", zap.Error(err)) - r.errHandler(w, req, err) - return - } - if r.opts.LogEnabled { o, _ := httputil.DumpRequest(req, r.opts.LogRequestBody) r.log.Debug("incoming request", zap.Any("request", string(o))) @@ -74,14 +68,6 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) { ) } -func (r route) validate(req *http.Request) (err error) { - if req.Method != r.method { - err = fmt.Errorf("invalid method %s", req.Method) - } - - return -} - func (r route) String() string { return fmt.Sprintf("%s %s", r.method, r.endpoint) } diff --git a/pkg/apigw/route_test.go b/pkg/apigw/route_test.go index f803c1aff..836381950 100644 --- a/pkg/apigw/route_test.go +++ b/pkg/apigw/route_test.go @@ -68,18 +68,6 @@ func Test_pl(t *testing.T) { expStatus: http.StatusTemporaryRedirect, expError: "{\"error\":{\"message\":\"test error\"}}\n", }, - { - name: "request method validation fail", - handler: &types.MockHandler{ - Handler_: func(rw http.ResponseWriter, r *http.Request) error { - rw.WriteHeader(http.StatusTemporaryRedirect) - return errors.New("test error") - }, - }, - method: "GET", - expStatus: http.StatusInternalServerError, - expError: "{\"error\":{\"message\":\"invalid method POST\"}}\n", - }, } ) diff --git a/pkg/apigw/service.go b/pkg/apigw/service.go index 28e1f659c..62ed8943c 100644 --- a/pkg/apigw/service.go +++ b/pkg/apigw/service.go @@ -44,10 +44,6 @@ func Service() *apigw { return apiGw } -func Set(a *apigw) { - apiGw = a -} - // Setup handles the singleton service func Setup(opts *options.ApigwOpt, log *zap.Logger, storer storer) { if apiGw != nil { @@ -74,6 +70,23 @@ func New(opts *options.ApigwOpt, logger *zap.Logger, storer storer) *apigw { // // When reloading routes, make sure to replace the original mux func (s *apigw) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if s.mx == nil { + http.Error(w, "API Gateway not initialized", http.StatusInternalServerError) + return + } + + if len(s.routes) == 0 { + helperDefaultResponse(s.opts)(w, r) + return + } + + // Remove route context for chi + // + // Without this, chi can not properly handle requests + // in API gateway's sub-router + r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, nil)) + + // Handle api-gw request s.mx.ServeHTTP(w, r) } @@ -94,21 +107,25 @@ func (s *apigw) Reload(ctx context.Context) (err error) { // Rebuild the mux s.mx = chi.NewMux() - s.mx.HandleFunc("/", helperDefaultResponse(s.opts)) + for _, r := range s.routes { - s.mx.Handle(r.endpoint, r) + // Register route handler on endpoint & method + s.mx.Method(r.method, r.endpoint, r) } + // API GW 404 handler + s.mx.NotFound(helperDefaultResponse(s.opts)) + return nil } -// Init all the routes -func (s *apigw) Init(ctx context.Context, route ...*route) { +// Init all routes +func (s *apigw) Init(ctx context.Context, routes ...*route) { var ( defaultPostFilter types.Handler ) - s.routes = route + s.routes = routes s.log.Debug("registering routes", zap.Int("count", len(s.routes))) @@ -138,17 +155,17 @@ func (s *apigw) Init(ctx context.Context, route ...*route) { continue } - for _, f := range regFilters { - flog := log.With(zap.String("ref", f.Ref)) + for _, rf := range regFilters { + flog := log.With(zap.String("ref", rf.Ref)) // make sure there is only one postfilter // on async routes - if r.meta.async && f.Kind == string(types.PostFilter) { + if r.meta.async && rf.Kind == string(types.PostFilter) { flog.Debug("not registering filter for async route") continue } - ff, err := s.registerFilter(f, r) + ff, err := s.registerFilter(rf, r) if err != nil { flog.Error("could not register filter", zap.Error(err))