Skip to content

Commit

Permalink
support location handlers inside proxy to redirect to different locat…
Browse files Browse the repository at this point in the history
…ions and ports
  • Loading branch information
allnash committed Nov 7, 2024
1 parent 1c1b0b5 commit 829f979
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 22 deletions.
37 changes: 31 additions & 6 deletions fast-server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,39 @@ func (s *SSLConfig) validate() error {
return nil
}

type Location struct {
Path string `yaml:"path"`
Proxy ProxyConfig `yaml:"proxy"`
}

type Domain struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
PublicDir string `yaml:"public_dir"`
Proxy ProxyConfig `yaml:"proxy"`
SSL SSLConfig `yaml:"ssl"`
Locations []Location `yaml:"locations,omitempty"` // Add this line
}

func (d *Domain) setDefaults() {
if d.Type == "proxy" {
// Set default for main proxy config
d.Proxy.setDefaults()

// If no locations provided, create default "/" location with main proxy config
if len(d.Locations) == 0 {
d.Locations = []Location{
{
Path: "/",
Proxy: d.Proxy,
},
}
} else {
// Set defaults for all provided locations
for i := range d.Locations {
d.Locations[i].Proxy.setDefaults()
}
}
}
}

Expand All @@ -81,14 +103,17 @@ func (d *Domain) validate() error {
return fmt.Errorf("invalid domain type: %s", d.Type)
}

if d.Type != "proxy" && d.PublicDir == "" {
return fmt.Errorf("public_dir is required for type: %s", d.Type)
}

if d.Type == "proxy" {
if err := d.Proxy.validate(); err != nil {
return fmt.Errorf("proxy configuration error for %s: %v", d.Name, err)
for _, loc := range d.Locations {
if loc.Path == "" {
return fmt.Errorf("location path cannot be empty")
}
if err := loc.Proxy.validate(); err != nil {
return fmt.Errorf("proxy configuration error for location %s: %v", loc.Path, err)
}
}
} else if d.PublicDir == "" {
return fmt.Errorf("public_dir is required for type: %s", d.Type)
}

return d.SSL.validate()
Expand Down
4 changes: 2 additions & 2 deletions fast-server/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ func SetupDomainRoutes(e *echo.Echo, domains []config.Domain) {
})

// Root handler
e.GET("/", handleRequest)
e.Any("/", handleRequest)

// Catch-all handler
e.GET("/*", handleRequest)
e.Any("/*", handleRequest)
}

func handleRequest(c echo.Context) error {
Expand Down
55 changes: 41 additions & 14 deletions fast-server/handlers/proxy_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,60 @@ import (
"github.com/labstack/echo/v4/middleware"
"net/http"
"net/url"
"strings"
"time"
)

func getMatchingLocation(path string, locations []config.Location) *config.Location {
var bestMatch *config.Location
var bestLen int

for i, loc := range locations {
if strings.HasPrefix(path, loc.Path) {
pathLen := len(loc.Path)
if bestMatch == nil || pathLen > bestLen {
bestMatch = &locations[i]
bestLen = pathLen
}
}
}

return bestMatch
}

func HandleProxy(c echo.Context, domain config.Domain) error {
// Determine target scheme from config
targetScheme := domain.Proxy.Protocol
requestPath := c.Request().URL.Path

// Get matching location
location := getMatchingLocation(requestPath, domain.Locations)
if location == nil {
c.Logger().Errorf("No matching location found for path: %s", requestPath)
return echo.ErrNotFound
}

// Use location's proxy config
proxyConfig := location.Proxy
targetScheme := proxyConfig.Protocol
if targetScheme == "" {
targetScheme = "http" // Default to HTTP if not specified
targetScheme = "http"
}

target, err := url.Parse(fmt.Sprintf("%s://%s:%d", targetScheme, domain.Proxy.Host, domain.Proxy.Port))
target, err := url.Parse(fmt.Sprintf("%s://%s:%d", targetScheme, proxyConfig.Host, proxyConfig.Port))
if err != nil {
c.Logger().Errorf("Error parsing proxy URL: %v", err)
return echo.ErrInternalServerError
}

originalHost := c.Request().Host
c.Logger().Infof("Proxying %s request from %s to %s",
c.Request().Method, originalHost, target.String())
c.Logger().Infof("Proxying %s request from %s to %s (location: %s)",
c.Request().Method, originalHost, target.String(), location.Path)

transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
InsecureSkipVerify: domain.Proxy.InsecureSkipVerify,
InsecureSkipVerify: proxyConfig.InsecureSkipVerify,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
Expand All @@ -51,24 +79,25 @@ func HandleProxy(c echo.Context, domain config.Domain) error {
DisableCompression: false,
}

// Get original scheme
sourceScheme := "http"
if c.Request().TLS != nil {
sourceScheme = "https"
}

// Strip the location path prefix for the upstream request
rewriteMap := map[string]string{
location.Path + "*": "/$1",
}

proxyMiddleware := middleware.ProxyWithConfig(middleware.ProxyConfig{
Balancer: middleware.NewRoundRobinBalancer([]*middleware.ProxyTarget{
{
URL: target,
},
}),
Rewrite: map[string]string{
"/*": "/$1",
},
Rewrite: rewriteMap,
Transport: transport,
ModifyResponse: func(res *http.Response) error {
// Optional: Modify response headers here if needed
return nil
},
})
Expand All @@ -78,8 +107,6 @@ func HandleProxy(c echo.Context, domain config.Domain) error {
c.Request().Header.Set("X-Real-IP", c.RealIP())
c.Request().Header.Set("X-Forwarded-For", c.RealIP())
c.Request().Header.Set("X-Forwarded-Proto", sourceScheme)

// Keep original host
c.Request().Host = originalHost

return proxyMiddleware(func(c echo.Context) error {
Expand Down

0 comments on commit 829f979

Please sign in to comment.