update 'middleware' types | rename driver.Driver.Load() into RunController() | add driver.Driver.LoadMiddleware() | add driver management to middleware.Registry at creation | [TODO] implement diver.Generic.LoadMiddleware()
This commit is contained in:
parent
74e4ce83cb
commit
2cfc5a2ba0
|
@ -9,8 +9,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load implements the Driver interface
|
// RunController implements the Driver interface
|
||||||
func (d *Generic) Load(_path []string, _method string) (func(response.Arguments) response.Response, e.Error) {
|
func (d *Generic) RunController(_path []string, _method string) (func(response.Arguments) response.Response, e.Error) {
|
||||||
|
|
||||||
/* (1) Build controller path */
|
/* (1) Build controller path */
|
||||||
path := strings.Join(_path, "-")
|
path := strings.Join(_path, "-")
|
||||||
|
|
|
@ -4,12 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.xdrm.io/go/aicra/err"
|
"git.xdrm.io/go/aicra/err"
|
||||||
"git.xdrm.io/go/aicra/response"
|
"git.xdrm.io/go/aicra/response"
|
||||||
|
"net/http"
|
||||||
"plugin"
|
"plugin"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load implements the Driver interface
|
// RunController implements the Driver interface
|
||||||
func (d *Plugin) Load(_path []string, _method string) (func(response.Arguments) response.Response, err.Error) {
|
func (d *Plugin) RunController(_path []string, _method string) (func(response.Arguments) response.Response, err.Error) {
|
||||||
|
|
||||||
/* (1) Build controller path */
|
/* (1) Build controller path */
|
||||||
path := strings.Join(_path, "-")
|
path := strings.Join(_path, "-")
|
||||||
|
@ -44,3 +45,45 @@ func (d *Plugin) Load(_path []string, _method string) (func(response.Arguments)
|
||||||
|
|
||||||
return callable, err.Success
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -3,26 +3,28 @@ package driver
|
||||||
import (
|
import (
|
||||||
"git.xdrm.io/go/aicra/err"
|
"git.xdrm.io/go/aicra/err"
|
||||||
"git.xdrm.io/go/aicra/response"
|
"git.xdrm.io/go/aicra/response"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Driver defines the driver interface to load controller/middleware implementation or executables
|
// Driver defines the driver interface to load controller/middleware implementation or executables
|
||||||
type Driver interface {
|
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)
|
// 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 HTTP method is send as the key _HTTP_METHOD_ (in upper case)
|
||||||
// The standard output must be a json corresponding to the data
|
// The standard output must be a json corresponding to the data
|
||||||
//
|
//
|
||||||
// FILE STRUCTURE
|
// CONTROLLER FILE STRUCTURE
|
||||||
// --------------
|
// --------------
|
||||||
// - the root (/) controller executable must be named <WORKDIR>/controller/ROOT
|
// - the root (/) controller executable must be named <WORKDIR>/controller/ROOT
|
||||||
// - the a/b/c controller executable must be named <WORKDIR>/controller/a/b/c
|
// - the a/b/c controller executable must be named <WORKDIR>/controller/a/b/c
|
||||||
type Generic struct{}
|
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 :
|
// 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
|
// Delete(d i.Arguments, r *i.Response) i.Response
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// FILE STRUCTURE
|
// CONTROLLER FILE STRUCTURE
|
||||||
// --------------
|
// --------------
|
||||||
// - the root (/) controller executable must be named <WORKDIR>/controller/ROOT/main.so
|
// - the root (/) controller executable must be named <WORKDIR>/controller/ROOT/main.so
|
||||||
// - the a/b/c controller executable must be named <WORKDIR>/controller/a/b/c/main.so
|
// - the a/b/c controller executable must be named <WORKDIR>/controller/a/b/c/main.so
|
||||||
|
@ -43,13 +45,13 @@ type Generic struct{}
|
||||||
// The compilation is handled with the command-line tool `aicra <WORKDIR>`
|
// The compilation is handled with the command-line tool `aicra <WORKDIR>`
|
||||||
type Plugin struct{}
|
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
|
// Warning: PHP only
|
||||||
//
|
//
|
||||||
// It will use the fastcgi protocol with php at <host>:<port>
|
// It will use the fastcgi protocol with php at <host>:<port>
|
||||||
//
|
//
|
||||||
// FILE STRUCTURE
|
// CONTROLLER FILE STRUCTURE
|
||||||
// --------------
|
// --------------
|
||||||
// - the root (/) controller executable must be named <WORKDIR>/controller/ROOT.php
|
// - the root (/) controller executable must be named <WORKDIR>/controller/ROOT.php
|
||||||
// - the a/b/c controller executable must be named <WORKDIR>/controller/a/b/c.php
|
// - the a/b/c controller executable must be named <WORKDIR>/controller/a/b/c.php
|
||||||
|
|
|
@ -96,6 +96,6 @@ func FetchFormData(req *http.Request) map[string]interface{} {
|
||||||
// checks for its given method ('Get', 'Post', 'Put', or 'Delete')
|
// 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) {
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"git.xdrm.io/go/aicra/driver"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"plugin"
|
"path"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateRegistry creates an empty middleware registry
|
// CreateRegistry creates an empty middleware registry
|
||||||
// - if loadDir is set -> load all available middlewares
|
// - if loadDir is set -> load all available middlewares
|
||||||
// inside the local ./middleware folder
|
// inside the local ./middleware folder
|
||||||
func CreateRegistry(loadDir ...string) *Registry {
|
func CreateRegistry(_driver driver.Driver, loadDir ...string) *Registry {
|
||||||
|
|
||||||
/* (1) Create registry */
|
/* (1) Create registry */
|
||||||
reg := &Registry{
|
reg := &Registry{
|
||||||
Middlewares: make([]MiddleWare, 0),
|
Middlewares: make([]*Wrapper, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (2) If no default to use -> empty registry */
|
/* (2) If no default to use -> empty registry */
|
||||||
|
@ -24,81 +23,31 @@ func CreateRegistry(loadDir ...string) *Registry {
|
||||||
return reg
|
return reg
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (3) List types */
|
/* (3) List middleware files */
|
||||||
plugins, err := ioutil.ReadDir(loadDir[0])
|
files, err := ioutil.ReadDir(loadDir[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (4) Else try to load each given default */
|
/* (4) Else try to load each given default */
|
||||||
for _, file := range plugins {
|
for _, file := range files {
|
||||||
|
|
||||||
// ignore non .so files
|
mwFunc, err := _driver.LoadMiddleware(path.Join(loadDir[0], file.Name()))
|
||||||
if !strings.HasSuffix(file.Name(), ".so") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := reg.add(file.Name())
|
|
||||||
if err != nil {
|
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
|
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)
|
// 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 */
|
/* (1) Initialise scope */
|
||||||
scope := Scope{}
|
scope := make([]string, 0)
|
||||||
|
|
||||||
/* (2) Execute each middleware */
|
/* (2) Execute each middleware */
|
||||||
for _, m := range reg.Middlewares {
|
for _, m := range reg.Middlewares {
|
||||||
|
|
|
@ -7,20 +7,26 @@ import (
|
||||||
// Scope represents a list of scope processed by middlewares
|
// Scope represents a list of scope processed by middlewares
|
||||||
// and used by the router to block/allow some uris
|
// and used by the router to block/allow some uris
|
||||||
// it is also passed to controllers
|
// 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
|
type Scope []string
|
||||||
|
|
||||||
// Inspector updates the @Scope passed to it according to
|
type MiddlewareFunc func(http.Request, *[]string)
|
||||||
// the @http.Request
|
|
||||||
type Inspector func(http.Request, *Scope)
|
|
||||||
|
|
||||||
// MiddleWare contains all necessary methods
|
// Middleware updates the @Scope passed to it according to
|
||||||
// for a Middleware provided by user/developer
|
// the @http.Request
|
||||||
type MiddleWare struct {
|
type Middleware interface {
|
||||||
Inspect func(http.Request, *Scope)
|
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
|
// Registry represents a registry containing all registered
|
||||||
// middlewares to be processed before routing any request
|
// middlewares to be processed before routing any request
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
Middlewares []MiddleWare
|
Middlewares []*Wrapper
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func New(_path string, _driver driver.Driver) (*Server, error) {
|
||||||
i.checker = checker.CreateRegistry(".build/type")
|
i.checker = checker.CreateRegistry(".build/type")
|
||||||
|
|
||||||
/* (4) Default middleware registry */
|
/* (4) Default middleware registry */
|
||||||
i.middleware = middleware.CreateRegistry(".build/middleware")
|
i.middleware = middleware.CreateRegistry(_driver, ".build/middleware")
|
||||||
|
|
||||||
return i, nil
|
return i, nil
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue