- *tls*: {} 启用tls
- *pub*: string 公钥pem路径
- *key*: string 私钥pem路径
-- routes: [] 路由, 可以动态增加/删除
+- routes: [] 路由
- path: string 路径
- pathAdd: bool 将客户端访问的路径附加在path上 例:/api/req => /ws => /ws/api/req
- rollRule: string 可选
- `lastResDur_MinFirst`(上次响应时间较短的优先)
- `resDur_MinFirst`(总响应时间较短的优先)
- (使用rand.Shuffle随机,默认)
- - dealer 将会附加到每个backs前
- - backs: [] 后端, 可以动态增加/删除
+ - reqBody: 请求后端前,请求数据过滤器
+ - action: string 可选`access`、`deny`。
+ - reqSize:string 限定请求数据大小,默认为`1M`
+ - matchExp: string `access`时如不匹配将结束请求。`deny`时如匹配将结束请求。
+ - setting... 将会给backs默认值
+ - backs: [] 后端
- name: string 后端名称,将在日志中显示
- to: string 后端地址,例`s://www.baidu.com`,会根据客户端自动添加http or ws在地址前
- weight: int 权重,按routes中的全部back的权重比分配,当权重为0时,将停止新请求的进入
- - dealer
+ - setting...
-dealer:
+setting:
- 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 具体处理哪个头
- - matchExp: string `access`时不匹配将结束请求。`deny`时匹配将结束请求。`replace`时结合value进行替换
- - value: string `replace`时结合matchExp进行替换。add时将附加值。`set`时将覆盖值。
-- resHeader: [] 返回后端的响应前,请求头处理器, 可以动态增加/删除
- - action: string 可选`access`、`deny`、`add`、`del`、`set`。
- - key: string 具体处理哪个头
- - matchExp: string `access`时不匹配将结束请求。`deny`时匹配将结束请求。`replace`时结合value进行替换
- - value: string `replace`时结合matchExp进行替换。`add`时将附加值。`set`时将覆盖值。
-- reqBody: [] 请求后端前,请求数据过滤器, 可以动态增加/删除
- - action: string 可选`access`、`deny`。
- - reqSize:string 限定请求数据大小,默认为`1M`
- - matchExp: string `access`时如不匹配将结束请求。`deny`时如匹配将结束请求。
+
+- filiter:
+ - reqUri: 请求后端前,请求路径过滤器
+ - accessRule: 布尔表达式,为true时才通过
+ - items: map[string]string
+ - id: matchExp
+ - reqHeader: 请求后端前,请求头处理器
+ - accessRule: 布尔表达式,为true时才通过
+ - items: map[string]{}
+ - id:
+ - key: string header头
+ - matchExp: string
+ - resHeader: 返回后端的响应前,请求头处理器
+ - accessRule: 布尔表达式,为true时才通过
+ - items: map[string]{}
+ - id:
+ - key: string header头
+ - matchExp: string
+- dealer:
+ - reqHeader:[] 请求后端前,请求头处理器
+ - action: string 可选`replace`、`add`、`del`、`set`。
+ - key: string 具体处理哪个头
+ - matchExp: string `replace`时结合value进行替换
+ - value: string `replace`时结合matchExp进行替换。add时将附加值。`set`时将覆盖值。
+ - resHeader:[] 返回后端的响应前,请求头处理器
+ - action: string 可选`add`、`del`、`set`。
+ - key: string 具体处理哪个头
+ - matchExp: string `replace`时结合value进行替换
+ - value: string `replace`时结合matchExp进行替换。`add`时将附加值。`set`时将覆盖值。
package front
import (
- "bytes"
"context"
"crypto/tls"
"errors"
"fmt"
- "io"
"net"
"net/http"
- "regexp"
+ "os"
"strings"
"sync"
"time"
- "github.com/dustin/go-humanize"
+ "github.com/qydysky/front/dealer"
+ filiter "github.com/qydysky/front/filiter"
pctx "github.com/qydysky/part/ctx"
- pio "github.com/qydysky/part/io"
pslice "github.com/qydysky/part/slice"
pweb "github.com/qydysky/part/web"
)
func (t *Config) Run(ctx context.Context, logger Logger) {
ctx, done := pctx.WithWait(ctx, 0, time.Minute)
- defer done()
+ defer func() {
+ _ = done()
+ }()
var matchfunc func(path string) (func(w http.ResponseWriter, r *http.Request), bool)
switch t.MatchRule {
logger.Info(`I:`, fmt.Sprintf("%v > %v", t.Addr, k))
t.routeMap.Store(k, route)
+ var logFormat = "%v%v %v %v"
+
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))
+ if len(r.RequestURI) > 8000 {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "BLOCK", ErrUriTooLong))
+ w.Header().Add(header+"Error", ErrUriTooLong.Error())
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ if ok, e := route.Filiter.ReqUri.Match(r); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "Err", e))
+ } else if !ok {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "BLOCK", ErrPatherCheckFail))
w.Header().Add(header+"Error", ErrPatherCheckFail.Error())
w.WriteHeader(http.StatusForbidden)
return
}
- if !HeaderMatchs(route.ReqHeader, r) {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v %v %v", route.config.Addr, route.Path, r.RequestURI, ErrHeaderCheckFail))
+ if ok, e := route.Filiter.ReqHeader.Match(r.Header); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "Err", e))
+ } else if !ok {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "BLOCK", ErrHeaderCheckFail))
w.Header().Add(header+"Error", ErrHeaderCheckFail.Error())
w.WriteHeader(http.StatusForbidden)
return
}
+ if ok, e := route.ReqBody.Match(r); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "Err", e))
+ } else if !ok {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "BLOCK", ErrBodyCheckFail))
+ w.Header().Add(header+"Error", ErrBodyCheckFail.Error())
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+
var backIs []*Back
if t, e := r.Cookie("_psign_" + cookie); e == nil {
- if backP, ok := route.backMap.Load(t.Value); ok && HeaderMatchs(backP.(*Back).ReqHeader, r) {
- backP.(*Back).cloneDealer()
- for i := 0; i < backP.(*Back).Weight; i++ {
- backIs = append(backIs, backP.(*Back))
+ if backP, aok := route.backMap.Load(t.Value); aok {
+
+ if ok, e := backP.(*Back).getFiliterReqUri().Match(r); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "Err", e))
+ } else if ok {
+ aok = false
+ }
+
+ if ok, e := backP.(*Back).getFiliterReqHeader().Match(r.Header); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "Err", e))
+ } else if ok {
+ aok = false
+ }
+
+ if aok {
+ for i := 0; i < backP.(*Back).Weight; i++ {
+ backIs = append(backIs, backP.(*Back))
+ }
}
}
}
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.RequestURI, ErrNoRoute))
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, route.config.Addr, route.Path, "BLOCK", ErrNoRoute))
w.Header().Add(header+"Error", ErrNoRoute.Error())
w.WriteHeader(http.StatusNotFound)
return
w.Header().Add(header+"Error", e.Error())
if errors.Is(e, ErrHeaderCheckFail) || errors.Is(e, ErrBodyCheckFail) {
w.WriteHeader(http.StatusForbidden)
+ } else if errors.Is(e, ErrAllBacksFail) {
+ w.WriteHeader(http.StatusBadGateway)
+ os.Exit(0)
} else {
t.routeP.GetConn(r).Close()
}
})
}
- var del = func(k string, route *Route, logger Logger) {
+ var del = func(k string, _ *Route, logger Logger) {
logger.Info(`I:`, fmt.Sprintf("%v x %v", t.Addr, k))
t.routeMap.Delete(k)
t.routeP.Store(k, nil)
config *Config `json:"-"`
Path string `json:"path"`
- PathAdd bool `json:"pathAdd"`
- RollRule string `json:"rollRule"`
- Dealer
+ PathAdd bool `json:"pathAdd"`
+ RollRule string `json:"rollRule"`
+ ReqBody filiter.Body `json:"reqBody"`
+ Setting
backMap sync.Map `json:"-"`
Backs []Back `json:"backs"`
func (t *Route) FiliterBackByRequest(r *http.Request) []*Back {
var backLink []*Back
for i := 0; i < len(t.Backs); i++ {
- if HeaderMatchs(t.Backs[i].ReqHeader, r) {
- t.Backs[i].cloneDealer()
+ if ok, e := t.Backs[i].getFiliterReqHeader().Match(r.Header); ok && e == nil {
+ t.Backs[i].route = t
for k := 0; k < t.Backs[i].Weight; k++ {
backLink = append(backLink, &t.Backs[i])
}
To string `json:"to"`
Weight int `json:"weight"`
- Splicing int `json:"-"`
- PathAdd bool `json:"-"`
- Dealer
- tmp Dealer `json:"-"`
+ Setting
}
-func (t *Back) cloneDealer() {
- t.PathAdd = t.route.PathAdd
- if t.Splicing == 0 {
- t.Splicing = t.route.Splicing
- }
+func (t *Back) Splicing() int {
+ return t.route.Splicing
+}
+func (t *Back) PathAdd() bool {
+ return t.route.PathAdd
+}
+func (t *Back) getErrBanSec() int {
if t.ErrBanSec == 0 {
- t.ErrBanSec = t.route.ErrBanSec
+ return t.route.ErrBanSec
+ } else {
+ return t.ErrBanSec
}
+}
+func (t *Back) getErrToSec() float64 {
if t.ErrToSec == 0 {
- t.ErrToSec = t.route.ErrToSec
+ return t.route.ErrToSec
+ } else {
+ return t.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...)
}
-
-func (t *Back) Id() string {
- return fmt.Sprintf("%p", t)
+func (t *Back) getFiliterReqHeader() *filiter.Header {
+ if !t.Filiter.ReqHeader.Valid() {
+ return &t.route.Filiter.ReqHeader
+ } else {
+ return &t.Filiter.ReqHeader
+ }
}
-
-func PatherMatchs(matchPath []Header, r *http.Request) bool {
- matchs := len(matchPath) - 1
- for ; matchs >= 0; matchs -= 1 {
- if !matchPath[matchs].Match(r.RequestURI) {
- break
- }
+func (t *Back) getFiliterReqUri() *filiter.Uri {
+ if !t.Filiter.ReqUri.Valid() {
+ return &t.route.Filiter.ReqUri
+ } else {
+ return &t.Filiter.ReqUri
}
- return matchs == -1
}
-
-func HeaderMatchs(matchHeader []Header, r *http.Request) bool {
- matchs := len(matchHeader) - 1
- for ; matchs >= 0; matchs -= 1 {
- if !matchHeader[matchs].Match(r.Header.Get(matchHeader[matchs].Key)) {
- break
- }
+func (t *Back) getFiliterResHeader() *filiter.Header {
+ if !t.Filiter.ResHeader.Valid() {
+ return &t.route.Filiter.ResHeader
+ } else {
+ return &t.Filiter.ResHeader
}
- return matchs == -1
}
-func BodyMatchs(matchBody []Body, r *http.Request) (reader io.ReadCloser, e error) {
- reader = r.Body
- for i := 0; i < len(matchBody); i++ {
- reader, e = matchBody[i].Match(reader)
- if e != nil {
- return
- }
- }
- return
+func (t *Back) Id() string {
+ return fmt.Sprintf("%p", t)
}
func (t *Back) be(opT time.Time) {
}
func (t *Back) Disable() {
- if t.ErrBanSec == 0 {
- t.ErrBanSec = 1
+ tmp := t.getErrBanSec()
+ if tmp == 0 {
+ tmp = 1
}
t.lock.Lock()
defer t.lock.Unlock()
t.disableC += 1
- t.upT = time.Now().Add(time.Second * time.Duration(t.ErrBanSec))
-}
-
-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"`
-}
-
-type Header struct {
- Action string `json:"action"`
- Key string `json:"key"`
- MatchExp string `json:"matchExp"`
- Value string `json:"value"`
-}
-
-func (t *Header) Match(value string) bool {
- if t.Action != "access" && t.Action != "deny" {
- return true
- }
- if t.MatchExp != "" {
- if exp, e := regexp.Compile(t.MatchExp); e != nil || !exp.MatchString(value) {
- return t.Action == "deny"
- }
- }
- return t.Action == "access"
-}
-
-type Body struct {
- Action string `json:"action"`
- ReqSize string `json:"reqSize"`
- MatchExp string `json:"matchExp"`
+ t.upT = time.Now().Add(time.Second * time.Duration(tmp))
}
-func (t *Body) Match(r io.ReadCloser) (d io.ReadCloser, err error) {
- if exp, e := regexp.Compile(t.MatchExp); e == nil {
- if t.ReqSize == "" {
- t.ReqSize = "1M"
- }
-
- var (
- size, err = humanize.ParseBytes(t.ReqSize)
- buf = make([]byte, size)
- n int
- )
-
- if err != nil {
- return nil, err
- }
-
- for n < int(size) && err == nil {
- var nn int
- nn, err = r.Read(buf[n:])
- n += nn
- }
- if n >= int(size) {
- return nil, errors.New("body overflow")
- } else if err != nil && !errors.Is(err, io.EOF) {
- return nil, err
- }
- buf = buf[:n]
-
- switch t.Action {
- case "access":
- if !exp.Match(buf) {
- return nil, errors.New("body deny")
- }
- case "deny":
- if exp.Match(buf) {
- return nil, errors.New("body deny")
- }
- }
-
- return pio.RWC{
- R: bytes.NewReader(buf).Read,
- C: func() error { return nil },
- }, nil
- } else {
- return nil, e
- }
+type Setting struct {
+ ErrToSec float64 `json:"errToSec"`
+ Splicing int `json:"splicing"`
+ ErrBanSec int `json:"errBanSec"`
+ Filiter filiter.Filiter `json:"filiter"`
+ Dealer dealer.Dealer `json:"dealer"`
}
--- /dev/null
+package dealer
+
+type Dealer struct {
+ ReqHeader []HeaderDealer `json:"reqHeader"`
+ ResHeader []HeaderDealer `json:"resHeader"`
+}
--- /dev/null
+package dealer
+
+import "github.com/qydysky/front/filiter"
+
+type HeaderDealer struct {
+ filiter.HeaderFiliter
+ Action string `json:"action"`
+ Value string `json:"value"`
+}
--- /dev/null
+package filiter
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "net/http"
+ "regexp"
+
+ "github.com/dustin/go-humanize"
+ pio "github.com/qydysky/part/io"
+)
+
+type Body struct {
+ Action string `json:"action"`
+ ReqSize string `json:"reqSize"`
+ MatchExp string `json:"matchExp"`
+}
+
+func (t *Body) Valid() bool {
+ return t.MatchExp != ""
+}
+
+func (t *Body) Match(r *http.Request) (ok bool, err error) {
+ if !t.Valid() {
+ return true, nil
+ }
+ if exp, e := regexp.Compile(t.MatchExp); e == nil {
+ if t.ReqSize == "" {
+ t.ReqSize = "1M"
+ }
+
+ var (
+ size, err = humanize.ParseBytes(t.ReqSize)
+ buf = make([]byte, size)
+ n int
+ )
+
+ if err != nil {
+ return false, err
+ }
+
+ for n < int(size) && err == nil {
+ var nn int
+ nn, err = r.Body.Read(buf[n:])
+ n += nn
+ }
+
+ if n >= int(size) {
+ r.Body = pio.RWC{
+ R: io.MultiReader(bytes.NewReader(buf), r.Body).Read,
+ C: r.Body.Close,
+ }
+ return false, errors.New("body overflow")
+ } else if err != nil && !errors.Is(err, io.EOF) {
+ r.Body.Close()
+ return false, err
+ }
+ buf = buf[:n]
+
+ switch t.Action {
+ case "access":
+ if !exp.Match(buf) {
+ r.Body.Close()
+ return false, nil
+ }
+ case "deny":
+ if exp.Match(buf) {
+ r.Body.Close()
+ return false, nil
+ }
+ }
+
+ r.Body = pio.RWC{
+ R: bytes.NewReader(buf).Read,
+ C: r.Body.Close,
+ }
+
+ return true, nil
+ } else {
+ return false, e
+ }
+}
--- /dev/null
+package filiter
+
+type Filiter struct {
+ ReqHeader Header `json:"reqHeader"`
+ ReqUri Uri `json:"reqUri"`
+ ResHeader Header `json:"resHeader"`
+}
--- /dev/null
+package filiter
+
+import (
+ "net/http"
+ "regexp"
+
+ boolS "github.com/qydysky/part/boolS"
+)
+
+type Header struct {
+ AccessRule string `json:"accessRule"`
+ Items map[string]HeaderFiliter `json:"items"`
+}
+
+func (t *Header) Valid() bool {
+ return t.AccessRule != "" && len(t.Items) != 0
+}
+
+func (t *Header) Match(h http.Header) (bool, error) {
+ if !t.Valid() {
+ return true, nil
+ }
+ m := map[string]func() bool{}
+ for k, v := range t.Items {
+ m[k] = func() bool { return v.Match(h) }
+ }
+ return boolS.New(t.AccessRule, m).Check()
+}
+
+type HeaderFiliter struct {
+ Key string `json:"key"`
+ MatchExp string `json:"matchExp"`
+}
+
+func (t *HeaderFiliter) Match(h http.Header) bool {
+ if t.MatchExp != "" {
+ if exp, e := regexp.Compile(t.MatchExp); e != nil {
+ return false
+ } else {
+ return exp.MatchString(h.Get(t.Key))
+ }
+ }
+ return true
+}
--- /dev/null
+package filiter
+
+import (
+ "log"
+ "net/http"
+ "regexp"
+
+ boolS "github.com/qydysky/part/boolS"
+)
+
+type Uri struct {
+ AccessRule string `json:"accessRule"`
+ Items map[string]string `json:"items"`
+}
+
+func (t *Uri) Valid() bool {
+ return t.AccessRule != "" && len(t.Items) != 0
+}
+
+func (t *Uri) Match(r *http.Request) (bool, error) {
+ if !t.Valid() {
+ return true, nil
+ }
+ m := map[string]func() bool{}
+ for k, v := range t.Items {
+ m[k] = func() bool {
+ if exp, e := regexp.Compile(v); e != nil {
+ log.Default().Println(e)
+ return false
+ } else {
+ return exp.MatchString(r.RequestURI)
+ }
+ }
+ }
+ return boolS.New(t.AccessRule, m).Check()
+}
require (
github.com/dustin/go-humanize v1.0.1
github.com/gorilla/websocket v1.5.1
- github.com/qydysky/part v0.28.20240310094912
- golang.org/x/net v0.18.0
+ github.com/qydysky/part v0.28.20240321070228
+ golang.org/x/net v0.22.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
- github.com/google/uuid v1.4.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
- github.com/tklauser/go-sysconf v0.3.12 // indirect
- github.com/tklauser/numcpus v0.6.1 // indirect
- github.com/yusufpapurcu/wmi v1.2.3 // indirect
- golang.org/x/sys v0.14.0 // indirect
+ github.com/tklauser/go-sysconf v0.3.13 // indirect
+ github.com/tklauser/numcpus v0.7.0 // indirect
+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
+ golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+
+// replace github.com/qydysky/part => ../part
-github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
-github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
-github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
-github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
+github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
+github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/qydysky/part v0.28.20240310094912 h1:n4pKPS3q6NmVkI76n447a/UM3XWXRAaLpZDzOgAo798=
-github.com/qydysky/part v0.28.20240310094912/go.mod h1:8Y4MrasGC0BLEM71QY/MuP2jl+v5b0Y+rqox3qJu97c=
+github.com/qydysky/part v0.28.20240321070228 h1:IGVEukobwFJ4Y629X82xmXJT+FLYeBqPh8peuoY51C4=
+github.com/qydysky/part v0.28.20240321070228/go.mod h1:XytV5dI1Y7+qvjhsa2TMvi55RBZQQf0LCDYQ1kUCYqM=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
-github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
-github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
-github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
-github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
-golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
-golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
+github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
+github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
+github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
-golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
-golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
-lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
-modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
-modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
-modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
-modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
-modernc.org/libc v1.34.4 h1:r9+5s4wNeoCsB8CuJE67UB4N07ernbvrcry9O3MLWtQ=
-modernc.org/libc v1.34.4/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
+modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8=
+modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
+modernc.org/libc v1.45.0 h1:qmAJZf9tYFqK/SFSFqpBc9uHWGsvoYWtRcMQdG+JEfM=
+modernc.org/libc v1.45.0/go.mod h1:YkRHLoN4L70OdO1cVmM83KZhRbRvsc3XogfVzbTXBwE=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
-modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
-modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8=
-modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
+modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE=
+modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
opT = time.Now()
resp *http.Response
chosenBack *Back
+ logFormat = "%v%v > %v http %v %v %v"
)
for 0 < len(backs) && resp == nil {
}
url := chosenBack.To
- if chosenBack.PathAdd {
+ if chosenBack.PathAdd() {
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 ErrBodyCheckFail
- }
-
- req, e := http.NewRequestWithContext(ctx, r.Method, url, reader)
+ req, e := http.NewRequestWithContext(ctx, r.Method, url, r.Body)
if e != nil {
return ErrReqCreFail
}
- if e := copyHeader(r.Header, req.Header, chosenBack.tmp.ReqHeader); 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 e
+ if e := copyHeader(r.Header, req.Header, chosenBack.Setting.Dealer.ReqHeader); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
+ return ErrDealReqHeader
}
client := http.Client{
}
resp, e = client.Do(req)
if e != nil && !errors.Is(e, ErrRedirect) && !errors.Is(e, context.Canceled) {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v http %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
chosenBack.Disable()
resp = nil
}
- if chosenBack.ErrToSec != 0 && time.Since(opT).Seconds() > chosenBack.ErrToSec {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v http 超时响应 %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, time.Since(opT)))
+ if chosenBack.getErrToSec() != 0 && time.Since(opT).Seconds() > chosenBack.getErrToSec() {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", ErrResTO, time.Since(opT)))
chosenBack.Disable()
resp = nil
}
}
if resp == nil {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v http %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrAllBacksFail, time.Since(opT)))
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", ErrAllBacksFail, time.Since(opT)))
return ErrAllBacksFail
}
- logger.Debug(`T:`, fmt.Sprintf("%v > %v > %v http ok %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, time.Since(opT)))
+ if ok, e := chosenBack.getFiliterResHeader().Match(resp.Header); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", e, time.Since(opT)))
+ } else if !ok {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", ErrHeaderCheckFail, time.Since(opT)))
+ w.Header().Add(header+"Error", ErrHeaderCheckFail.Error())
+ w.WriteHeader(http.StatusForbidden)
+ return ErrHeaderCheckFail
+ }
+
+ logger.Debug(`T:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, r.Method, r.RequestURI, time.Since(opT)))
if chosenBack.route.RollRule != `` {
chosenBack.be(opT)
defer chosenBack.ed()
}
- if chosenBack.Splicing != 0 {
+ if chosenBack.Splicing() != 0 {
cookie := &http.Cookie{
Name: "_psign_" + cookie,
Value: chosenBack.Id(),
- MaxAge: chosenBack.Splicing,
+ MaxAge: chosenBack.Splicing(),
Path: "/",
}
if validCookieDomain(r.Host) {
w.Header().Add(header+"Info", cookie+";"+chosenBack.Name)
- if e := copyHeader(resp.Header, w.Header(), chosenBack.tmp.ResHeader); 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 e
+ if e := copyHeader(resp.Header, w.Header(), chosenBack.Setting.Dealer.ResHeader); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
+ return ErrDealResHeader
}
w.WriteHeader(resp.StatusCode)
defer resp.Body.Close()
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)))
+ logger.Error(`E:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
chosenBack.Disable()
return ErrCopy
} else {
defer put()
if _, e = io.CopyBuffer(w, resp.Body, tmpbuf); e != nil {
- logger.Error(`E:`, fmt.Sprintf("%v > %v > %v http %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ logger.Error(`E:`, fmt.Sprintf(logFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
if !errors.Is(e, context.Canceled) {
chosenBack.Disable()
}
"time"
_ "unsafe"
+ "github.com/qydysky/front/dealer"
pctx "github.com/qydysky/part/ctx"
pweb "github.com/qydysky/part/web"
)
WriteTimeout: time.Second * time.Duration(10),
})
defer s.Shutdown()
+
s.Handle(map[string]func(http.ResponseWriter, *http.Request){
- `/`: func(w http.ResponseWriter, _ *http.Request) {
- _, _ = w.Write([]byte("ok"))
+ `/`: func(w http.ResponseWriter, r *http.Request) {
+ if strings.ToLower((r.Header.Get("Upgrade"))) == "websocket" {
+ conn, _ := Upgrade(w, r, http.Header{
+ "Upgrade": []string{"websocket"},
+ "Connection": []string{"upgrade"},
+ "Sec-Websocket-Accept": []string{computeAcceptKey(r.Header.Get("Sec-WebSocket-Key"))},
+ })
+ conn.Close()
+ } else {
+ _, _ = io.Copy(io.Discard, r.Body)
+ r.Body.Close()
+ _, _ = w.Write([]byte("ok"))
+ }
},
})
<-ctx1.Done()
return md5k, nil
}
-func copyHeader(s, t http.Header, app []Header) error {
+func copyHeader(s, t http.Header, app []dealer.HeaderDealer) error {
sm := (map[string][]string)(s)
tm := (map[string][]string)(t)
for k, v := range sm {
}
for _, v := range app {
switch v.Action {
- case `deny`:
- if va := t.Get(v.Key); va != "" {
- if exp, e := regexp.Compile(v.MatchExp); e == nil && exp.MatchString(va) {
- return ErrHeaderCheckFail
- }
- }
- case `access`:
- if va := t.Get(v.Key); va != "" {
- if exp, e := regexp.Compile(v.MatchExp); e != nil || !exp.MatchString(va) {
- return ErrHeaderCheckFail
- }
- } else {
- return ErrHeaderCheckFail
- }
case `replace`:
if va := t.Get(v.Key); va != "" {
t.Set(v.Key, regexp.MustCompile(v.MatchExp).ReplaceAllString(va, v.Value))
ErrReqCreFail = errors.New("ErrReqCreFail")
ErrReqDoFail = errors.New("ErrReqDoFail")
ErrResDoFail = errors.New("ErrResDoFail")
+ ErrResTO = errors.New("ErrResTO")
+ ErrUriTooLong = errors.New("ErrUriTooLong")
ErrPatherCheckFail = errors.New("ErrPatherCheckFail")
ErrHeaderCheckFail = errors.New("ErrHeaderCheckFail")
ErrBodyCheckFail = errors.New("ErrBodyCheckFail")
ErrAllBacksFail = errors.New("ErrAllBacksFail")
ErrBackFail = errors.New("ErrBackFail")
ErrNoRoute = errors.New("ErrNoRoute")
+ ErrDealReqHeader = errors.New("ErrDealReqHeader")
+ ErrDealResHeader = errors.New("ErrDealResHeader")
)
var (
opT = time.Now()
resp *http.Response
+ e error
conn net.Conn
chosenBack *Back
+ errFormat = "%v > %v > %v ws %v %v"
)
for 0 < len(backs) && (resp == nil || conn == nil) {
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 ErrBodyCheckFail
- }
-
url := chosenBack.To
- if chosenBack.PathAdd {
+ if chosenBack.PathAdd() {
url += r.RequestURI
}
reqHeader := make(http.Header)
- if e := copyHeader(r.Header, reqHeader, chosenBack.tmp.ReqHeader); 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 e
+ if e := copyHeader(r.Header, reqHeader, chosenBack.Setting.Dealer.ReqHeader); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ return ErrDealReqHeader
}
conn, resp, e = DialContext(ctx, url, reqHeader)
if e != nil && !errors.Is(e, context.Canceled) {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
chosenBack.Disable()
conn = nil
resp = nil
}
- if chosenBack.ErrToSec != 0 && time.Since(opT).Seconds() > chosenBack.ErrToSec {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v ws 超时响应 %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, time.Since(opT)))
+ if chosenBack.getErrToSec() != 0 && time.Since(opT).Seconds() > chosenBack.getErrToSec() {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrResTO, time.Since(opT)))
chosenBack.Disable()
conn.Close()
conn = nil
}
if resp == nil || conn == nil {
- logger.Warn(`W:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrBackFail, time.Since(opT)))
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrBackFail, time.Since(opT)))
return ErrAllBacksFail
}
return context.Canceled
}
+ if ok, e := chosenBack.getFiliterResHeader().Match(resp.Header); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ } else if !ok {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrHeaderCheckFail, time.Since(opT)))
+ w.Header().Add(header+"Error", ErrHeaderCheckFail.Error())
+ w.WriteHeader(http.StatusForbidden)
+ return ErrHeaderCheckFail
+ }
+
logger.Debug(`T:`, fmt.Sprintf("%v > %v > %v ws ok %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, time.Since(opT)))
if chosenBack.route.RollRule != `` {
defer chosenBack.ed()
}
- if chosenBack.Splicing != 0 {
+ if chosenBack.Splicing() != 0 {
cookie := &http.Cookie{
Name: "_psign_" + cookie,
Value: chosenBack.Id(),
- MaxAge: chosenBack.Splicing,
+ MaxAge: chosenBack.Splicing(),
Path: "/",
}
if validCookieDomain(r.Host) {
defer conn.Close()
resHeader := make(http.Header)
- if e := copyHeader(resp.Header, resHeader, chosenBack.tmp.ResHeader); 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 e
+ if e := copyHeader(resp.Header, resHeader, chosenBack.Setting.Dealer.ResHeader); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ return ErrDealResHeader
}
if req, e := Upgrade(w, r, resHeader); e != nil {
if !errors.Is(e, context.Canceled) {
chosenBack.Disable()
}
- logger.Error(`E:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ logger.Error(`E:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
return ErrCopy
}
case e := <-copyWsMsg(conn, req, blocksi):
if !errors.Is(e, context.Canceled) {
chosenBack.Disable()
}
- logger.Error(`E:`, fmt.Sprintf("%v > %v > %v ws %v %v", chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ logger.Error(`E:`, fmt.Sprintf(errFormat, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
return ErrCopy
}
case <-ctx.Done():
}
}
- var br *bufio.Reader
- if br == nil {
- if d.ReadBufferSize == 0 {
- d.ReadBufferSize = defaultReadBufferSize
- } else if d.ReadBufferSize < maxControlFramePayloadSize {
- // must be large enough for control frame
- d.ReadBufferSize = maxControlFramePayloadSize
- }
- br = bufio.NewReaderSize(netConn, d.ReadBufferSize)
+ if d.ReadBufferSize == 0 {
+ d.ReadBufferSize = defaultReadBufferSize
+ } else if d.ReadBufferSize < maxControlFramePayloadSize {
+ // must be large enough for control frame
+ d.ReadBufferSize = maxControlFramePayloadSize
}
+ br := bufio.NewReaderSize(netConn, d.ReadBufferSize)
if err := req.Write(netConn); err != nil {
return nil, nil, err
u.WriteBufferSize = defaultWriteBufferSize
}
u.WriteBufferSize += maxFrameHeaderSize
- if writeBuf == nil && u.WriteBufferPool == nil {
+ if u.WriteBufferPool == nil {
writeBuf = make([]byte, u.WriteBufferSize)
}
}