first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,235 @@
package main
import (
"encoding/json"
"fmt"
"sync"
"time"
"github.com/maximhq/bifrost/core/schemas"
)
// Plugin configuration
type PluginConfig struct {
RequireAuth bool `json:"require_auth"` // Toggle auth header enforcement
RateLimit int `json:"rate_limit"` // Max requests per window (0 = unlimited)
RateWindow int `json:"rate_window"` // Rate limit window in seconds (default: 60)
MaxBodySize int `json:"max_body_size"` // Max request body size in bytes (0 = unlimited)
}
var (
// Default configuration
pluginConfig = &PluginConfig{
RequireAuth: true, // Require auth by default
RateLimit: 10, // 10 requests per window by default
RateWindow: 60, // 60 second window by default
MaxBodySize: 1024 * 1024, // 1MB by default
}
rateLimiter = &RateLimiter{
requests: make(map[string][]time.Time),
}
)
type RateLimiter struct {
mu sync.Mutex
requests map[string][]time.Time
}
func (rl *RateLimiter) Allow(key string, limit int, window int) bool {
// If rate limiting is disabled (limit = 0), allow all requests
if limit <= 0 {
return true
}
rl.mu.Lock()
defer rl.mu.Unlock()
now := time.Now()
windowStart := now.Add(-time.Duration(window) * time.Second)
// Clean old requests
if reqs, ok := rl.requests[key]; ok {
validReqs := []time.Time{}
for _, t := range reqs {
if t.After(windowStart) {
validReqs = append(validReqs, t)
}
}
rl.requests[key] = validReqs
// Check if limit exceeded
if len(validReqs) >= limit {
return false
}
}
// Add new request
rl.requests[key] = append(rl.requests[key], now)
return true
}
// Init is called when the plugin is loaded (optional)
func Init(config any) error {
fmt.Println("[HTTP-Transport-Only Plugin] Init called")
// Parse configuration
if configMap, ok := config.(map[string]interface{}); ok {
// Parse require_auth toggle
if requireAuth, ok := configMap["require_auth"].(bool); ok {
pluginConfig.RequireAuth = requireAuth
fmt.Printf("[HTTP-Transport-Only Plugin] Auth enforcement: %v\n", pluginConfig.RequireAuth)
}
// Parse rate_limit
if rateLimit, ok := configMap["rate_limit"].(float64); ok {
pluginConfig.RateLimit = int(rateLimit)
if pluginConfig.RateLimit <= 0 {
fmt.Println("[HTTP-Transport-Only Plugin] Rate limiting disabled")
} else {
fmt.Printf("[HTTP-Transport-Only Plugin] Rate limit: %d requests per %d seconds\n",
pluginConfig.RateLimit, pluginConfig.RateWindow)
}
}
// Parse rate_window
if rateWindow, ok := configMap["rate_window"].(float64); ok {
pluginConfig.RateWindow = int(rateWindow)
fmt.Printf("[HTTP-Transport-Only Plugin] Rate window: %d seconds\n", pluginConfig.RateWindow)
}
// Parse max_body_size
if maxBodySize, ok := configMap["max_body_size"].(float64); ok {
pluginConfig.MaxBodySize = int(maxBodySize)
if pluginConfig.MaxBodySize <= 0 {
fmt.Println("[HTTP-Transport-Only Plugin] Request size validation disabled")
} else {
fmt.Printf("[HTTP-Transport-Only Plugin] Max body size: %d bytes\n", pluginConfig.MaxBodySize)
}
}
}
fmt.Printf("[HTTP-Transport-Only Plugin] Configuration loaded: %+v\n", pluginConfig)
return nil
}
// GetName returns the name of the plugin (required)
// This is the system identifier - not editable by users
// Users can set a custom display_name in the config for the UI
func GetName() string {
return "http-transport-only"
}
// HTTPTransportPreHook is called at the HTTP layer before requests enter Bifrost core
// This example demonstrates authentication, rate limiting, and request validation
func HTTPTransportPreHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest) (*schemas.HTTPResponse, error) {
fmt.Println("[HTTP-Transport-Only Plugin] HTTPTransportPreHook called")
fmt.Printf("[HTTP-Transport-Only Plugin] Method: %s, Path: %s\n", req.Method, req.Path)
// Example 1: Authentication check (configurable)
authHeader := req.CaseInsensitiveHeaderLookup("Authorization")
if pluginConfig.RequireAuth && authHeader == "" {
fmt.Println("[HTTP-Transport-Only Plugin] Missing authorization header")
return &schemas.HTTPResponse{
StatusCode: 401,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"error": "Unauthorized: Missing authorization header"}`),
}, nil
}
// Example 2: Rate limiting by API key (configurable)
if pluginConfig.RateLimit > 0 {
apiKey := authHeader // In real implementation, extract from Bearer token
if apiKey == "" {
apiKey = "anonymous" // Default key for unauthenticated requests
}
if !rateLimiter.Allow(apiKey, pluginConfig.RateLimit, pluginConfig.RateWindow) {
fmt.Println("[HTTP-Transport-Only Plugin] Rate limit exceeded")
return &schemas.HTTPResponse{
StatusCode: 429,
Headers: map[string]string{
"Content-Type": "application/json",
"Retry-After": fmt.Sprintf("%d", pluginConfig.RateWindow),
"X-RateLimit-Limit": fmt.Sprintf("%d", pluginConfig.RateLimit),
},
Body: []byte(`{"error": "Rate limit exceeded. Please try again later."}`),
}, nil
}
}
// Example 3: Request validation (configurable)
if pluginConfig.MaxBodySize > 0 && req.Method == "POST" && len(req.Body) > pluginConfig.MaxBodySize {
fmt.Printf("[HTTP-Transport-Only Plugin] Request body too large: %d bytes (max: %d)\n",
len(req.Body), pluginConfig.MaxBodySize)
return &schemas.HTTPResponse{
StatusCode: 413,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(fmt.Sprintf(`{"error": "Request body too large. Max size: %d bytes"}`, pluginConfig.MaxBodySize)),
}, nil
}
// Example 4: Add custom headers
req.Headers["X-Plugin-Processed"] = "true"
req.Headers["X-Request-Time"] = time.Now().Format(time.RFC3339)
// Store metadata in context for PostHook
ctx.SetValue(schemas.BifrostContextKey("http-plugin-start-time"), time.Now())
// Return nil to continue processing
return nil, nil
}
// HTTPTransportPostHook is called at the HTTP layer after Bifrost core processes the request
// This example demonstrates response modification and logging
func HTTPTransportPostHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, resp *schemas.HTTPResponse) error {
fmt.Println("[HTTP-Transport-Only Plugin] HTTPTransportPostHook called")
// Calculate request duration
startTime := ctx.Value(schemas.BifrostContextKey("http-plugin-start-time"))
if t, ok := startTime.(time.Time); ok {
duration := time.Since(t)
fmt.Printf("[HTTP-Transport-Only Plugin] Request duration: %v\n", duration)
// Add duration header
resp.Headers["X-Request-Duration-Ms"] = fmt.Sprintf("%d", duration.Milliseconds())
}
// Example: Add CORS headers
resp.Headers["Access-Control-Allow-Origin"] = "*"
resp.Headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
resp.Headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
// Example: Add security headers
resp.Headers["X-Content-Type-Options"] = "nosniff"
resp.Headers["X-Frame-Options"] = "DENY"
resp.Headers["X-XSS-Protection"] = "1; mode=block"
// Example: Log response details
fmt.Printf("[HTTP-Transport-Only Plugin] Response status: %d, size: %d bytes\n",
resp.StatusCode, len(resp.Body))
// Example: Modify error responses to add custom metadata
if resp.StatusCode >= 400 {
var errorBody map[string]interface{}
if err := json.Unmarshal(resp.Body, &errorBody); err == nil {
errorBody["timestamp"] = time.Now().Format(time.RFC3339)
errorBody["request_id"] = ctx.Value(schemas.BifrostContextKey("request_id"))
if newBody, err := json.Marshal(errorBody); err == nil {
resp.Body = newBody
}
}
}
return nil
}
// Cleanup is called when the plugin is unloaded (required)
func Cleanup() error {
fmt.Println("[HTTP-Transport-Only Plugin] Cleanup called")
return nil
}