add driver management + moved implementation of default driver into 'driver/plugin'

This commit is contained in:
Adrien Marquès 2018-09-27 13:43:36 +02:00
parent 8c02bc53a5
commit 3feae783dc
8 changed files with 130 additions and 47 deletions

46
driver/plugin.go Normal file
View File

@ -0,0 +1,46 @@
package driver
import (
"fmt"
"git.xdrm.io/go/aicra/err"
"git.xdrm.io/go/aicra/response"
"plugin"
"strings"
)
// Load implements the Driver interface
func (d *Plugin) Load(_path []string, _method string) (func(response.Arguments, *response.Response) response.Response, err.Error) {
/* (1) Build controller path */
path := strings.Join(_path, "-")
if len(path) == 0 {
path = fmt.Sprintf(".build/controller/ROOT.so")
} else {
path = fmt.Sprintf(".build/controller/%s.so", path)
}
/* (2) Format url */
tmp := []byte(strings.ToLower(_method))
tmp[0] = tmp[0] - ('a' - 'A')
method := string(tmp)
/* (2) Try to load plugin */
p, err2 := plugin.Open(path)
if err2 != nil {
return nil, err.UncallableController
}
/* (3) Try to extract method */
m, err2 := p.Lookup(method)
if err2 != nil {
return nil, err.UncallableMethod
}
/* (4) Check signature */
callable, validSignature := m.(func(response.Arguments, *response.Response) response.Response)
if !validSignature {
return nil, err.UncallableMethod
}
return callable, err.Success
}

58
driver/types.go Normal file
View File

@ -0,0 +1,58 @@
package driver
import (
"git.xdrm.io/go/aicra/err"
"git.xdrm.io/go/aicra/response"
)
type Driver interface {
Load(_path []string, _method string) (func(response.Arguments, *response.Response) response.Response, err.Error)
}
// Generic tells the aicra instance to use the generic driver to load controller's executables
//
// It will call an executable with the json input into the standard input
// 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
// --------------
// - 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
//
// It will load go .so plugins with the following interface :
//
// type Plugin interface {
// Get(d i.Arguments, r *i.Response) i.Response
// Post(d i.Arguments, r *i.Response) i.Response
// Put(d i.Arguments, r *i.Response) i.Response
// Delete(d i.Arguments, r *i.Response) i.Response
// }
//
// 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
//
// COMPILATION
// -----------
// 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
//
// Warning: PHP only
//
// It will use the fastcgi protocol with php at <host>:<port>
//
// 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
type FastCGI struct {
host string
port string
}

View File

@ -3,11 +3,11 @@ package apirequest
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"git.xdrm.io/go/aicra/driver"
"git.xdrm.io/go/aicra/err" "git.xdrm.io/go/aicra/err"
"git.xdrm.io/go/aicra/response" "git.xdrm.io/go/aicra/response"
"log" "log"
"net/http" "net/http"
"plugin"
"strings" "strings"
"time" "time"
) )
@ -94,39 +94,8 @@ func FetchFormData(req *http.Request) map[string]interface{} {
// LoadController tries to load a controller from its uri // LoadController tries to load a controller from its uri
// 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) (func(response.Arguments, *response.Response) response.Response, err.Error) { func (i *Request) LoadController(_method string, _driver driver.Driver) (func(response.Arguments, *response.Response) response.Response, err.Error) {
/* (1) Build controller path */ return _driver.Load(i.Path, _method)
path := strings.Join(i.Path, "-")
if len(path) == 0 {
path = fmt.Sprintf(".build/controller/ROOT.so")
} else {
path = fmt.Sprintf(".build/controller/%s.so", path)
}
/* (2) Format url */
tmp := []byte(strings.ToLower(method))
tmp[0] = tmp[0] - ('a' - 'A')
method = string(tmp)
/* (2) Try to load plugin */
p, err2 := plugin.Open(path)
if err2 != nil {
return nil, err.UncallableController
}
/* (3) Try to extract method */
m, err2 := p.Lookup(method)
if err2 != nil {
return nil, err.UncallableMethod
}
/* (4) Check signature */
callable, validSignature := m.(func(response.Arguments, *response.Response) response.Response)
if !validSignature {
return nil, err.UncallableMethod
}
return callable, err.Success
} }

