frist
This commit is contained in:
171
internal/importer/parser.go
Normal file
171
internal/importer/parser.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package importer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"proxyrotator/internal/model"
|
||||
)
|
||||
|
||||
var (
|
||||
// 匹配 host:port 格式
|
||||
hostPortRegex = regexp.MustCompile(`^([a-zA-Z0-9.-]+):(\d+)$`)
|
||||
// 匹配 user:pass@host:port 格式
|
||||
userPassHostPortRegex = regexp.MustCompile(`^([^:@]+):([^@]+)@([a-zA-Z0-9.-]+):(\d+)$`)
|
||||
// 匹配 host:port:user:pass 格式
|
||||
hostPortUserPassRegex = regexp.MustCompile(`^([a-zA-Z0-9.-]+):(\d+):([^:]+):(.+)$`)
|
||||
)
|
||||
|
||||
// ParseProxyLine 解析单行代理格式
|
||||
// 支持格式:
|
||||
// - host:port
|
||||
// - user:pass@host:port
|
||||
// - host:port:user:pass
|
||||
// - http://host:port
|
||||
// - http://user:pass@host:port
|
||||
// - socks5://host:port
|
||||
// - socks5://user:pass@host:port
|
||||
func ParseProxyLine(raw string, protocolHint string) (*model.Proxy, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return nil, fmt.Errorf("empty line")
|
||||
}
|
||||
|
||||
// 尝试解析为 URL
|
||||
if strings.Contains(raw, "://") {
|
||||
return parseAsURL(raw)
|
||||
}
|
||||
|
||||
// 尝试解析 host:port:user:pass 格式
|
||||
if matches := hostPortUserPassRegex.FindStringSubmatch(raw); matches != nil {
|
||||
port, err := strconv.Atoi(matches[2])
|
||||
if err != nil || port <= 0 || port >= 65536 {
|
||||
return nil, fmt.Errorf("invalid port: %s", matches[2])
|
||||
}
|
||||
|
||||
protocol := inferProtocol(protocolHint, port)
|
||||
return &model.Proxy{
|
||||
Protocol: protocol,
|
||||
Host: matches[1],
|
||||
Port: port,
|
||||
Username: matches[3],
|
||||
Password: matches[4],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 尝试解析 user:pass@host:port 格式
|
||||
if matches := userPassHostPortRegex.FindStringSubmatch(raw); matches != nil {
|
||||
port, err := strconv.Atoi(matches[4])
|
||||
if err != nil || port <= 0 || port >= 65536 {
|
||||
return nil, fmt.Errorf("invalid port: %s", matches[4])
|
||||
}
|
||||
|
||||
protocol := inferProtocol(protocolHint, port)
|
||||
return &model.Proxy{
|
||||
Protocol: protocol,
|
||||
Host: matches[3],
|
||||
Port: port,
|
||||
Username: matches[1],
|
||||
Password: matches[2],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 尝试解析 host:port 格式
|
||||
if matches := hostPortRegex.FindStringSubmatch(raw); matches != nil {
|
||||
port, err := strconv.Atoi(matches[2])
|
||||
if err != nil || port <= 0 || port >= 65536 {
|
||||
return nil, fmt.Errorf("invalid port: %s", matches[2])
|
||||
}
|
||||
|
||||
protocol := inferProtocol(protocolHint, port)
|
||||
return &model.Proxy{
|
||||
Protocol: protocol,
|
||||
Host: matches[1],
|
||||
Port: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized format")
|
||||
}
|
||||
|
||||
// parseAsURL 解析 URL 格式的代理
|
||||
func parseAsURL(raw string) (*model.Proxy, error) {
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid URL: %w", err)
|
||||
}
|
||||
|
||||
var protocol model.ProxyProtocol
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http":
|
||||
protocol = model.ProtoHTTP
|
||||
case "https":
|
||||
protocol = model.ProtoHTTPS
|
||||
case "socks5":
|
||||
protocol = model.ProtoSOCKS5
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported protocol: %s", u.Scheme)
|
||||
}
|
||||
|
||||
host := u.Hostname()
|
||||
if host == "" {
|
||||
return nil, fmt.Errorf("missing host")
|
||||
}
|
||||
|
||||
portStr := u.Port()
|
||||
if portStr == "" {
|
||||
// 默认端口
|
||||
switch protocol {
|
||||
case model.ProtoHTTP:
|
||||
portStr = "80"
|
||||
case model.ProtoHTTPS:
|
||||
portStr = "443"
|
||||
case model.ProtoSOCKS5:
|
||||
portStr = "1080"
|
||||
}
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil || port <= 0 || port >= 65536 {
|
||||
return nil, fmt.Errorf("invalid port: %s", portStr)
|
||||
}
|
||||
|
||||
var username, password string
|
||||
if u.User != nil {
|
||||
username = u.User.Username()
|
||||
password, _ = u.User.Password()
|
||||
}
|
||||
|
||||
return &model.Proxy{
|
||||
Protocol: protocol,
|
||||
Host: host,
|
||||
Port: port,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// inferProtocol 根据提示和端口推断协议
|
||||
func inferProtocol(hint string, port int) model.ProxyProtocol {
|
||||
switch strings.ToLower(hint) {
|
||||
case "http":
|
||||
return model.ProtoHTTP
|
||||
case "https":
|
||||
return model.ProtoHTTPS
|
||||
case "socks5":
|
||||
return model.ProtoSOCKS5
|
||||
}
|
||||
|
||||
// 根据端口推断
|
||||
switch port {
|
||||
case 443:
|
||||
return model.ProtoHTTPS
|
||||
case 1080:
|
||||
return model.ProtoSOCKS5
|
||||
default:
|
||||
return model.ProtoHTTP
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user