diff --git a/driver/generic.go b/driver/generic.go index 0e3de04..bbb8b6c 100644 --- a/driver/generic.go +++ b/driver/generic.go @@ -9,8 +9,8 @@ import ( "strings" ) -// Load implements the Driver interface -func (d *Generic) Load(_path []string, _method string) (func(response.Arguments) response.Response, e.Error) { +// RunController implements the Driver interface +func (d *Generic) RunController(_path []string, _method string) (func(response.Arguments) response.Response, e.Error) { /* (1) Build controller path */ path := strings.Join(_path, "-") diff --git a/driver/plugin.go b/driver/plugin.go index 8e7c845..37d4967 100644 --- a/driver/plugin.go +++ b/driver/plugin.go @@ -4,12 +4,13 @@ import ( "fmt" "git.xdrm.io/go/aicra/err" "git.xdrm.io/go/aicra/response" + "net/http" "plugin" "strings" ) -// Load implements the Driver interface -func (d *Plugin) Load(_path []string, _method string) (func(response.Arguments) response.Response, err.Error) { +// RunController implements the Driver interface +func (d *Plugin) RunController(_path []string, _method string) (func(response.Arguments) response.Response, err.Error) { /* (1) Build controller path */ path := strings.Join(_path, "-") @@ -44,3 +45,45 @@ func (d *Plugin) Load(_path []string, _method string) (func(response.Arguments) return callable, err.Success } + +// LoadMiddleware returns a new middleware function; it must be a +// valid and existing folder/filename file with or without the .so extension +// it must be located in the relative directory .build/middleware +func (d *Plugin) LoadMiddleware(_path string) (func(http.Request, *[]string), error) { + + // ignore non .so files + if !strings.HasSuffix(_path, ".so") { + return nil, fmt.Errorf("Invalid name") + } + + /* (1) Check plugin name */ + if len(_path) < 1 { + return nil, fmt.Errorf("Plugin name must not be empty") + } + + /* (2) Check plugin extension */ + if !strings.HasSuffix(_path, ".so") { + _path = fmt.Sprintf("%s.so", _path) + } + + /* (3) Try to load the plugin */ + p, err := plugin.Open(_path) + if err != nil { + return nil, err + } + + /* (4) Export wanted properties */ + inspect, err := p.Lookup("Inspect") + if err != nil { + return nil, fmt.Errorf("Missing method 'Inspect()'; %s", err) + } + + /* (5) Cast Inspect */ + mware, ok := inspect.(func(http.Request, *[]string)) + if !ok { + return nil, fmt.Errorf("Inspect() is malformed") + } + + /* (6) Add type to registry */ + return mware, nil +} diff --git a/driver/types.go b/driver/types.go index 2645d89..26774cd 100644 --- a/driver/types.go +++ b/driver/types.go @@ -3,26 +3,28 @@ package driver import ( "git.xdrm.io/go/aicra/err" "git.xdrm.io/go/aicra/response" + "net/http" ) // Driver defines the driver interface to load controller/middleware implementation or executables type Driver interface { - Load(_path []string, _method string) (func(response.Arguments) response.Response, err.Error) + RunController(_path []string, _method string) (func(response.Arguments) response.Response, err.Error) + LoadMiddleware(_path string) (func(http.Request, *[]string), error) } -// Generic tells the aicra instance to use the generic driver to load controller's executables +// Generic tells the aicra instance to use the generic driver to load controller/middleware executables // // It will call an executable with the json input into the standard input (argument 1) // the HTTP method is send as the key _HTTP_METHOD_ (in upper case) // The standard output must be a json corresponding to the data // -// FILE STRUCTURE +// CONTROLLER FILE STRUCTURE // -------------- // - the root (/) controller executable must be named /controller/ROOT // - the a/b/c controller executable must be named /controller/a/b/c type Generic struct{} -// Plugin tells the aicra instance to use the plugin driver to load controller's executables +// Plugin tells the aicra instance to use the plugin driver to load controller/middleware executables // // It will load go .so plugins with the following interface : // @@ -33,7 +35,7 @@ type Generic struct{} // Delete(d i.Arguments, r *i.Response) i.Response // } // -// FILE STRUCTURE +// CONTROLLER FILE STRUCTURE // -------------- // - the root (/) controller executable must be named /controller/ROOT/main.so // - the a/b/c controller executable must be named /controller/a/b/c/main.so @@ -43,13 +45,13 @@ type Generic struct{} // The compilation is handled with the command-line tool `aicra ` type Plugin struct{} -// FastCGI tells the aicra instance to use the fastcgi driver to load controller's executables +// FastCGI tells the aicra instance to use the fastcgi driver to load controller/middleware executables // // Warning: PHP only // // It will use the fastcgi protocol with php at : // -// FILE STRUCTURE +// CONTROLLER FILE STRUCTURE // -------------- // - the root (/) controller executable must be named /controller/ROOT.php // - the a/b/c controller executable must be named /controller/a/b/c.php diff --git a/internal/request/request.go b/internal/request/request.go index 9dde061..f966375 100644 --- a/internal/request/request.go +++ b/internal/request/request.go @@ -96,6 +96,6 @@ func FetchFormData(req *http.Request) map[string]interface{} { // checks for its given method ('Get', 'Post', 'Put', or 'Delete') func (i *Request) LoadController(_method string, _driver driver.Driver) (func(response.Arguments) response.Response, err.Error) { - return _driver.Load(i.Path, _method) + return _driver.RunController(i.Path, _method) } diff --git a/middleware/public.go b/middleware/public.go index 58cc8f5..228e088 100644 --- a/middleware/public.go +++ b/middleware/public.go @@ -1,22 +1,21 @@ package middleware import ( - "fmt" + "git.xdrm.io/go/aicra/driver" "io/ioutil" "log" "net/http" - "plugin" - "strings" + "path" ) // CreateRegistry creates an empty middleware registry // - if loadDir is set -> load all available middlewares // inside the local ./middleware folder -func CreateRegistry(loadDir ...string) *Registry { +func CreateRegistry(_driver driver.Driver, loadDir ...string) *Registry { /* (1) Create registry */ reg := &Registry{ - Middlewares: make([]MiddleWare, 0), + Middlewares: make([]*Wrapper, 0), } /* (2) If no default to use -> empty registry */ @@ -24,81 +23,31 @@ func CreateRegistry(loadDir ...string) *Registry { return reg } - /* (3) List types */ - plugins, err := ioutil.ReadDir(loadDir[0]) + /* (3) List middleware files */ + files, err := ioutil.ReadDir(loadDir[0]) if err != nil { log.Fatal(err) } /* (4) Else try to load each given default */ - for _, file := range plugins { + for _, file := range files { - // ignore non .so files - if !strings.HasSuffix(file.Name(), ".so") { - continue - } - - err := reg.add(file.Name()) + mwFunc, err := _driver.LoadMiddleware(path.Join(loadDir[0], file.Name())) if err != nil { - log.Fatalf("Cannot load plugin '%s'", file.Name()) + log.Printf("Cannot load middleware '%s' | %s", file.Name(), err) } + reg.Middlewares = append(reg.Middlewares, &Wrapper{Inspect: mwFunc}) } return reg } -// add adds a middleware to the registry; it must be a -// valid and existing plugin name with or without the .so extension -// it must be located in the relative directory .build/middleware -func (reg *Registry) add(pluginName string) error { - - /* (1) Check plugin name */ - if len(pluginName) < 1 { - return fmt.Errorf("Plugin name must not be empty") - } - - /* (2) Check if valid plugin name */ - if strings.ContainsAny(pluginName, "/") { - return fmt.Errorf("'%s' can only be a name, not a path", pluginName) - } - - /* (3) Check plugin extension */ - if !strings.HasSuffix(pluginName, ".so") { - pluginName = fmt.Sprintf("%s.so", pluginName) - } - - /* (4) Try to load the plugin */ - p, err := plugin.Open(fmt.Sprintf(".build/middleware/%s", pluginName)) - if err != nil { - return err - } - - /* (5) Export wanted properties */ - inspect, err := p.Lookup("Inspect") - if err != nil { - return fmt.Errorf("Missing method 'Inspect()'; %s", err) - } - - /* (6) Cast Inspect */ - inspectCast, ok := inspect.(func(http.Request, *Scope)) - if !ok { - return fmt.Errorf("Inspect() is malformed") - } - - /* (7) Add type to registry */ - reg.Middlewares = append(reg.Middlewares, MiddleWare{ - Inspect: inspectCast, - }) - - return nil -} - // Run executes all middlewares (default browse order) -func (reg Registry) Run(req http.Request) Scope { +func (reg Registry) Run(req http.Request) []string { /* (1) Initialise scope */ - scope := Scope{} + scope := make([]string, 0) /* (2) Execute each middleware */ for _, m := range reg.Middlewares { diff --git a/middleware/types.go b/middleware/types.go index 3f96059..b63f39a 100644 --- a/middleware/types.go +++ b/middleware/types.go @@ -7,20 +7,26 @@ import ( // Scope represents a list of scope processed by middlewares // and used by the router to block/allow some uris // it is also passed to controllers +// +// DISCLAIMER: it is used to help developers but for compatibility +// purposes, the type is always used as its definition ([]string) type Scope []string -// Inspector updates the @Scope passed to it according to -// the @http.Request -type Inspector func(http.Request, *Scope) +type MiddlewareFunc func(http.Request, *[]string) -// MiddleWare contains all necessary methods -// for a Middleware provided by user/developer -type MiddleWare struct { - Inspect func(http.Request, *Scope) +// Middleware updates the @Scope passed to it according to +// the @http.Request +type Middleware interface { + Inspect(http.Request, *[]string) +} + +// Wrapper is a struct that stores middleware Inspect() method +type Wrapper struct { + Inspect func(http.Request, *[]string) } // Registry represents a registry containing all registered // middlewares to be processed before routing any request type Registry struct { - Middlewares []MiddleWare + Middlewares []*Wrapper } diff --git a/server.go b/server.go index add1fae..fc1cb28 100644 --- a/server.go +++ b/server.go @@ -50,7 +50,7 @@ func New(_path string, _driver driver.Driver) (*Server, error) { i.checker = checker.CreateRegistry(".build/type") /* (4) Default middleware registry */ - i.middleware = middleware.CreateRegistry(".build/middleware") + i.middleware = middleware.CreateRegistry(_driver, ".build/middleware") return i, nil