package hostutil import ( "fmt" "net/url" "strings" "github.com/bluesky-social/indigo/atproto/syntax" ) func ParseHostname(raw string) (hostname string, noSSL bool, err error) { // handle case of bare hostname if !strings.Contains(raw, "://") { if strings.HasPrefix(raw, "localhost:") { raw = "http://" + raw } else { raw = "https://" + raw } } u, err := url.Parse(raw) if err != nil { return "", false, fmt.Errorf("not a valid host URL: %w", err) } switch u.Scheme { case "https", "wss": noSSL = false case "http", "ws": noSSL = true default: return "", false, fmt.Errorf("unsupported URL scheme: %s", u.Scheme) } // 'localhost' (exact string) is allowed *with* a required port number; SSL is optional if u.Hostname() == "localhost" { if u.Port() == "" || !strings.HasPrefix(u.Host, "localhost:") { return "", false, fmt.Errorf("port number is required for localhost") } return u.Host, noSSL, nil } // port numbers not allowed otherwise if u.Port() != "" { return "", false, fmt.Errorf("port number not allowed for non-local names") } // check it is a real hostname (eg, not IP address or single-word alias) h, err := syntax.ParseHandle(u.Host) if err != nil { return "", false, fmt.Errorf("not a public hostname") } // lower-case in response return h.Normalize().String(), noSSL, nil }