feat: 去除port范围限制,在添加时动态检查操作系统内的占用情况
This commit is contained in:
parent
a30a7e38b4
commit
35f7eaf3c8
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"port-forward/server/forwarder"
|
"port-forward/server/forwarder"
|
||||||
"port-forward/server/stats"
|
"port-forward/server/stats"
|
||||||
"port-forward/server/tunnel"
|
"port-forward/server/tunnel"
|
||||||
|
"port-forward/server/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -24,13 +25,13 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHandler 创建新的 API 处理器
|
// NewHandler 创建新的 API 处理器
|
||||||
func NewHandler(database *db.Database, fwdMgr *forwarder.Manager, ts *tunnel.Server, portFrom, portEnd int) *Handler {
|
func NewHandler(database *db.Database, fwdMgr *forwarder.Manager, ts *tunnel.Server) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
db: database,
|
db: database,
|
||||||
forwarderMgr: fwdMgr,
|
forwarderMgr: fwdMgr,
|
||||||
tunnelServer: ts,
|
tunnelServer: ts,
|
||||||
portRangeFrom: portFrom,
|
// portRangeFrom: portFrom,
|
||||||
portRangeEnd: portEnd,
|
// portRangeEnd: portEnd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +110,12 @@ func (h *Handler) handleCreateMapping(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
used := utils.PortCheck(req.SourcePort)
|
||||||
|
if used {
|
||||||
|
h.writeError(w, http.StatusConflict, "端口已被占用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 根据请求决定使用哪种模式
|
// 根据请求决定使用哪种模式
|
||||||
if req.UseTunnel {
|
if req.UseTunnel {
|
||||||
// 隧道模式,检查隧道服务器是否可用
|
// 隧道模式,检查隧道服务器是否可用
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ func setupTestHandler(t *testing.T, useTunnel bool) (*Handler, *db.Database, fun
|
||||||
tunnelServer.Start()
|
tunnelServer.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := NewHandler(database, fwdMgr, tunnelServer, 10000, 20000)
|
handler := NewHandler(database, fwdMgr, tunnelServer)
|
||||||
|
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
fwdMgr.StopAll()
|
fwdMgr.StopAll()
|
||||||
|
|
@ -543,7 +543,7 @@ func BenchmarkHandleHealth(b *testing.B) {
|
||||||
defer database.Close()
|
defer database.Close()
|
||||||
|
|
||||||
fwdMgr := forwarder.NewManager()
|
fwdMgr := forwarder.NewManager()
|
||||||
handler := NewHandler(database, fwdMgr, nil, 10000, 20000)
|
handler := NewHandler(database, fwdMgr, nil)
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
||||||
|
|
||||||
|
|
@ -568,7 +568,7 @@ func BenchmarkHandleListMappings(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fwdMgr := forwarder.NewManager()
|
fwdMgr := forwarder.NewManager()
|
||||||
handler := NewHandler(database, fwdMgr, nil, 10000, 20000)
|
handler := NewHandler(database, fwdMgr, nil)
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/mapping/list", nil)
|
req := httptest.NewRequest(http.MethodGet, "/api/mapping/list", nil)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
// Config 应用配置结构
|
// Config 应用配置结构
|
||||||
type Config struct {
|
type Config struct {
|
||||||
PortRange PortRangeConfig `yaml:"port_range"`
|
// PortRange PortRangeConfig `yaml:"port_range"`
|
||||||
Tunnel TunnelConfig `yaml:"tunnel"`
|
Tunnel TunnelConfig `yaml:"tunnel"`
|
||||||
API APIConfig `yaml:"api"`
|
API APIConfig `yaml:"api"`
|
||||||
Database DatabaseConfig `yaml:"database"`
|
Database DatabaseConfig `yaml:"database"`
|
||||||
|
|
@ -59,15 +59,15 @@ func Load(path string) (*Config, error) {
|
||||||
|
|
||||||
// Validate 验证配置的有效性
|
// Validate 验证配置的有效性
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
if c.PortRange.From <= 0 || c.PortRange.End <= 0 {
|
// if c.PortRange.From <= 0 || c.PortRange.End <= 0 {
|
||||||
return fmt.Errorf("端口范围必须大于 0")
|
// return fmt.Errorf("端口范围必须大于 0")
|
||||||
}
|
// }
|
||||||
if c.PortRange.From > c.PortRange.End {
|
// if c.PortRange.From > c.PortRange.End {
|
||||||
return fmt.Errorf("起始端口不能大于结束端口")
|
// return fmt.Errorf("起始端口不能大于结束端口")
|
||||||
}
|
// }
|
||||||
if c.PortRange.End-c.PortRange.From > 30000 {
|
// if c.PortRange.End-c.PortRange.From > 30000 {
|
||||||
return fmt.Errorf("端口范围过大,最多支持 30000 个端口")
|
// return fmt.Errorf("端口范围过大,最多支持 30000 个端口")
|
||||||
}
|
// }
|
||||||
if c.Tunnel.Enabled && c.Tunnel.ListenPort <= 0 {
|
if c.Tunnel.Enabled && c.Tunnel.ListenPort <= 0 {
|
||||||
return fmt.Errorf("内网穿透端口必须大于 0")
|
return fmt.Errorf("内网穿透端口必须大于 0")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,12 @@ database:
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证配置
|
// 验证配置
|
||||||
if cfg.PortRange.From != 10000 {
|
// if cfg.PortRange.From != 10000 {
|
||||||
t.Errorf("期望起始端口为 10000,得到 %d", cfg.PortRange.From)
|
// t.Errorf("期望起始端口为 10000,得到 %d", cfg.PortRange.From)
|
||||||
}
|
// }
|
||||||
if cfg.PortRange.End != 10100 {
|
// if cfg.PortRange.End != 10100 {
|
||||||
t.Errorf("期望结束端口为 10100,得到 %d", cfg.PortRange.End)
|
// t.Errorf("期望结束端口为 10100,得到 %d", cfg.PortRange.End)
|
||||||
}
|
// }
|
||||||
if !cfg.Tunnel.Enabled {
|
if !cfg.Tunnel.Enabled {
|
||||||
t.Error("期望隧道启用")
|
t.Error("期望隧道启用")
|
||||||
}
|
}
|
||||||
|
|
@ -111,14 +111,14 @@ database:
|
||||||
tmpFile.Write([]byte(configContent))
|
tmpFile.Write([]byte(configContent))
|
||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
|
|
||||||
cfg, err := Load(tmpFile.Name())
|
// cfg, err := Load(tmpFile.Name())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Errorf("边界值配置应该有效: %v", err)
|
// t.Errorf("边界值配置应该有效: %v", err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if cfg != nil && (cfg.PortRange.End-cfg.PortRange.From) != 9999 {
|
// if cfg != nil && (cfg.PortRange.End-cfg.PortRange.From) != 9999 {
|
||||||
t.Errorf("端口范围计算不正确")
|
// t.Errorf("端口范围计算不正确")
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// BenchmarkLoadConfig 基准测试配置加载
|
// BenchmarkLoadConfig 基准测试配置加载
|
||||||
|
|
@ -150,7 +150,7 @@ database:
|
||||||
// BenchmarkValidateConfig 基准测试配置验证
|
// BenchmarkValidateConfig 基准测试配置验证
|
||||||
func BenchmarkValidateConfig(b *testing.B) {
|
func BenchmarkValidateConfig(b *testing.B) {
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
PortRange: PortRangeConfig{From: 10000, End: 20000},
|
// PortRange: PortRangeConfig{From: 10000, End: 20000},
|
||||||
Tunnel: TunnelConfig{Enabled: true, ListenPort: 9000},
|
Tunnel: TunnelConfig{Enabled: true, ListenPort: 9000},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: "./data/db.sqlite"},
|
Database: DatabaseConfig{Path: "./data/db.sqlite"},
|
||||||
|
|
@ -170,7 +170,7 @@ func TestValidateConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "有效配置",
|
name: "有效配置",
|
||||||
config: Config{
|
config: Config{
|
||||||
PortRange: PortRangeConfig{From: 10000, End: 10100},
|
// PortRange: PortRangeConfig{From: 10000, End: 10100},
|
||||||
Tunnel: TunnelConfig{Enabled: true, ListenPort: 9000},
|
Tunnel: TunnelConfig{Enabled: true, ListenPort: 9000},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: "./data/test.db"},
|
Database: DatabaseConfig{Path: "./data/test.db"},
|
||||||
|
|
@ -180,7 +180,7 @@ func TestValidateConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "无效端口范围 - 起始端口为0",
|
name: "无效端口范围 - 起始端口为0",
|
||||||
config: Config{
|
config: Config{
|
||||||
PortRange: PortRangeConfig{From: 0, End: 10100},
|
// PortRange: PortRangeConfig{From: 0, End: 10100},
|
||||||
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: "./data/test.db"},
|
Database: DatabaseConfig{Path: "./data/test.db"},
|
||||||
|
|
@ -190,7 +190,7 @@ func TestValidateConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "无效端口范围 - 起始大于结束",
|
name: "无效端口范围 - 起始大于结束",
|
||||||
config: Config{
|
config: Config{
|
||||||
PortRange: PortRangeConfig{From: 10100, End: 10000},
|
// PortRange: PortRangeConfig{From: 10100, End: 10000},
|
||||||
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: "./data/test.db"},
|
Database: DatabaseConfig{Path: "./data/test.db"},
|
||||||
|
|
@ -200,7 +200,7 @@ func TestValidateConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "端口范围过大",
|
name: "端口范围过大",
|
||||||
config: Config{
|
config: Config{
|
||||||
PortRange: PortRangeConfig{From: 1, End: 40000},
|
// PortRange: PortRangeConfig{From: 1, End: 40000},
|
||||||
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: "./data/test.db"},
|
Database: DatabaseConfig{Path: "./data/test.db"},
|
||||||
|
|
@ -210,7 +210,7 @@ func TestValidateConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "启用隧道但端口无效",
|
name: "启用隧道但端口无效",
|
||||||
config: Config{
|
config: Config{
|
||||||
PortRange: PortRangeConfig{From: 10000, End: 10100},
|
// PortRange: PortRangeConfig{From: 10000, End: 10100},
|
||||||
Tunnel: TunnelConfig{Enabled: true, ListenPort: 0},
|
Tunnel: TunnelConfig{Enabled: true, ListenPort: 0},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: "./data/test.db"},
|
Database: DatabaseConfig{Path: "./data/test.db"},
|
||||||
|
|
@ -220,7 +220,7 @@ func TestValidateConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "数据库路径为空",
|
name: "数据库路径为空",
|
||||||
config: Config{
|
config: Config{
|
||||||
PortRange: PortRangeConfig{From: 10000, End: 10100},
|
// PortRange: PortRangeConfig{From: 10000, End: 10100},
|
||||||
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
Tunnel: TunnelConfig{Enabled: false, ListenPort: 0},
|
||||||
API: APIConfig{ListenPort: 8080},
|
API: APIConfig{ListenPort: 8080},
|
||||||
Database: DatabaseConfig{Path: ""},
|
Database: DatabaseConfig{Path: ""},
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"port-forward/server/db"
|
"port-forward/server/db"
|
||||||
"port-forward/server/forwarder"
|
"port-forward/server/forwarder"
|
||||||
"port-forward/server/tunnel"
|
"port-forward/server/tunnel"
|
||||||
|
"port-forward/server/utils"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -60,8 +61,16 @@ func main() {
|
||||||
|
|
||||||
for _, mapping := range mappings {
|
for _, mapping := range mappings {
|
||||||
// 验证端口在范围内
|
// 验证端口在范围内
|
||||||
if mapping.SourcePort < cfg.PortRange.From || mapping.SourcePort > cfg.PortRange.End {
|
// if mapping.SourcePort < cfg.PortRange.From || mapping.SourcePort > cfg.PortRange.End {
|
||||||
log.Printf("警告: 端口 %d 超出范围,跳过", mapping.SourcePort)
|
// log.Printf("警告: 端口 %d 超出范围,跳过", mapping.SourcePort)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
|
||||||
|
used := utils.PortCheck(mapping.SourcePort)
|
||||||
|
|
||||||
|
if used {
|
||||||
|
log.Printf("警告: 端口 %d 已被占!", mapping.SourcePort)
|
||||||
|
os.Exit(1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,8 +100,8 @@ func main() {
|
||||||
database,
|
database,
|
||||||
fwdManager,
|
fwdManager,
|
||||||
tunnelServer,
|
tunnelServer,
|
||||||
cfg.PortRange.From,
|
// cfg.PortRange.From,
|
||||||
cfg.PortRange.End,
|
// cfg.PortRange.End,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 启动 HTTP API 服务器
|
// 启动 HTTP API 服务器
|
||||||
|
|
@ -123,7 +132,7 @@ func main() {
|
||||||
|
|
||||||
log.Println("===========================================")
|
log.Println("===========================================")
|
||||||
log.Printf("服务器启动成功!")
|
log.Printf("服务器启动成功!")
|
||||||
log.Printf("端口范围: %d-%d", cfg.PortRange.From, cfg.PortRange.End)
|
// log.Printf("端口范围: %d-%d", cfg.PortRange.From, cfg.PortRange.End)
|
||||||
log.Printf("HTTP API: http://localhost:%d", cfg.API.ListenPort)
|
log.Printf("HTTP API: http://localhost:%d", cfg.API.ListenPort)
|
||||||
// log.Printf("调试接口: http://localhost:%d/debug/pprof/", pprofPort)
|
// log.Printf("调试接口: http://localhost:%d/debug/pprof/", pprofPort)
|
||||||
if cfg.Tunnel.Enabled {
|
if cfg.Tunnel.Enabled {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PortCheck 检查端口是否可用,可用-true 不可用-false
|
||||||
|
func PortCheck(port int) bool {
|
||||||
|
l, err := net.Listen("tcp", fmt.Sprintf(":%s", strconv.Itoa(port)))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue