middleware

package
v2.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 26, 2025 License: MIT Imports: 15 Imported by: 0

README

Middleware Package

The middleware package provides the core Gin middleware for integrating UsageFlow API tracking and metering into your Go web applications.

Overview

This package implements:

  • Request interception and monitoring
  • Usage tracking and allocation
  • User identification from various sources
  • Graceful degradation when WebSocket is disconnected
  • Automatic configuration updates

Core Components

UsageFlowAPI

Main middleware structure that manages the entire UsageFlow integration.

type UsageFlowAPI struct {
    APIKey                      string
    ApplicationId                string
    ApiConfig                   []config.ApiConfigStrategy
    ApplicationEndpointPolicies *config.PolicyResponse
    policyMap                   PolicyMap
    mu                          sync.RWMutex
    socketManager               *socket.UsageFlowSocketManager
    connected                   bool
}

Thread Safety: All public methods are thread-safe using sync.RWMutex.

Key Methods
New(apiKey string) *UsageFlowAPI

Creates a new UsageFlowAPI instance and initializes the WebSocket connection pool.

uf := middleware.New("your-api-key")

Initialization:

  • Creates WebSocket manager with connection pool
  • Starts automatic config updater (every 30 seconds)
  • Initializes connection status tracking
RequestInterceptor(routes, whiteListRoutes []config.Route) gin.HandlerFunc

Creates a Gin middleware handler for intercepting and monitoring requests.

routes := []config.Route{
    {Method: "*", URL: "*"},
}
whiteList := []config.Route{
    {Method: "GET", URL: "/health"},
}

r.Use(uf.RequestInterceptor(routes, whiteList))

Behavior:

  1. Checks if route should be monitored (based on routes parameter)
  2. Checks if route is whitelisted (skips monitoring)
  3. Captures request metadata
  4. Executes allocation request (if socket connected)
  5. Processes original request
  6. Executes fulfill request after completion

Graceful Degradation:

  • If WebSocket is disconnected, requests continue normally
  • No errors are returned to the client
  • Middleware silently skips UsageFlow operations
FetchApiConfig() ([]config.ApiConfigStrategy, error)

Fetches the latest API configuration from UsageFlow service via WebSocket.

Automatic Updates:

  • Called immediately on initialization
  • Automatically called every 30 seconds
  • Updates are thread-safe
GetUserPrefix(c *gin.Context, method, url string) string

Extracts user identifier from the request based on configuration.

Supported Identity Locations:

  • "headers": HTTP header value
  • "query": Query parameter
  • "path_params": URL path parameter
  • "query_params": Query parameter (alias)
  • "body": JSON body field
  • "bearer_token": JWT token claim

Returns: Transformed ledger ID format (lowercase, underscores)

ExecuteRequestWithMetadata(ledgerId, method, url string, metadata map[string]interface{}, c *gin.Context) (bool, error)

Executes the initial allocation request before processing the main request.

Behavior:

  • Creates allocation via WebSocket
  • Stores allocation ID in context (eventId)
  • Returns true on success or if socket disconnected
  • Returns false only on actual errors when connected
ExecuteFulfillRequestWithMetadata(ledgerId, method, url string, metadata map[string]interface{}, c *gin.Context) (bool, error)

Executes the fulfill request after the main request is processed.

Behavior:

  • Retrieves allocation ID from context
  • Calculates request duration
  • Sends fulfill request via WebSocket
  • Always returns true to allow request to complete

Utility Functions

GetPatternedURL(c *gin.Context) string

Extracts the URL pattern from Gin context, using FullPath() if available, falling back to raw path.

ExtractBearerToken(c *gin.Context) (string, error)

Extracts Bearer token from Authorization header.

DecodeJWTUnverified(token string) (map[string]interface{}, error)

Decodes JWT token without signature verification (for identity extraction only).

TransformToLedgerId(input string) string

Converts input string to valid ledger ID format:

  • Lowercase
  • Non-alphanumeric characters replaced with underscores
GetRequestBody(c *gin.Context) (string, error)

Reads request body as string (preserves body for subsequent handlers).

Request Flow

1. Request arrives
   ↓
2. Check if route should be monitored
   ↓
3. Check if route is whitelisted
   ↓
4. Capture request metadata
   ↓
5. Extract user identifier (if configured)
   ↓
6. Execute allocation request (if connected)
   ↓
7. Process original request (c.Next())
   ↓
8. Execute fulfill request (if connected)
   ↓
9. Request completes

Connection Status

The middleware tracks WebSocket connection status:

  • isConnected(): Checks actual connection status from socket manager
  • Updates status automatically before each operation
  • Gracefully handles disconnections

Error Handling

Allocation Errors:

  • If socket disconnected: Continue normally (no error)
  • If socket error: Update status, continue normally
  • Only fails if socket connected AND actual error occurs

Fulfill Errors:

  • Always continues normally (request already processed)
  • Errors are logged but don't affect response

Configuration Updates

Configuration is automatically updated:

  • Initial: Fetched immediately on New()
  • Periodic: Every 30 seconds in background goroutine
  • Thread-safe: Uses mutex for updates
  • Non-blocking: Updates don't block request processing