View File

@ -1,4 +1,4 @@
package controller package config
import ( import (
"encoding/json" "encoding/json"

View File

@ -1,4 +1,4 @@
package controller package config
import ( import (
"git.xdrm.io/go/aicra/middleware" "git.xdrm.io/go/aicra/middleware"

View File

@ -1,4 +1,4 @@
package controller package config
/* (1) Configuration /* (1) Configuration
---------------------------------------------------------*/ ---------------------------------------------------------*/

View File

@ -1,10 +1,12 @@
package aicra package aicra
import ( import (
"errors"
"git.xdrm.io/go/aicra/driver"
e "git.xdrm.io/go/aicra/err" e "git.xdrm.io/go/aicra/err"
"git.xdrm.io/go/aicra/internal/apirequest" "git.xdrm.io/go/aicra/internal/apirequest"
"git.xdrm.io/go/aicra/internal/checker" "git.xdrm.io/go/aicra/internal/checker"
"git.xdrm.io/go/aicra/internal/controller" "git.xdrm.io/go/aicra/internal/config"
"git.xdrm.io/go/aicra/middleware" "git.xdrm.io/go/aicra/middleware"
"git.xdrm.io/go/aicra/response" "git.xdrm.io/go/aicra/response"
"log" "log"
@ -16,22 +18,30 @@ import (
// * its middlewares // * its middlewares
// * its controllers (config) // * its controllers (config)
type Server struct { type Server struct {
controller *controller.Controller // controllers controller *config.Controller // controllers
checker *checker.Registry // type checker registry checker *checker.Registry // type checker registry
middleware *middleware.Registry // middlewares middleware *middleware.Registry // middlewares
driver driver.Driver
} }
var ErrNilDriver = errors.New("the driver is <nil>")
// New creates a framework instance from a configuration file // New creates a framework instance from a configuration file
func New(path string) (*Server, error) { func New(_path string, _driver driver.Driver) (*Server, error) {
if _driver == nil {
return nil, ErrNilDriver
}
/* (1) Init instance */ /* (1) Init instance */
var err error var err error
var i = &Server{ var i = &Server{
controller: nil, controller: nil,
driver: _driver,
} }
/* (2) Load configuration */ /* (2) Load configuration */
i.controller, err = controller.Load(path) i.controller, err = config.Load(_path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -90,7 +100,7 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
/* (5) Load controller /* (5) Load controller
---------------------------------------------------------*/ ---------------------------------------------------------*/
controllerImplementation, callErr := apiRequest.LoadController(req.Method) controllerImplementation, callErr := apiRequest.LoadController(req.Method, s.driver)
if callErr.Code != e.Success.Code { if callErr.Code != e.Success.Code {
httpError(res, callErr) httpError(res, callErr)
log.Printf("[err] %s\n", err) log.Printf("[err] %s\n", err)
@ -126,7 +136,7 @@ func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// extractParameters extracts parameters for the request and checks // extractParameters extracts parameters for the request and checks
// every single one according to configuration options // every single one according to configuration options
func (s *Server) extractParameters(req *apirequest.Request, methodParam map[string]*controller.Parameter) (map[string]interface{}, e.Error) { func (s *Server) extractParameters(req *apirequest.Request, methodParam map[string]*config.Parameter) (map[string]interface{}, e.Error) {
// init vars // init vars
err := e.Success err := e.Success

View File

@ -4,13 +4,13 @@ import (
"encoding/json" "encoding/json"
"git.xdrm.io/go/aicra/err" "git.xdrm.io/go/aicra/err"
"git.xdrm.io/go/aicra/internal/apirequest" "git.xdrm.io/go/aicra/internal/apirequest"
"git.xdrm.io/go/aicra/internal/controller" "git.xdrm.io/go/aicra/internal/config"
"git.xdrm.io/go/aicra/response" "git.xdrm.io/go/aicra/response"
"log" "log"
"net/http" "net/http"
) )
func (s *Server) matchController(req *apirequest.Request) *controller.Controller { func (s *Server) matchController(req *apirequest.Request) *config.Controller {
/* (1) Try to browse by URI */ /* (1) Try to browse by URI */
pathi, ctl := s.controller.Browse(req.URI) pathi, ctl := s.controller.Browse(req.URI)