Feat/fileserver (#60)

* cleanup code for URL type

* fix makefile for trace mode

* refactor, merge Entry, RawEntry and Route into one. 

* Implement fileserver.

* refactor: rename HTTPRoute to ReverseProxyRoute to avoid confusion

* refactor: move metrics logger to middleware package

- fix prometheus metrics for load balanced routes
  - route will now fail when health monitor fail to start

* fix extra output of ls-* commands by defer initializaing stuff, speed up start time

* add test for path traversal attack, small fix on FileServer.Start method

* rename rule.on.bypass to pass

* refactor and fixed map-to-map  deserialization

* updated route loading logic

* schemas: add "add_prefix" option to modify_request middleware


* updated route JSONMarshalling

---------

Co-authored-by: yusing <yusing@6uo.me>
This commit is contained in:
Yuzerion
2025-02-06 18:23:10 +08:00
committed by GitHub
parent 4d47eb0e91
commit 1a5f3735cf
79 changed files with 1484 additions and 1276 deletions

View File

@@ -2,6 +2,8 @@ package health
import (
"time"
"github.com/yusing/go-proxy/internal/common"
)
type HealthCheckConfig struct {
@@ -11,3 +13,8 @@ type HealthCheckConfig struct {
Interval time.Duration `json:"interval" validate:"omitempty,min=1s"`
Timeout time.Duration `json:"timeout" validate:"omitempty,min=1s"`
}
var DefaultHealthConfig = &HealthCheckConfig{
Interval: common.HealthCheckIntervalDefault,
Timeout: common.HealthCheckTimeoutDefault,
}

View File

@@ -0,0 +1,36 @@
package monitor
import (
"os"
"time"
"github.com/yusing/go-proxy/internal/watcher/health"
)
type FileServerHealthMonitor struct {
*monitor
path string
}
func NewFileServerHealthMonitor(alias string, config *health.HealthCheckConfig, path string) *FileServerHealthMonitor {
mon := &FileServerHealthMonitor{path: path}
mon.monitor = newMonitor(nil, config, mon.CheckHealth)
mon.service = alias
return mon
}
func (s *FileServerHealthMonitor) CheckHealth() (*health.HealthCheckResult, error) {
start := time.Now()
_, err := os.Stat(s.path)
detail := ""
if err != nil {
detail = err.Error()
}
return &health.HealthCheckResult{
Healthy: err == nil,
Latency: time.Since(start),
Detail: detail,
}, nil
}

View File

@@ -26,7 +26,7 @@ var pinger = &http.Client{
},
}
func NewHTTPHealthMonitor(url types.URL, config *health.HealthCheckConfig) *HTTPHealthMonitor {
func NewHTTPHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *HTTPHealthMonitor {
mon := new(HTTPHealthMonitor)
mon.monitor = newMonitor(url, config, mon.CheckHealth)
if config.UseGet {
@@ -37,7 +37,7 @@ func NewHTTPHealthMonitor(url types.URL, config *health.HealthCheckConfig) *HTTP
return mon
}
func NewHTTPHealthChecker(url types.URL, config *health.HealthCheckConfig) health.HealthChecker {
func NewHTTPHealthChecker(url *types.URL, config *health.HealthCheckConfig) health.HealthChecker {
return NewHTTPHealthMonitor(url, config)
}

View File

@@ -5,7 +5,7 @@ import (
"strconv"
"time"
"github.com/yusing/go-proxy/internal/net/types"
net "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils"
"github.com/yusing/go-proxy/internal/watcher/health"
)
@@ -19,7 +19,7 @@ type JSONRepresentation struct {
Latency time.Duration
LastSeen time.Time
Detail string
URL types.URL
URL *net.URL
Extra map[string]any
}

View File

@@ -23,7 +23,7 @@ type (
monitor struct {
service string
config *health.HealthCheckConfig
url atomic.Value[types.URL]
url atomic.Value[*types.URL]
status atomic.Value[health.Status]
lastResult *health.HealthCheckResult
@@ -39,7 +39,7 @@ type (
var ErrNegativeInterval = errors.New("negative interval")
func newMonitor(url types.URL, config *health.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
func newMonitor(url *types.URL, config *health.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
mon := &monitor{
config: config,
checkHealth: healthCheckFunc,
@@ -118,12 +118,12 @@ func (mon *monitor) Finish(reason any) {
}
// UpdateURL implements HealthChecker.
func (mon *monitor) UpdateURL(url types.URL) {
func (mon *monitor) UpdateURL(url *types.URL) {
mon.url.Store(url)
}
// URL implements HealthChecker.
func (mon *monitor) URL() types.URL {
func (mon *monitor) URL() *types.URL {
return mon.url.Load()
}
@@ -205,7 +205,7 @@ func (mon *monitor) checkUpdateHealth() error {
if !result.Healthy {
extras.Add("Last Seen", strutils.FormatLastSeen(GetLastSeen(mon.service)))
}
if !mon.url.Load().Nil() {
if mon.url.Load() != nil {
extras.Add("Service URL", mon.url.Load().String())
}
if result.Detail != "" {

View File

@@ -15,7 +15,7 @@ type (
}
)
func NewRawHealthMonitor(url types.URL, config *health.HealthCheckConfig) *RawHealthMonitor {
func NewRawHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *RawHealthMonitor {
mon := new(RawHealthMonitor)
mon.monitor = newMonitor(url, config, mon.CheckHealth)
mon.dialer = &net.Dialer{
@@ -25,7 +25,7 @@ func NewRawHealthMonitor(url types.URL, config *health.HealthCheckConfig) *RawHe
return mon
}
func NewRawHealthChecker(url types.URL, config *health.HealthCheckConfig) health.HealthChecker {
func NewRawHealthChecker(url *types.URL, config *health.HealthCheckConfig) health.HealthChecker {
return NewRawHealthMonitor(url, config)
}

View File

@@ -30,8 +30,8 @@ type (
}
HealthChecker interface {
CheckHealth() (result *HealthCheckResult, err error)
URL() types.URL
URL() *types.URL
Config() *HealthCheckConfig
UpdateURL(url types.URL)
UpdateURL(url *types.URL)
}
)