From 7650951518a558d9864eb0ab3a882f568c6d8069 Mon Sep 17 00:00:00 2001
From: starainrt
Date: Thu, 19 Jun 2025 23:47:39 +0800
Subject: [PATCH] =?UTF-8?q?1.http=20server=E6=94=AF=E6=8C=81=E9=BB=91?=
=?UTF-8?q?=E7=99=BD=E8=B7=AF=E7=94=B1=E5=90=8D=E5=8D=95=E5=92=8C=E5=AD=90?=
=?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9=E6=8C=82=E8=BD=BD=202.http=E5=8F=8D?=
=?UTF-8?q?=E5=90=91=E4=BB=A3=E7=90=86=E6=94=AF=E6=8C=81=E5=90=8C=E4=B8=80?=
=?UTF-8?q?=E4=B8=AA=E7=AB=AF=E5=8F=A3=E6=8C=89host=E5=8C=BA=E5=88=86?=
=?UTF-8?q?=E4=B8=8D=E9=80=9A=E6=9C=8D=E5=8A=A1=EF=BC=8C=E4=BB=A5=E5=8F=8A?=
=?UTF-8?q?=E9=BB=91=E7=99=BD=E8=B7=AF=E7=94=B1=E5=90=8D=E5=8D=95=E6=94=AF?=
=?UTF-8?q?=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
httpreverse/cmd.go | 14 +-
httpreverse/reverse.go | 77 +++++--
httpreverse/route.go | 117 ++++++++++
httpreverse/service.go | 503 +++++++++++++++++++++++++++++++++++------
httpserver/cmd.go | 27 ++-
httpserver/route.go | 117 ++++++++++
httpserver/server.go | 142 ++++++++++--
netforward/cmd.go | 1 +
netforward/forward.go | 7 +-
tls/cert.go | 103 +++++++--
utils/cert.go | 14 +-
utils/cert_test.go | 2 +-
12 files changed, 984 insertions(+), 140 deletions(-)
create mode 100644 httpreverse/route.go
create mode 100644 httpserver/route.go
diff --git a/httpreverse/cmd.go b/httpreverse/cmd.go
index f757e2f..0120261 100644
--- a/httpreverse/cmd.go
+++ b/httpreverse/cmd.go
@@ -63,20 +63,26 @@ var Cmd = &cobra.Command{
starlog.Errorln(err)
os.Exit(3)
}
- reverse := ReverseConfig{
+ cfg := &SingleReverseConfig{
Name: "web",
- Addr: addr,
Host: host,
- ReverseURL: map[string]*url.URL{
+ ReverseURL: map[string]any{
"/": u,
},
- Port: port,
UsingSSL: enablessl,
SkipSSLVerify: skipsslverify,
Key: key,
Cert: cert,
IPFilterMode: 1,
}
+ reverse := ReverseConfig{
+ Addr: addr,
+ Port: port,
+ Config: []*SingleReverseConfig{cfg},
+ routes: map[string]*SingleReverseConfig{
+ host: cfg,
+ },
+ }
go func() {
sig := make(chan os.Signal)
signal.Notify(sig, os.Kill, os.Interrupt)
diff --git a/httpreverse/reverse.go b/httpreverse/reverse.go
index e7e0cec..399c5dd 100644
--- a/httpreverse/reverse.go
+++ b/httpreverse/reverse.go
@@ -12,21 +12,31 @@ import (
"net/http"
"net/url"
"os"
+ "path"
"strings"
"sync"
)
type ReverseConfig struct {
+ Addr string
+ Port int
+ httpmux http.ServeMux
+ httpserver http.Server
+ Config []*SingleReverseConfig
+ routes map[string]*SingleReverseConfig
+ autogenCert bool //是否自动生成证书
+}
+
+type SingleReverseConfig struct {
Name string
- Addr string
- ReverseURL map[string]*url.URL
- Port int
+ ReverseURL map[string]any
UsingSSL bool
AllowHTTPWithHttps bool
AutoGenerateCert bool
Key string
Cert string
Host string
+ ProxyHost string
SkipSSLVerify bool
InHeader [][2]string
OutHeader [][2]string
@@ -34,14 +44,13 @@ type ReverseConfig struct {
ReplaceList [][2]string
ReplaceOnce bool
proxy map[string]*rp.ReverseProxy
+ httpPage map[string]string
IPFilterMode int //0=off 1=useremote 2=add 3=filter
FilterXForward bool
FilterRemoteAddr bool
FilterMustKey string
FilterSetKey string
FilterFile string
- httpmux http.ServeMux
- httpserver http.Server
CIDR []*net.IPNet
basicAuthUser string
@@ -51,25 +60,40 @@ type ReverseConfig struct {
whiteip map[string]int
warningpage string
warnpagedata []byte
+ router Router
+ blackpath Router
+ whitepath Router
+ rootLeaf any
}
type HttpReverseServer struct {
Config []*ReverseConfig
}
-func Parse(path string) (HttpReverseServer, error) {
+func Parse(cfgPath string) (HttpReverseServer, error) {
var res HttpReverseServer
ini := sysconf.NewIni()
- err := ini.ParseFromFile(path)
+ err := ini.ParseFromFile(cfgPath)
if err != nil {
return res, err
}
+ serverMap := make(map[string]*ReverseConfig)
for _, v := range ini.Data {
- var ins = ReverseConfig{
+ var cfg *ReverseConfig
+ keyID := v.Get("addr") + v.Get("port")
+ if _, ok := serverMap[keyID]; ok {
+ cfg = serverMap[keyID]
+ } else {
+ cfg = &ReverseConfig{
+ Addr: v.Get("addr"),
+ Port: v.Int("port"),
+ routes: make(map[string]*SingleReverseConfig),
+ }
+ serverMap[keyID] = cfg
+ }
+ var ins = SingleReverseConfig{
Name: v.Name,
Host: v.Get("host"),
- Addr: v.Get("addr"),
- Port: v.Int("port"),
UsingSSL: v.Bool("enablessl"),
AllowHTTPWithHttps: v.Bool("tlsallowhttp"),
AutoGenerateCert: v.Bool("autogencert"),
@@ -125,16 +149,21 @@ func Parse(path string) (HttpReverseServer, error) {
ins.warnpagedata = data
}
ins.proxy = make(map[string]*rp.ReverseProxy)
- ins.ReverseURL = make(map[string]*url.URL)
+ ins.ReverseURL = make(map[string]any)
for _, reverse := range v.GetAll("reverse") {
kv := strings.SplitN(reverse, "::", 2)
if len(kv) != 2 {
return res, errors.New("reverse settings not correct:" + reverse)
}
- ins.ReverseURL[strings.TrimSpace(kv[0])], err = url.Parse(strings.TrimSpace(kv[1]))
- if err != nil {
- return res, err
+ if !strings.HasPrefix(strings.TrimSpace(kv[1]), "http://") && !strings.HasPrefix(strings.TrimSpace(kv[1]), "https://") {
+ ins.ReverseURL[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
+ } else {
+ ins.ReverseURL[strings.TrimSpace(kv[0])], err = url.Parse(strings.TrimSpace(kv[1]))
+ if err != nil {
+ return res, err
+ }
}
+
}
for _, header := range v.GetAll("inheader") {
kv := strings.SplitN(header, "::", 2)
@@ -154,6 +183,20 @@ func Parse(path string) (HttpReverseServer, error) {
}
ins.blackip[ip] = cidr
}
+ for _, blackpath := range v.GetAll("blackpath") {
+ isStar := path.Base(blackpath) == "*"
+ if isStar {
+ blackpath = path.Dir(blackpath)
+ }
+ ins.blackpath.AddLeaf(blackpath, isStar)
+ }
+ for _, whitepath := range v.GetAll("whitepath") {
+ isStar := path.Base(whitepath) == "*"
+ if isStar {
+ whitepath = path.Dir(whitepath)
+ }
+ ins.whitepath.AddLeaf(whitepath, isStar)
+ }
ins.whiteip = make(map[string]int)
for _, whiteip := range v.GetAll("whiteip") {
ip, cidr, err := IPCIDR(whiteip)
@@ -183,7 +226,11 @@ func Parse(path string) (HttpReverseServer, error) {
}
ins.ReplaceList = append(ins.ReplaceList, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
}
- res.Config = append(res.Config, &ins)
+ cfg.routes[ins.Host] = &ins
+ cfg.Config = append(cfg.Config, &ins)
+ }
+ for _, v := range serverMap {
+ res.Config = append(res.Config, v)
}
return res, nil
}
diff --git a/httpreverse/route.go b/httpreverse/route.go
new file mode 100644
index 0000000..b66ea1c
--- /dev/null
+++ b/httpreverse/route.go
@@ -0,0 +1,117 @@
+package httpreverse
+
+import "strings"
+
+type Router struct {
+ Hostname string
+ Leaf *Leaf
+ leafMap map[string]*Leaf
+}
+
+type Leaf struct {
+ Name string
+ Val any
+ Last *Leaf
+ Next map[string]*Leaf
+ FullPath string
+}
+
+func NewRouter(hostname string) *Router {
+ return &Router{
+ Hostname: hostname,
+ Leaf: &Leaf{
+ Name: "/",
+ Next: make(map[string]*Leaf),
+ FullPath: "/",
+ },
+ leafMap: make(map[string]*Leaf),
+ }
+}
+
+func (r *Router) AddLeaf(path string, val any) {
+ if r.Leaf == nil {
+ r.Leaf = &Leaf{
+ Name: "/",
+ Next: make(map[string]*Leaf),
+ FullPath: "/",
+ }
+ }
+ if r.leafMap == nil {
+ r.leafMap = make(map[string]*Leaf)
+ r.leafMap["/"] = r.Leaf
+ }
+ names := strings.Split(path, "/")
+ leaf := r.Leaf
+ for _, name := range names {
+ if name == "" {
+ continue
+ }
+ if leaf.Next[name] == nil {
+ fullPath := leaf.FullPath + "/" + name
+ if leaf.FullPath == "/" {
+ fullPath = "/" + name
+ }
+ leaf.Next[name] = &Leaf{
+ Name: name,
+ Next: make(map[string]*Leaf),
+ FullPath: fullPath,
+ }
+ r.leafMap[fullPath] = leaf.Next[name]
+ }
+ leaf = leaf.Next[name]
+ }
+ leaf.Val = val
+}
+
+func (r *Router) GetLeaf(path string) *Leaf {
+ return r.leafMap[path]
+}
+
+func (r *Router) NearestLeaf(path string) *Leaf {
+ if path == "/" {
+ return r.Leaf
+ }
+ if leaf, ok := r.leafMap[path]; ok {
+ return leaf
+ }
+ parts := strings.Split(path, "/")
+ leaf := r.Leaf
+ for _, v := range parts {
+ if v == "" {
+ continue
+ }
+ if leaf.Next[v] == nil {
+ return leaf
+ }
+ leaf = leaf.Next[v]
+ }
+ return leaf
+}
+
+func (r *Router) NearestLeafWithVal(path string) *Leaf {
+ if path == "/" {
+ return r.Leaf
+ }
+ if leaf, ok := r.leafMap[path]; ok && leaf.Val != nil {
+ return leaf
+ }
+ var lastValue *Leaf
+ parts := strings.Split(path, "/")
+ leaf := r.Leaf
+ if leaf.Val != nil {
+ lastValue = leaf
+ }
+ for _, v := range parts {
+ if v == "" {
+ continue
+ }
+ if leaf.Next[v] == nil {
+ return lastValue
+ }
+ leaf = leaf.Next[v]
+ if leaf.Val != nil {
+ lastValue = leaf
+ }
+ }
+ return lastValue
+}
diff --git a/httpreverse/service.go b/httpreverse/service.go
index 652202d..778748d 100644
--- a/httpreverse/service.go
+++ b/httpreverse/service.go
@@ -3,18 +3,25 @@ package httpreverse
import (
"b612.me/apps/b612/httpreverse/rp"
"b612.me/apps/b612/utils"
+ "b612.me/starcrypto"
"b612.me/starlog"
"b612.me/starnet"
+ "b612.me/staros"
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
+ "errors"
"fmt"
+ "github.com/gabriel-vasile/mimetype"
+ "io"
"io/ioutil"
"net"
"net/http"
"net/url"
+ "os"
+ "path/filepath"
"strconv"
"strings"
"time"
@@ -27,64 +34,147 @@ func (h *ReverseConfig) Run() error {
if err != nil {
return err
}
- for key, proxy := range h.proxy {
- h.httpmux.HandleFunc(key, func(writer http.ResponseWriter, request *http.Request) {
- starlog.Infof("<%s> Req Path:%s ListenAddr:%s UA:%s\n", h.Name, request.URL.Path, request.RemoteAddr, request.Header.Get("User-Agent"))
- if !h.BasicAuth(writer, request) {
- h.SetResponseHeader(writer)
+ h.httpmux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
+ c, ok := h.routes[request.Host]
+ if !ok {
+ if _, ok := h.routes[""]; ok {
+ c = h.routes[""]
+ } else {
+ if len(h.routes) > 0 {
+ for _, v := range h.routes {
+ c = v
+ break
+ }
+ }
+ }
+ }
+ rejectWith403 := func(writer http.ResponseWriter, request *http.Request) {
+ c.SetResponseHeader(writer)
+ writer.WriteHeader(403)
+ if len(c.warnpagedata) != 0 {
+ writer.Write(c.warnpagedata)
return
}
- if !h.filter(writer, request) {
- h.SetResponseHeader(writer)
- writer.WriteHeader(403)
- if len(h.warnpagedata) != 0 {
- writer.Write(h.warnpagedata)
+ writer.Write([]byte(`
+
+ 403 Forbidden
+
+
403 Forbidden
+ You Are Not Allowed to Access This Page
+ Please Contact Site Administrator For Help
+
B612 HTTP REVERSE SERVER
+
+ `))
+ }
+ starlog.Infof("<%s> Req Path:%s ListenAddr:%s UA:%s\n", c.Name, request.URL.Path, request.RemoteAddr, request.Header.Get("User-Agent"))
+ checkPath := request.URL.Path
+ if strings.HasSuffix(checkPath, "/") && checkPath != "/" {
+ checkPath = strings.TrimSuffix(checkPath, "/")
+ }
+ if c.blackpath.Leaf != nil {
+ bp := c.blackpath.NearestLeafWithVal(checkPath)
+ if bp != nil {
+ if ppr, ok := bp.Val.(bool); ok {
+ if ppr || (!ppr && bp.FullPath == checkPath) {
+ starlog.Errorf("<%s> Path:%s is in black path, reject request\n", c.Name, checkPath)
+ rejectWith403(writer, request)
+ return
+ }
+ }
+ }
+ }
+ if c.whitepath.Leaf != nil {
+ bp := c.whitepath.NearestLeafWithVal(checkPath)
+ if bp == nil {
+ starlog.Errorf("<%s> Path:%s is not in the write path, reject request\n", c.Name, checkPath)
+ rejectWith403(writer, request)
+ return
+ }
+ if ppr, ok := bp.Val.(bool); !ok {
+ starlog.Errorf("<%s> Path:%s is not in the write path, reject request\n", c.Name, checkPath)
+ rejectWith403(writer, request)
+ return
+ } else {
+ if !ppr && bp.FullPath != checkPath {
+ fmt.Println(bp.FullPath, checkPath)
+ starlog.Errorf("<%s> Path:%s is not in the write path, reject request\n", c.Name, checkPath)
+ rejectWith403(writer, request)
return
}
- writer.Write([]byte(`
-
- 403 Forbidden
-
- 403 Forbidden
- You Are Not Allowed to Access This Page
- Please Contact Site Administrator For Help
-
B612 HTTP REVERSE SERVER
-
- `))
- return
}
- proxy.ServeHTTP(writer, request)
- })
- }
+ }
+ if !c.BasicAuth(writer, request) {
+ c.SetResponseHeader(writer)
+ return
+ }
+ if !c.filter(writer, request) {
+ rejectWith403(writer, request)
+ return
+ }
+ leaf := c.router.NearestLeafWithVal(checkPath)
+ if request.URL.Path == "/" && c.rootLeaf != nil {
+ leaf = &Leaf{
+ Name: leaf.Name,
+ Val: c.rootLeaf,
+ Last: leaf.Last,
+ Next: leaf.Next,
+ FullPath: leaf.FullPath,
+ }
+ }
+ fmt.Println(leaf.Val)
+ if leaf == nil {
+ starlog.Errorf("<%s> No Reverse Proxy Found For Path:%s\n", c.Name, request.URL.Path)
+ writer.WriteHeader(404)
+ writer.Write([]byte("404 Not Found"))
+ return
+ }
+ if rp, ok := leaf.Val.(*rp.ReverseProxy); ok {
+ rp.ServeHTTP(writer, request)
+ return
+ }
+ if page, ok := leaf.Val.(string); ok {
+ h.fileHandle(leaf.FullPath, page, writer, request)
+ return
+ }
+ writer.WriteHeader(500)
+ })
h.httpserver = http.Server{
Addr: fmt.Sprintf("%s:%d", h.Addr, h.Port),
Handler: &h.httpmux,
}
- starlog.Infoln(h.Name + " Listening on " + h.Addr + ":" + strconv.Itoa(h.Port))
- if !h.UsingSSL && !h.AutoGenerateCert {
+ var realUsingsSSL bool
+ var realAllowHTTPWithHttps bool
+ for _, c := range h.Config {
+ if c.UsingSSL {
+ realUsingsSSL = true
+ }
+ if c.AutoGenerateCert {
+ h.autogenCert = true
+ }
+ if c.AllowHTTPWithHttps {
+ realAllowHTTPWithHttps = true
+ }
+ starlog.Infoln(c.Name + " Listening on " + h.Addr + ":" + strconv.Itoa(h.Port))
+ }
+
+ if !realUsingsSSL {
if err := h.httpserver.ListenAndServe(); err != http.ErrServerClosed {
return err
}
return nil
}
-
- if !h.AllowHTTPWithHttps && !h.AutoGenerateCert {
- if h.httpserver.ListenAndServeTLS(h.Cert, h.Key); err != http.ErrServerClosed {
- return err
+ /*
+ if !realAllowHTTPWithHttps && !realAutoGenCert {
+ if h.httpserver.ListenAndServeTLS(h.Cert, h.Key); err != http.ErrServerClosed {
+ return err
+ }
+ return nil
}
- return nil
- }
+ */
var lis net.Listener
- if !h.AutoGenerateCert {
- lis, err = starnet.ListenTLS("tcp", fmt.Sprintf("%s:%d", h.Addr, h.Port), h.Cert, h.Key, h.AllowHTTPWithHttps)
- if err != nil {
- return err
- }
- } else {
- lis, err = starnet.ListenTLSWithConfig("tcp", fmt.Sprintf("%s:%d", h.Addr, h.Port), &tls.Config{}, autoGenCert, h.AllowHTTPWithHttps)
- if err != nil {
- return err
- }
+ lis, err = starnet.ListenTLSWithConfig("tcp", fmt.Sprintf("%s:%d", h.Addr, h.Port), &tls.Config{}, h.getCert, realAllowHTTPWithHttps)
+ if err != nil {
+ return err
}
defer lis.Close()
if err := h.httpserver.Serve(lis); err != http.ErrServerClosed {
@@ -97,7 +187,242 @@ var certCache = make(map[string]tls.Certificate)
var toolCa *x509.Certificate
var toolCaKey any
-func autoGenCert(hostname string) *tls.Config {
+func (h *ReverseConfig) CalcRange(r *http.Request) (int64, int64) {
+ var rangeStart, rangeEnd int64 = -1, -1
+ ranges := r.Header.Get("Range")
+ if ranges == "" {
+ return rangeStart, rangeEnd
+ }
+ if !strings.Contains(ranges, "bytes=") {
+ return rangeStart, rangeEnd
+ }
+ ranges = strings.TrimPrefix(ranges, "bytes=")
+ data := strings.Split(ranges, "-")
+ if len(data) == 0 {
+ return rangeStart, rangeEnd
+ }
+ rangeStart, _ = strconv.ParseInt(data[0], 10, 64)
+ if len(data) > 1 {
+ rangeEnd, _ = strconv.ParseInt(data[1], 10, 64)
+ }
+ if rangeEnd == 0 {
+ rangeEnd = -1
+ }
+ return rangeStart, rangeEnd
+}
+
+func (h *ReverseConfig) BuildHeader(w http.ResponseWriter, r *http.Request, fullpath string) error {
+ h.SetResponseHeader(w)
+ if r.Method == "OPTIONS" {
+ w.Header().Set("Allow", "OPTIONS,GET,HEAD")
+ w.Header().Set("Content-Length", "0")
+ }
+ w.Header().Set("Date", strings.ReplaceAll(time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT"))
+ if staros.IsFolder(fullpath) {
+ return nil
+ }
+ mime, _ := mimetype.DetectFile(fullpath)
+ if mime == nil {
+ w.Header().Set("Content-Type", "application/download")
+ w.Header().Set("Content-Disposition", "attachment;filename="+filepath.Base(fullpath))
+ w.Header().Set("Content-Transfer-Encoding", "binary")
+ } else {
+ w.Header().Set("Content-Type", mime.String())
+ }
+ if staros.Exists(fullpath) {
+ finfo, err := os.Stat(fullpath)
+ if err != nil {
+ w.WriteHeader(502)
+ w.Write([]byte("Failed to Read " + fullpath + ",reason is " + err.Error()))
+ return err
+ }
+ w.Header().Set("Accept-Ranges", "bytes")
+ w.Header().Set("ETag", starcrypto.Md5Str([]byte(finfo.ModTime().String())))
+ w.Header().Set("Last-Modified", strings.ReplaceAll(finfo.ModTime().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT"))
+ if r.Method != "OPTIONS" {
+ start, end := h.CalcRange(r)
+ if start != -1 {
+ if end == -1 {
+ w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size()-start, 10))
+ w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(finfo.Size()-1, 10)+"/"+strconv.FormatInt(finfo.Size(), 10))
+ //w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size()-rangeStart, 10))
+ } else {
+ w.Header().Set("Content-Length", strconv.FormatInt(end-start+1, 10))
+ w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(finfo.Size(), 10))
+ //w.Header().Set("Content-Length", strconv.FormatInt(1+rangeEnd-rangeStart, 10))
+ }
+ } else {
+ w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size(), 10))
+ }
+ }
+ }
+ return nil
+}
+
+func (h *ReverseConfig) getFile(w http.ResponseWriter, r *http.Request, fullpath string) error {
+ if !staros.Exists(fullpath) {
+ w.WriteHeader(404)
+ return errors.New("File Not Found! 404 ERROR")
+ }
+ //starlog.Debugln(r.Header)
+ startRange, endRange := h.CalcRange(r)
+ fp, err := os.Open(fullpath)
+ if err != nil {
+ starlog.Errorf("Failed to open file %s,reason:%v\n", r.URL.Path, err)
+ w.WriteHeader(502)
+ w.Write([]byte(`B612 Http Server502 SERVER ERROR
`))
+ return err
+ }
+ defer fp.Close()
+ var transferData int
+ defer func() {
+ if transferData != 0 {
+ var tani string
+ tani = fmt.Sprintf("%v Byte", transferData)
+ if f64 := float64(transferData) / 1024; f64 > 1 {
+ tani = fmt.Sprintf("%v KiB", f64)
+ if f64 = float64(f64) / 1024; f64 > 1 {
+ tani = fmt.Sprintf("%v MiB", f64)
+ if f64 = float64(f64) / 1024; f64 > 1 {
+ tani = fmt.Sprintf("%v GiB", f64)
+ }
+ }
+ }
+ starlog.Infof("Tranfered File %s %d bytes (%s) to remote %v\n", r.URL.Path,
+ transferData, tani, r.RemoteAddr)
+ }
+ }()
+ if startRange == -1 {
+ w.WriteHeader(200)
+ for {
+ buf := make([]byte, 16384)
+ n, err := fp.Read(buf)
+ if n != 0 {
+ ns, err := w.Write(buf[:n])
+ transferData += ns
+ if err != nil {
+ starlog.Errorf("Transfer File %s to Remote Failed:%v\n", fullpath, err)
+ return err
+ }
+ }
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ starlog.Errorf("Read File %s Failed:%v\n", fullpath, err)
+ return err
+ }
+ }
+ return nil
+ }
+ starlog.Debugf("206 transfer mode for %v %v start %v end %v\n", r.URL.Path, r.RemoteAddr, startRange, endRange)
+ w.WriteHeader(206)
+ fp.Seek(int64(startRange), 0)
+ count := startRange
+ for {
+ buf := make([]byte, 16384)
+ n, err := fp.Read(buf)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ starlog.Errorf("Read File %s Failed:%v\n", r.URL.Path, err)
+ return err
+ }
+ if endRange == -1 {
+ ns, err := w.Write(buf[:n])
+ transferData += ns
+ if err != nil {
+ starlog.Errorf("Transfer File %s to Remote Failed:%v\n", r.URL.Path, err)
+ return err
+ }
+ } else {
+ if count > endRange {
+ break
+ }
+ writeNum := n
+ if count+int64(n) > endRange {
+ writeNum = int(endRange - count + 1)
+ }
+ ns, err := w.Write(buf[0:writeNum])
+ if err != nil {
+ starlog.Errorln("Transfer Error:", err)
+ return err
+ }
+ transferData += ns
+ count += int64(n)
+ }
+ }
+ return nil
+}
+
+func (h *ReverseConfig) fileHandle(dirPath, diskpath string, writer http.ResponseWriter, request *http.Request) {
+ var checkUrl = request.URL.Path
+ if strings.HasSuffix(request.URL.Path, "/") && len(request.URL.Path) > 1 {
+ checkUrl = strings.TrimSuffix(request.URL.Path, "/")
+ }
+ if staros.IsFile(diskpath) {
+ if checkUrl != dirPath {
+ h.SetResponseHeader(writer)
+ writer.WriteHeader(404)
+ return
+ }
+ h.BuildHeader(writer, request, diskpath)
+ if request.Method == "OPTIONS" {
+ return
+ }
+ h.getFile(writer, request, diskpath)
+ return
+ }
+ reqPath := strings.TrimPrefix(checkUrl, dirPath)
+ realPath := filepath.Join(diskpath, reqPath)
+ if !staros.Exists(realPath) {
+ h.SetResponseHeader(writer)
+ writer.WriteHeader(404)
+ return
+ }
+ if staros.IsFile(realPath) {
+ h.BuildHeader(writer, request, realPath)
+ if request.Method == "OPTIONS" {
+ return
+ }
+ h.getFile(writer, request, realPath)
+ return
+ }
+ h.SetResponseHeader(writer)
+ writer.WriteHeader(403)
+}
+
+func (h *ReverseConfig) getCert(hostname string) *tls.Config {
+ if h.autogenCert {
+ return h.autoGenCert(hostname)
+ }
+ c, ok := h.routes[hostname]
+ if !ok {
+ if _, ok := h.routes[""]; ok {
+ c = h.routes[""]
+ } else {
+ if len(h.routes) > 0 {
+ for _, v := range h.routes {
+ c = v
+ break
+ }
+ }
+ }
+ }
+ if c == nil {
+ return &tls.Config{}
+ }
+ cert, err := tls.LoadX509KeyPair(c.Cert, c.Key)
+ if err != nil {
+ starlog.Errorln("Load X509 Key Pair Error:", err)
+ return &tls.Config{}
+ }
+ return &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ }
+}
+func (h *ReverseConfig) autoGenCert(hostname string) *tls.Config {
if cert, ok := certCache[hostname]; ok {
return &tls.Config{Certificates: []tls.Certificate{cert}}
}
@@ -136,7 +461,7 @@ func (h *ReverseConfig) Close() error {
return h.httpserver.Shutdown(ctx)
}
-func (h *ReverseConfig) dialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
+func (h *SingleReverseConfig) dialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(network, addr, time.Second*20)
if err != nil {
return nil, err
@@ -146,8 +471,8 @@ func (h *ReverseConfig) dialTLS(ctx context.Context, network, addr string) (net.
if err != nil {
return nil, err
}
- if h.Host != "" {
- host = h.Host
+ if h.ProxyHost != "" {
+ host = h.ProxyHost
}
cfg := &tls.Config{ServerName: host}
tlsConn := tls.Client(conn, cfg)
@@ -170,40 +495,78 @@ func (h *ReverseConfig) dialTLS(ctx context.Context, network, addr string) (net.
}
func (h *ReverseConfig) init() error {
+ for _, v := range h.Config {
+ v.init()
+ }
+ return nil
+}
+
+func (h *SingleReverseConfig) init() error {
h.proxy = make(map[string]*rp.ReverseProxy)
for key, val := range h.ReverseURL {
- h.proxy[key] = &rp.ReverseProxy{
- Transport: &http.Transport{DialTLSContext: h.dialTLS},
- }
- h.proxy[key].ModifyResponse = h.ModifyResponse()
- h.proxy[key].Director = func(req *http.Request) {
- targetQuery := val.RawQuery
- req.URL.Scheme = val.Scheme
- if h.Host == "" {
- req.Host = val.Host
- } else {
- req.Host = h.Host
+ switch tp := val.(type) {
+ case *url.URL:
+ if err := h.newReverseProxy(key, tp); err != nil {
+ return fmt.Errorf("new reverse proxy for %s failed: %w", key, err)
}
- req.URL.Host = val.Host
- req.URL.Path, req.URL.RawPath = joinURLPath(val, req.URL, key)
- if targetQuery == "" || req.URL.RawQuery == "" {
- req.URL.RawQuery = targetQuery + req.URL.RawQuery
- } else {
- req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
+ case string:
+ if h.httpPage == nil {
+ h.httpPage = make(map[string]string)
}
- h.ModifyRequest(req, val)
+ h.httpPage[key] = tp
+ if key == "=/" {
+ h.rootLeaf = tp
+ continue
+ }
+ h.router.AddLeaf(key, tp)
}
}
return nil
}
+func (h *SingleReverseConfig) newReverseProxy(key string, val *url.URL) error {
+ h.proxy[key] = &rp.ReverseProxy{
+ Transport: &http.Transport{DialTLSContext: h.dialTLS},
+ }
+ h.proxy[key].ModifyResponse = h.ModifyResponse()
+ h.proxy[key].Director = func(req *http.Request) {
+ targetQuery := val.RawQuery
+ req.URL.Scheme = val.Scheme
+ if h.ProxyHost != "" {
+ req.Host = h.ProxyHost
+ } else {
+ req.Host = val.Host
+ }
+ req.URL.Host = val.Host
+ req.URL.Path, req.URL.RawPath = joinURLPath(val, req.URL, key)
+ if targetQuery == "" || req.URL.RawQuery == "" {
+ req.URL.RawQuery = targetQuery + req.URL.RawQuery
+ } else {
+ req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
+ }
+ h.ModifyRequest(req, val)
+ }
+ if key == "=/" {
+ h.rootLeaf = h.proxy[key]
+ return nil
+ }
+ h.router.AddLeaf(key, h.proxy[key])
+ return nil
+}
+
func (h *ReverseConfig) SetResponseHeader(resp http.ResponseWriter) {
resp.Header().Set("X-Powered-By", "B612.ME")
resp.Header().Set("Server", "B612/"+version)
resp.Header().Set("X-Proxy", "B612 Reverse Proxy")
}
-func (h *ReverseConfig) ModifyResponse() func(*http.Response) error {
+func (h *SingleReverseConfig) SetResponseHeader(resp http.ResponseWriter) {
+ resp.Header().Set("X-Powered-By", "B612.ME")
+ resp.Header().Set("Server", "B612/"+version)
+ resp.Header().Set("X-Proxy", "B612 Reverse Proxy")
+}
+
+func (h *SingleReverseConfig) ModifyResponse() func(*http.Response) error {
return func(resp *http.Response) error {
for _, v := range h.OutHeader {
resp.Header.Set(v[0], v[1])
@@ -236,7 +599,7 @@ func (h *ReverseConfig) ModifyResponse() func(*http.Response) error {
}
}
-func (h *ReverseConfig) isInCIDR(ip string) bool {
+func (h *SingleReverseConfig) isInCIDR(ip string) bool {
nip := net.ParseIP(strings.TrimSpace(ip))
if nip == nil {
return false
@@ -249,7 +612,7 @@ func (h *ReverseConfig) isInCIDR(ip string) bool {
return false
}
-func (h *ReverseConfig) ModifyRequest(req *http.Request, remote *url.URL) {
+func (h *SingleReverseConfig) ModifyRequest(req *http.Request, remote *url.URL) {
switch h.IPFilterMode {
case 1:
req.Header.Set("X-Forwarded-For", strings.Split(req.RemoteAddr, ":")[0])
@@ -297,7 +660,7 @@ func (h *ReverseConfig) ModifyRequest(req *http.Request, remote *url.URL) {
}
}
-func (h *ReverseConfig) GiveBasicAuth(w http.ResponseWriter) {
+func (h *SingleReverseConfig) GiveBasicAuth(w http.ResponseWriter) {
w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`)
w.WriteHeader(401)
w.Write([]byte(`
@@ -310,7 +673,7 @@ func (h *ReverseConfig) GiveBasicAuth(w http.ResponseWriter) {