测试代理是否可用

This commit is contained in:
2025-01-09 17:19:44 +08:00
parent fee0c7d845
commit c7124bda81
13 changed files with 3499 additions and 188 deletions

277
core/proxy_checker.go Normal file
View File

@@ -0,0 +1,277 @@
package core
import (
"bufio"
"fmt"
"httppp/common"
"log"
"os"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"time"
)
// simplifyErrorMessage 简化错误消息
func simplifyErrorMessage(msg string) string {
// 常见错误类型
errorPatterns := map[string]string{
"no such host": "域名解析失败",
"connection refused": "连接被拒绝",
"connection reset": "连接被重置",
"timeout": "连接超时",
"EOF": "连接中断",
"i/o timeout": "连接超时",
"connection timed out": "连接超时",
"network is unreachable": "网络不可达",
"certificate": "证书错误",
"tls": "TLS错误",
"read: connection": "连接断开",
}
msg = strings.ToLower(msg)
for pattern, simpleMsg := range errorPatterns {
if strings.Contains(msg, pattern) {
return simpleMsg
}
}
return "连接失败"
}
type ProxyResult struct {
Node *common.Node
Success bool
Message string
}
type ProxyChecker struct {
workers int
nodes []*common.Node
targetURL string
progress *Progress
config common.ProxyCheck
}
type Progress struct {
total int
current int
mu sync.Mutex
startTime time.Time
}
func (p *Progress) increment() {
p.mu.Lock()
defer p.mu.Unlock()
p.current++
elapsed := time.Since(p.startTime)
fmt.Printf("\r进度: %d/%d (%.1f%%) 已用时间: %v",
p.current, p.total,
float64(p.current)/float64(p.total)*100,
elapsed.Round(time.Second))
}
func NewProxyChecker(config common.ProxyCheck, nodes []*common.Node, targetURL string) *ProxyChecker {
return &ProxyChecker{
workers: config.Workers,
nodes: nodes,
targetURL: targetURL,
config: config,
}
}
func (pc *ProxyChecker) Check() ([]*common.Node, []*common.Node) {
pc.progress = &Progress{
total: len(pc.nodes),
startTime: time.Now(),
}
results := make(chan ProxyResult, len(pc.nodes))
var wg sync.WaitGroup
// 创建工作协程池
nodeChan := make(chan *common.Node, pc.workers)
// 启动工作协程
for i := 0; i < pc.workers; i++ {
wg.Add(1)
go pc.worker(&wg, nodeChan, results)
}
// 发送任务到工作协程
go func() {
for _, node := range pc.nodes {
nodeChan <- node
}
close(nodeChan)
}()
// 等待所有检测完成
go func() {
wg.Wait()
close(results)
}()
return pc.processResults(results)
}
func (pc *ProxyChecker) worker(wg *sync.WaitGroup, nodes chan *common.Node, results chan<- ProxyResult) {
defer wg.Done()
for node := range nodes {
success, message := pc.checkWithRetry(node, 3)
results <- ProxyResult{
Node: node,
Success: success,
Message: message,
}
pc.progress.increment()
}
}
func (pc *ProxyChecker) checkWithRetry(node *common.Node, maxRetries int) (bool, string) {
for i := 0; i < maxRetries; i++ {
success, message := CheckHttpsProxy(node, pc.targetURL)
if success {
return true, message
}
if !strings.Contains(message, "timeout") {
return false, message
}
time.Sleep(time.Second)
}
return false, fmt.Sprintf("重试%d次后仍然失败", maxRetries)
}
// 添加一个新的结构体来保存节点和延迟信息
type NodeWithLatency struct {
Node *common.Node
Latency int64 // 延迟(毫秒)
Message string // 保存原始消息
}
func (pc *ProxyChecker) processResults(results <-chan ProxyResult) ([]*common.Node, []*common.Node) {
var availableNodes []*common.Node
var unavailableNodes []*common.Node
var nodesWithLatency []NodeWithLatency
// 收集结果
for result := range results {
if result.Success {
// 从消息中提取延迟时间
latency := extractLatency(result.Message)
nodesWithLatency = append(nodesWithLatency, NodeWithLatency{
Node: result.Node,
Latency: latency,
Message: result.Message, // 保存原始消息
})
} else {
if strings.Contains(result.Message, "标题") {
fmt.Printf("❌ %s:%d [%s]\n",
result.Node.Addr,
result.Node.Port,
result.Message)
} else {
fmt.Printf("❌ %s:%d (%s)\n",
result.Node.Addr,
result.Node.Port,
simplifyErrorMessage(result.Message))
}
unavailableNodes = append(unavailableNodes, result.Node)
}
}
// 按延迟排序
sort.Slice(nodesWithLatency, func(i, j int) bool {
return nodesWithLatency[i].Latency < nodesWithLatency[j].Latency
})
// 输出排序后的结果
for _, nwl := range nodesWithLatency {
fmt.Printf("✅ %s:%d [%s]\n",
nwl.Node.Addr,
nwl.Node.Port,
extractTitleAndLatency(nwl.Message))
availableNodes = append(availableNodes, nwl.Node)
}
fmt.Println() // 添加一个空行,使输出更清晰
return availableNodes, unavailableNodes
}
// 从消息中提取延迟时间
func extractLatency(message string) int64 {
re := regexp.MustCompile(`延迟: (\d+)ms`)
matches := re.FindStringSubmatch(message)
if len(matches) > 1 {
if latency, err := strconv.ParseInt(matches[1], 10, 64); err == nil {
return latency
}
}
return 0
}
// 从消息中提取标题和延迟信息
func extractTitleAndLatency(message string) string {
re := regexp.MustCompile(`标题: (.*?), 延迟: (\d+)ms`)
matches := re.FindStringSubmatch(message)
if len(matches) > 2 {
return matches[0] // 返回完整的匹配结果
}
return message
}
func SaveResults(availableNodes []*common.Node, totalNodes int) {
fmt.Printf("\n检测完成! 总计 %d 个节点:\n", totalNodes)
fmt.Printf("✅ 可用节点: %d\n", len(availableNodes))
fmt.Printf("❌ 不可用节点: %d\n", totalNodes-len(availableNodes))
if len(availableNodes) > 0 {
filename := fmt.Sprintf("available_nodes_%s.txt", time.Now().Format("20060102_150405"))
if err := common.SaveAvailableNodes(filename, availableNodes); err != nil {
log.Printf("保存可用节点失败: %v", err)
} else {
fmt.Printf("可用节点已保存到: %s\n", filename)
}
}
}
func SaveDetailedReport(filename string, availableNodes []*common.Node, unavailableNodes []*common.Node, duration time.Duration) {
file, err := os.Create(filename)
if err != nil {
log.Printf("创建报告文件失败: %v", err)
return
}
defer file.Close()
w := bufio.NewWriter(file)
// 写入报告头部
fmt.Fprintf(w, "代理测试报告\n")
fmt.Fprintf(w, "测试时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
fmt.Fprintf(w, "测试用时: %v\n", duration)
fmt.Fprintf(w, "总节点数: %d\n", len(availableNodes)+len(unavailableNodes))
fmt.Fprintf(w, "可用节点: %d\n", len(availableNodes))
fmt.Fprintf(w, "不可用节点: %d\n\n", len(unavailableNodes))
// 写入可用节点
fmt.Fprintf(w, "=== 可用节点列表 ===\n")
for _, node := range availableNodes {
fmt.Fprintf(w, "https://%s:%s@%s:%d\n",
node.Username, node.Password, node.Addr, node.Port)
}
// 写入不可用节点
fmt.Fprintf(w, "\n=== 不可用节点列表 ===\n")
for _, node := range unavailableNodes {
fmt.Fprintf(w, "https://%s:%s@%s:%d\n",
node.Username, node.Password, node.Addr, node.Port)
}
// 写入报告时间
fmt.Fprintf(w, "\n报告生成时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
w.Flush()
//fmt.Printf("详细报告已保存到: %s\n", filename)
}