~james/core

core/hostutil/hostutil.go -rw-r--r-- 1.8 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
}

func EnsureHttpScheme(host string) (string, error) {
	hostname, noSSL, err := ParseHostname(host)
	if err != nil {
		return "", err
	}
	if noSSL {
		return "http://" + hostname, nil
	} else {
		return "https://" + hostname, nil
	}
}

func EnsureWsScheme(host string) (string, error) {
	hostname, noSSL, err := ParseHostname(host)
	if err != nil {
		return "", err
	}
	if noSSL {
		return "ws://" + hostname, nil
	} else {
		return "wss://" + hostname, nil
	}
}