Release 1.2.0 Windows amd64 with Hysteria 2
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package hysteria2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NormalizeShareLink accepts hysteria2:// or hy2:// share links (or already-normalized URI).
|
||||
// Returns a canonical hysteria2:// URI suitable for the official client's `server` field.
|
||||
func NormalizeShareLink(raw string) (string, string, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return "", "", fmt.Errorf("empty hysteria2 URI")
|
||||
}
|
||||
|
||||
remark := ""
|
||||
if i := strings.IndexByte(raw, '#'); i >= 0 {
|
||||
remark = strings.TrimSpace(raw[i+1:])
|
||||
if u, err := url.QueryUnescape(remark); err == nil {
|
||||
remark = u
|
||||
}
|
||||
raw = raw[:i]
|
||||
}
|
||||
|
||||
lower := strings.ToLower(raw)
|
||||
switch {
|
||||
case strings.HasPrefix(lower, "hy2://"):
|
||||
raw = "hysteria2://" + raw[len("hy2://"):]
|
||||
case strings.HasPrefix(lower, "hysteria2://"):
|
||||
// ok
|
||||
default:
|
||||
return "", "", fmt.Errorf("ожидалась ссылка hysteria2:// или hy2://")
|
||||
}
|
||||
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("parse hysteria2 URI: %w", err)
|
||||
}
|
||||
if u.Host == "" {
|
||||
return "", "", fmt.Errorf("hysteria2 URI missing host")
|
||||
}
|
||||
if u.Scheme != "hysteria2" {
|
||||
u.Scheme = "hysteria2"
|
||||
}
|
||||
return u.String(), remark, nil
|
||||
}
|
||||
|
||||
// Detect returns true if raw looks like a Hysteria 2 share link.
|
||||
func Detect(raw string) bool {
|
||||
lower := strings.ToLower(strings.TrimSpace(raw))
|
||||
return strings.HasPrefix(lower, "hysteria2://") || strings.HasPrefix(lower, "hy2://")
|
||||
}
|
||||
|
||||
// HostPort extracts host and a single port for reachability checks.
|
||||
func HostPort(shareURI string) (host, port string, err error) {
|
||||
normalized, _, err := NormalizeShareLink(shareURI)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
u, err := url.Parse(normalized)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
host = u.Hostname()
|
||||
port = u.Port()
|
||||
if port == "" {
|
||||
// Multi-port may be in Hostname() for weird parses; Host may be "ex.com:443,5000-6000"
|
||||
hostField := u.Host
|
||||
if h, p, ok := splitMultiPortHost(hostField); ok {
|
||||
return h, p, nil
|
||||
}
|
||||
port = "443"
|
||||
} else if strings.Contains(port, ",") || strings.Contains(port, "-") {
|
||||
port = firstPortToken(port)
|
||||
}
|
||||
if host == "" {
|
||||
return "", "", fmt.Errorf("нет хоста")
|
||||
}
|
||||
if port == "" {
|
||||
port = "443"
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
func splitMultiPortHost(hostField string) (host, port string, ok bool) {
|
||||
// example.com:443,5000-6000
|
||||
i := strings.LastIndex(hostField, ":")
|
||||
if i < 0 {
|
||||
return "", "", false
|
||||
}
|
||||
host = hostField[:i]
|
||||
port = firstPortToken(hostField[i+1:])
|
||||
if host == "" || port == "" {
|
||||
return "", "", false
|
||||
}
|
||||
return host, port, true
|
||||
}
|
||||
|
||||
func firstPortToken(ports string) string {
|
||||
ports = strings.TrimSpace(ports)
|
||||
if ports == "" {
|
||||
return ""
|
||||
}
|
||||
part := ports
|
||||
if i := strings.IndexByte(ports, ','); i >= 0 {
|
||||
part = ports[:i]
|
||||
}
|
||||
if i := strings.IndexByte(part, '-'); i >= 0 {
|
||||
part = part[:i]
|
||||
}
|
||||
return strings.TrimSpace(part)
|
||||
}
|
||||
Reference in New Issue
Block a user