From d6c22b5ff0c1a8ea6003f56fd081caadda735d2c Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Sat, 21 Mar 2020 14:49:36 +0100 Subject: [PATCH] adapt server to previous api changes --- go.sum | 0 http.go | 126 +++++++++++++++++++++++++----------------------------- server.go | 15 +++---- util.go | 90 -------------------------------------- 4 files changed, 66 insertions(+), 165 deletions(-) create mode 100644 go.sum diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/http.go b/http.go index 44f8acc..1f825f9 100644 --- a/http.go +++ b/http.go @@ -3,7 +3,6 @@ package aicra import ( "log" "net/http" - "strings" "git.xdrm.io/go/aicra/api" "git.xdrm.io/go/aicra/internal/reqdata" @@ -13,103 +12,96 @@ import ( type httpServer Server // ServeHTTP implements http.Handler and has to be called on each request -func (server httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() +func (server httpServer) ServeHTTP(res http.ResponseWriter, req *http.Request) { + defer req.Body.Close() - /* (1) create api.Request from http.Request - ---------------------------------------------------------*/ - request, err := api.NewRequest(r) + // 1. find a matching service in the config + service := server.config.Find(req) + if service == nil { + response := api.NewResponse(api.ErrorUnknownService()) + response.ServeHTTP(res, req) + logError(response) + return + } + + // 2. build input parameter receiver + dataset := reqdata.New(service) + + // 3. extract URI data + err := dataset.ExtractURI(req) if err != nil { - log.Fatal(err) - } - - // 2. find a matching service for this path in the config - serviceConf, pathIndex := server.config.Browse(request.URI) - if serviceConf == nil { + response := api.NewResponse(api.ErrorMissingParam()) + response.ServeHTTP(res, req) + logError(response) return } - // 3. extract the service path from request URI - servicePath := strings.Join(request.URI[:pathIndex], "/") - if !strings.HasPrefix(servicePath, "/") { - servicePath = "/" + servicePath - } - - // 4. find method configuration from http method */ - var methodConf = serviceConf.Method(r.Method) - if methodConf == nil { - res := api.NewResponse(api.ErrorUnknownMethod()) - res.ServeHTTP(w, r) - logError(res) + // 4. extract query data + err = dataset.ExtractQuery(req) + if err != nil { + response := api.NewResponse(api.ErrorMissingParam()) + response.ServeHTTP(res, req) + logError(response) return } - // 5. parse data from the request (uri, query, form, json) - data := reqdata.New(request.URI[pathIndex:], r) - - /* (2) check parameters - ---------------------------------------------------------*/ - parameters, paramError := server.extractParameters(data, methodConf.Parameters) - - // Fail if argument check failed - if paramError.Code != api.ErrorSuccess().Code { - res := api.NewResponse(paramError) - res.ServeHTTP(w, r) - logError(res) + // 5. extract form/json data + err = dataset.ExtractForm(req) + if err != nil { + response := api.NewResponse(api.ErrorMissingParam()) + response.ServeHTTP(res, req) + logError(response) return } - request.Param = parameters - - /* (3) search for the handler - ---------------------------------------------------------*/ + // 6. find a matching handler var foundHandler *api.Handler var found bool for _, handler := range server.handlers { - if handler.GetPath() == servicePath { + if handler.GetMethod() == service.Method && handler.GetPath() == service.Pattern { found = true - if handler.GetMethod() == r.Method { - foundHandler = handler - } } } - // fail if found no handler + // 7. fail if found no handler if foundHandler == nil { if found { - res := api.NewResponse() - res.SetError(api.ErrorUncallableMethod(), servicePath, r.Method) - res.ServeHTTP(w, r) - logError(res) + r := api.NewResponse() + r.SetError(api.ErrorUncallableService(), service.Method, service.Pattern) + r.ServeHTTP(res, req) + logError(r) return } - res := api.NewResponse() - res.SetError(api.ErrorUncallableService(), servicePath) - res.ServeHTTP(w, r) - logError(res) + r := api.NewResponse() + r.SetError(api.ErrorUnknownService(), service.Method, service.Pattern) + r.ServeHTTP(res, req) + logError(r) return } - /* (4) execute handler and return response - ---------------------------------------------------------*/ - // 1. feed request with configuration scope - request.Scope = methodConf.Scope + // 8. build api.Request from http.Request + apireq, err := api.NewRequest(req) + if err != nil { + log.Fatal(err) + } - // 2. execute - res := api.NewResponse() - foundHandler.Handle(*request, res) + // 9. feed request with scope & parameters + apireq.Scope = service.Scope + apireq.Param = dataset.Data - // 3. apply headers - for key, values := range res.Headers { + // 10. execute + response := api.NewResponse() + foundHandler.Handle(*apireq, response) + + // 11. apply headers + for key, values := range response.Headers { for _, value := range values { - w.Header().Add(key, value) + res.Header().Add(key, value) } } - // 4. write to response - res.ServeHTTP(w, r) - return - + // 12. write to response + response.ServeHTTP(res, req) } diff --git a/server.go b/server.go index 4c40977..cacc0e4 100644 --- a/server.go +++ b/server.go @@ -6,20 +6,18 @@ import ( "os" "git.xdrm.io/go/aicra/api" - + "git.xdrm.io/go/aicra/datatype" "git.xdrm.io/go/aicra/internal/config" - checker "git.xdrm.io/go/aicra/typecheck" ) // Server represents an AICRA instance featuring: type checkers, services type Server struct { - config *config.Service - Checkers *checker.Set + config *config.Server handlers []*api.Handler } // New creates a framework instance from a configuration file -func New(configPath string) (*Server, error) { +func New(configPath string, dtypes ...datatype.T) (*Server, error) { var ( err error configFile io.ReadCloser @@ -28,7 +26,6 @@ func New(configPath string) (*Server, error) { // 1. init instance var i = &Server{ config: nil, - Checkers: checker.New(), handlers: make([]*api.Handler, 0), } @@ -40,14 +37,16 @@ func New(configPath string) (*Server, error) { defer configFile.Close() // 3. load configuration - i.config, err = config.Parse(configFile) + i.config, err = config.Parse(configFile, dtypes...) if err != nil { return nil, err } // 4. log configuration services log.Printf("🔧 Reading configuration '%s'\n", configPath) - logService(*i.config, "") + for _, service := range i.config.Services { + log.Printf(" ->\t%s\t'%s'\n", service.Method, service.Pattern) + } return i, nil diff --git a/util.go b/util.go index 949acb7..a143dcd 100644 --- a/util.go +++ b/util.go @@ -5,101 +5,11 @@ import ( "net/http" "git.xdrm.io/go/aicra/api" - "git.xdrm.io/go/aicra/internal/config" - "git.xdrm.io/go/aicra/internal/reqdata" ) -// extractParameters extracts parameters for the request and checks -// every single one according to configuration options -func (s *httpServer) extractParameters(store *reqdata.Store, methodParam map[string]*config.Parameter) (map[string]interface{}, api.Error) { - - // init vars - parameters := make(map[string]interface{}) - - // for each param of the config - for name, param := range methodParam { - - // 1. extract value - p, isset := store.Set[name] - - // 2. fail if required & missing - if !isset && !param.Optional { - apiErr := api.ErrorMissingParam() - apiErr.SetArguments(name) - return nil, apiErr - } - - // 3. optional & missing: set default value - if !isset { - p = &reqdata.Parameter{ - Parsed: true, - File: param.Type == "FILE", - Value: nil, - } - if param.Default != nil { - p.Value = *param.Default - } - - // we are done - parameters[param.Rename] = p.Value - continue - } - - // 4. parse parameter if not file - if !p.File { - p.Parse() - } - - // 5. fail on unexpected multipart file - waitFile, gotFile := param.Type == "FILE", p.File - if gotFile && !waitFile || !gotFile && waitFile { - apiErr := api.ErrorInvalidParam() - apiErr.SetArguments(param.Rename, "FILE") - return nil, apiErr - } - - // 6. ignore type check if file - if gotFile { - parameters[param.Rename] = p.Value - continue - } - - // 7. check type - if s.Checkers.Run(param.Type, p.Value) != nil { - apiErr := api.ErrorInvalidParam() - apiErr.SetArguments(param.Rename, param.Type, p.Value) - return nil, apiErr - } - - parameters[param.Rename] = p.Value - - } - - return parameters, api.ErrorSuccess() -} - var handledMethods = []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete} // Prints an error as HTTP response func logError(res *api.Response) { log.Printf("[http.fail] %v\n", res) } - -// logService logs a service details -func logService(s config.Service, path string) { - for _, method := range handledMethods { - if m := s.Method(method); m != nil { - if path == "" { - log.Printf(" ->\t%s\t'/'\n", method) - } else { - log.Printf(" ->\t%s\t'%s'\n", method, path) - } - } - } - - if s.Children != nil { - for subPath, child := range s.Children { - logService(*child, path+"/"+subPath) - } - } -}