|
55 | 55 | import java.net.URI;
|
56 | 56 | import java.net.URISyntaxException;
|
57 | 57 | import java.nio.charset.Charset;
|
| 58 | +import java.util.Arrays; |
58 | 59 | import java.util.Collection;
|
59 | 60 | import java.util.Collections;
|
60 | 61 | import java.util.EnumSet;
|
| 62 | +import java.util.HashSet; |
61 | 63 | import java.util.List;
|
62 | 64 | import java.util.Locale;
|
63 | 65 | import java.util.Set;
|
@@ -526,8 +528,8 @@ public static URI authorityToUri(String authority) {
|
526 | 528 | */
|
527 | 529 | public static String checkAuthority(String authority) {
|
528 | 530 | URI uri = authorityToUri(authority);
|
529 |
| - checkArgument(uri.getHost() != null, "No host in authority '%s'", authority); |
530 |
| - checkArgument(uri.getUserInfo() == null, |
| 531 | + // Verify that the user Info is not provided. |
| 532 | + checkArgument(uri.getAuthority().indexOf('@') == -1, |
531 | 533 | "Userinfo must not be present on authority: '%s'", authority);
|
532 | 534 | return authority;
|
533 | 535 | }
|
@@ -859,5 +861,92 @@ static <T> boolean iterableContains(Iterable<T> iterable, T item) {
|
859 | 861 | return false;
|
860 | 862 | }
|
861 | 863 |
|
| 864 | + /** |
| 865 | + * Percent encode the {@code authority} based on |
| 866 | + * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2. |
| 867 | + * |
| 868 | + * <p>When escaping a String, the following rules apply: |
| 869 | + * |
| 870 | + * <ul> |
| 871 | + * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain |
| 872 | + * the same. |
| 873 | + * <li>The unreserved characters ".", "-", "~", and "_" remain the same. |
| 874 | + * <li>The general delimiters for authority, "[", "]", "@" and ":" remain the same. |
| 875 | + * <li>The subdelimiters "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", and "=" remain |
| 876 | + * the same. |
| 877 | + * <li>The space character " " is converted into %20. |
| 878 | + * <li>All other characters are converted into one or more bytes using UTF-8 encoding and each |
| 879 | + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, |
| 880 | + * uppercase, hexadecimal representation of the byte value. |
| 881 | + * </ul> |
| 882 | + * |
| 883 | + * <p>This section does not use URLEscapers from Guava Net as its not Android-friendly thus core |
| 884 | + * can't depend on it. |
| 885 | + */ |
| 886 | + public static class AuthorityEscaper { |
| 887 | + // Escapers should output upper case hex digits. |
| 888 | + private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray(); |
| 889 | + private static final Set<Character> UNRESERVED_CHARACTERS = Collections |
| 890 | + .unmodifiableSet(new HashSet<>(Arrays.asList('-', '_', '.', '~'))); |
| 891 | + private static final Set<Character> SUB_DELIMS = Collections |
| 892 | + .unmodifiableSet(new HashSet<>( |
| 893 | + Arrays.asList('!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='))); |
| 894 | + private static final Set<Character> AUTHORITY_DELIMS = Collections |
| 895 | + .unmodifiableSet(new HashSet<>(Arrays.asList(':', '[', ']', '@'))); |
| 896 | + |
| 897 | + private static boolean shouldEscape(char c) { |
| 898 | + // Only encode ASCII. |
| 899 | + if (c > 127) { |
| 900 | + return false; |
| 901 | + } |
| 902 | + // Letters don't need an escape. |
| 903 | + if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) { |
| 904 | + return false; |
| 905 | + } |
| 906 | + // Numbers don't need to be escaped. |
| 907 | + if ((c >= '0' && c <= '9')) { |
| 908 | + return false; |
| 909 | + } |
| 910 | + // Don't escape allowed characters. |
| 911 | + if (UNRESERVED_CHARACTERS.contains(c) |
| 912 | + || SUB_DELIMS.contains(c) |
| 913 | + || AUTHORITY_DELIMS.contains(c)) { |
| 914 | + return false; |
| 915 | + } |
| 916 | + return true; |
| 917 | + } |
| 918 | + |
| 919 | + public static String encodeAuthority(String authority) { |
| 920 | + Preconditions.checkNotNull(authority, "authority"); |
| 921 | + int authorityLength = authority.length(); |
| 922 | + int hexCount = 0; |
| 923 | + // Calculate how many characters actually need escaping. |
| 924 | + for (int index = 0; index < authorityLength; index++) { |
| 925 | + char c = authority.charAt(index); |
| 926 | + if (shouldEscape(c)) { |
| 927 | + hexCount++; |
| 928 | + } |
| 929 | + } |
| 930 | + // If no char need escaping, just return the original string back. |
| 931 | + if (hexCount == 0) { |
| 932 | + return authority; |
| 933 | + } |
| 934 | + |
| 935 | + // Allocate enough space as encoded characters need 2 extra chars. |
| 936 | + StringBuilder encoded_authority = new StringBuilder((2 * hexCount) + authorityLength); |
| 937 | + for (int index = 0; index < authorityLength; index++) { |
| 938 | + char c = authority.charAt(index); |
| 939 | + if (shouldEscape(c)) { |
| 940 | + encoded_authority.append('%'); |
| 941 | + encoded_authority.append(UPPER_HEX_DIGITS[c >>> 4]); |
| 942 | + encoded_authority.append(UPPER_HEX_DIGITS[c & 0xF]); |
| 943 | + } else { |
| 944 | + encoded_authority.append(c); |
| 945 | + } |
| 946 | + } |
| 947 | + return encoded_authority.toString(); |
| 948 | + } |
| 949 | + } |
| 950 | + |
862 | 951 | private GrpcUtil() {}
|
863 | 952 | }
|
0 commit comments