- setting... 将会给backs默认值
- backs: [] 后端
- name: string 后端名称,将在日志中显示
- - to: string 后端地址,例`s://www.baidu.com`,会根据客户端自动添加http or ws在地址前
+ - to: string 后端地址,说明如下:
+ - 含有`://`时,例`s://www.baidu.com`,会根据客户端自动添加http or ws在地址前
+ - 不含`://`时,将会尝试解析成本地文件
- weight: int 权重,按routes中的全部back的权重比分配,当权重为0时,将停止新请求的进入
- alwaysUp: bool 总是在线, 当只有一个后端时,默认为true
- setting...
"github.com/qydysky/front/dealer"
filiter "github.com/qydysky/front/filiter"
+ component2 "github.com/qydysky/part/component2"
pctx "github.com/qydysky/part/ctx"
pslice "github.com/qydysky/part/slice"
pweb "github.com/qydysky/part/web"
return
}
- var e error
- if strings.ToLower((r.Header.Get("Upgrade"))) == "websocket" {
- e = wsDealer(r.Context(), w, r, routePath, backIs, logger, t.BlocksI)
- } else {
- e = httpDealer(r.Context(), w, r, routePath, backIs, logger, t.BlocksI)
+ var e error = ErrAllBacksFail
+
+ type reqDealer interface {
+ Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error
+ }
+
+ for i := 0; i < len(backIs); i++ {
+ if !backIs[i].IsLive() {
+ continue
+ }
+
+ if !strings.Contains(backIs[i].To, "://") {
+ e = component2.Get[reqDealer]("local").Deal(r.Context(), w, r, routePath, backIs[i], logger, t.BlocksI)
+ } else if strings.ToLower((r.Header.Get("Upgrade"))) == "websocket" {
+ e = component2.Get[reqDealer]("ws").Deal(r.Context(), w, r, routePath, backIs[i], logger, t.BlocksI)
+ } else {
+ e = component2.Get[reqDealer]("http").Deal(r.Context(), w, r, routePath, backIs[i], logger, t.BlocksI)
+ }
}
+
if e != nil {
w.Header().Add(header+"Error", e.Error())
if errors.Is(e, ErrHeaderCheckFail) || errors.Is(e, ErrBodyCheckFail) {
_ "unsafe"
"github.com/qydysky/front/utils"
+ component2 "github.com/qydysky/part/component2"
pslice "github.com/qydysky/part/slice"
)
-func httpDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, backs []*Back, logger Logger, blocksi pslice.BlocksI[byte]) error {
+func init() {
+ type I interface {
+ Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error
+ }
+ if e := component2.Register[I]("http", httpDealer{}); e != nil {
+ panic(e)
+ }
+}
+
+type httpDealer struct{}
+func (httpDealer) Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error {
var (
- opT = time.Now()
- resp *http.Response
- chosenBack *Back
- logFormat = "%v %v%v > %v http %v %v %v"
+ opT = time.Now()
+ resp *http.Response
+ logFormat = "%v %v%v > %v http %v %v %v"
)
- for i := 0; i < len(backs) && resp == nil; i++ {
- if !backs[i].IsLive() {
- continue
- }
- chosenBack = backs[i]
-
- url := chosenBack.To
- if chosenBack.PathAdd() {
- url += r.RequestURI
- }
+ url := chosenBack.To
+ if chosenBack.PathAdd() {
+ url += r.RequestURI
+ }
- url = "http" + url
+ url = "http" + url
- url = dealUri(url, chosenBack.getDealerReqUri())
+ url = dealUri(url, chosenBack.getDealerReqUri())
- req, e := http.NewRequestWithContext(ctx, r.Method, url, r.Body)
- if e != nil {
- return ErrReqCreFail
- }
+ req, e := http.NewRequestWithContext(ctx, r.Method, url, r.Body)
+ if e != nil {
+ return ErrReqCreFail
+ }
- if e := copyHeader(r.Header, req.Header, chosenBack.getDealerReqHeader()); e != nil {
- logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
- return ErrDealReqHeader
- }
+ if e := copyHeader(r.Header, req.Header, chosenBack.getDealerReqHeader()); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
+ return ErrDealReqHeader
+ }
- customTransport := http.DefaultTransport.(*http.Transport).Clone()
+ customTransport := http.DefaultTransport.(*http.Transport).Clone()
- customTransport.TLSClientConfig = &tls.Config{
- InsecureSkipVerify: chosenBack.getInsecureSkipVerify(),
- }
+ customTransport.TLSClientConfig = &tls.Config{
+ InsecureSkipVerify: chosenBack.getInsecureSkipVerify(),
+ }
- if cer, err := chosenBack.getVerifyPeerCer(); err == nil {
- pool := x509.NewCertPool()
- if pool.AppendCertsFromPEM(cer) {
- customTransport.TLSClientConfig.InsecureSkipVerify = true
- customTransport.TLSClientConfig.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) (e error) {
- if len(rawCerts) == 0 {
- return ErrCerVerify
- }
- if serCer, err := x509.ParseCertificate(rawCerts[0]); err != nil {
- return err
- } else if _, err = serCer.Verify(x509.VerifyOptions{Intermediates: pool, Roots: pool}); err != nil {
- return err
- }
- return
+ if cer, err := chosenBack.getVerifyPeerCer(); err == nil {
+ pool := x509.NewCertPool()
+ if pool.AppendCertsFromPEM(cer) {
+ customTransport.TLSClientConfig.InsecureSkipVerify = true
+ customTransport.TLSClientConfig.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) (e error) {
+ if len(rawCerts) == 0 {
+ return ErrCerVerify
+ }
+ if serCer, err := x509.ParseCertificate(rawCerts[0]); err != nil {
+ return err
+ } else if _, err = serCer.Verify(x509.VerifyOptions{Intermediates: pool, Roots: pool}); err != nil {
+ return err
}
- } else {
- logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", ErrCerVerify, time.Since(opT)))
+ return
}
- } else if err != ErrEmptyVerifyPeerCerByte {
- logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", err, time.Since(opT)))
+ } else {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", ErrCerVerify, time.Since(opT)))
}
+ } else if err != ErrEmptyVerifyPeerCerByte {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", err, time.Since(opT)))
+ }
- client := http.Client{
- Transport: customTransport,
- CheckRedirect: func(req *http.Request, via []*http.Request) error {
- return ErrRedirect
- },
- }
+ client := http.Client{
+ Transport: customTransport,
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ return ErrRedirect
+ },
+ }
- resp, e = client.Do(req)
- if e != nil && !errors.Is(e, ErrRedirect) && !errors.Is(e, context.Canceled) {
- logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", e, time.Since(opT)))
- chosenBack.Disable()
- resp = nil
- }
+ resp, e = client.Do(req)
+ if e != nil && !errors.Is(e, ErrRedirect) && !errors.Is(e, context.Canceled) {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "Err", e, time.Since(opT)))
+ chosenBack.Disable()
+ resp = nil
+ }
- if chosenBack.getErrToSec() != 0 && time.Since(opT).Seconds() > chosenBack.getErrToSec() {
- logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", ErrResTO, time.Since(opT)))
- chosenBack.Disable()
- resp = nil
- }
+ if chosenBack.getErrToSec() != 0 && time.Since(opT).Seconds() > chosenBack.getErrToSec() {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", ErrResTO, time.Since(opT)))
+ chosenBack.Disable()
+ resp = nil
}
if chosenBack == nil {
--- /dev/null
+package front
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+ _ "unsafe"
+
+ component2 "github.com/qydysky/part/component2"
+ pfile "github.com/qydysky/part/file"
+ pslice "github.com/qydysky/part/slice"
+)
+
+func init() {
+ type I interface {
+ Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error
+ }
+ if e := component2.Register[I]("local", localDealer{}); e != nil {
+ panic(e)
+ }
+}
+
+type localDealer struct{}
+
+func (localDealer) Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error {
+ var (
+ opT = time.Now()
+ logFormat = "%v %v%v > %v local %v %v %v"
+ )
+
+ url := chosenBack.To
+ if chosenBack.PathAdd() {
+ url += r.RequestURI
+ }
+
+ if !pfile.New(url, 0, true).IsExist() {
+ return ErrReqDoFail
+ }
+
+ if e := copyHeader(http.Header{}, w.Header(), chosenBack.getDealerResHeader()); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, "BLOCK", e, time.Since(opT)))
+ return ErrDealResHeader
+ }
+
+ logger.Debug(`T:`, fmt.Sprintf(logFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, r.Method, r.RequestURI, time.Since(opT)))
+
+ http.ServeFile(w, r.WithContext(ctx), url)
+ return nil
+}
"github.com/gorilla/websocket"
utils "github.com/qydysky/front/utils"
+ component2 "github.com/qydysky/part/component2"
pctx "github.com/qydysky/part/ctx"
pslice "github.com/qydysky/part/slice"
"golang.org/x/net/proxy"
)
-func wsDealer(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, backs []*Back, logger Logger, blocksi pslice.BlocksI[byte]) error {
+func init() {
+ type I interface {
+ Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error
+ }
+ if e := component2.Register[I]("ws", wsDealer{}); e != nil {
+ panic(e)
+ }
+}
+
+type wsDealer struct{}
+
+func (wsDealer) Deal(ctx context.Context, w http.ResponseWriter, r *http.Request, routePath string, chosenBack *Back, logger Logger, blocksi pslice.BlocksI[byte]) error {
var (
- opT = time.Now()
- resp *http.Response
- e error
- conn net.Conn
- chosenBack *Back
- errFormat = "%v %v > %v > %v ws %v %v"
+ opT = time.Now()
+ resp *http.Response
+ conn net.Conn
+ errFormat = "%v %v > %v > %v ws %v %v"
)
- for i := 0; i < len(backs) && (resp == nil || conn == nil); i++ {
- if !backs[i].IsLive() {
- continue
- }
- chosenBack = backs[i]
+ // for i := 0; i < len(backs) && (resp == nil || conn == nil); i++ {
+ // if !backs[i].IsLive() {
+ // continue
+ // }
+ // chosenBack = backs[i]
- url := chosenBack.To
- if chosenBack.PathAdd() {
- url += r.RequestURI
- }
+ url := chosenBack.To
+ if chosenBack.PathAdd() {
+ url += r.RequestURI
+ }
- url = "ws" + url
+ url = "ws" + url
- url = dealUri(url, chosenBack.getDealerReqUri())
+ url = dealUri(url, chosenBack.getDealerReqUri())
- reqHeader := make(http.Header)
+ reqHeader := make(http.Header)
- if e := copyHeader(r.Header, reqHeader, chosenBack.getDealerReqHeader()); e != nil {
- logger.Warn(`W:`, fmt.Sprintf(errFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
- return ErrDealReqHeader
- }
+ if e := copyHeader(r.Header, reqHeader, chosenBack.getDealerReqHeader()); e != nil {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ return ErrDealReqHeader
+ }
- conn, resp, e = DialContext(ctx, url, reqHeader, chosenBack)
- if e != nil && !errors.Is(e, context.Canceled) {
- logger.Warn(`W:`, fmt.Sprintf(errFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
- chosenBack.Disable()
- conn = nil
- resp = nil
- }
+ var e error
+ conn, resp, e = DialContext(ctx, url, reqHeader, chosenBack)
+ if e != nil && !errors.Is(e, context.Canceled) {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, e, time.Since(opT)))
+ chosenBack.Disable()
+ conn = nil
+ resp = nil
+ }
- if chosenBack.getErrToSec() != 0 && time.Since(opT).Seconds() > chosenBack.getErrToSec() {
- logger.Warn(`W:`, fmt.Sprintf(errFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrResTO, time.Since(opT)))
- chosenBack.Disable()
- conn.Close()
- conn = nil
- resp = nil
- }
+ if chosenBack.getErrToSec() != 0 && time.Since(opT).Seconds() > chosenBack.getErrToSec() {
+ logger.Warn(`W:`, fmt.Sprintf(errFormat, r.RemoteAddr, chosenBack.route.config.Addr, routePath, chosenBack.Name, ErrResTO, time.Since(opT)))
+ chosenBack.Disable()
+ conn.Close()
+ conn = nil
+ resp = nil
}
+ // }
if chosenBack == nil {
return ErrAllBacksFail