Example Usage

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/usageflow/usageflow-go-middleware/pkg/config"
    "github.com/usageflow/usageflow-go-middleware/pkg/middleware"
)

func main() {
    r := gin.Default()

    // Initialize UsageFlow
    uf := middleware.New("your-api-key")

    // Configure routes
    routes := []config.Route{
        {Method: "*", URL: "*"},
    }

    whiteList := []config.Route{
        {Method: "GET", URL: "/health"},
    }

    // Apply middleware
    r.Use(uf.RequestInterceptor(routes, whiteList))

    // Your routes
    r.GET("/api/users", handleUsers)

    r.Run(":8080")
}

Thread Safety

All public methods are thread-safe:

  • Uses sync.RWMutex for read/write operations
  • Safe for concurrent request handling
  • Safe for concurrent config updates

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConvertToType added in v2.2.0

func ConvertToType[T any](obj any) (T, error)

func DecodeJWTUnverified

func DecodeJWTUnverified(token string) (map[string]interface{}, error)

DecodeJWTUnverified decodes a JWT without verifying its signature

func ExtractBearerToken

func ExtractBearerToken(c *gin.Context) (string, error)

ExtractBearerToken extracts the bearer token from the Authorization header

func GetCookieValue added in v2.4.0

func GetCookieValue(c *gin.Context, cookieName string) string

GetCookieValue extracts a specific cookie value from the Cookie header It handles both "cookie" and "Cookie" header names (case-insensitive)

func GetPatternedURL

func GetPatternedURL(c *gin.Context) string

GetPatternedURL returns a standardized URL pattern for the current request

func GetRequestBody

func GetRequestBody(c *gin.Context) (string, error)

GetRequestBody reads and returns the request body as a string

func TransformToLedgerId

func TransformToLedgerId(input string) string

TransformToLedgerId converts an input string to a valid ledger ID format

Types

type JwtCookieInfo added in v2.4.0

type JwtCookieInfo struct {
	CookieName string
	Claim      string
}

JwtCookieInfo represents parsed JWT cookie information

func ParseJwtCookieField added in v2.4.0

func ParseJwtCookieField(fieldName string) *JwtCookieInfo

ParseJwtCookieField parses JWT cookie field format: '[technique=jwt]cookieName[pick=claim]' Returns the cookie name and claim if the format matches, otherwise returns nil

type UsageFlowAPI

type UsageFlowAPI struct {
	APIKey                      string                     `json:"apiKey"`
	ApplicationId               string                     `json:"applicationId"`
	ApiConfig                   []config.ApiConfigStrategy `json:"apiConfig"`
	BlockedEndpoints            map[string]bool            `json:"blockedEndpoints"`
	ApplicationEndpointPolicies *config.PolicyResponse     `json:"applicationEndpointPolicies"`
	WhitelistEndpoints          []config.Route             `json:"whitelistEndpoints"`
	MonitoringPaths             []config.Route             `json:"monitoringPaths"`
	// contains filtered or unexported fields
}

func New

func New(apiKey string) *UsageFlowAPI

New creates a new instance of UsageFlowAPI

func (*UsageFlowAPI) ExecuteFulfillRequestWithMetadata

func (u *UsageFlowAPI) ExecuteFulfillRequestWithMetadata(ledgerId, method, url string, metadata map[string]interface{}, c *gin.Context) (bool, error)

ExecuteFulfillRequestWithMetadata executes the fulfill request after the main request is processed

func (*UsageFlowAPI) ExecuteRequestWithMetadata

func (u *UsageFlowAPI) ExecuteRequestWithMetadata(ledgerId, method, url string, metadata map[string]interface{}, c *gin.Context, rateLimited bool) (bool, error)

ExecuteRequestWithMetadata executes the initial allocation request

func (*UsageFlowAPI) FetchApiConfig

func (u *UsageFlowAPI) FetchApiConfig() ([]config.ApiConfigStrategy, error)

func (*UsageFlowAPI) FetchApplicationConfig added in v2.2.0

func (u *UsageFlowAPI) FetchApplicationConfig() (config.ApplicationConfigResponse, error)

func (*UsageFlowAPI) FetchBlockedEndpoints added in v2.1.0

func (u *UsageFlowAPI) FetchBlockedEndpoints() error

func (*UsageFlowAPI) GetUserPrefix

func (u *UsageFlowAPI) GetUserPrefix(c *gin.Context, method, url string) (string, bool)

GetUserPrefix attempts to extract a user identifier prefix based on the API configuration

func (*UsageFlowAPI) GuessLedgerId

func (u *UsageFlowAPI) GuessLedgerId(c *gin.Context) string

GuessLedgerId attempts to extract a ledger ID from various sources

func (*UsageFlowAPI) RequestInterceptor

func (u *UsageFlowAPI) RequestInterceptor() gin.HandlerFunc

RequestInterceptor creates a Gin middleware for intercepting requests

func (*UsageFlowAPI) StartConfigUpdater

func (u *UsageFlowAPI) StartConfigUpdater()

StartConfigUpdater begins periodic updates of the API configuration

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL