formatHost ensures the device address is properly formatted for URL construction. IPv6 addresses are wrapped in brackets per RFC 3986. IPv4 and FQDN addresses pass through unchanged. Invalid addresses return an error.
(source string)
| 521 | // IPv6 addresses are wrapped in brackets per RFC 3986. |
| 522 | // IPv4 and FQDN addresses pass through unchanged. |
| 523 | // Invalid addresses return an error. |
| 524 | func formatHost(source string) (string, error) { |
| 525 | if source == "" { |
| 526 | return "", errors.New("device address cannot be empty") |
| 527 | } |
| 528 | |
| 529 | // Handle already-bracketed input |
| 530 | if strings.HasPrefix(source, "[") && strings.HasSuffix(source, "]") { |
| 531 | inner := source[1 : len(source)-1] |
| 532 | ip := net.ParseIP(inner) |
| 533 | if ip == nil { |
| 534 | return "", fmt.Errorf("invalid IP address in brackets: %q", inner) |
| 535 | } |
| 536 | if ip.To4() != nil { |
| 537 | // IPv4 in brackets is an RFC 3986 violation |
| 538 | return "", fmt.Errorf("IPv4 address must not be bracketed: %q", source) |
| 539 | } |
| 540 | // IPv6 in brackets — already correctly formatted |
| 541 | return source, nil |
| 542 | } |
| 543 | |
| 544 | // Try parsing as IP address |
| 545 | if ip := net.ParseIP(source); ip != nil { |
| 546 | if ip.To4() == nil { |
| 547 | // IPv6 — must wrap in brackets for URL use |
| 548 | return "[" + source + "]", nil |
| 549 | } |
| 550 | // IPv4 — use as-is |
| 551 | return source, nil |
| 552 | } |
| 553 | |
| 554 | // Not an IP — treat as hostname/FQDN. |
| 555 | // Build a probe URL and verify the input did not bleed into another URL |
| 556 | // component (path, query, fragment, userinfo). The Host and Path must |
| 557 | // match exactly what we expect — anything else means the input contained |
| 558 | // URL-significant characters (e.g. "/", "?", "#", "@") and could later |
| 559 | // corrupt the API endpoint when substituted into request templates. |
| 560 | const probePath = "/api/test" |
| 561 | testURL := fmt.Sprintf("https://%s%s", source, probePath) |
| 562 | u, err := url.Parse(testURL) |
| 563 | if err != nil { |
| 564 | return "", fmt.Errorf("invalid device address %q: %w", source, err) |
| 565 | } |
| 566 | if u.Host != source || u.Path != probePath { |
| 567 | return "", fmt.Errorf("invalid device address %q", source) |
| 568 | } |
| 569 | |
| 570 | return source, nil |
| 571 | } |
| 572 | |
| 573 | // NewAPI returns an pointer of API struct with default values. |
no outgoing calls
searching dependent graphs…