|
15 | 15 | package endpoint |
16 | 16 |
|
17 | 17 | import ( |
| 18 | + "net" |
18 | 19 | "net/url" |
19 | | - "regexp" |
| 20 | + "path" |
| 21 | + "strings" |
20 | 22 | ) |
21 | 23 |
|
22 | | -var ( |
23 | | - STRIP_PORT_REGEXP = regexp.MustCompile("(.*):([0-9]+)") |
| 24 | +type CredsRequirement int |
| 25 | + |
| 26 | +const ( |
| 27 | + // CREDS_REQUIRE - Credentials/certificate required for thi type of connection. |
| 28 | + CREDS_REQUIRE CredsRequirement = iota |
| 29 | + // CREDS_DROP - Credentials/certificate not needed and should get ignored. |
| 30 | + CREDS_DROP |
| 31 | + // CREDS_OPTIONAL - Credentials/certificate might be used if supplied |
| 32 | + CREDS_OPTIONAL |
24 | 33 | ) |
25 | 34 |
|
26 | | -func stripPort(ep string) string { |
27 | | - return STRIP_PORT_REGEXP.ReplaceAllString(ep, "$1") |
| 35 | +func extractHostFromHostPort(ep string) string { |
| 36 | + host, _, err := net.SplitHostPort(ep) |
| 37 | + if err != nil { |
| 38 | + return ep |
| 39 | + } |
| 40 | + return host |
28 | 41 | } |
29 | 42 |
|
30 | | -func translateEndpoint(ep string) (addr string, serverName string, requireCreds bool) { |
31 | | - url, err := url.Parse(ep) |
32 | | - if err != nil { |
33 | | - return ep, stripPort(ep), false |
| 43 | +func extractHostFromPath(pathStr string) string { |
| 44 | + return extractHostFromHostPort(path.Base(pathStr)) |
| 45 | +} |
| 46 | + |
| 47 | +//split2 returns the values from strings.SplitN(s, sep, 2). |
| 48 | +//If sep is not found, it returns ("", "", false) instead. |
| 49 | +func split2(s, sep string) (string, string, bool) { |
| 50 | + spl := strings.SplitN(s, sep, 2) |
| 51 | + if len(spl) < 2 { |
| 52 | + return "", "", false |
34 | 53 | } |
35 | | - switch url.Scheme { |
36 | | - case "http", "https": |
37 | | - return url.Host, url.Hostname(), url.Scheme == "https" |
38 | | - case "unix", "unixs": |
39 | | - requireCreds = url.Scheme == "unixs" |
40 | | - if url.Opaque != "" { |
41 | | - return "unix:" + url.Opaque, stripPort(url.Opaque), requireCreds |
42 | | - } else if url.Path != "" { |
43 | | - return "unix://" + url.Host + url.Path, url.Host + url.Path, requireCreds |
44 | | - } else { |
45 | | - return "unix:" + url.Host, url.Hostname(), requireCreds |
46 | | - } |
| 54 | + return spl[0], spl[1], true |
| 55 | +} |
| 56 | + |
| 57 | +func schemeToCredsRequirement(schema string) CredsRequirement { |
| 58 | + switch schema { |
| 59 | + case "https": |
| 60 | + return CREDS_REQUIRE |
| 61 | + case "unixs": |
| 62 | + return CREDS_REQUIRE |
| 63 | + case "http": |
| 64 | + return CREDS_DROP |
| 65 | + case "unix": |
| 66 | + // Preserving previous behavior from: |
| 67 | + // https://github.com/etcd-io/etcd/blob/dae29bb719dd69dc119146fc297a0628fcc1ccf8/client/v3/client.go#L212 |
| 68 | + // that likely was a bug due to missing 'fallthrough'. |
| 69 | + // At the same time it seems legit to let the users decide whether they |
| 70 | + // want credential control or not (and 'unixs' schema is not a standard thing). |
| 71 | + return CREDS_OPTIONAL |
47 | 72 | case "": |
48 | | - return url.Host + url.Path, url.Host + url.Path, false |
| 73 | + return CREDS_OPTIONAL |
49 | 74 | default: |
50 | | - return ep, stripPort(ep), false |
| 75 | + return CREDS_OPTIONAL |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +// This function translates endpoints names supported by etcd server into |
| 80 | +// endpoints as supported by grpc with additional information |
| 81 | +// (server_name for cert validation, requireCreds - whether certs are needed). |
| 82 | +// The main differences: |
| 83 | +// - etcd supports unixs & https names as opposed to unix & http to |
| 84 | +// distinguish need to configure certificates. |
| 85 | +// - etcd support http(s) names as opposed to tcp supported by grpc/dial method. |
| 86 | +// - etcd supports unix(s)://local-file naming schema |
| 87 | +// (as opposed to unit:local-file canonical name). |
| 88 | +// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon) |
| 89 | +// is considered serverName - to allow local testing of cert-protected communication. |
| 90 | +// See more: |
| 91 | +// - https://github.com/grpc/grpc-go/blob/26c143bd5f59344a4b8a1e491e0f5e18aa97abc7/internal/grpcutil/target.go#L47 |
| 92 | +// - https://golang.org/pkg/net/#Dial |
| 93 | +// - https://github.com/grpc/grpc/blob/master/doc/naming.md |
| 94 | +func translateEndpoint(ep string) (addr string, serverName string, requireCreds CredsRequirement) { |
| 95 | + if strings.HasPrefix(ep, "unix:") || strings.HasPrefix(ep, "unixs:") { |
| 96 | + if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") { |
| 97 | + // absolute path case |
| 98 | + schema, absolutePath, _ := split2(ep, "://") |
| 99 | + return "unix://" + absolutePath, extractHostFromPath(absolutePath), schemeToCredsRequirement(schema) |
| 100 | + } |
| 101 | + if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") { |
| 102 | + // legacy etcd local path |
| 103 | + schema, localPath, _ := split2(ep, "://") |
| 104 | + return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema) |
| 105 | + } |
| 106 | + schema, localPath, _ := split2(ep, ":") |
| 107 | + return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema) |
| 108 | + } |
| 109 | + |
| 110 | + if strings.Contains(ep, "://") { |
| 111 | + url, err := url.Parse(ep) |
| 112 | + if err != nil { |
| 113 | + return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL |
| 114 | + } |
| 115 | + if url.Scheme == "http" || url.Scheme == "https" { |
| 116 | + return url.Host, url.Hostname(), schemeToCredsRequirement(url.Scheme) |
| 117 | + } |
| 118 | + return ep, url.Hostname(), schemeToCredsRequirement(url.Scheme) |
51 | 119 | } |
| 120 | + // Handles plain addresses like 10.0.0.44:437 |
| 121 | + return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL |
52 | 122 | } |
53 | 123 |
|
54 | 124 | // RequiresCredentials returns whether given endpoint requires |
55 | 125 | // credentials/certificates for connection. |
56 | | -func RequiresCredentials(ep string) bool { |
| 126 | +func RequiresCredentials(ep string) CredsRequirement { |
57 | 127 | _, _, requireCreds := translateEndpoint(ep) |
58 | 128 | return requireCreds |
59 | 129 | } |
|
0 commit comments