added updated code for core wiring
1. Removed tracing 2. Skipped Registration
This commit is contained in:
101
core/module/client/registery.go
Normal file
101
core/module/client/registery.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/beckn/beckn-onix/pkg/model"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
// Config struct to hold configuration parameters.
|
||||
type Config struct {
|
||||
RegisteryURL string
|
||||
RetryMax int
|
||||
RetryWaitMin time.Duration
|
||||
RetryWaitMax time.Duration
|
||||
}
|
||||
|
||||
// RegisteryClient encapsulates the logic for calling the subscribe and lookup endpoints.
|
||||
type RegisteryClient struct {
|
||||
Config *Config
|
||||
Client *retryablehttp.Client // Retryable HTTP Client
|
||||
}
|
||||
|
||||
// NewRegisteryClient creates a new instance of Client.
|
||||
func NewRegisteryClient(config *Config) *RegisteryClient {
|
||||
retryClient := retryablehttp.NewClient()
|
||||
|
||||
return &RegisteryClient{Config: config, Client: retryClient}
|
||||
}
|
||||
|
||||
// Subscribe calls the /subscribe endpoint with retry.
|
||||
func (c *RegisteryClient) Subscribe(ctx context.Context, subscription *model.Subscription) error {
|
||||
subscribeURL := fmt.Sprintf("%s/subscribe", c.Config.RegisteryURL)
|
||||
|
||||
jsonData, err := json.Marshal(subscription)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal subscription data: %w", err)
|
||||
}
|
||||
|
||||
req, err := retryablehttp.NewRequest("POST", subscribeURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send request with retry: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("subscribe request failed with status: %s", resp.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup calls the /lookup endpoint with retry and returns a slice of Subscription.
|
||||
func (c *RegisteryClient) Lookup(ctx context.Context, subscription *model.Subscription) ([]model.Subscription, error) {
|
||||
lookupURL := fmt.Sprintf("%s/lookUp", c.Config.RegisteryURL)
|
||||
|
||||
jsonData, err := json.Marshal(subscription)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal subscription data: %w", err)
|
||||
}
|
||||
|
||||
req, err := retryablehttp.NewRequest("POST", lookupURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send request with retry: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("lookup request failed with status: %s", resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
var results []model.Subscription
|
||||
err = json.Unmarshal(body, &results)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response body: %w", err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
117
core/module/handler/config.go
Normal file
117
core/module/handler/config.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/beckn/beckn-onix/pkg/model"
|
||||
"github.com/beckn/beckn-onix/pkg/plugin"
|
||||
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||
)
|
||||
|
||||
// PluginManager defines the methods required for managing plugins in stdHandler.
|
||||
type PluginManager interface {
|
||||
Middleware(ctx context.Context, cfg *plugin.Config) (func(http.Handler) http.Handler, error)
|
||||
SignValidator(ctx context.Context, cfg *plugin.Config) (definition.Verifier, error)
|
||||
Validator(ctx context.Context, cfg *plugin.Config) (definition.SchemaValidator, error)
|
||||
Router(ctx context.Context, cfg *plugin.Config) (definition.Router, error)
|
||||
Publisher(ctx context.Context, cfg *plugin.Config) (definition.Publisher, error)
|
||||
Signer(ctx context.Context, cfg *plugin.Config) (definition.Signer, error)
|
||||
Step(ctx context.Context, cfg *plugin.Config) (definition.Step, error)
|
||||
Cache(ctx context.Context, cfg *plugin.Config) (definition.Cache, error)
|
||||
KeyManager(ctx context.Context, cache definition.Cache, rLookup definition.RegistryLookup, cfg *plugin.Config) (definition.KeyManager, error)
|
||||
SchemaValidator(ctx context.Context, cfg *plugin.Config) (definition.SchemaValidator, error)
|
||||
}
|
||||
|
||||
// Provider represents a function that initializes an HTTP handler using a PluginManager.
|
||||
type Provider func(ctx context.Context, mgr PluginManager, cfg *Config) (http.Handler, error)
|
||||
|
||||
// Type defines different handler types for processing requests.
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// HandlerTypeStd represents the standard handler type used for general request processing.
|
||||
HandlerTypeStd Type = "std"
|
||||
|
||||
// HandlerTypeRegSub represents the registry subscriber handler type for handling registry subscription requests.
|
||||
HandlerTypeRegSub Type = "regSub"
|
||||
|
||||
// HandlerTypeNPSub represents the network participant subscriber handler type for handling network participant subscription requests.
|
||||
HandlerTypeNPSub Type = "npSub"
|
||||
|
||||
// HandlerTypeLookup represents the lookup handler type used for resolving service details.
|
||||
HandlerTypeLookup Type = "lookUp"
|
||||
)
|
||||
|
||||
// pluginCfg holds the configuration for various plugins.
|
||||
type pluginCfg struct {
|
||||
SchemaValidator *plugin.Config `yaml:"schemaValidator,omitempty"`
|
||||
SignValidator *plugin.Config `yaml:"signValidator,omitempty"`
|
||||
Publisher *plugin.Config `yaml:"publisher,omitempty"`
|
||||
Signer *plugin.Config `yaml:"signer,omitempty"`
|
||||
Router *plugin.Config `yaml:"router,omitempty"`
|
||||
Cache *plugin.Config `yaml:"cache,omitempty"`
|
||||
KeyManager *plugin.Config `yaml:"keyManager,omitempty"`
|
||||
Middleware []plugin.Config `yaml:"middleware,omitempty"`
|
||||
Steps []plugin.Config
|
||||
}
|
||||
|
||||
// Config holds the configuration for request processing handlers.
|
||||
type Config struct {
|
||||
Plugins pluginCfg `yaml:"plugins"`
|
||||
Steps []string
|
||||
Type Type
|
||||
RegistryURL string `yaml:"registryUrl"`
|
||||
Role model.Role
|
||||
SubscriberID string `yaml:"subscriberId"`
|
||||
Trace map[string]bool
|
||||
}
|
||||
|
||||
// Step represents a named processing step.
|
||||
type Step string
|
||||
|
||||
const (
|
||||
// StepInitialize represents the initialization phase of the request processing pipeline.
|
||||
StepInitialize Step = "initialize"
|
||||
|
||||
// StepValidate represents the validation phase, where input data is checked for correctness.
|
||||
StepValidate Step = "validate"
|
||||
|
||||
// StepProcess represents the core processing phase of the request.
|
||||
StepProcess Step = "process"
|
||||
|
||||
// StepFinalize represents the finalization phase, where the response is prepared and sent.
|
||||
StepFinalize Step = "finalize"
|
||||
)
|
||||
|
||||
// validSteps ensures only allowed step values are used.
|
||||
var validSteps = map[Step]bool{
|
||||
StepInitialize: true,
|
||||
StepValidate: true,
|
||||
StepProcess: true,
|
||||
StepFinalize: true,
|
||||
}
|
||||
|
||||
// UnmarshalYAML customizes YAML unmarshalling for Step to enforce valid values.
|
||||
func (s *Step) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var stepName string
|
||||
if err := unmarshal(&stepName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
step := Step(stepName)
|
||||
if !validSteps[step] {
|
||||
return fmt.Errorf("invalid step: %s", stepName)
|
||||
}
|
||||
*s = step
|
||||
return nil
|
||||
}
|
||||
|
||||
// DummyHandler is a basic HTTP handler that returns a fixed response.
|
||||
func DummyHandler(ctx context.Context, mgr PluginManager, cfg *Config) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("Dummy Handler Response"))
|
||||
}), nil
|
||||
}
|
||||
264
core/module/handler/stdHandler.go
Normal file
264
core/module/handler/stdHandler.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/beckn/beckn-onix/core/module/client"
|
||||
"github.com/beckn/beckn-onix/pkg/log"
|
||||
"github.com/beckn/beckn-onix/pkg/model"
|
||||
"github.com/beckn/beckn-onix/pkg/plugin"
|
||||
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||
"github.com/beckn/beckn-onix/pkg/response"
|
||||
)
|
||||
|
||||
// stdHandler orchestrates the execution of defined processing steps.
|
||||
type stdHandler struct {
|
||||
signer definition.Signer
|
||||
steps []definition.Step
|
||||
signValidator definition.Verifier
|
||||
cache definition.Cache
|
||||
km definition.KeyManager
|
||||
schemaValidator definition.SchemaValidator
|
||||
router definition.Router
|
||||
publisher definition.Publisher
|
||||
SubscriberID string
|
||||
role model.Role
|
||||
}
|
||||
|
||||
// NewStdHandler initializes a new processor with plugins and steps.
|
||||
func NewStdHandler(ctx context.Context, mgr PluginManager, cfg *Config) (http.Handler, error) {
|
||||
h := &stdHandler{
|
||||
steps: []definition.Step{},
|
||||
SubscriberID: cfg.SubscriberID,
|
||||
role: cfg.Role,
|
||||
}
|
||||
// Initialize plugins
|
||||
if err := h.initPlugins(ctx, mgr, &cfg.Plugins, cfg.RegistryURL); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize plugins: %w", err)
|
||||
}
|
||||
// Initialize steps
|
||||
if err := h.initSteps(ctx, mgr, cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize steps: %w", err)
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// ServeHTTP processes an incoming HTTP request and executes defined processing steps.
|
||||
func (h *stdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, err := h.stepCtx(r, w.Header())
|
||||
if err != nil {
|
||||
log.Errorf(r.Context(), err, "stepCtx(r):%v", err)
|
||||
response.SendNack(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
log.Request(r.Context(), r, ctx.Body)
|
||||
|
||||
// Execute processing steps
|
||||
for _, step := range h.steps {
|
||||
if err := step.Run(ctx); err != nil {
|
||||
log.Errorf(ctx, err, "%T.run(%v):%v", step, ctx, err)
|
||||
response.SendNack(ctx, w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Restore request body before forwarding or publishing
|
||||
r.Body = io.NopCloser(bytes.NewReader(ctx.Body))
|
||||
if ctx.Route == nil {
|
||||
response.SendAck(w)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle routing based on the defined route type
|
||||
route(ctx, r, w, h.publisher)
|
||||
}
|
||||
|
||||
// stepCtx creates a new StepContext for processing an HTTP request.
|
||||
func (h *stdHandler) stepCtx(r *http.Request, rh http.Header) (*model.StepContext, error) {
|
||||
var bodyBuffer bytes.Buffer
|
||||
if _, err := io.Copy(&bodyBuffer, r.Body); err != nil {
|
||||
return nil, model.NewBadReqErr(err)
|
||||
}
|
||||
r.Body.Close()
|
||||
subID := h.subID(r.Context())
|
||||
if len(subID) == 0 {
|
||||
return nil, model.NewBadReqErr(fmt.Errorf("subscriberID not set"))
|
||||
}
|
||||
return &model.StepContext{
|
||||
Context: r.Context(),
|
||||
Request: r,
|
||||
Body: bodyBuffer.Bytes(),
|
||||
Role: h.role,
|
||||
SubID: subID,
|
||||
RespHeader: rh,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// subID retrieves the subscriber ID from the request context.
|
||||
func (h *stdHandler) subID(ctx context.Context) string {
|
||||
rSubID, ok := ctx.Value("subscriber_id").(string)
|
||||
if ok {
|
||||
return rSubID
|
||||
}
|
||||
return h.SubscriberID
|
||||
}
|
||||
|
||||
// route handles request forwarding or message publishing based on the routing type.
|
||||
func route(ctx *model.StepContext, r *http.Request, w http.ResponseWriter, pb definition.Publisher) {
|
||||
log.Debugf(ctx, "Routing to ctx.Route to %#v", ctx.Route)
|
||||
switch ctx.Route.Type {
|
||||
case "url":
|
||||
log.Infof(ctx.Context, "Forwarding request to URL: %s", ctx.Route.URL)
|
||||
proxy(r, w, ctx.Route.URL)
|
||||
return
|
||||
case "publisher":
|
||||
if pb == nil {
|
||||
err := fmt.Errorf("publisher plugin not configured")
|
||||
log.Errorf(ctx.Context, err, "Invalid configuration:%v", err)
|
||||
response.SendNack(ctx, w, err)
|
||||
return
|
||||
}
|
||||
log.Infof(ctx.Context, "Publishing message to: %s", ctx.Route.Publisher)
|
||||
if err := pb.Publish(ctx, ctx.Body); err != nil {
|
||||
log.Errorf(ctx.Context, err, "Failed to publish message")
|
||||
http.Error(w, "Error publishing message", http.StatusInternalServerError)
|
||||
response.SendNack(ctx, w, err)
|
||||
return
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("unknown route type: %s", ctx.Route.Type)
|
||||
log.Errorf(ctx.Context, err, "Invalid configuration:%v", err)
|
||||
response.SendNack(ctx, w, err)
|
||||
return
|
||||
}
|
||||
response.SendAck(w)
|
||||
}
|
||||
|
||||
// proxy forwards the request to a target URL using a reverse proxy.
|
||||
func proxy(r *http.Request, w http.ResponseWriter, target *url.URL) {
|
||||
r.URL.Scheme = target.Scheme
|
||||
r.URL.Host = target.Host
|
||||
r.URL.Path = target.Path
|
||||
|
||||
r.Header.Set("X-Forwarded-Host", r.Host)
|
||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
||||
log.Infof(r.Context(), "Proxying request to: %s", target)
|
||||
|
||||
proxy.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// loadPlugin is a generic function to load and validate plugins.
|
||||
func loadPlugin[T any](ctx context.Context, name string, cfg *plugin.Config, mgrFunc func(context.Context, *plugin.Config) (T, error)) (T, error) {
|
||||
var zero T
|
||||
if cfg == nil {
|
||||
log.Debugf(ctx, "Skipping %s plugin: not configured", name)
|
||||
return zero, nil
|
||||
}
|
||||
|
||||
plugin, err := mgrFunc(ctx, cfg)
|
||||
if err != nil {
|
||||
return zero, fmt.Errorf("failed to load %s plugin (%s): %w", name, cfg.ID, err)
|
||||
}
|
||||
|
||||
log.Debugf(ctx, "Loaded %s plugin: %s", name, cfg.ID)
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
// loadKeyManager loads the KeyManager plugin using the provided PluginManager, cache, and registry URL.
|
||||
func loadKeyManager(ctx context.Context, mgr PluginManager, cache definition.Cache, cfg *plugin.Config, regURL string) (definition.KeyManager, error) {
|
||||
if cfg == nil {
|
||||
log.Debug(ctx, "Skipping KeyManager plugin: not configured")
|
||||
return nil, nil
|
||||
}
|
||||
if cache == nil {
|
||||
return nil, fmt.Errorf("failed to load KeyManager plugin (%s): Cache plugin not configured", cfg.ID)
|
||||
}
|
||||
rClient := client.NewRegisteryClient(&client.Config{RegisteryURL: regURL})
|
||||
km, err := mgr.KeyManager(ctx, cache, rClient, cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load cache plugin (%s): %w", cfg.ID, err)
|
||||
}
|
||||
|
||||
log.Debugf(ctx, "Loaded Keymanager plugin: %s", cfg.ID)
|
||||
return km, nil
|
||||
}
|
||||
|
||||
// initPlugins initializes required plugins for the processor.
|
||||
func (h *stdHandler) initPlugins(ctx context.Context, mgr PluginManager, cfg *pluginCfg, regURL string) error {
|
||||
var err error
|
||||
if h.cache, err = loadPlugin(ctx, "Cache", cfg.Cache, mgr.Cache); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.km, err = loadKeyManager(ctx, mgr, h.cache, cfg.KeyManager, regURL); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.signValidator, err = loadPlugin(ctx, "SignValidator", cfg.SignValidator, mgr.SignValidator); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.schemaValidator, err = loadPlugin(ctx, "SchemaValidator", cfg.SchemaValidator, mgr.SchemaValidator); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.router, err = loadPlugin(ctx, "Router", cfg.Router, mgr.Router); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.publisher, err = loadPlugin(ctx, "Publisher", cfg.Publisher, mgr.Publisher); err != nil {
|
||||
return err
|
||||
}
|
||||
if h.signer, err = loadPlugin(ctx, "Signer", cfg.Signer, mgr.Signer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf(ctx, "All required plugins successfully loaded for stdHandler")
|
||||
return nil
|
||||
}
|
||||
|
||||
// initSteps initializes and validates processing steps for the processor.
|
||||
func (h *stdHandler) initSteps(ctx context.Context, mgr PluginManager, cfg *Config) error {
|
||||
steps := make(map[string]definition.Step)
|
||||
|
||||
// Load plugin-based steps
|
||||
for _, c := range cfg.Plugins.Steps {
|
||||
step, err := mgr.Step(ctx, &c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize plugin step %s: %w", c.ID, err)
|
||||
}
|
||||
steps[c.ID] = step
|
||||
}
|
||||
|
||||
// Register processing steps
|
||||
for _, step := range cfg.Steps {
|
||||
var s definition.Step
|
||||
var err error
|
||||
|
||||
switch step {
|
||||
case "sign":
|
||||
s, err = newSignStep(h.signer, h.km)
|
||||
case "validateSign":
|
||||
s, err = newValidateSignStep(h.signValidator, h.km)
|
||||
case "validateSchema":
|
||||
s, err = newValidateSchemaStep(h.schemaValidator)
|
||||
case "addRoute":
|
||||
s, err = newRouteStep(h.router)
|
||||
case "broadcast":
|
||||
s = &broadcastStep{}
|
||||
default:
|
||||
if customStep, exists := steps[step]; exists {
|
||||
s = customStep
|
||||
} else {
|
||||
return fmt.Errorf("unrecognized step: %s", step)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.steps = append(h.steps, s)
|
||||
}
|
||||
log.Infof(ctx, "Processor steps initialized: %v", cfg.Steps)
|
||||
return nil
|
||||
}
|
||||
181
core/module/handler/step.go
Normal file
181
core/module/handler/step.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beckn/beckn-onix/pkg/log"
|
||||
"github.com/beckn/beckn-onix/pkg/model"
|
||||
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||
)
|
||||
|
||||
// signStep represents the signing step in the processing pipeline.
|
||||
type signStep struct {
|
||||
signer definition.Signer
|
||||
km definition.KeyManager
|
||||
}
|
||||
|
||||
// newSignStep initializes and returns a new signing step.
|
||||
func newSignStep(signer definition.Signer, km definition.KeyManager) (definition.Step, error) {
|
||||
if signer == nil {
|
||||
return nil, fmt.Errorf("invalid config: Signer plugin not configured")
|
||||
}
|
||||
if km == nil {
|
||||
return nil, fmt.Errorf("invalid config: KeyManager plugin not configured")
|
||||
}
|
||||
|
||||
return &signStep{signer: signer, km: km}, nil
|
||||
}
|
||||
|
||||
// Run executes the signing step.
|
||||
func (s *signStep) Run(ctx *model.StepContext) error {
|
||||
keyID, key, err := s.km.SigningPrivateKey(ctx, ctx.SubID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get signing key: %w", err)
|
||||
}
|
||||
createdAt := time.Now().Unix()
|
||||
validTill := time.Now().Add(5 * time.Minute).Unix()
|
||||
sign, err := s.signer.Sign(ctx, ctx.Body, key, createdAt, validTill)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sign request: %w", err)
|
||||
}
|
||||
authHeader := fmt.Sprintf("Signature keyId=\"%s|%s|ed25519\",algorithm=\"ed25519\",created=\"%d\",expires=\"%d\",headers=\"(created) (expires) digest\",signature=\"%s\"", ctx.SubID, keyID, createdAt, validTill, sign)
|
||||
header := model.AuthHeaderSubscriber
|
||||
if ctx.Role == model.RoleGateway {
|
||||
header = model.AuthHeaderGateway
|
||||
}
|
||||
ctx.Request.Header.Set(header, authHeader)
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSignStep represents the signature validation step.
|
||||
type validateSignStep struct {
|
||||
validator definition.Verifier
|
||||
km definition.KeyManager
|
||||
}
|
||||
|
||||
// newValidateSignStep initializes and returns a new validate sign step.
|
||||
func newValidateSignStep(signValidator definition.Verifier, km definition.KeyManager) (definition.Step, error) {
|
||||
if signValidator == nil {
|
||||
return nil, fmt.Errorf("invalid config: SignValidator plugin not configured")
|
||||
}
|
||||
if km == nil {
|
||||
return nil, fmt.Errorf("invalid config: KeyManager plugin not configured")
|
||||
}
|
||||
return &validateSignStep{validator: signValidator, km: km}, nil
|
||||
}
|
||||
|
||||
// Run executes the validation step.
|
||||
func (s *validateSignStep) Run(ctx *model.StepContext) error {
|
||||
unauthHeader := fmt.Sprintf("Signature realm=\"%s\",headers=\"(created) (expires) digest\"", ctx.SubID)
|
||||
headerValue := ctx.Request.Header.Get(model.AuthHeaderGateway)
|
||||
if len(headerValue) != 0 {
|
||||
if err := s.validate(ctx, headerValue); err != nil {
|
||||
ctx.RespHeader.Set(model.UnaAuthorizedHeaderGateway, unauthHeader)
|
||||
return model.NewSignValidationErrf("failed to validate %s: %w", model.AuthHeaderGateway, err)
|
||||
}
|
||||
}
|
||||
headerValue = ctx.Request.Header.Get(model.AuthHeaderSubscriber)
|
||||
if len(headerValue) == 0 {
|
||||
ctx.RespHeader.Set(model.UnaAuthorizedHeaderSubscriber, unauthHeader)
|
||||
return model.NewSignValidationErrf("%s missing", model.UnaAuthorizedHeaderSubscriber)
|
||||
}
|
||||
if err := s.validate(ctx, headerValue); err != nil {
|
||||
ctx.RespHeader.Set(model.UnaAuthorizedHeaderSubscriber, unauthHeader)
|
||||
return model.NewSignValidationErrf("failed to validate %s: %w", model.AuthHeaderSubscriber, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate checks the validity of the provided signature header.
|
||||
func (s *validateSignStep) validate(ctx *model.StepContext, value string) error {
|
||||
headerParts := strings.Split(value, "|")
|
||||
ids := strings.Split(headerParts[0], "\"")
|
||||
if len(ids) < 2 || len(headerParts) < 3 {
|
||||
return fmt.Errorf("malformed sign header")
|
||||
}
|
||||
subID := ids[1]
|
||||
keyID := headerParts[1]
|
||||
key, err := s.km.SigningPublicKey(ctx, subID, keyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get validation key: %w", err)
|
||||
}
|
||||
if _, err := s.validator.Verify(ctx, ctx.Body, []byte(value), key); err != nil {
|
||||
return fmt.Errorf("sign validation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSchemaStep represents the schema validation step.
|
||||
type validateSchemaStep struct {
|
||||
validator definition.SchemaValidator
|
||||
}
|
||||
|
||||
// newValidateSchemaStep creates and returns the validateSchema step after validation
|
||||
func newValidateSchemaStep(schemaValidator definition.SchemaValidator) (definition.Step, error) {
|
||||
if schemaValidator == nil {
|
||||
return nil, fmt.Errorf("invalid config: SchemaValidator plugin not configured")
|
||||
}
|
||||
log.Debug(context.Background(), "adding schema validator")
|
||||
return &validateSchemaStep{validator: schemaValidator}, nil
|
||||
}
|
||||
|
||||
// Run executes the schema validation step.
|
||||
func (s *validateSchemaStep) Run(ctx *model.StepContext) error {
|
||||
if err := s.validator.Validate(ctx, ctx.Request.URL, ctx.Body); err != nil {
|
||||
return fmt.Errorf("schema validation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addRouteStep represents the route determination step.
|
||||
type addRouteStep struct {
|
||||
router definition.Router
|
||||
}
|
||||
|
||||
// newRouteStep initializes and returns a new routing step.
|
||||
func newRouteStep(router definition.Router) (definition.Step, error) {
|
||||
if router == nil {
|
||||
return nil, fmt.Errorf("invalid config: Router plugin not configured")
|
||||
}
|
||||
return &addRouteStep{router: router}, nil
|
||||
}
|
||||
|
||||
// Run executes the routing step.
|
||||
func (s *addRouteStep) Run(ctx *model.StepContext) error {
|
||||
route, err := s.router.Route(ctx, ctx.Request.URL, ctx.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determine route: %w", err)
|
||||
}
|
||||
log.Debugf(ctx, "Routing to %#v", route)
|
||||
ctx.Route = route
|
||||
|
||||
log.Debugf(ctx, "ctx.Route to %#v", ctx.Route)
|
||||
return nil
|
||||
}
|
||||
|
||||
// broadcastStep is a stub for broadcasting.
|
||||
type broadcastStep struct{}
|
||||
|
||||
// Run is a placeholder for future implementation.
|
||||
func (b *broadcastStep) Run(ctx *model.StepContext) error {
|
||||
// TODO: Implement broadcast logic if needed
|
||||
return nil
|
||||
}
|
||||
|
||||
// subscribeStep is a stub for subscription handling.
|
||||
type subscribeStep struct{}
|
||||
|
||||
// Run is a placeholder for future implementation.
|
||||
func (s *subscribeStep) Run(ctx *model.StepContext) error {
|
||||
// TODO: Implement subscription logic if needed
|
||||
return nil
|
||||
}
|
||||
|
||||
// tracingStep wraps a Step with OpenTelemetry tracing
|
||||
type tracingStep struct {
|
||||
step definition.Step
|
||||
name string
|
||||
}
|
||||
84
core/module/module.go
Normal file
84
core/module/module.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/beckn/beckn-onix/core/module/handler"
|
||||
"github.com/beckn/beckn-onix/pkg/log"
|
||||
)
|
||||
|
||||
// Config represents the configuration for a module.
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Path string `yaml:"path"`
|
||||
Handler handler.Config
|
||||
}
|
||||
|
||||
// handlerProviders maintains a mapping of handler types to their respective providers.
|
||||
var handlerProviders = map[handler.Type]handler.Provider{
|
||||
handler.HandlerTypeStd: handler.NewStdHandler,
|
||||
}
|
||||
|
||||
// GetDummyHandlerProviders returns a dummy handler provider mapping for testing purposes.
|
||||
func GetDummyHandlerProviders() map[handler.Type]handler.Provider {
|
||||
return map[handler.Type]handler.Provider{
|
||||
handler.HandlerTypeStd: handler.DummyHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// getHandlerProviders is a function to retrieve handler providers, which can be overridden for testing.
|
||||
var getHandlerProviders = func() map[handler.Type]handler.Provider {
|
||||
return handlerProviders
|
||||
}
|
||||
|
||||
// Register initializes and registers handlers based on the provided configuration.
|
||||
// It iterates over the module configurations, retrieves appropriate handler providers,
|
||||
// and registers the handlers with the HTTP multiplexer.
|
||||
func Register(ctx context.Context, mCfgs []Config, mux *http.ServeMux, mgr handler.PluginManager) error {
|
||||
log.Debugf(ctx, "Registering modules with config: %#v", mCfgs)
|
||||
|
||||
providers := getHandlerProviders()
|
||||
// Iterate over the handlers in the configuration.
|
||||
for _, c := range mCfgs {
|
||||
rmp, ok := providers[c.Handler.Type]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid module : %s", c.Name)
|
||||
}
|
||||
h, err := rmp(ctx, mgr, &c.Handler)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s : %w", c.Name, err)
|
||||
}
|
||||
h, err = addMiddleware(ctx, mgr, h, &c.Handler)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add middleware: %w", err)
|
||||
|
||||
}
|
||||
log.Debugf(ctx, "Registering handler %s, of type %s @ %s", c.Name, c.Handler.Type, c.Path)
|
||||
mux.Handle(c.Path, h)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addMiddleware applies middleware plugins to the provided handler in reverse order.
|
||||
// It retrieves middleware instances from the plugin manager and chains them to the handler.
|
||||
func addMiddleware(ctx context.Context, mgr handler.PluginManager, handler http.Handler, hCfg *handler.Config) (http.Handler, error) {
|
||||
mws := hCfg.Plugins.Middleware
|
||||
log.Debugf(ctx, "Applying %d middleware(s) to the handler", len(mws))
|
||||
// Apply the middleware in reverse order.
|
||||
for i := len(mws) - 1; i >= 0; i-- {
|
||||
log.Debugf(ctx, "Loading middleware: %s", mws[i].ID)
|
||||
mw, err := mgr.Middleware(ctx, &mws[i])
|
||||
if err != nil {
|
||||
log.Errorf(ctx, err, "Failed to load middleware %s: %v", mws[i].ID, err)
|
||||
return nil, fmt.Errorf("failed to load middleware %s: %w", mws[i].ID, err)
|
||||
}
|
||||
// Apply the middleware to the handler.
|
||||
handler = mw(handler)
|
||||
log.Debugf(ctx, "Applied middleware: %s", mws[i].ID)
|
||||
}
|
||||
|
||||
log.Debugf(ctx, "Middleware chain setup completed")
|
||||
return handler, nil
|
||||
}
|
||||
Reference in New Issue
Block a user