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:
Adrien Marquès 2018-09-28 10:54:13 +02:00
parent 74e4ce83cb
commit 2cfc5a2ba0
7 changed files with 84 additions and 84 deletions

View File

@ -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, "-")

View File

@ -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
}

View File

@ -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 <WORKDIR>/controller/ROOT
// - the a/b/c controller executable must be named <WORKDIR>/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 <WORKDIR>/controller/ROOT/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>`
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 <host>:<port>
//
// FILE STRUCTURE
// CONTROLLER FILE STRUCTURE
// --------------
// - 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

View File

@ -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)
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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