1
0
mirror of https://github.com/umputun/reproxy.git synced 2025-11-29 22:08:14 +02:00
Files
reproxy/app/discovery/provider/file.go
Sergey Ninua fe24cf99ef Implement Host header bypassing (#155)
* Initial implementation of keep-host argument

* Add keep-host parsing to the consulcatalog provider

* Update docs

* update from the current master

---------
by @ffix
2024-01-25 03:28:54 -06:00

134 lines
3.1 KiB
Go

package provider
import (
"context"
"fmt"
"os"
"regexp"
"sort"
"time"
log "github.com/go-pkgz/lgr"
"gopkg.in/yaml.v3"
"github.com/umputun/reproxy/app/discovery"
)
// File implements file-based provider, defined with yaml file
type File struct {
FileName string
CheckInterval time.Duration
Delay time.Duration
}
// Events returns channel updating on file change only
func (d *File) Events(ctx context.Context) <-chan discovery.ProviderID {
res := make(chan discovery.ProviderID)
// no need to queue multiple events
trySubmit := func(ch chan discovery.ProviderID) bool {
select {
case ch <- discovery.PIFile:
return true
default:
return false
}
}
// check once if config file in place and it is file for real and not a directory
fi, err := os.Stat(d.FileName)
if err != nil {
log.Printf("[WARN] configuration file %s not found", d.FileName)
}
if err == nil && fi.IsDir() {
log.Printf("[WARN] %s is directory but configuration file expected", d.FileName)
}
go func() {
tk := time.NewTicker(d.CheckInterval)
lastModif := time.Time{}
for {
select {
case <-tk.C:
fi, err := os.Stat(d.FileName)
if err != nil {
continue
}
if fi.ModTime() != lastModif {
// don't react on modification right away
if fi.ModTime().Sub(lastModif) < d.Delay {
continue
}
log.Printf("[DEBUG] file %s changed, %s -> %s", d.FileName,
lastModif.Format(time.RFC3339Nano), fi.ModTime().Format(time.RFC3339Nano))
if trySubmit(res) {
lastModif = fi.ModTime()
}
}
case <-ctx.Done():
close(res)
tk.Stop()
return
}
}
}()
return res
}
// List all src dst pairs
func (d *File) List() (res []discovery.URLMapper, err error) {
var fileConf map[string][]struct {
SourceRoute string `yaml:"route"`
Dest string `yaml:"dest"`
Ping string `yaml:"ping"`
AssetsEnabled bool `yaml:"assets"`
AssetsSPA bool `yaml:"spa"`
KeepHost *bool `yaml:"keep-host,omitempty"`
OnlyFrom string `yaml:"remote"`
}
fh, err := os.Open(d.FileName)
if err != nil {
return nil, fmt.Errorf("can't open %s: %w", d.FileName, err)
}
defer fh.Close() //nolint gosec
if err = yaml.NewDecoder(fh).Decode(&fileConf); err != nil {
return nil, fmt.Errorf("can't parse %s: %w", d.FileName, err)
}
log.Printf("[DEBUG] file provider %+v", res)
for srv, fl := range fileConf {
for _, f := range fl {
rx, e := regexp.Compile(f.SourceRoute)
if e != nil {
return nil, fmt.Errorf("can't parse regex %s: %w", f.SourceRoute, e)
}
if srv == "default" {
srv = "*"
}
mapper := discovery.URLMapper{
Server: srv,
SrcMatch: *rx,
Dst: f.Dest,
PingURL: f.Ping,
KeepHost: f.KeepHost,
ProviderID: discovery.PIFile,
MatchType: discovery.MTProxy,
OnlyFromIPs: discovery.ParseOnlyFrom(f.OnlyFrom),
}
if f.AssetsEnabled || f.AssetsSPA {
mapper.MatchType = discovery.MTStatic
mapper.AssetsSPA = f.AssetsSPA
}
res = append(res, mapper)
}
}
sort.Slice(res, func(i, j int) bool {
return len(res[i].Server) > len(res[j].Server)
})
err = fh.Close()
return res, err
}