host.go (2021B)
1 package http 2 3 import ( 4 "fmt" 5 "net" 6 "strconv" 7 "strings" 8 ) 9 10 // ValidateEndpointHost validates that the host string passed in is a valid RFC 11 // 3986 host. Returns error if the host is not valid. 12 func ValidateEndpointHost(host string) error { 13 var errors strings.Builder 14 var hostname string 15 var port string 16 var err error 17 18 if strings.Contains(host, ":") { 19 hostname, port, err = net.SplitHostPort(host) 20 if err != nil { 21 errors.WriteString(fmt.Sprintf("\n endpoint %v, failed to parse, got ", host)) 22 errors.WriteString(err.Error()) 23 } 24 25 if !ValidPortNumber(port) { 26 errors.WriteString(fmt.Sprintf("port number should be in range [0-65535], got %v", port)) 27 } 28 } else { 29 hostname = host 30 } 31 32 labels := strings.Split(hostname, ".") 33 for i, label := range labels { 34 if i == len(labels)-1 && len(label) == 0 { 35 // Allow trailing dot for FQDN hosts. 36 continue 37 } 38 39 if !ValidHostLabel(label) { 40 errors.WriteString("\nendpoint host domain labels must match \"[a-zA-Z0-9-]{1,63}\", but found: ") 41 errors.WriteString(label) 42 } 43 } 44 45 if len(hostname) == 0 && len(port) != 0 { 46 errors.WriteString("\nendpoint host with port must not be empty") 47 } 48 49 if len(hostname) > 255 { 50 errors.WriteString(fmt.Sprintf("\nendpoint host must be less than 255 characters, but was %d", len(hostname))) 51 } 52 53 if len(errors.String()) > 0 { 54 return fmt.Errorf("invalid endpoint host%s", errors.String()) 55 } 56 return nil 57 } 58 59 // ValidPortNumber returns whether the port is valid RFC 3986 port. 60 func ValidPortNumber(port string) bool { 61 i, err := strconv.Atoi(port) 62 if err != nil { 63 return false 64 } 65 66 if i < 0 || i > 65535 { 67 return false 68 } 69 return true 70 } 71 72 // ValidHostLabel returns whether the label is a valid RFC 3986 host abel. 73 func ValidHostLabel(label string) bool { 74 if l := len(label); l == 0 || l > 63 { 75 return false 76 } 77 for _, r := range label { 78 switch { 79 case r >= '0' && r <= '9': 80 case r >= 'A' && r <= 'Z': 81 case r >= 'a' && r <= 'z': 82 case r == '-': 83 default: 84 return false 85 } 86 } 87 88 return true 89 }