feat: Add reverse proxy functionality with enhanced routing capabilities

- Introduced IgnoreRequestPath option in proxy configuration to allow exact match routing.
- Implemented proxy_pass directive in routing extension to handle backend requests.
- Enhanced error handling for backend unavailability and timeouts.
- Added integration tests for reverse proxy, including basic requests, exact match routes, regex routes, header forwarding, and query string preservation.
- Created helper functions for setting up test servers and backends, along with assertion utilities for response validation.
- Updated server initialization to support extension management and middleware chaining.
- Improved logging for debugging purposes during request handling.
This commit is contained in:
Илья Глазунов
2025-12-12 00:38:30 +03:00
parent 8f5b9a5cd1
commit 881028c1e6
17 changed files with 3574 additions and 176 deletions
+40 -12
View File
@@ -11,19 +11,19 @@ import (
"time"
"github.com/konduktor/konduktor/internal/config"
"github.com/konduktor/konduktor/internal/extension"
"github.com/konduktor/konduktor/internal/logging"
"github.com/konduktor/konduktor/internal/middleware"
"github.com/konduktor/konduktor/internal/routing"
)
const Version = "0.1.0"
const Version = "0.2.0"
// Server represents the Konduktor HTTP server
type Server struct {
config *config.Config
httpServer *http.Server
router *routing.Router
logger *logging.Logger
config *config.Config
httpServer *http.Server
extensionManager *extension.Manager
logger *logging.Logger
}
// New creates a new server instance
@@ -37,12 +37,31 @@ func New(cfg *config.Config) (*Server, error) {
return nil, fmt.Errorf("failed to create logger: %w", err)
}
router := routing.New(cfg, logger)
// Create extension manager
extManager := extension.NewManager(logger)
// Load extensions from config
for _, extCfg := range cfg.Extensions {
// Add static_dir to routing config if not present
if extCfg.Type == "routing" {
if extCfg.Config == nil {
extCfg.Config = make(map[string]interface{})
}
if _, ok := extCfg.Config["static_dir"]; !ok {
extCfg.Config["static_dir"] = cfg.HTTP.StaticDir
}
}
if err := extManager.LoadExtension(extCfg.Type, extCfg.Config); err != nil {
logger.Error("Failed to load extension", "type", extCfg.Type, "error", err)
// Continue loading other extensions
}
}
srv := &Server{
config: cfg,
router: router,
logger: logger,
config: cfg,
extensionManager: extManager,
logger: logger,
}
return srv, nil
@@ -86,9 +105,15 @@ func (s *Server) Run() error {
// buildHandler builds the HTTP handler chain
func (s *Server) buildHandler() http.Handler {
var handler http.Handler = s.router
// Create base handler that returns 404
baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
})
// Add middleware
// Wrap with extension manager
var handler http.Handler = s.extensionManager.Handler(baseHandler)
// Add middleware (applied in reverse order)
handler = middleware.AccessLog(handler, s.logger)
handler = middleware.ServerHeader(handler, Version)
handler = middleware.Recovery(handler, s.logger)
@@ -115,6 +140,9 @@ func (s *Server) waitForShutdown(errChan <-chan error) error {
s.logger.Info("Shutting down server...")
// Cleanup extensions
s.extensionManager.Cleanup()
if err := s.httpServer.Shutdown(ctx); err != nil {
s.logger.Error("Error during shutdown", "error", err)
return err