2021-05-11 16:14:10 +02:00
package plugins
import (
"context"
"fmt"
"net/http"
"path"
"reflect"
"strings"
"github.com/mitchellh/mapstructure"
2022-01-14 12:22:06 +01:00
"github.com/traefik/yaegi/interp"
2021-05-11 16:14:10 +02:00
)
// Build builds a middleware plugin.
func ( b Builder ) Build ( pName string , config map [ string ] interface { } , middlewareName string ) ( Constructor , error ) {
2022-01-14 12:22:06 +01:00
if b . middlewareBuilders == nil {
2021-05-11 16:14:10 +02:00
return nil , fmt . Errorf ( "no plugin definition in the static configuration: %s" , pName )
}
2022-01-14 12:22:06 +01:00
descriptor , ok := b . middlewareBuilders [ pName ]
2021-05-11 16:14:10 +02:00
if ! ok {
return nil , fmt . Errorf ( "unknown plugin type: %s" , pName )
}
m , err := newMiddleware ( descriptor , config , middlewareName )
if err != nil {
return nil , err
}
return m . NewHandler , err
}
2022-01-14 12:22:06 +01:00
type middlewareBuilder struct {
2021-05-11 16:14:10 +02:00
fnNew reflect . Value
2022-01-14 12:22:06 +01:00
fnCreateConfig reflect . Value
2021-05-11 16:14:10 +02:00
}
2022-01-14 12:22:06 +01:00
func newMiddlewareBuilder ( i * interp . Interpreter , basePkg , imp string ) ( * middlewareBuilder , error ) {
2021-05-11 16:14:10 +02:00
if basePkg == "" {
2022-01-14 12:22:06 +01:00
basePkg = strings . ReplaceAll ( path . Base ( imp ) , "-" , "_" )
}
fnNew , err := i . Eval ( basePkg + ` .New ` )
if err != nil {
return nil , fmt . Errorf ( "failed to eval New: %w" , err )
2021-05-11 16:14:10 +02:00
}
2022-01-14 12:22:06 +01:00
fnCreateConfig , err := i . Eval ( basePkg + ` .CreateConfig ` )
2021-05-11 16:14:10 +02:00
if err != nil {
return nil , fmt . Errorf ( "failed to eval CreateConfig: %w" , err )
}
2022-01-14 12:22:06 +01:00
return & middlewareBuilder {
fnNew : fnNew ,
fnCreateConfig : fnCreateConfig ,
} , nil
}
func ( p middlewareBuilder ) newHandler ( ctx context . Context , next http . Handler , cfg reflect . Value , middlewareName string ) ( http . Handler , error ) {
args := [ ] reflect . Value { reflect . ValueOf ( ctx ) , reflect . ValueOf ( next ) , cfg , reflect . ValueOf ( middlewareName ) }
results := p . fnNew . Call ( args )
if len ( results ) > 1 && results [ 1 ] . Interface ( ) != nil {
err , ok := results [ 1 ] . Interface ( ) . ( error )
if ! ok {
return nil , fmt . Errorf ( "invalid error type: %T" , results [ 0 ] . Interface ( ) )
}
return nil , err
}
handler , ok := results [ 0 ] . Interface ( ) . ( http . Handler )
if ! ok {
return nil , fmt . Errorf ( "invalid handler type: %T" , results [ 0 ] . Interface ( ) )
}
return handler , nil
}
func ( p middlewareBuilder ) createConfig ( config map [ string ] interface { } ) ( reflect . Value , error ) {
results := p . fnCreateConfig . Call ( nil )
if len ( results ) != 1 {
return reflect . Value { } , fmt . Errorf ( "invalid number of return for the CreateConfig function: %d" , len ( results ) )
}
vConfig := results [ 0 ]
2022-09-15 11:00:09 +02:00
if len ( config ) == 0 {
return vConfig , nil
}
2022-01-14 12:22:06 +01:00
2021-05-11 16:14:10 +02:00
cfg := & mapstructure . DecoderConfig {
2022-08-01 15:12:08 +02:00
DecodeHook : mapstructure . StringToSliceHookFunc ( "," ) ,
2021-05-11 16:14:10 +02:00
WeaklyTypedInput : true ,
Result : vConfig . Interface ( ) ,
}
decoder , err := mapstructure . NewDecoder ( cfg )
if err != nil {
2022-01-14 12:22:06 +01:00
return reflect . Value { } , fmt . Errorf ( "failed to create configuration decoder: %w" , err )
2021-05-11 16:14:10 +02:00
}
err = decoder . Decode ( config )
if err != nil {
2022-01-14 12:22:06 +01:00
return reflect . Value { } , fmt . Errorf ( "failed to decode configuration: %w" , err )
2021-05-11 16:14:10 +02:00
}
2022-01-14 12:22:06 +01:00
return vConfig , nil
}
// Middleware is an HTTP handler plugin wrapper.
type Middleware struct {
middlewareName string
config reflect . Value
builder * middlewareBuilder
}
func newMiddleware ( builder * middlewareBuilder , config map [ string ] interface { } , middlewareName string ) ( * Middleware , error ) {
vConfig , err := builder . createConfig ( config )
2021-05-11 16:14:10 +02:00
if err != nil {
2022-01-14 12:22:06 +01:00
return nil , err
2021-05-11 16:14:10 +02:00
}
return & Middleware {
middlewareName : middlewareName ,
config : vConfig ,
2022-01-14 12:22:06 +01:00
builder : builder ,
2021-05-11 16:14:10 +02:00
} , nil
}
// NewHandler creates a new HTTP handler.
func ( m * Middleware ) NewHandler ( ctx context . Context , next http . Handler ) ( http . Handler , error ) {
2022-01-14 12:22:06 +01:00
return m . builder . newHandler ( ctx , next , m . config , m . middlewareName )
2021-05-11 16:14:10 +02:00
}