From d137a764d050e3d5296da2830a32f6d83bdb364f Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 9 Oct 2023 00:19:42 +0900 Subject: support path-based proxy --- conf.example.hcl | 21 +++++++++++++++++++++ config.go | 17 ++++++++++++++++- server.go | 16 +++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/conf.example.hcl b/conf.example.hcl index 8f1a56c..dfe18f8 100644 --- a/conf.example.hcl +++ b/conf.example.hcl @@ -11,4 +11,25 @@ server http { port = 8001 } } + + proxy b { + from { + path = "/b/" + } + to { + host = "127.0.0.1" + port = 8002 + } + } + + proxy c { + from { + host = "c.localhost" + path = "/c/" + } + to { + host = "127.0.0.1" + port = 8003 + } + } } diff --git a/config.go b/config.go index ed4b3e0..6ad988f 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import ( "fmt" "net/netip" "net/url" + "strings" "github.com/hashicorp/hcl/v2/hclsimple" ) @@ -36,6 +37,7 @@ type ProxyConfig struct { type ProxyFromConfig struct { Host string + Path string } type ProxyToConfig struct { @@ -70,7 +72,8 @@ type InternalHCLProxyConfig struct { } type InternalHCLProxyFromConfig struct { - Host string `hcl:"host"` + Host string `hcl:"host,optional"` + Path string `hcl:"path,optional"` } type InternalHCLProxyToConfig struct { @@ -93,6 +96,7 @@ func fromHCLConfigToConfig(hclConfig *InternalHCLConfig) *Config { Name: p.Name, From: ProxyFromConfig{ Host: p.From.Host, + Path: p.From.Path, }, To: ProxyToConfig{ Host: p.To.Host, @@ -180,6 +184,17 @@ func LoadConfig(fileName string) (*Config, error) { } for _, p := range server.Proxies { + if p.From.Path != "" { + if !strings.HasPrefix(p.From.Path, "/") { + return nil, fmt.Errorf("Path must start with '/'") + } + if !strings.HasSuffix(p.From.Path, "/") { + return nil, fmt.Errorf("Path must end with '/'") + } + } + if p.From.Host == "" && p.From.Path == "" { + return nil, fmt.Errorf("Either host or path must be specified") + } _, err := url.Parse(fmt.Sprintf("http://%s:%d", p.To.Host, p.To.Port)) if err != nil { return nil, fmt.Errorf("Invalid host or port: %s:%d", p.To.Host, p.To.Port) diff --git a/server.go b/server.go index 47c52dd..830a6e4 100644 --- a/server.go +++ b/server.go @@ -8,6 +8,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "strings" ) type multipleReverseProxyServer struct { @@ -16,10 +17,22 @@ type multipleReverseProxyServer struct { type rewriteRule struct { fromHost string + fromPath string toUrl *url.URL proxy *httputil.ReverseProxy } +func (r *rewriteRule) matches(host, path string) bool { + ret := true + if r.fromHost != "" { + ret = ret && r.fromHost == host + } + if r.fromPath != "" { + ret = ret && strings.HasPrefix(path+"/", r.fromPath) + } + return ret +} + func newMultipleReverseProxyServer(ps []ProxyConfig) *multipleReverseProxyServer { var rules []rewriteRule for _, p := range ps { @@ -30,6 +43,7 @@ func newMultipleReverseProxyServer(ps []ProxyConfig) *multipleReverseProxyServer } rules = append(rules, rewriteRule{ fromHost: p.From.Host, + fromPath: p.From.Path, toUrl: targetUrl, proxy: &httputil.ReverseProxy{ Rewrite: func(r *httputil.ProxyRequest) { @@ -50,7 +64,7 @@ func (s *multipleReverseProxyServer) tryServeHTTP( hostWithoutPort string, ) bool { for _, rule := range s.rules { - if rule.fromHost == hostWithoutPort { + if rule.matches(hostWithoutPort, r.URL.Path) { rule.proxy.ServeHTTP(w, r) return true } -- cgit v1.2.3-70-g09d2