diff --git a/okhttp-tests/src/test/java/okhttp3/CookieTest.java b/okhttp-tests/src/test/java/okhttp3/CookieTest.java index dacbddb9d..5ea0813ac 100644 --- a/okhttp-tests/src/test/java/okhttp3/CookieTest.java +++ b/okhttp-tests/src/test/java/okhttp3/CookieTest.java @@ -19,7 +19,6 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import okhttp3.internal.Util; -import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -27,8 +26,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -public class CookieTest { - HttpUrl url = HttpUrl.parse("http://example.com/"); +public final class CookieTest { + HttpUrl url = HttpUrl.parse("https://example.com/"); @Test public void test() throws Exception { Cookie cookie = Cookie.parse(url, "SID=31d4d96e407aad42"); @@ -179,7 +178,6 @@ public class CookieTest { Cookie.parse(url, "a=b; Expires=Thu, 01 Jan 1970 00:00:60 GMT").expiresAt()); } - @Ignore("cookie matching isn't implemented") @Test public void domainMatches() throws Exception { Cookie cookie = Cookie.parse(url, "a=b; domain=example.com"); assertTrue(cookie.matches(HttpUrl.parse("http://example.com"))); @@ -188,7 +186,6 @@ public class CookieTest { } /** If no domain is present, match only the origin domain. */ - @Ignore("cookie matching isn't implemented") @Test public void domainMatchesNoDomain() throws Exception { Cookie cookie = Cookie.parse(url, "a=b"); assertTrue(cookie.matches(HttpUrl.parse("http://example.com"))); @@ -197,7 +194,6 @@ public class CookieTest { } /** Ignore an optional leading `.` in the domain. */ - @Ignore("cookie matching isn't implemented") @Test public void domainMatchesIgnoresLeadingDot() throws Exception { Cookie cookie = Cookie.parse(url, "a=b; domain=.example.com"); assertTrue(cookie.matches(HttpUrl.parse("http://example.com"))); @@ -206,7 +202,6 @@ public class CookieTest { } /** Ignore the entire attribute if the domain ends with `.`. */ - @Ignore("cookie matching isn't implemented") @Test public void domainIgnoredWithTrailingDot() throws Exception { Cookie cookie = Cookie.parse(url, "a=b; domain=example.com."); assertTrue(cookie.matches(HttpUrl.parse("http://example.com"))); @@ -214,6 +209,64 @@ public class CookieTest { assertFalse(cookie.matches(HttpUrl.parse("http://square.com"))); } + @Test public void idnDomainMatches() throws Exception { + Cookie cookie = Cookie.parse(HttpUrl.parse("http://☃.net/"), "a=b; domain=☃.net"); + assertTrue(cookie.matches(HttpUrl.parse("http://☃.net/"))); + assertTrue(cookie.matches(HttpUrl.parse("http://xn--n3h.net/"))); + assertTrue(cookie.matches(HttpUrl.parse("http://www.☃.net/"))); + assertTrue(cookie.matches(HttpUrl.parse("http://www.xn--n3h.net/"))); + } + + @Test public void punycodeDomainMatches() throws Exception { + Cookie cookie = Cookie.parse(HttpUrl.parse("http://xn--n3h.net/"), "a=b; domain=xn--n3h.net"); + assertTrue(cookie.matches(HttpUrl.parse("http://☃.net/"))); + assertTrue(cookie.matches(HttpUrl.parse("http://xn--n3h.net/"))); + assertTrue(cookie.matches(HttpUrl.parse("http://www.☃.net/"))); + assertTrue(cookie.matches(HttpUrl.parse("http://www.xn--n3h.net/"))); + } + + @Test public void domainMatchesIpAddress() throws Exception { + HttpUrl urlWithIp = HttpUrl.parse("http://123.45.234.56/"); + assertNull(Cookie.parse(urlWithIp, "a=b; domain=234.56")); + assertEquals("123.45.234.56", Cookie.parse(urlWithIp, "a=b; domain=123.45.234.56").domain()); + } + + @Test public void hostOnly() throws Exception { + assertTrue(Cookie.parse(url, "a=b").hostOnly()); + assertFalse(Cookie.parse(url, "a=b; domain=example.com").hostOnly()); + } + + @Test public void defaultPath() throws Exception { + assertEquals("/foo", Cookie.parse(HttpUrl.parse("http://example.com/foo/bar"), "a=b").path()); + assertEquals("/foo", Cookie.parse(HttpUrl.parse("http://example.com/foo/"), "a=b").path()); + assertEquals("/", Cookie.parse(HttpUrl.parse("http://example.com/foo"), "a=b").path()); + assertEquals("/", Cookie.parse(HttpUrl.parse("http://example.com/"), "a=b").path()); + } + + @Test public void defaultPathIsUsedIfPathDoesntHaveLeadingSlash() throws Exception { + assertEquals("/foo", Cookie.parse(HttpUrl.parse("http://example.com/foo/bar"), + "a=b; path=quux").path()); + assertEquals("/foo", Cookie.parse(HttpUrl.parse("http://example.com/foo/bar"), + "a=b; path=").path()); + } + + @Test public void pathAttributeDoesntNeedToMatch() throws Exception { + assertEquals("/quux", Cookie.parse(HttpUrl.parse("http://example.com/"), + "a=b; path=/quux").path()); + assertEquals("/quux", Cookie.parse(HttpUrl.parse("http://example.com/foo/bar"), + "a=b; path=/quux").path()); + } + + @Test public void httpOnly() throws Exception { + assertFalse(Cookie.parse(url, "a=b").httpOnly()); + assertTrue(Cookie.parse(url, "a=b; HttpOnly").httpOnly()); + } + + @Test public void secure() throws Exception { + assertFalse(Cookie.parse(url, "a=b").secure()); + assertTrue(Cookie.parse(url, "a=b; Secure").secure()); + } + @Test public void maxAgeTakesPrecedenceOverExpires() throws Exception { // Max-Age = 1, Expires = 2. In either order. assertEquals(1000L, Cookie.parse( diff --git a/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java b/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java index 113a71a17..6d9ee3bf3 100644 --- a/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java +++ b/okhttp-tests/src/test/java/okhttp3/internal/tls/HostnameVerifierTest.java @@ -517,28 +517,28 @@ public final class HostnameVerifierTest { @Test public void verifyAsIpAddress() { // IPv4 - assertTrue(OkHostnameVerifier.verifyAsIpAddress("127.0.0.1")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("1.2.3.4")); + assertTrue(Util.verifyAsIpAddress("127.0.0.1")); + assertTrue(Util.verifyAsIpAddress("1.2.3.4")); // IPv6 - assertTrue(OkHostnameVerifier.verifyAsIpAddress("::1")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("2001:db8::1")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("::192.168.0.1")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("::ffff:192.168.0.1")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("1080:0:0:0:8:800:200C:417A")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("1080::8:800:200C:417A")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("FF01::101")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("0:0:0:0:0:0:13.1.68.3")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("0:0:0:0:0:FFFF:129.144.52.38")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("::13.1.68.3")); - assertTrue(OkHostnameVerifier.verifyAsIpAddress("::FFFF:129.144.52.38")); + assertTrue(Util.verifyAsIpAddress("::1")); + assertTrue(Util.verifyAsIpAddress("2001:db8::1")); + assertTrue(Util.verifyAsIpAddress("::192.168.0.1")); + assertTrue(Util.verifyAsIpAddress("::ffff:192.168.0.1")); + assertTrue(Util.verifyAsIpAddress("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210")); + assertTrue(Util.verifyAsIpAddress("1080:0:0:0:8:800:200C:417A")); + assertTrue(Util.verifyAsIpAddress("1080::8:800:200C:417A")); + assertTrue(Util.verifyAsIpAddress("FF01::101")); + assertTrue(Util.verifyAsIpAddress("0:0:0:0:0:0:13.1.68.3")); + assertTrue(Util.verifyAsIpAddress("0:0:0:0:0:FFFF:129.144.52.38")); + assertTrue(Util.verifyAsIpAddress("::13.1.68.3")); + assertTrue(Util.verifyAsIpAddress("::FFFF:129.144.52.38")); // Hostnames - assertFalse(OkHostnameVerifier.verifyAsIpAddress("go")); - assertFalse(OkHostnameVerifier.verifyAsIpAddress("localhost")); - assertFalse(OkHostnameVerifier.verifyAsIpAddress("squareup.com")); - assertFalse(OkHostnameVerifier.verifyAsIpAddress("www.nintendo.co.jp")); + assertFalse(Util.verifyAsIpAddress("go")); + assertFalse(Util.verifyAsIpAddress("localhost")); + assertFalse(Util.verifyAsIpAddress("squareup.com")); + assertFalse(Util.verifyAsIpAddress("www.nintendo.co.jp")); } private X509Certificate certificate(String certificate) throws Exception { diff --git a/okhttp/src/main/java/okhttp3/Cookie.java b/okhttp/src/main/java/okhttp3/Cookie.java index 1de7fa407..82048f370 100644 --- a/okhttp/src/main/java/okhttp3/Cookie.java +++ b/okhttp/src/main/java/okhttp3/Cookie.java @@ -23,9 +23,17 @@ import java.util.regex.Pattern; import static okhttp3.internal.Util.UTC; import static okhttp3.internal.Util.delimiterOffset; +import static okhttp3.internal.Util.domainToAscii; import static okhttp3.internal.Util.trimSubstring; +import static okhttp3.internal.Util.verifyAsIpAddress; -/** An RFC 6265 Cookie. */ +/** + * An RFC 6265 Cookie. + * + *

This class doesn't support additional attributes on cookies, like Chromium's Priority=HIGH + * extension. + */ public final class Cookie { private static final Pattern YEAR_PATTERN = Pattern.compile("(\\d{2,4})[^\\d]*"); @@ -41,20 +49,20 @@ public final class Cookie { private final long expiresAt; private final String domain; private final String path; - private final boolean secureOnly; + private final boolean secure; private final boolean httpOnly; - private final boolean persistent; - private final boolean hostOnly; // True if there's no domain attribute + private final boolean persistent; // True if 'expires' or 'max-age' is present. + private final boolean hostOnly; // True unless 'domain' is present. private Cookie(String name, String value, long expiresAt, String domain, String path, - boolean secureOnly, boolean httpOnly, boolean hostOnly, boolean persistent) { + boolean secure, boolean httpOnly, boolean hostOnly, boolean persistent) { this.name = name; this.value = value; this.expiresAt = expiresAt; this.domain = domain; this.path = path; - this.secureOnly = secureOnly; + this.secure = secure; this.httpOnly = httpOnly; this.hostOnly = hostOnly; this.persistent = persistent; @@ -102,6 +110,84 @@ public final class Cookie { return hostOnly; } + /** + * Returns the cookie's domain. If {@link #hostOnly()} returns true this is the only domain that + * matches this cookie; otherwise it matches this domain and all subdomains. + */ + public String domain() { + return domain; + } + + /** + * Returns this cookie's path. This cookie matches URLs prefixed with path segments that match + * this path's segments. For example, if this path is {@code /foo} this cookie matches requests to + * {@code /foo} and {@code /foo/bar}, but not {@code /} or {@code /football}. + */ + public String path() { + return path; + } + + /** + * Returns true if this cookie should be limited to only HTTP APIs. In web browsers this prevents + * the cookie from being accessible to scripts. + */ + public boolean httpOnly() { + return httpOnly; + } + + /** Returns true if this cookie should be limited to only HTTPS requests. */ + public boolean secure() { + return secure; + } + + /** + * Returns true if this cookie should be included on a request to {@code url}. In addition to this + * check callers should also confirm that this cookie has not expired. + */ + public boolean matches(HttpUrl url) { + boolean domainMatch = hostOnly + ? url.host().equals(domain) + : domainMatch(url, domain); + if (!domainMatch) return false; + + if (!pathMatch(url, path)) return false; + + if (secure && !url.isHttps()) return false; + + return true; + } + + private static boolean domainMatch(HttpUrl url, String domain) { + String urlHost = url.host(); + + if (urlHost.equals(domain)) { + return true; // As in 'example.com' matching 'example.com'. + } + + if (urlHost.endsWith(domain) + && urlHost.charAt(urlHost.length() - domain.length() - 1) == '.' + && !verifyAsIpAddress(urlHost)) { + return true; // As in 'example.com' matching 'www.example.com'. + } + + return false; + } + + private static boolean pathMatch(HttpUrl url, String path) { + String urlPath = url.encodedPath(); + + if (urlPath.equals(path)) { + return true; // As in '/foo' matching '/foo'. + } + + if (urlPath.startsWith(path)) { + if (path.endsWith("/")) return true; // As in '/' matching '/foo'. + if (urlPath.charAt(path.length()) == '/') return true; // As in '/foo' matching '/foo/bar'. + } + + return false; + } + /** * Attempt to parse a {@code Set-Cookie} HTTP header value {@code setCookie} as a cookie. Returns * null if {@code setCookie} is not a well-formed cookie. @@ -157,8 +243,12 @@ public final class Cookie { // Ignore this attribute, it isn't recognizable as a max age. } } else if (attributeName.equalsIgnoreCase("domain")) { - domain = parseDomain(attributeValue); - hostOnly = false; + try { + domain = parseDomain(attributeValue); + hostOnly = false; + } catch (IllegalArgumentException e) { + // Ignore this attribute, it isn't recognizable as a domain. + } } else if (attributeName.equalsIgnoreCase("path")) { path = attributeValue; } else if (attributeName.equalsIgnoreCase("secure")) { @@ -182,16 +272,21 @@ public final class Cookie { if (expiresAt < currentTimeMillis) expiresAt = Long.MAX_VALUE; // Clamp overflow. } + // If the domain is present, it must domain match. Otherwise we have a host-only cookie. + if (domain == null) { + domain = url.host(); + } else if (!domainMatch(url, domain)) { + return null; // No domain match? This is either incompetence or malice! + } + // If the path is absent or didn't start with '/', use the default path. It's a string like // '/foo/bar' for a URL like 'http://example.com/foo/bar/baz'. It always starts with '/'. if (path == null || !path.startsWith("/")) { String encodedPath = url.encodedPath(); int lastSlash = encodedPath.lastIndexOf('/'); - path = lastSlash == 0 ? encodedPath.substring(0, lastSlash) : "/"; + path = lastSlash != 0 ? encodedPath.substring(0, lastSlash) : "/"; } - // TODO(jwilson): validate that the domain matches. - return new Cookie(cookieName, cookieValue, expiresAt, domain, path, secureOnly, httpOnly, hostOnly, persistent); } @@ -295,14 +390,17 @@ public final class Cookie { * or {@code .example.com}. */ private static String parseDomain(String s) { + if (s.endsWith(".")) { + throw new IllegalArgumentException(); + } if (s.startsWith(".")) { s = s.substring(1); } - return s.toLowerCase(Locale.US); - } - - public boolean matches(HttpUrl url) { - throw new UnsupportedOperationException("TODO"); + String canonicalDomain = domainToAscii(s); + if (canonicalDomain == null) { + throw new IllegalArgumentException(); + } + return canonicalDomain; } @Override public String toString() { diff --git a/okhttp/src/main/java/okhttp3/HttpUrl.java b/okhttp/src/main/java/okhttp3/HttpUrl.java index 64135d7c7..59cbdd435 100644 --- a/okhttp/src/main/java/okhttp3/HttpUrl.java +++ b/okhttp/src/main/java/okhttp3/HttpUrl.java @@ -15,7 +15,6 @@ */ package okhttp3; -import java.net.IDN; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URI; @@ -27,11 +26,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.Locale; import java.util.Set; import okio.Buffer; import static okhttp3.internal.Util.delimiterOffset; +import static okhttp3.internal.Util.domainToAscii; import static okhttp3.internal.Util.skipLeadingAsciiWhitespace; import static okhttp3.internal.Util.skipTrailingAsciiWhitespace; @@ -1389,47 +1388,6 @@ public final class HttpUrl { return true; // Success. } - /** - * Performs IDN ToASCII encoding and canonicalize the result to lowercase. e.g. This converts - * {@code ☃.net} to {@code xn--n3h.net}, and {@code WwW.GoOgLe.cOm} to {@code www.google.com}. - * {@code null} will be returned if the input cannot be ToASCII encoded or if the result - * contains unsupported ASCII characters. - */ - private static String domainToAscii(String input) { - try { - String result = IDN.toASCII(input).toLowerCase(Locale.US); - if (result.isEmpty()) return null; - - // Confirm that the IDN ToASCII result doesn't contain any illegal characters. - if (containsInvalidHostnameAsciiCodes(result)) { - return null; - } - // TODO: implement all label limits. - return result; - } catch (IllegalArgumentException e) { - return null; - } - } - - private static boolean containsInvalidHostnameAsciiCodes(String hostnameAscii) { - for (int i = 0; i < hostnameAscii.length(); i++) { - char c = hostnameAscii.charAt(i); - // The WHATWG Host parsing rules accepts some character codes which are invalid by - // definition for OkHttp's host header checks (and the WHATWG Host syntax definition). Here - // we rule out characters that would cause problems in host headers. - if (c <= '\u001f' || c >= '\u007f') { - return true; - } - // Check for the characters mentioned in the WHATWG Host parsing spec: - // U+0000, U+0009, U+000A, U+000D, U+0020, "#", "%", "/", ":", "?", "@", "[", "\", and "]" - // (excluding the characters covered above). - if (" #%/:?@[\\]".indexOf(c) != -1) { - return true; - } - } - return false; - } - private static String inet6AddressToAscii(byte[] address) { // Go through the address looking for the longest run of 0s. Each group is 2-bytes. int longestRunOffset = -1; diff --git a/okhttp/src/main/java/okhttp3/internal/Util.java b/okhttp/src/main/java/okhttp3/internal/Util.java index 268e76f89..e9efad04c 100644 --- a/okhttp/src/main/java/okhttp3/internal/Util.java +++ b/okhttp/src/main/java/okhttp3/internal/Util.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; +import java.net.IDN; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.Charset; @@ -30,10 +31,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import okhttp3.HttpUrl; import okio.Buffer; import okio.ByteString; @@ -50,6 +53,19 @@ public final class Util { /** GMT and UTC are equivalent for our purposes. */ public static final TimeZone UTC = TimeZone.getTimeZone("GMT"); + /** + * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation + * of Android's private InetAddress#isNumeric API. + * + *

This matches IPv6 addresses as a hex string containing at least one colon, and possibly + * including dots after the first colon. It matches IPv4 addresses as strings containing only + * decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP + * addresses nor hostnames; they will be verified as IP addresses (which is a more strict + * verification). + */ + private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile( + "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); + private Util() { } @@ -371,4 +387,50 @@ public final class Util { } return limit; } + + /** + * Performs IDN ToASCII encoding and canonicalize the result to lowercase. e.g. This converts + * {@code ☃.net} to {@code xn--n3h.net}, and {@code WwW.GoOgLe.cOm} to {@code www.google.com}. + * {@code null} will be returned if the input cannot be ToASCII encoded or if the result + * contains unsupported ASCII characters. + */ + public static String domainToAscii(String input) { + try { + String result = IDN.toASCII(input).toLowerCase(Locale.US); + if (result.isEmpty()) return null; + + // Confirm that the IDN ToASCII result doesn't contain any illegal characters. + if (containsInvalidHostnameAsciiCodes(result)) { + return null; + } + // TODO: implement all label limits. + return result; + } catch (IllegalArgumentException e) { + return null; + } + } + + private static boolean containsInvalidHostnameAsciiCodes(String hostnameAscii) { + for (int i = 0; i < hostnameAscii.length(); i++) { + char c = hostnameAscii.charAt(i); + // The WHATWG Host parsing rules accepts some character codes which are invalid by + // definition for OkHttp's host header checks (and the WHATWG Host syntax definition). Here + // we rule out characters that would cause problems in host headers. + if (c <= '\u001f' || c >= '\u007f') { + return true; + } + // Check for the characters mentioned in the WHATWG Host parsing spec: + // U+0000, U+0009, U+000A, U+000D, U+0020, "#", "%", "/", ":", "?", "@", "[", "\", and "]" + // (excluding the characters covered above). + if (" #%/:?@[\\]".indexOf(c) != -1) { + return true; + } + } + return false; + } + + /** Returns true if {@code host} is not a host name and might be an IP address. */ + public static boolean verifyAsIpAddress(String host) { + return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); + } } diff --git a/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java b/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java index 524623ba3..e6ad9b8c6 100644 --- a/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java +++ b/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java @@ -25,31 +25,19 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.regex.Pattern; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; +import static okhttp3.internal.Util.verifyAsIpAddress; + /** * A HostnameVerifier consistent with RFC 2818. */ public final class OkHostnameVerifier implements HostnameVerifier { public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier(); - /** - * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation - * of Android's private InetAddress#isNumeric API. - * - *

This matches IPv6 addresses as a hex string containing at least one colon, and possibly - * including dots after the first colon. It matches IPv4 addresses as strings containing only - * decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP - * addresses nor hostnames; they will be verified as IP addresses (which is a more strict - * verification). - */ - private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile( - "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); - private static final int ALT_DNS_NAME = 2; private static final int ALT_IPA_NAME = 7; @@ -72,13 +60,7 @@ public final class OkHostnameVerifier implements HostnameVerifier { : verifyHostName(host, certificate); } - static boolean verifyAsIpAddress(String host) { - return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); - } - - /** - * Returns true if {@code certificate} matches {@code ipAddress}. - */ + /** Returns true if {@code certificate} matches {@code ipAddress}. */ private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) { List altNames = getSubjectAltNames(certificate, ALT_IPA_NAME); for (int i = 0, size = altNames.size(); i < size; i++) { @@ -89,9 +71,7 @@ public final class OkHostnameVerifier implements HostnameVerifier { return false; } - /** - * Returns true if {@code certificate} matches {@code hostName}. - */ + /** Returns true if {@code certificate} matches {@code hostName}. */ private boolean verifyHostName(String hostName, X509Certificate certificate) { hostName = hostName.toLowerCase(Locale.US); boolean hasDns = false;