]> 127.0.0.1 Git - front/.git/commitdiff
1 v0.1.20241006195936
authorqydysky <qydysky@foxmail.com>
Sun, 6 Oct 2024 19:58:07 +0000 (03:58 +0800)
committerqydysky <qydysky@foxmail.com>
Sun, 6 Oct 2024 19:58:07 +0000 (03:58 +0800)
README.md
config.go
http.go
local.go [new file with mode: 0644]
ws.go

index 85a63be23656c787f5f3c3883df4d391e422ef9b..a845dc6665d84b413a6dceb150ff7c3612dd464d 100755 (executable)
--- a/README.md
+++ b/README.md
@@ -40,7 +40,9 @@ config:
     - 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...
index 8b210499ffce0a8ad50209f560159b02f225cc68..e3520c2662b5aa150b3c046ecbf1d3ea6cf57d95 100755 (executable)
--- a/config.go
+++ b/config.go
@@ -15,6 +15,7 @@ import (
 
        "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"
@@ -161,12 +162,26 @@ func (t *Config) SwapSign(ctx context.Context, logger Logger) {
                                        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) {
diff --git a/http.go b/http.go
index 854ee179b27e928fa32f0082f9327119322652bb..1311e77e69f5c1278cf87c3571c7b0dbb8b5bfa2 100644 (file)
--- a/http.go
+++ b/http.go
@@ -12,90 +12,93 @@ import (
        _ "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 {
diff --git a/local.go b/local.go
new file mode 100644 (file)
index 0000000..cd4991f
--- /dev/null
+++ b/local.go
@@ -0,0 +1,50 @@
+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
+}
diff --git a/ws.go b/ws.go
index 70875a0f1c7654159df4b9a833136dfec612b509..7fcf935ee2a1b4f614914be00591c27688930518 100644 (file)
--- a/ws.go
+++ b/ws.go
@@ -19,59 +19,70 @@ import (
 
        "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