From 3adb2a5115fbb0c2d6c68edc96ff9a269ede931b Mon Sep 17 00:00:00 2001 From: qydysky Date: Thu, 14 Mar 2024 23:01:28 +0800 Subject: [PATCH] 1 --- README.md | 6 +++++- config.go | 26 ++++++++++++++++++++++++-- http.go | 15 ++++++++++----- main.go | 1 + main/main.json | 12 +++++++++--- ws.go | 15 ++++++++++----- 6 files changed, 59 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0c87390..1ba4e2f 100755 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - 故障转移 - 自定义头 - 多种轮询 +- 请求路径过滤 - 请求头过滤 - 请求数据过滤 @@ -45,6 +46,9 @@ dealer: - splicing: int 当客户端支持cookie时,将会固定使用后端多少秒,默认不启用 - errToSec: float64 当后端响应超过(ws则指初次返回时间)指定秒,将会触发errBanSec - errBanSec: int 当后端错误时(指连接失败,不指后端错误响应),将会禁用若干秒 +- reqPather: [] 请求后端前,请求路径过滤器, 可以动态增加/删除 + - action: string 可选`access`、`deny`。 + - matchExp: string `access`时如不匹配将结束请求。`deny`时如匹配将结束请求。 - reqHeader: [] 请求后端前,请求头处理器, 可以动态增加/删除 - action: string 可选`access`、`deny`、`replace`、`add`、`del`、`set`。 - key: string 具体处理哪个头 @@ -58,4 +62,4 @@ dealer: - reqBody: [] 请求后端前,请求数据过滤器, 可以动态增加/删除 - action: string 可选`access`、`deny`。 - reqSize:string 限定请求数据大小,默认为`1M` - - matchExp: string `access`时如不匹配将结束请求。d`eny`时如匹配将结束请求。 + - matchExp: string `access`时如不匹配将结束请求。`deny`时如匹配将结束请求。 diff --git a/config.go b/config.go index b36e86d..b5eaeac 100755 --- a/config.go +++ b/config.go @@ -86,8 +86,18 @@ func (t *Config) SwapSign(ctx context.Context, logger Logger) { t.routeMap.Store(k, route) t.routeP.Store(route.Path, func(w http.ResponseWriter, r *http.Request) { + if !PatherMatchs(route.ReqPather, r) { + logger.Warn(`W:`, fmt.Sprintf("%v > %v %v %v", route.config.Addr, route.Path, r.RequestURI, ErrPatherCheckFail)) + w.Header().Add(header+"Error", ErrPatherCheckFail.Error()) + w.WriteHeader(http.StatusForbidden) + return + } + if !HeaderMatchs(route.ReqHeader, r) { - w.WriteHeader(http.StatusNotFound) + logger.Warn(`W:`, fmt.Sprintf("%v > %v %v %v", route.config.Addr, route.Path, r.RequestURI, ErrHeaderCheckFail)) + w.Header().Add(header+"Error", ErrHeaderCheckFail.Error()) + w.WriteHeader(http.StatusForbidden) + return } var backIs []*Back @@ -104,7 +114,7 @@ func (t *Config) SwapSign(ctx context.Context, logger Logger) { backIs = append(backIs, route.FiliterBackByRequest(r)...) if len(backIs) == 0 { - logger.Warn(`W:`, fmt.Sprintf("%v > %v %v %v", route.config.Addr, route.Path, r.URL.RequestURI(), ErrNoRoute)) + logger.Warn(`W:`, fmt.Sprintf("%v > %v %v %v", route.config.Addr, route.Path, r.RequestURI, ErrNoRoute)) w.Header().Add(header+"Error", ErrNoRoute.Error()) w.WriteHeader(http.StatusNotFound) return @@ -259,6 +269,7 @@ func (t *Back) cloneDealer() { if t.route.ErrToSec != 0 { t.ErrToSec = t.route.ErrToSec } + t.tmp.ReqPather = append(t.route.ReqPather, t.ReqPather...) t.tmp.ReqHeader = append(t.route.ReqHeader, t.ReqHeader...) t.tmp.ResHeader = append(t.route.ResHeader, t.ResHeader...) t.tmp.ReqBody = append(t.route.ReqBody, t.ReqBody...) @@ -268,6 +279,16 @@ func (t *Back) Id() string { return fmt.Sprintf("%p", t) } +func PatherMatchs(matchPath []Header, r *http.Request) bool { + matchs := len(matchPath) - 1 + for ; matchs >= 0; matchs -= 1 { + if !matchPath[matchs].Match(r.RequestURI) { + break + } + } + return matchs == -1 +} + func HeaderMatchs(matchHeader []Header, r *http.Request) bool { matchs := len(matchHeader) - 1 for ; matchs >= 0; matchs -= 1 { @@ -324,6 +345,7 @@ type Dealer struct { ErrToSec float64 `json:"errToSec"` Splicing int `json:"splicing"` ErrBanSec int `json:"errBanSec"` + ReqPather []Header `json:"reqPather"` ReqHeader []Header `json:"reqHeader"` ResHeader []Header `json:"resHeader"` ReqBody []Body `json:"reqBody"` diff --git a/http.go b/http.go index b05cd9b..e49f0c0 100644 --- a/http.go +++ b/http.go @@ -30,20 +30,25 @@ func httpDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, rou url := chosenBack.To if chosenBack.PathAdd { - url += r.URL.RequestURI() + url += r.RequestURI } url = "http" + url + if !PatherMatchs(chosenBack.ReqPather, r) { + logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v http %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrPatherCheckFail, time.Since(opT))) + return ErrPatherCheckFail + } + reader, e := BodyMatchs(chosenBack.tmp.ReqBody, r) if e != nil { logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v http %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT))) - return errors.Join(ErrBodyCheckFail, e) + return ErrBodyCheckFail } req, e := http.NewRequestWithContext(ctx, r.Method, url, reader) if e != nil { - return errors.Join(ErrReqCreFail, e) + return ErrReqCreFail } if e := copyHeader(r.Header, req.Header, chosenBack.tmp.ReqHeader); e != nil { @@ -112,7 +117,7 @@ func httpDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, rou if tmpbuf, put, e := blocksi.Get(); e != nil { logger.Error(`E:`, fmt.Sprintf("%v > %v > %v http %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT))) chosenBack.Disable() - return errors.Join(ErrCopy, e) + return ErrCopy } else { defer put() if _, e = io.CopyBuffer(w, resp.Body, tmpbuf); e != nil { @@ -120,7 +125,7 @@ func httpDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, rou if !errors.Is(e, context.Canceled) { chosenBack.Disable() } - return errors.Join(ErrCopy, e) + return ErrCopy } } return nil diff --git a/main.go b/main.go index 249513f..25c0710 100755 --- a/main.go +++ b/main.go @@ -176,6 +176,7 @@ var ( ErrReqCreFail = errors.New("ErrReqCreFail") ErrReqDoFail = errors.New("ErrReqDoFail") ErrResDoFail = errors.New("ErrResDoFail") + ErrPatherCheckFail = errors.New("ErrPatherCheckFail") ErrHeaderCheckFail = errors.New("ErrHeaderCheckFail") ErrBodyCheckFail = errors.New("ErrBodyCheckFail") ErrAllBacksFail = errors.New("ErrAllBacksFail") diff --git a/main/main.json b/main/main.json index 6375fb2..fff498f 100755 --- a/main/main.json +++ b/main/main.json @@ -6,8 +6,14 @@ "routes": [ { "path": "/", - "splicing": 100, "pathAdd": true, + "rollRule": "resDur_MinFirst", + "reqPather": [ + { + "action": "deny", + "matchExp": "(\\.\/|\/\\.|\/\/|\\|<|>|\\.sh|\\.cmd|\\.so|\\.exe|\\.bat|http|ftp)" + } + ], "backs": [ { "name": "baidu1", @@ -17,12 +23,12 @@ { "name": "baidu2", "to": "s://www.baidu.com", - "weight": 1 + "weight": 2 }, { "name": "baidu3", "to": "s://www.baidu.com", - "weight": 0 + "weight": 1 } ] } diff --git a/ws.go b/ws.go index dcfdf2a..3dd9900 100644 --- a/ws.go +++ b/ws.go @@ -38,15 +38,20 @@ func wsDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, route continue } + if !PatherMatchs(chosenBack.ReqPather, r) { + logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrPatherCheckFail, time.Since(opT))) + return ErrPatherCheckFail + } + _, e := BodyMatchs(chosenBack.tmp.ReqBody, r) if e != nil { logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT))) - return errors.Join(ErrBodyCheckFail, e) + return ErrBodyCheckFail } url := chosenBack.To if chosenBack.PathAdd { - url += r.URL.RequestURI() + url += r.RequestURI } url = "ws" + url @@ -115,7 +120,7 @@ func wsDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, route } if req, e := Upgrade(w, r, resHeader); e != nil { - return errors.Join(ErrResDoFail, e) + return ErrResDoFail } else { defer req.Close() @@ -126,7 +131,7 @@ func wsDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, route chosenBack.Disable() } logger.Error(`E:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT))) - return errors.Join(ErrCopy, e) + return ErrCopy } case e := <-copyWsMsg(conn, req, blocksi): if e != nil { @@ -134,7 +139,7 @@ func wsDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, route chosenBack.Disable() } logger.Error(`E:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT))) - return errors.Join(ErrCopy, e) + return ErrCopy } case <-ctx.Done(): } -- 2.39.2