HttpUrlTest.java revision 0771ad685deef1ac376a28dd67ca1921c52bfd8a
1/* 2 * Copyright (C) 2015 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.squareup.okhttp; 17 18import com.squareup.okhttp.UrlComponentEncodingTester.Component; 19import com.squareup.okhttp.UrlComponentEncodingTester.Encoding; 20import java.net.MalformedURLException; 21import java.net.URI; 22import java.net.URL; 23import java.net.UnknownHostException; 24import java.util.Arrays; 25import java.util.Collections; 26import java.util.LinkedHashSet; 27import org.junit.Ignore; 28import org.junit.Test; 29 30import static java.util.Collections.singletonList; 31import static org.junit.Assert.assertEquals; 32import static org.junit.Assert.assertNull; 33import static org.junit.Assert.fail; 34 35public final class HttpUrlTest { 36 @Test public void parseTrimsAsciiWhitespace() throws Exception { 37 HttpUrl expected = HttpUrl.parse("http://host/"); 38 assertEquals(expected, HttpUrl.parse("http://host/\f\n\t \r")); // Leading. 39 assertEquals(expected, HttpUrl.parse("\r\n\f \thttp://host/")); // Trailing. 40 assertEquals(expected, HttpUrl.parse(" http://host/ ")); // Both. 41 assertEquals(expected, HttpUrl.parse(" http://host/ ")); // Both. 42 assertEquals(expected, HttpUrl.parse("http://host/").resolve(" ")); 43 assertEquals(expected, HttpUrl.parse("http://host/").resolve(" . ")); 44 } 45 46 @Test public void parseHostAsciiNonPrintable() throws Exception { 47 String host = "host\u0001"; 48 assertNull(HttpUrl.parse("http://" + host + "/")); 49 } 50 51 @Test public void parseDoesNotTrimOtherWhitespaceCharacters() throws Exception { 52 // Whitespace characters list from Google's Guava team: http://goo.gl/IcR9RD 53 assertEquals("/%0B", HttpUrl.parse("http://h/\u000b").encodedPath()); // line tabulation 54 assertEquals("/%1C", HttpUrl.parse("http://h/\u001c").encodedPath()); // information separator 4 55 assertEquals("/%1D", HttpUrl.parse("http://h/\u001d").encodedPath()); // information separator 3 56 assertEquals("/%1E", HttpUrl.parse("http://h/\u001e").encodedPath()); // information separator 2 57 assertEquals("/%1F", HttpUrl.parse("http://h/\u001f").encodedPath()); // information separator 1 58 assertEquals("/%C2%85", HttpUrl.parse("http://h/\u0085").encodedPath()); // next line 59 assertEquals("/%C2%A0", HttpUrl.parse("http://h/\u00a0").encodedPath()); // non-breaking space 60 assertEquals("/%E1%9A%80", HttpUrl.parse("http://h/\u1680").encodedPath()); // ogham space mark 61 assertEquals("/%E1%A0%8E", HttpUrl.parse("http://h/\u180e").encodedPath()); // mongolian vowel separator 62 assertEquals("/%E2%80%80", HttpUrl.parse("http://h/\u2000").encodedPath()); // en quad 63 assertEquals("/%E2%80%81", HttpUrl.parse("http://h/\u2001").encodedPath()); // em quad 64 assertEquals("/%E2%80%82", HttpUrl.parse("http://h/\u2002").encodedPath()); // en space 65 assertEquals("/%E2%80%83", HttpUrl.parse("http://h/\u2003").encodedPath()); // em space 66 assertEquals("/%E2%80%84", HttpUrl.parse("http://h/\u2004").encodedPath()); // three-per-em space 67 assertEquals("/%E2%80%85", HttpUrl.parse("http://h/\u2005").encodedPath()); // four-per-em space 68 assertEquals("/%E2%80%86", HttpUrl.parse("http://h/\u2006").encodedPath()); // six-per-em space 69 assertEquals("/%E2%80%87", HttpUrl.parse("http://h/\u2007").encodedPath()); // figure space 70 assertEquals("/%E2%80%88", HttpUrl.parse("http://h/\u2008").encodedPath()); // punctuation space 71 assertEquals("/%E2%80%89", HttpUrl.parse("http://h/\u2009").encodedPath()); // thin space 72 assertEquals("/%E2%80%8A", HttpUrl.parse("http://h/\u200a").encodedPath()); // hair space 73 assertEquals("/%E2%80%8B", HttpUrl.parse("http://h/\u200b").encodedPath()); // zero-width space 74 assertEquals("/%E2%80%8C", HttpUrl.parse("http://h/\u200c").encodedPath()); // zero-width non-joiner 75 assertEquals("/%E2%80%8D", HttpUrl.parse("http://h/\u200d").encodedPath()); // zero-width joiner 76 assertEquals("/%E2%80%8E", HttpUrl.parse("http://h/\u200e").encodedPath()); // left-to-right mark 77 assertEquals("/%E2%80%8F", HttpUrl.parse("http://h/\u200f").encodedPath()); // right-to-left mark 78 assertEquals("/%E2%80%A8", HttpUrl.parse("http://h/\u2028").encodedPath()); // line separator 79 assertEquals("/%E2%80%A9", HttpUrl.parse("http://h/\u2029").encodedPath()); // paragraph separator 80 assertEquals("/%E2%80%AF", HttpUrl.parse("http://h/\u202f").encodedPath()); // narrow non-breaking space 81 assertEquals("/%E2%81%9F", HttpUrl.parse("http://h/\u205f").encodedPath()); // medium mathematical space 82 assertEquals("/%E3%80%80", HttpUrl.parse("http://h/\u3000").encodedPath()); // ideographic space 83 } 84 85 @Test public void scheme() throws Exception { 86 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host/")); 87 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("Http://host/")); 88 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host/")); 89 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("HTTP://host/")); 90 assertEquals(HttpUrl.parse("https://host/"), HttpUrl.parse("https://host/")); 91 assertEquals(HttpUrl.parse("https://host/"), HttpUrl.parse("HTTPS://host/")); 92 assertEquals(HttpUrl.Builder.ParseResult.UNSUPPORTED_SCHEME, 93 new HttpUrl.Builder().parse(null, "image640://480.png")); 94 assertEquals(null, HttpUrl.parse("httpp://host/")); 95 assertEquals(null, HttpUrl.parse("0ttp://host/")); 96 assertEquals(null, HttpUrl.parse("ht+tp://host/")); 97 assertEquals(null, HttpUrl.parse("ht.tp://host/")); 98 assertEquals(null, HttpUrl.parse("ht-tp://host/")); 99 assertEquals(null, HttpUrl.parse("ht1tp://host/")); 100 assertEquals(null, HttpUrl.parse("httpss://host/")); 101 } 102 103 @Test public void parseNoScheme() throws Exception { 104 assertEquals(null, HttpUrl.parse("//host")); 105 assertEquals(null, HttpUrl.parse("/path")); 106 assertEquals(null, HttpUrl.parse("path")); 107 assertEquals(null, HttpUrl.parse("?query")); 108 assertEquals(null, HttpUrl.parse("#fragment")); 109 } 110 111 @Test public void resolveNoScheme() throws Exception { 112 HttpUrl base = HttpUrl.parse("http://host/a/b"); 113 assertEquals(HttpUrl.parse("http://host2/"), base.resolve("//host2")); 114 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("/path")); 115 assertEquals(HttpUrl.parse("http://host/a/path"), base.resolve("path")); 116 assertEquals(HttpUrl.parse("http://host/a/b?query"), base.resolve("?query")); 117 assertEquals(HttpUrl.parse("http://host/a/b#fragment"), base.resolve("#fragment")); 118 assertEquals(HttpUrl.parse("http://host/a/b"), base.resolve("")); 119 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("\\path")); 120 } 121 122 @Test public void resolveUnsupportedScheme() throws Exception { 123 HttpUrl base = HttpUrl.parse("http://a/"); 124 assertEquals(null, base.resolve("ftp://b")); 125 assertEquals(null, base.resolve("ht+tp://b")); 126 assertEquals(null, base.resolve("ht-tp://b")); 127 assertEquals(null, base.resolve("ht.tp://b")); 128 } 129 130 @Test public void resolveSchemeLikePath() throws Exception { 131 HttpUrl base = HttpUrl.parse("http://a/"); 132 assertEquals(HttpUrl.parse("http://a/http//b/"), base.resolve("http//b/")); 133 assertEquals(HttpUrl.parse("http://a/ht+tp//b/"), base.resolve("ht+tp//b/")); 134 assertEquals(HttpUrl.parse("http://a/ht-tp//b/"), base.resolve("ht-tp//b/")); 135 assertEquals(HttpUrl.parse("http://a/ht.tp//b/"), base.resolve("ht.tp//b/")); 136 } 137 138 @Test public void parseAuthoritySlashCountDoesntMatter() throws Exception { 139 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:host/path")); 140 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/host/path")); 141 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\host/path")); 142 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://host/path")); 143 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\/host/path")); 144 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/\\host/path")); 145 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\\\host/path")); 146 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:///host/path")); 147 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\//host/path")); 148 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/\\/host/path")); 149 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://\\host/path")); 150 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\\\/host/path")); 151 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/\\\\host/path")); 152 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\\\\\host/path")); 153 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:////host/path")); 154 } 155 156 @Test public void resolveAuthoritySlashCountDoesntMatterWithDifferentScheme() throws Exception { 157 HttpUrl base = HttpUrl.parse("https://a/b/c"); 158 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:host/path")); 159 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/host/path")); 160 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\host/path")); 161 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://host/path")); 162 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\/host/path")); 163 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\host/path")); 164 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\host/path")); 165 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:///host/path")); 166 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\//host/path")); 167 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\/host/path")); 168 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://\\host/path")); 169 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\/host/path")); 170 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\\\host/path")); 171 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\\\host/path")); 172 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:////host/path")); 173 } 174 175 @Test public void resolveAuthoritySlashCountMattersWithSameScheme() throws Exception { 176 HttpUrl base = HttpUrl.parse("http://a/b/c"); 177 assertEquals(HttpUrl.parse("http://a/b/host/path"), base.resolve("http:host/path")); 178 assertEquals(HttpUrl.parse("http://a/host/path"), base.resolve("http:/host/path")); 179 assertEquals(HttpUrl.parse("http://a/host/path"), base.resolve("http:\\host/path")); 180 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://host/path")); 181 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\/host/path")); 182 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\host/path")); 183 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\host/path")); 184 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:///host/path")); 185 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\//host/path")); 186 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\/host/path")); 187 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://\\host/path")); 188 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\/host/path")); 189 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\\\host/path")); 190 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\\\host/path")); 191 assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:////host/path")); 192 } 193 194 @Test public void username() throws Exception { 195 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://@host/path")); 196 assertEquals(HttpUrl.parse("http://user@host/path"), HttpUrl.parse("http://user@host/path")); 197 } 198 199 /** Given multiple '@' characters, the last one is the delimiter. */ 200 @Test public void authorityWithMultipleAtSigns() throws Exception { 201 HttpUrl httpUrl = HttpUrl.parse("http://foo@bar@baz/path"); 202 assertEquals("foo@bar", httpUrl.username()); 203 assertEquals("", httpUrl.password()); 204 assertEquals(HttpUrl.parse("http://foo%40bar@baz/path"), httpUrl); 205 } 206 207 /** Given multiple ':' characters, the first one is the delimiter. */ 208 @Test public void authorityWithMultipleColons() throws Exception { 209 HttpUrl httpUrl = HttpUrl.parse("http://foo:pass1@bar:pass2@baz/path"); 210 assertEquals("foo", httpUrl.username()); 211 assertEquals("pass1@bar:pass2", httpUrl.password()); 212 assertEquals(HttpUrl.parse("http://foo:pass1%40bar%3Apass2@baz/path"), httpUrl); 213 } 214 215 @Test public void usernameAndPassword() throws Exception { 216 assertEquals(HttpUrl.parse("http://username:password@host/path"), 217 HttpUrl.parse("http://username:password@host/path")); 218 assertEquals(HttpUrl.parse("http://username@host/path"), 219 HttpUrl.parse("http://username:@host/path")); 220 } 221 222 @Test public void passwordWithEmptyUsername() throws Exception { 223 // Chrome doesn't mind, but Firefox rejects URLs with empty usernames and non-empty passwords. 224 assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://:@host/path")); 225 assertEquals("password%40", HttpUrl.parse("http://:password@@host/path").encodedPassword()); 226 } 227 228 @Test public void unprintableCharactersArePercentEncoded() throws Exception { 229 assertEquals("/%00", HttpUrl.parse("http://host/\u0000").encodedPath()); 230 assertEquals("/%08", HttpUrl.parse("http://host/\u0008").encodedPath()); 231 assertEquals("/%EF%BF%BD", HttpUrl.parse("http://host/\ufffd").encodedPath()); 232 } 233 234 @Test public void usernameCharacters() throws Exception { 235 new UrlComponentEncodingTester() 236 .override(Encoding.PERCENT, '[', ']', '{', '}', '|', '^', '\'', ';', '=', '@') 237 .override(Encoding.SKIP, ':', '/', '\\', '?', '#') 238 .skipForUri('%') 239 .test(Component.USER); 240 } 241 242 @Test public void passwordCharacters() throws Exception { 243 new UrlComponentEncodingTester() 244 .override(Encoding.PERCENT, '[', ']', '{', '}', '|', '^', '\'', ':', ';', '=', '@') 245 .override(Encoding.SKIP, '/', '\\', '?', '#') 246 .skipForUri('%') 247 .test(Component.PASSWORD); 248 } 249 250 @Test public void hostContainsIllegalCharacter() throws Exception { 251 assertEquals(null, HttpUrl.parse("http://\n/")); 252 assertEquals(null, HttpUrl.parse("http:// /")); 253 assertEquals(null, HttpUrl.parse("http://%20/")); 254 } 255 256 @Test public void hostnameLowercaseCharactersMappedDirectly() throws Exception { 257 assertEquals("abcd", HttpUrl.parse("http://abcd").host()); 258 assertEquals("xn--4xa", HttpUrl.parse("http://σ").host()); 259 } 260 261 @Test public void hostnameUppercaseCharactersConvertedToLowercase() throws Exception { 262 assertEquals("abcd", HttpUrl.parse("http://ABCD").host()); 263 assertEquals("xn--4xa", HttpUrl.parse("http://Σ").host()); 264 } 265 266 @Test public void hostnameIgnoredCharacters() throws Exception { 267 // The soft hyphen () should be ignored. 268 assertEquals("abcd", HttpUrl.parse("http://AB\u00adCD").host()); 269 } 270 271 @Test public void hostnameMultipleCharacterMapping() throws Exception { 272 // Map the single character telephone symbol (℡) to the string "tel". 273 assertEquals("tel", HttpUrl.parse("http://\u2121").host()); 274 } 275 276 @Test public void hostnameMappingLastMappedCodePoint() throws Exception { 277 assertEquals("xn--pu5l", HttpUrl.parse("http://\uD87E\uDE1D").host()); 278 } 279 280 @Ignore("The java.net.IDN implementation doesn't ignore characters that it should.") 281 @Test public void hostnameMappingLastIgnoredCodePoint() throws Exception { 282 assertEquals("abcd", HttpUrl.parse("http://ab\uDB40\uDDEFcd").host()); 283 } 284 285 @Test public void hostnameMappingLastDisallowedCodePoint() throws Exception { 286 assertEquals(null, HttpUrl.parse("http://\uDBFF\uDFFF")); 287 } 288 289 @Test public void hostIpv6() throws Exception { 290 // Square braces are absent from host()... 291 assertEquals("::1", HttpUrl.parse("http://[::1]/").host()); 292 293 // ... but they're included in toString(). 294 assertEquals("http://[::1]/", HttpUrl.parse("http://[::1]/").toString()); 295 296 // IPv6 colons don't interfere with port numbers or passwords. 297 assertEquals(8080, HttpUrl.parse("http://[::1]:8080/").port()); 298 assertEquals("password", HttpUrl.parse("http://user:password@[::1]/").password()); 299 assertEquals("::1", HttpUrl.parse("http://user:password@[::1]:8080/").host()); 300 301 // Permit the contents of IPv6 addresses to be percent-encoded... 302 assertEquals("::1", HttpUrl.parse("http://[%3A%3A%31]/").host()); 303 304 // Including the Square braces themselves! (This is what Chrome does.) 305 assertEquals("::1", HttpUrl.parse("http://%5B%3A%3A1%5D/").host()); 306 } 307 308 @Test public void hostIpv6AddressDifferentFormats() throws Exception { 309 // Multiple representations of the same address; see http://tools.ietf.org/html/rfc5952. 310 String a3 = "2001:db8::1:0:0:1"; 311 assertEquals(a3, HttpUrl.parse("http://[2001:db8:0:0:1:0:0:1]").host()); 312 assertEquals(a3, HttpUrl.parse("http://[2001:0db8:0:0:1:0:0:1]").host()); 313 assertEquals(a3, HttpUrl.parse("http://[2001:db8::1:0:0:1]").host()); 314 assertEquals(a3, HttpUrl.parse("http://[2001:db8::0:1:0:0:1]").host()); 315 assertEquals(a3, HttpUrl.parse("http://[2001:0db8::1:0:0:1]").host()); 316 assertEquals(a3, HttpUrl.parse("http://[2001:db8:0:0:1::1]").host()); 317 assertEquals(a3, HttpUrl.parse("http://[2001:db8:0000:0:1::1]").host()); 318 assertEquals(a3, HttpUrl.parse("http://[2001:DB8:0:0:1::1]").host()); 319 } 320 321 @Test public void hostIpv6AddressLeadingCompression() throws Exception { 322 assertEquals("::1", HttpUrl.parse("http://[::0001]").host()); 323 assertEquals("::1", HttpUrl.parse("http://[0000::0001]").host()); 324 assertEquals("::1", HttpUrl.parse("http://[0000:0000:0000:0000:0000:0000:0000:0001]").host()); 325 assertEquals("::1", HttpUrl.parse("http://[0000:0000:0000:0000:0000:0000::0001]").host()); 326 } 327 328 @Test public void hostIpv6AddressTrailingCompression() throws Exception { 329 assertEquals("1::", HttpUrl.parse("http://[0001:0000::]").host()); 330 assertEquals("1::", HttpUrl.parse("http://[0001::0000]").host()); 331 assertEquals("1::", HttpUrl.parse("http://[0001::]").host()); 332 assertEquals("1::", HttpUrl.parse("http://[1::]").host()); 333 } 334 335 @Test public void hostIpv6AddressTooManyDigitsInGroup() throws Exception { 336 assertEquals(null, HttpUrl.parse("http://[00000:0000:0000:0000:0000:0000:0000:0001]")); 337 assertEquals(null, HttpUrl.parse("http://[::00001]")); 338 } 339 340 @Test public void hostIpv6AddressMisplacedColons() throws Exception { 341 assertEquals(null, HttpUrl.parse("http://[:0000:0000:0000:0000:0000:0000:0000:0001]")); 342 assertEquals(null, HttpUrl.parse("http://[:::0000:0000:0000:0000:0000:0000:0000:0001]")); 343 assertEquals(null, HttpUrl.parse("http://[:1]")); 344 assertEquals(null, HttpUrl.parse("http://[:::1]")); 345 assertEquals(null, HttpUrl.parse("http://[0000:0000:0000:0000:0000:0000:0001:]")); 346 assertEquals(null, HttpUrl.parse("http://[0000:0000:0000:0000:0000:0000:0000:0001:]")); 347 assertEquals(null, HttpUrl.parse("http://[0000:0000:0000:0000:0000:0000:0000:0001::]")); 348 assertEquals(null, HttpUrl.parse("http://[0000:0000:0000:0000:0000:0000:0000:0001:::]")); 349 assertEquals(null, HttpUrl.parse("http://[1:]")); 350 assertEquals(null, HttpUrl.parse("http://[1:::]")); 351 assertEquals(null, HttpUrl.parse("http://[1:::1]")); 352 assertEquals(null, HttpUrl.parse("http://[00000:0000:0000:0000::0000:0000:0000:0001]")); 353 } 354 355 @Test public void hostIpv6AddressTooManyGroups() throws Exception { 356 assertEquals(null, HttpUrl.parse("http://[00000:0000:0000:0000:0000:0000:0000:0000:0001]")); 357 } 358 359 @Test public void hostIpv6AddressTooMuchCompression() throws Exception { 360 assertEquals(null, HttpUrl.parse("http://[0000::0000:0000:0000:0000::0001]")); 361 assertEquals(null, HttpUrl.parse("http://[::0000:0000:0000:0000::0001]")); 362 } 363 364 @Test public void hostIpv6ScopedAddress() throws Exception { 365 // java.net.InetAddress parses scoped addresses. These aren't valid in URLs. 366 assertEquals(null, HttpUrl.parse("http://[::1%2544]")); 367 } 368 369 @Test public void hostIpv6WithIpv4Suffix() throws Exception { 370 assertEquals("::1:ffff:ffff", HttpUrl.parse("http://[::1:255.255.255.255]/").host()); 371 assertEquals("::1:0:0", HttpUrl.parse("http://[0:0:0:0:0:1:0.0.0.0]/").host()); 372 } 373 374 @Test public void hostIpv6WithIpv4SuffixWithOctalPrefix() throws Exception { 375 // Chrome interprets a leading '0' as octal; Firefox rejects them. (We reject them.) 376 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:0.0.0.000000]/")); 377 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:0.010.0.010]/")); 378 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:0.0.0.000001]/")); 379 } 380 381 @Test public void hostIpv6WithIpv4SuffixWithHexadecimalPrefix() throws Exception { 382 // Chrome interprets a leading '0x' as hexadecimal; Firefox rejects them. (We reject them.) 383 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:0.0x10.0.0x10]/")); 384 } 385 386 @Test public void hostIpv6WithMalformedIpv4Suffix() throws Exception { 387 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:0.0:0.0]/")); 388 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:0.0-0.0]/")); 389 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:.255.255.255]/")); 390 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:255..255.255]/")); 391 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:255.255..255]/")); 392 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:0:1:255.255]/")); 393 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:256.255.255.255]/")); 394 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:ff.255.255.255]/")); 395 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:0:1:255.255.255.255]/")); 396 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:1:255.255.255.255]/")); 397 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:1:0.0.0.0:1]/")); 398 assertEquals(null, HttpUrl.parse("http://[0:0.0.0.0:1:0:0:0:0:1]/")); 399 assertEquals(null, HttpUrl.parse("http://[0.0.0.0:0:0:0:0:0:1]/")); 400 } 401 402 @Test public void hostIpv6WithIncompleteIpv4Suffix() throws Exception { 403 // To Chrome & Safari these are well-formed; Firefox disagrees. (We're consistent with Firefox). 404 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:255.255.255.]/")); 405 assertEquals(null, HttpUrl.parse("http://[0:0:0:0:0:1:255.255.255]/")); 406 } 407 408 @Test public void hostIpv6CanonicalForm() throws Exception { 409 assertEquals("abcd:ef01:2345:6789:abcd:ef01:2345:6789", 410 HttpUrl.parse("http://[abcd:ef01:2345:6789:abcd:ef01:2345:6789]/").host()); 411 assertEquals("a::b:0:0:0", HttpUrl.parse("http://[a:0:0:0:b:0:0:0]/").host()); 412 assertEquals("a:b:0:0:c::", HttpUrl.parse("http://[a:b:0:0:c:0:0:0]/").host()); 413 assertEquals("a:b::c:0:0", HttpUrl.parse("http://[a:b:0:0:0:c:0:0]/").host()); 414 assertEquals("a::b:0:0:0", HttpUrl.parse("http://[a:0:0:0:b:0:0:0]/").host()); 415 assertEquals("::a:b:0:0:0", HttpUrl.parse("http://[0:0:0:a:b:0:0:0]/").host()); 416 assertEquals("::a:0:0:0:b", HttpUrl.parse("http://[0:0:0:a:0:0:0:b]/").host()); 417 assertEquals("::a:b:c:d:e:f:1", HttpUrl.parse("http://[0:a:b:c:d:e:f:1]/").host()); 418 assertEquals("a:b:c:d:e:f:1::", HttpUrl.parse("http://[a:b:c:d:e:f:1:0]/").host()); 419 assertEquals("ff01::101", HttpUrl.parse("http://[FF01:0:0:0:0:0:0:101]/").host()); 420 assertEquals("1::", HttpUrl.parse("http://[1:0:0:0:0:0:0:0]/").host()); 421 assertEquals("::1", HttpUrl.parse("http://[0:0:0:0:0:0:0:1]/").host()); 422 assertEquals("::", HttpUrl.parse("http://[0:0:0:0:0:0:0:0]/").host()); 423 } 424 425 @Test public void hostIpv4CanonicalForm() throws Exception { 426 assertEquals("255.255.255.255", HttpUrl.parse("http://255.255.255.255/").host()); 427 assertEquals("1.2.3.4", HttpUrl.parse("http://1.2.3.4/").host()); 428 assertEquals("0.0.0.0", HttpUrl.parse("http://0.0.0.0/").host()); 429 } 430 431 @Ignore("java.net.IDN strips trailing trailing dots on Java 7, but not on Java 8.") 432 @Test public void hostWithTrailingDot() throws Exception { 433 assertEquals("host.", HttpUrl.parse("http://host./").host()); 434 } 435 436 @Test public void port() throws Exception { 437 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host:80/")); 438 assertEquals(HttpUrl.parse("http://host:99/"), HttpUrl.parse("http://host:99/")); 439 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host:/")); 440 assertEquals(65535, HttpUrl.parse("http://host:65535/").port()); 441 assertEquals(null, HttpUrl.parse("http://host:0/")); 442 assertEquals(null, HttpUrl.parse("http://host:65536/")); 443 assertEquals(null, HttpUrl.parse("http://host:-1/")); 444 assertEquals(null, HttpUrl.parse("http://host:a/")); 445 assertEquals(null, HttpUrl.parse("http://host:%39%39/")); 446 } 447 448 @Test public void pathCharacters() throws Exception { 449 new UrlComponentEncodingTester() 450 .override(Encoding.PERCENT, '^', '{', '}', '|') 451 .override(Encoding.SKIP, '\\', '?', '#') 452 .skipForUri('%', '[', ']') 453 .test(Component.PATH); 454 } 455 456 @Test public void queryCharacters() throws Exception { 457 new UrlComponentEncodingTester() 458 .override(Encoding.IDENTITY, '?', '`') 459 // ANDROID-CHANGED: http://b/30405333 460 // .override(Encoding.PERCENT, '\'') 461 .override(Encoding.IDENTITY, '\'') 462 // ANDROID-CHANGED end. 463 .override(Encoding.SKIP, '#', '+') 464 .skipForUri('%', '\\', '^', '`', '{', '|', '}') 465 .test(Component.QUERY); 466 } 467 468 @Test public void fragmentCharacters() throws Exception { 469 new UrlComponentEncodingTester() 470 .override(Encoding.IDENTITY, ' ', '"', '#', '<', '>', '?', '`') 471 .skipForUri('%', ' ', '"', '#', '<', '>', '\\', '^', '`', '{', '|', '}') 472 .identityForNonAscii() 473 .test(Component.FRAGMENT); 474 } 475 476 @Test public void fragmentNonAscii() throws Exception { 477 HttpUrl url = HttpUrl.parse("http://host/#Σ"); 478 assertEquals("http://host/#Σ", url.toString()); 479 assertEquals("Σ", url.fragment()); 480 assertEquals("Σ", url.encodedFragment()); 481 assertEquals("http://host/#Σ", url.uri().toString()); 482 } 483 484 @Test public void fragmentNonAsciiThatOffendsJavaNetUri() throws Exception { 485 HttpUrl url = HttpUrl.parse("http://host/#\u0080"); 486 assertEquals("http://host/#\u0080", url.toString()); 487 assertEquals("\u0080", url.fragment()); 488 assertEquals("\u0080", url.encodedFragment()); 489 assertEquals(new URI("http://host/#"), url.uri()); // Control characters may be stripped! 490 } 491 492 @Test public void fragmentPercentEncodedNonAscii() throws Exception { 493 HttpUrl url = HttpUrl.parse("http://host/#%C2%80"); 494 assertEquals("http://host/#%C2%80", url.toString()); 495 assertEquals("\u0080", url.fragment()); 496 assertEquals("%C2%80", url.encodedFragment()); 497 assertEquals("http://host/#%C2%80", url.uri().toString()); 498 } 499 500 @Test public void fragmentPercentEncodedPartialCodePoint() throws Exception { 501 HttpUrl url = HttpUrl.parse("http://host/#%80"); 502 assertEquals("http://host/#%80", url.toString()); 503 assertEquals("\ufffd", url.fragment()); // Unicode replacement character. 504 assertEquals("%80", url.encodedFragment()); 505 assertEquals("http://host/#%80", url.uri().toString()); 506 } 507 508 @Test public void relativePath() throws Exception { 509 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 510 assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("d/e/f")); 511 assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("../../d/e/f")); 512 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("..")); 513 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../..")); 514 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../..")); 515 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve(".")); 516 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("././..")); 517 assertEquals(HttpUrl.parse("http://host/a/b/c/"), base.resolve("c/d/../e/../")); 518 assertEquals(HttpUrl.parse("http://host/a/b/..e/"), base.resolve("..e/")); 519 assertEquals(HttpUrl.parse("http://host/a/b/e/f../"), base.resolve("e/f../")); 520 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2E.")); 521 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".%2E")); 522 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2E%2E")); 523 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2e.")); 524 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".%2e")); 525 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2e%2e")); 526 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("%2E")); 527 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("%2e")); 528 } 529 530 @Test public void relativePathWithTrailingSlash() throws Exception { 531 HttpUrl base = HttpUrl.parse("http://host/a/b/c/"); 532 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("..")); 533 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("../")); 534 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("../..")); 535 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("../../")); 536 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../..")); 537 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../")); 538 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../..")); 539 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../../")); 540 assertEquals(HttpUrl.parse("http://host/a"), base.resolve("../../../../a")); 541 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../../a/..")); 542 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("../../../../a/b/..")); 543 } 544 545 @Test public void pathWithBackslash() throws Exception { 546 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 547 assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("d\\e\\f")); 548 assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("../..\\d\\e\\f")); 549 assertEquals(HttpUrl.parse("http://host/"), base.resolve("..\\..")); 550 } 551 552 @Test public void relativePathWithSameScheme() throws Exception { 553 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 554 assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("http:d/e/f")); 555 assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("http:../../d/e/f")); 556 } 557 558 @Test public void decodeUsername() { 559 assertEquals("user", HttpUrl.parse("http://user@host/").username()); 560 assertEquals("\uD83C\uDF69", HttpUrl.parse("http://%F0%9F%8D%A9@host/").username()); 561 } 562 563 @Test public void decodePassword() { 564 assertEquals("password", HttpUrl.parse("http://user:password@host/").password()); 565 assertEquals("", HttpUrl.parse("http://user:@host/").password()); 566 assertEquals("\uD83C\uDF69", HttpUrl.parse("http://user:%F0%9F%8D%A9@host/").password()); 567 } 568 569 @Test public void decodeSlashCharacterInDecodedPathSegment() { 570 assertEquals(Arrays.asList("a/b/c"), 571 HttpUrl.parse("http://host/a%2Fb%2Fc").pathSegments()); 572 } 573 574 @Test public void decodeEmptyPathSegments() { 575 assertEquals(Arrays.asList(""), 576 HttpUrl.parse("http://host/").pathSegments()); 577 } 578 579 @Test public void percentDecode() throws Exception { 580 assertEquals(Arrays.asList("\u0000"), 581 HttpUrl.parse("http://host/%00").pathSegments()); 582 assertEquals(Arrays.asList("a", "\u2603", "c"), 583 HttpUrl.parse("http://host/a/%E2%98%83/c").pathSegments()); 584 assertEquals(Arrays.asList("a", "\uD83C\uDF69", "c"), 585 HttpUrl.parse("http://host/a/%F0%9F%8D%A9/c").pathSegments()); 586 assertEquals(Arrays.asList("a", "b", "c"), 587 HttpUrl.parse("http://host/a/%62/c").pathSegments()); 588 assertEquals(Arrays.asList("a", "z", "c"), 589 HttpUrl.parse("http://host/a/%7A/c").pathSegments()); 590 assertEquals(Arrays.asList("a", "z", "c"), 591 HttpUrl.parse("http://host/a/%7a/c").pathSegments()); 592 } 593 594 @Test public void malformedPercentEncoding() { 595 assertEquals(Arrays.asList("a%f", "b"), 596 HttpUrl.parse("http://host/a%f/b").pathSegments()); 597 assertEquals(Arrays.asList("%", "b"), 598 HttpUrl.parse("http://host/%/b").pathSegments()); 599 assertEquals(Arrays.asList("%"), 600 HttpUrl.parse("http://host/%").pathSegments()); 601 assertEquals(Arrays.asList("%00"), 602 HttpUrl.parse("http://github.com/%%30%30").pathSegments()); 603 } 604 605 @Test public void malformedUtf8Encoding() { 606 // Replace a partial UTF-8 sequence with the Unicode replacement character. 607 assertEquals(Arrays.asList("a", "\ufffdx", "c"), 608 HttpUrl.parse("http://host/a/%E2%98x/c").pathSegments()); 609 } 610 611 @Test public void incompleteUrlComposition() throws Exception { 612 try { 613 new HttpUrl.Builder().scheme("http").build(); 614 fail(); 615 } catch (IllegalStateException expected) { 616 assertEquals("host == null", expected.getMessage()); 617 } 618 try { 619 new HttpUrl.Builder().host("host").build(); 620 fail(); 621 } catch (IllegalStateException expected) { 622 assertEquals("scheme == null", expected.getMessage()); 623 } 624 } 625 626 @Test public void minimalUrlComposition() throws Exception { 627 HttpUrl url = new HttpUrl.Builder().scheme("http").host("host").build(); 628 assertEquals("http://host/", url.toString()); 629 assertEquals("http", url.scheme()); 630 assertEquals("", url.username()); 631 assertEquals("", url.password()); 632 assertEquals("host", url.host()); 633 assertEquals(80, url.port()); 634 assertEquals("/", url.encodedPath()); 635 assertEquals(null, url.query()); 636 assertEquals(null, url.fragment()); 637 } 638 639 @Test public void fullUrlComposition() throws Exception { 640 HttpUrl url = new HttpUrl.Builder() 641 .scheme("http") 642 .username("username") 643 .password("password") 644 .host("host") 645 .port(8080) 646 .addPathSegment("path") 647 .query("query") 648 .fragment("fragment") 649 .build(); 650 assertEquals("http://username:password@host:8080/path?query#fragment", url.toString()); 651 assertEquals("http", url.scheme()); 652 assertEquals("username", url.username()); 653 assertEquals("password", url.password()); 654 assertEquals("host", url.host()); 655 assertEquals(8080, url.port()); 656 assertEquals("/path", url.encodedPath()); 657 assertEquals("query", url.query()); 658 assertEquals("fragment", url.fragment()); 659 } 660 661 @Test public void changingSchemeChangesDefaultPort() throws Exception { 662 assertEquals(443, HttpUrl.parse("http://example.com") 663 .newBuilder() 664 .scheme("https") 665 .build().port()); 666 667 assertEquals(80, HttpUrl.parse("https://example.com") 668 .newBuilder() 669 .scheme("http") 670 .build().port()); 671 672 assertEquals(1234, HttpUrl.parse("https://example.com:1234") 673 .newBuilder() 674 .scheme("http") 675 .build().port()); 676 } 677 678 @Test public void composeEncodesWhitespace() throws Exception { 679 HttpUrl url = new HttpUrl.Builder() 680 .scheme("http") 681 .username("a\r\n\f\t b") 682 .password("c\r\n\f\t d") 683 .host("host") 684 .addPathSegment("e\r\n\f\t f") 685 .query("g\r\n\f\t h") 686 .fragment("i\r\n\f\t j") 687 .build(); 688 assertEquals("http://a%0D%0A%0C%09%20b:c%0D%0A%0C%09%20d@host" 689 + "/e%0D%0A%0C%09%20f?g%0D%0A%0C%09%20h#i%0D%0A%0C%09 j", url.toString()); 690 assertEquals("a\r\n\f\t b", url.username()); 691 assertEquals("c\r\n\f\t d", url.password()); 692 assertEquals("e\r\n\f\t f", url.pathSegments().get(0)); 693 assertEquals("g\r\n\f\t h", url.query()); 694 assertEquals("i\r\n\f\t j", url.fragment()); 695 } 696 697 @Test public void composeFromUnencodedComponents() throws Exception { 698 HttpUrl url = new HttpUrl.Builder() 699 .scheme("http") 700 .username("a:\u0001@/\\?#%b") 701 .password("c:\u0001@/\\?#%d") 702 .host("ef") 703 .port(8080) 704 .addPathSegment("g:\u0001@/\\?#%h") 705 .query("i:\u0001@/\\?#%j") 706 .fragment("k:\u0001@/\\?#%l") 707 .build(); 708 assertEquals("http://a%3A%01%40%2F%5C%3F%23%25b:c%3A%01%40%2F%5C%3F%23%25d@ef:8080/" 709 + "g:%01@%2F%5C%3F%23%25h?i:%01@/\\?%23%25j#k:%01@/\\?#%25l", url.toString()); 710 assertEquals("http", url.scheme()); 711 assertEquals("a:\u0001@/\\?#%b", url.username()); 712 assertEquals("c:\u0001@/\\?#%d", url.password()); 713 assertEquals(Arrays.asList("g:\u0001@/\\?#%h"), url.pathSegments()); 714 assertEquals("i:\u0001@/\\?#%j", url.query()); 715 assertEquals("k:\u0001@/\\?#%l", url.fragment()); 716 assertEquals("a%3A%01%40%2F%5C%3F%23%25b", url.encodedUsername()); 717 assertEquals("c%3A%01%40%2F%5C%3F%23%25d", url.encodedPassword()); 718 assertEquals("/g:%01@%2F%5C%3F%23%25h", url.encodedPath()); 719 assertEquals("i:%01@/\\?%23%25j", url.encodedQuery()); 720 assertEquals("k:%01@/\\?#%25l", url.encodedFragment()); 721 } 722 723 @Test public void composeFromEncodedComponents() throws Exception { 724 HttpUrl url = new HttpUrl.Builder() 725 .scheme("http") 726 .encodedUsername("a:\u0001@/\\?#%25b") 727 .encodedPassword("c:\u0001@/\\?#%25d") 728 .host("ef") 729 .port(8080) 730 .addEncodedPathSegment("g:\u0001@/\\?#%25h") 731 .encodedQuery("i:\u0001@/\\?#%25j") 732 .encodedFragment("k:\u0001@/\\?#%25l") 733 .build(); 734 assertEquals("http://a%3A%01%40%2F%5C%3F%23%25b:c%3A%01%40%2F%5C%3F%23%25d@ef:8080/" 735 + "g:%01@%2F%5C%3F%23%25h?i:%01@/\\?%23%25j#k:%01@/\\?#%25l", url.toString()); 736 assertEquals("http", url.scheme()); 737 assertEquals("a:\u0001@/\\?#%b", url.username()); 738 assertEquals("c:\u0001@/\\?#%d", url.password()); 739 assertEquals(Arrays.asList("g:\u0001@/\\?#%h"), url.pathSegments()); 740 assertEquals("i:\u0001@/\\?#%j", url.query()); 741 assertEquals("k:\u0001@/\\?#%l", url.fragment()); 742 assertEquals("a%3A%01%40%2F%5C%3F%23%25b", url.encodedUsername()); 743 assertEquals("c%3A%01%40%2F%5C%3F%23%25d", url.encodedPassword()); 744 assertEquals("/g:%01@%2F%5C%3F%23%25h", url.encodedPath()); 745 assertEquals("i:%01@/\\?%23%25j", url.encodedQuery()); 746 assertEquals("k:%01@/\\?#%25l", url.encodedFragment()); 747 } 748 749 @Test public void composeWithEncodedPath() throws Exception { 750 HttpUrl url = new HttpUrl.Builder() 751 .scheme("http") 752 .host("host") 753 .encodedPath("/a%2Fb/c") 754 .build(); 755 assertEquals("http://host/a%2Fb/c", url.toString()); 756 assertEquals("/a%2Fb/c", url.encodedPath()); 757 assertEquals(Arrays.asList("a/b", "c"), url.pathSegments()); 758 } 759 760 @Test public void composeMixingPathSegments() throws Exception { 761 HttpUrl url = new HttpUrl.Builder() 762 .scheme("http") 763 .host("host") 764 .encodedPath("/a%2fb/c") 765 .addPathSegment("d%25e") 766 .addEncodedPathSegment("f%25g") 767 .build(); 768 assertEquals("http://host/a%2fb/c/d%2525e/f%25g", url.toString()); 769 assertEquals("/a%2fb/c/d%2525e/f%25g", url.encodedPath()); 770 assertEquals(Arrays.asList("a%2fb", "c", "d%2525e", "f%25g"), url.encodedPathSegments()); 771 assertEquals(Arrays.asList("a/b", "c", "d%25e", "f%g"), url.pathSegments()); 772 } 773 774 @Test public void composeWithAddSegment() throws Exception { 775 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 776 assertEquals("/a/b/c/", base.newBuilder().addPathSegment("").build().encodedPath()); 777 assertEquals("/a/b/c/d", 778 base.newBuilder().addPathSegment("").addPathSegment("d").build().encodedPath()); 779 assertEquals("/a/b/", base.newBuilder().addPathSegment("..").build().encodedPath()); 780 assertEquals("/a/b/", base.newBuilder().addPathSegment("").addPathSegment("..").build() 781 .encodedPath()); 782 assertEquals("/a/b/c/", base.newBuilder().addPathSegment("").addPathSegment("").build() 783 .encodedPath()); 784 } 785 786 @Test public void pathSize() throws Exception { 787 assertEquals(1, HttpUrl.parse("http://host/").pathSize()); 788 assertEquals(3, HttpUrl.parse("http://host/a/b/c").pathSize()); 789 } 790 791 @Test public void addPathSegmentDotDoesNothing() throws Exception { 792 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 793 assertEquals("/a/b/c", base.newBuilder().addPathSegment(".").build().encodedPath()); 794 } 795 796 @Test public void addPathSegmentEncodes() throws Exception { 797 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 798 assertEquals("/a/b/c/%252e", 799 base.newBuilder().addPathSegment("%2e").build().encodedPath()); 800 assertEquals("/a/b/c/%252e%252e", 801 base.newBuilder().addPathSegment("%2e%2e").build().encodedPath()); 802 } 803 804 @Test public void addPathSegmentDotDotPopsDirectory() throws Exception { 805 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 806 assertEquals("/a/b/", base.newBuilder().addPathSegment("..").build().encodedPath()); 807 } 808 809 @Test public void addPathSegmentDotAndIgnoredCharacter() throws Exception { 810 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 811 assertEquals("/a/b/c/.%0A", base.newBuilder().addPathSegment(".\n").build().encodedPath()); 812 } 813 814 @Test public void addEncodedPathSegmentDotAndIgnoredCharacter() throws Exception { 815 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 816 assertEquals("/a/b/c", base.newBuilder().addEncodedPathSegment(".\n").build().encodedPath()); 817 } 818 819 @Test public void addEncodedPathSegmentDotDotAndIgnoredCharacter() throws Exception { 820 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 821 assertEquals("/a/b/", base.newBuilder().addEncodedPathSegment("..\n").build().encodedPath()); 822 } 823 824 @Test public void setPathSegment() throws Exception { 825 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 826 assertEquals("/d/b/c", base.newBuilder().setPathSegment(0, "d").build().encodedPath()); 827 assertEquals("/a/d/c", base.newBuilder().setPathSegment(1, "d").build().encodedPath()); 828 assertEquals("/a/b/d", base.newBuilder().setPathSegment(2, "d").build().encodedPath()); 829 } 830 831 @Test public void setPathSegmentEncodes() throws Exception { 832 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 833 assertEquals("/%2525/b/c", base.newBuilder().setPathSegment(0, "%25").build().encodedPath()); 834 assertEquals("/.%0A/b/c", base.newBuilder().setPathSegment(0, ".\n").build().encodedPath()); 835 assertEquals("/%252e/b/c", base.newBuilder().setPathSegment(0, "%2e").build().encodedPath()); 836 } 837 838 @Test public void setPathSegmentAcceptsEmpty() throws Exception { 839 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 840 assertEquals("//b/c", base.newBuilder().setPathSegment(0, "").build().encodedPath()); 841 assertEquals("/a/b/", base.newBuilder().setPathSegment(2, "").build().encodedPath()); 842 } 843 844 @Test public void setPathSegmentRejectsDot() throws Exception { 845 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 846 try { 847 base.newBuilder().setPathSegment(0, "."); 848 fail(); 849 } catch (IllegalArgumentException expected) { 850 } 851 } 852 853 @Test public void setPathSegmentRejectsDotDot() throws Exception { 854 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 855 try { 856 base.newBuilder().setPathSegment(0, ".."); 857 fail(); 858 } catch (IllegalArgumentException expected) { 859 } 860 } 861 862 @Test public void setPathSegmentWithSlash() throws Exception { 863 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 864 HttpUrl url = base.newBuilder().setPathSegment(1, "/").build(); 865 assertEquals("/a/%2F/c", url.encodedPath()); 866 } 867 868 @Test public void setPathSegmentOutOfBounds() throws Exception { 869 try { 870 new HttpUrl.Builder().setPathSegment(1, "a"); 871 fail(); 872 } catch (IndexOutOfBoundsException expected) { 873 } 874 } 875 876 @Test public void setEncodedPathSegmentEncodes() throws Exception { 877 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 878 assertEquals("/%25/b/c", 879 base.newBuilder().setEncodedPathSegment(0, "%25").build().encodedPath()); 880 } 881 882 @Test public void setEncodedPathSegmentRejectsDot() throws Exception { 883 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 884 try { 885 base.newBuilder().setEncodedPathSegment(0, "."); 886 fail(); 887 } catch (IllegalArgumentException expected) { 888 } 889 } 890 891 @Test public void setEncodedPathSegmentRejectsDotAndIgnoredCharacter() throws Exception { 892 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 893 try { 894 base.newBuilder().setEncodedPathSegment(0, ".\n"); 895 fail(); 896 } catch (IllegalArgumentException expected) { 897 } 898 } 899 900 @Test public void setEncodedPathSegmentRejectsDotDot() throws Exception { 901 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 902 try { 903 base.newBuilder().setEncodedPathSegment(0, ".."); 904 fail(); 905 } catch (IllegalArgumentException expected) { 906 } 907 } 908 909 @Test public void setEncodedPathSegmentRejectsDotDotAndIgnoredCharacter() throws Exception { 910 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 911 try { 912 base.newBuilder().setEncodedPathSegment(0, "..\n"); 913 fail(); 914 } catch (IllegalArgumentException expected) { 915 } 916 } 917 918 @Test public void setEncodedPathSegmentWithSlash() throws Exception { 919 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 920 HttpUrl url = base.newBuilder().setEncodedPathSegment(1, "/").build(); 921 assertEquals("/a/%2F/c", url.encodedPath()); 922 } 923 924 @Test public void setEncodedPathSegmentOutOfBounds() throws Exception { 925 try { 926 new HttpUrl.Builder().setEncodedPathSegment(1, "a"); 927 fail(); 928 } catch (IndexOutOfBoundsException expected) { 929 } 930 } 931 932 @Test public void removePathSegment() throws Exception { 933 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 934 HttpUrl url = base.newBuilder() 935 .removePathSegment(0) 936 .build(); 937 assertEquals("/b/c", url.encodedPath()); 938 } 939 940 @Test public void removePathSegmentDoesntRemovePath() throws Exception { 941 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 942 HttpUrl url = base.newBuilder() 943 .removePathSegment(0) 944 .removePathSegment(0) 945 .removePathSegment(0) 946 .build(); 947 assertEquals(Arrays.asList(""), url.pathSegments()); 948 assertEquals("/", url.encodedPath()); 949 } 950 951 @Test public void removePathSegmentOutOfBounds() throws Exception { 952 try { 953 new HttpUrl.Builder().removePathSegment(1); 954 fail(); 955 } catch (IndexOutOfBoundsException expected) { 956 } 957 } 958 959 @Test public void toJavaNetUrl() throws Exception { 960 HttpUrl httpUrl = HttpUrl.parse("http://username:password@host/path?query#fragment"); 961 URL javaNetUrl = httpUrl.url(); 962 assertEquals("http://username:password@host/path?query#fragment", javaNetUrl.toString()); 963 } 964 965 @Test public void toUri() throws Exception { 966 HttpUrl httpUrl = HttpUrl.parse("http://username:password@host/path?query#fragment"); 967 URI uri = httpUrl.uri(); 968 assertEquals("http://username:password@host/path?query#fragment", uri.toString()); 969 } 970 971 @Test public void toUriSpecialQueryCharacters() throws Exception { 972 HttpUrl httpUrl = HttpUrl.parse("http://host/?d=abc!@[]^`{}|\\"); 973 URI uri = httpUrl.uri(); 974 assertEquals("http://host/?d=abc!@[]%5E%60%7B%7D%7C%5C", uri.toString()); 975 } 976 977 @Test public void toUriWithUsernameNoPassword() throws Exception { 978 HttpUrl httpUrl = new HttpUrl.Builder() 979 .scheme("http") 980 .username("user") 981 .host("host") 982 .build(); 983 assertEquals("http://user@host/", httpUrl.toString()); 984 assertEquals("http://user@host/", httpUrl.uri().toString()); 985 } 986 987 @Test public void toUriUsernameSpecialCharacters() throws Exception { 988 HttpUrl url = new HttpUrl.Builder() 989 .scheme("http") 990 .host("host") 991 .username("=[]:;\"~|?#@^/$%*") 992 .build(); 993 assertEquals("http://%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", url.toString()); 994 assertEquals("http://%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", url.uri().toString()); 995 } 996 997 @Test public void toUriPasswordSpecialCharacters() throws Exception { 998 HttpUrl url = new HttpUrl.Builder() 999 .scheme("http") 1000 .host("host") 1001 .username("user") 1002 .password("=[]:;\"~|?#@^/$%*") 1003 .build(); 1004 assertEquals("http://user:%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", url.toString()); 1005 assertEquals("http://user:%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", 1006 url.uri().toString()); 1007 } 1008 1009 @Test public void toUriPathSpecialCharacters() throws Exception { 1010 HttpUrl url = new HttpUrl.Builder() 1011 .scheme("http") 1012 .host("host") 1013 .addPathSegment("=[]:;\"~|?#@^/$%*") 1014 .build(); 1015 assertEquals("http://host/=[]:;%22~%7C%3F%23@%5E%2F$%25*", url.toString()); 1016 assertEquals("http://host/=%5B%5D:;%22~%7C%3F%23@%5E%2F$%25*", url.uri().toString()); 1017 } 1018 1019 @Test public void toUriQueryParameterNameSpecialCharacters() throws Exception { 1020 HttpUrl url = new HttpUrl.Builder() 1021 .scheme("http") 1022 .host("host") 1023 .addQueryParameter("=[]:;\"~|?#@^/$%*", "a") 1024 .build(); 1025 assertEquals("http://host/?%3D[]:;%22~|?%23@^/$%25*=a", url.toString()); 1026 assertEquals("http://host/?%3D[]:;%22~%7C?%23@%5E/$%25*=a", url.uri().toString()); 1027 } 1028 1029 @Test public void toUriQueryParameterValueSpecialCharacters() throws Exception { 1030 HttpUrl url = new HttpUrl.Builder() 1031 .scheme("http") 1032 .host("host") 1033 .addQueryParameter("a", "=[]:;\"~|?#@^/$%*") 1034 .build(); 1035 assertEquals("http://host/?a=%3D[]:;%22~|?%23@^/$%25*", url.toString()); 1036 assertEquals("http://host/?a=%3D[]:;%22~%7C?%23@%5E/$%25*", url.uri().toString()); 1037 } 1038 1039 @Test public void toUriQueryValueSpecialCharacters() throws Exception { 1040 HttpUrl url = new HttpUrl.Builder() 1041 .scheme("http") 1042 .host("host") 1043 .query("=[]:;\"~|?#@^/$%*") 1044 .build(); 1045 assertEquals("http://host/?=[]:;%22~|?%23@^/$%25*", url.toString()); 1046 assertEquals("http://host/?=[]:;%22~%7C?%23@%5E/$%25*", url.uri().toString()); 1047 } 1048 1049 @Test public void toUriFragmentSpecialCharacters() throws Exception { 1050 HttpUrl url = new HttpUrl.Builder() 1051 .scheme("http") 1052 .host("host") 1053 .fragment("=[]:;\"~|?#@^/$%*") 1054 .build(); 1055 assertEquals("http://host/#=[]:;\"~|?#@^/$%25*", url.toString()); 1056 assertEquals("http://host/#=[]:;%22~%7C?%23@%5E/$%25*", url.uri().toString()); 1057 } 1058 1059 @Test public void toUriWithControlCharacters() throws Exception { 1060 // Percent-encoded in the path. 1061 assertEquals(new URI("http://host/a%00b"), HttpUrl.parse("http://host/a\u0000b").uri()); 1062 assertEquals(new URI("http://host/a%C2%80b"), HttpUrl.parse("http://host/a\u0080b").uri()); 1063 assertEquals(new URI("http://host/a%C2%9Fb"), HttpUrl.parse("http://host/a\u009fb").uri()); 1064 // Percent-encoded in the query. 1065 assertEquals(new URI("http://host/?a%00b"), HttpUrl.parse("http://host/?a\u0000b").uri()); 1066 assertEquals(new URI("http://host/?a%C2%80b"), HttpUrl.parse("http://host/?a\u0080b").uri()); 1067 assertEquals(new URI("http://host/?a%C2%9Fb"), HttpUrl.parse("http://host/?a\u009fb").uri()); 1068 // Stripped from the fragment. 1069 assertEquals(new URI("http://host/#a%00b"), HttpUrl.parse("http://host/#a\u0000b").uri()); 1070 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u0080b").uri()); 1071 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u009fb").uri()); 1072 } 1073 1074 @Test public void toUriWithSpaceCharacters() throws Exception { 1075 // Percent-encoded in the path. 1076 assertEquals(new URI("http://host/a%0Bb"), HttpUrl.parse("http://host/a\u000bb").uri()); 1077 assertEquals(new URI("http://host/a%20b"), HttpUrl.parse("http://host/a b").uri()); 1078 assertEquals(new URI("http://host/a%E2%80%89b"), HttpUrl.parse("http://host/a\u2009b").uri()); 1079 assertEquals(new URI("http://host/a%E3%80%80b"), HttpUrl.parse("http://host/a\u3000b").uri()); 1080 // Percent-encoded in the query. 1081 assertEquals(new URI("http://host/?a%0Bb"), HttpUrl.parse("http://host/?a\u000bb").uri()); 1082 assertEquals(new URI("http://host/?a%20b"), HttpUrl.parse("http://host/?a b").uri()); 1083 assertEquals(new URI("http://host/?a%E2%80%89b"), HttpUrl.parse("http://host/?a\u2009b").uri()); 1084 assertEquals(new URI("http://host/?a%E3%80%80b"), HttpUrl.parse("http://host/?a\u3000b").uri()); 1085 // Stripped from the fragment. 1086 assertEquals(new URI("http://host/#a%0Bb"), HttpUrl.parse("http://host/#a\u000bb").uri()); 1087 assertEquals(new URI("http://host/#a%20b"), HttpUrl.parse("http://host/#a b").uri()); 1088 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u2009b").uri()); 1089 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u3000b").uri()); 1090 } 1091 1092 @Test public void toUriWithNonHexPercentEscape() throws Exception { 1093 assertEquals(new URI("http://host/%25xx"), HttpUrl.parse("http://host/%xx").uri()); 1094 } 1095 1096 @Test public void toUriWithTruncatedPercentEscape() throws Exception { 1097 assertEquals(new URI("http://host/%25a"), HttpUrl.parse("http://host/%a").uri()); 1098 assertEquals(new URI("http://host/%25"), HttpUrl.parse("http://host/%").uri()); 1099 } 1100 1101 @Test public void fromJavaNetUrl() throws Exception { 1102 URL javaNetUrl = new URL("http://username:password@host/path?query#fragment"); 1103 HttpUrl httpUrl = HttpUrl.get(javaNetUrl); 1104 assertEquals("http://username:password@host/path?query#fragment", httpUrl.toString()); 1105 } 1106 1107 // ANDROID-BEGIN 1108 @Ignore // Android's URL implementation does not support mailto: 1109 // ANDROID-END 1110 @Test public void fromJavaNetUrlUnsupportedScheme() throws Exception { 1111 URL javaNetUrl = new URL("mailto:user@example.com"); 1112 assertEquals(null, HttpUrl.get(javaNetUrl)); 1113 } 1114 1115 @Test public void fromUri() throws Exception { 1116 URI uri = new URI("http://username:password@host/path?query#fragment"); 1117 HttpUrl httpUrl = HttpUrl.get(uri); 1118 assertEquals("http://username:password@host/path?query#fragment", httpUrl.toString()); 1119 } 1120 1121 @Test public void fromUriUnsupportedScheme() throws Exception { 1122 URI uri = new URI("mailto:user@example.com"); 1123 assertEquals(null, HttpUrl.get(uri)); 1124 } 1125 1126 @Test public void fromUriPartial() throws Exception { 1127 URI uri = new URI("/path"); 1128 assertEquals(null, HttpUrl.get(uri)); 1129 } 1130 1131 @Test public void fromJavaNetUrl_checked() throws Exception { 1132 HttpUrl httpUrl = HttpUrl.getChecked("http://username:password@host/path?query#fragment"); 1133 assertEquals("http://username:password@host/path?query#fragment", httpUrl.toString()); 1134 } 1135 1136 @Test public void fromJavaNetUrlUnsupportedScheme_checked() throws Exception { 1137 try { 1138 HttpUrl.getChecked("mailto:user@example.com"); 1139 fail(); 1140 } catch (MalformedURLException e) { 1141 } 1142 } 1143 1144 @Test public void fromJavaNetUrlBadHost_checked() throws Exception { 1145 try { 1146 HttpUrl.getChecked("http://hostw ithspace/"); 1147 fail(); 1148 } catch (UnknownHostException expected) { 1149 } 1150 } 1151 1152 @Test public void composeQueryWithComponents() throws Exception { 1153 HttpUrl base = HttpUrl.parse("http://host/"); 1154 HttpUrl url = base.newBuilder().addQueryParameter("a+=& b", "c+=& d").build(); 1155 assertEquals("http://host/?a%2B%3D%26%20b=c%2B%3D%26%20d", url.toString()); 1156 assertEquals("c+=& d", url.queryParameterValue(0)); 1157 assertEquals("a+=& b", url.queryParameterName(0)); 1158 assertEquals("c+=& d", url.queryParameter("a+=& b")); 1159 assertEquals(Collections.singleton("a+=& b"), url.queryParameterNames()); 1160 assertEquals(singletonList("c+=& d"), url.queryParameterValues("a+=& b")); 1161 assertEquals(1, url.querySize()); 1162 assertEquals("a+=& b=c+=& d", url.query()); // Ambiguous! (Though working as designed.) 1163 assertEquals("a%2B%3D%26%20b=c%2B%3D%26%20d", url.encodedQuery()); 1164 } 1165 1166 @Test public void composeQueryWithEncodedComponents() throws Exception { 1167 HttpUrl base = HttpUrl.parse("http://host/"); 1168 HttpUrl url = base.newBuilder().addEncodedQueryParameter("a+=& b", "c+=& d").build(); 1169 assertEquals("http://host/?a+%3D%26%20b=c+%3D%26%20d", url.toString()); 1170 assertEquals("c =& d", url.queryParameter("a =& b")); 1171 } 1172 1173 @Test public void composeQueryRemoveQueryParameter() throws Exception { 1174 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1175 .addQueryParameter("a+=& b", "c+=& d") 1176 .removeAllQueryParameters("a+=& b") 1177 .build(); 1178 assertEquals("http://host/", url.toString()); 1179 assertEquals(null, url.queryParameter("a+=& b")); 1180 } 1181 1182 @Test public void composeQueryRemoveEncodedQueryParameter() throws Exception { 1183 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1184 .addEncodedQueryParameter("a+=& b", "c+=& d") 1185 .removeAllEncodedQueryParameters("a+=& b") 1186 .build(); 1187 assertEquals("http://host/", url.toString()); 1188 assertEquals(null, url.queryParameter("a =& b")); 1189 } 1190 1191 @Test public void composeQuerySetQueryParameter() throws Exception { 1192 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1193 .addQueryParameter("a+=& b", "c+=& d") 1194 .setQueryParameter("a+=& b", "ef") 1195 .build(); 1196 assertEquals("http://host/?a%2B%3D%26%20b=ef", url.toString()); 1197 assertEquals("ef", url.queryParameter("a+=& b")); 1198 } 1199 1200 @Test public void composeQuerySetEncodedQueryParameter() throws Exception { 1201 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1202 .addEncodedQueryParameter("a+=& b", "c+=& d") 1203 .setEncodedQueryParameter("a+=& b", "ef") 1204 .build(); 1205 assertEquals("http://host/?a+%3D%26%20b=ef", url.toString()); 1206 assertEquals("ef", url.queryParameter("a =& b")); 1207 } 1208 1209 @Test public void composeQueryMultipleEncodedValuesForParameter() throws Exception { 1210 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1211 .addQueryParameter("a+=& b", "c+=& d") 1212 .addQueryParameter("a+=& b", "e+=& f") 1213 .build(); 1214 assertEquals("http://host/?a%2B%3D%26%20b=c%2B%3D%26%20d&a%2B%3D%26%20b=e%2B%3D%26%20f", 1215 url.toString()); 1216 assertEquals(2, url.querySize()); 1217 assertEquals(Collections.singleton("a+=& b"), url.queryParameterNames()); 1218 assertEquals(Arrays.asList("c+=& d", "e+=& f"), url.queryParameterValues("a+=& b")); 1219 } 1220 1221 @Test public void absentQueryIsZeroNameValuePairs() throws Exception { 1222 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1223 .query(null) 1224 .build(); 1225 assertEquals(0, url.querySize()); 1226 } 1227 1228 @Test public void emptyQueryIsSingleNameValuePairWithEmptyKey() throws Exception { 1229 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1230 .query("") 1231 .build(); 1232 assertEquals(1, url.querySize()); 1233 assertEquals("", url.queryParameterName(0)); 1234 assertEquals(null, url.queryParameterValue(0)); 1235 } 1236 1237 @Test public void ampersandQueryIsTwoNameValuePairsWithEmptyKeys() throws Exception { 1238 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1239 .query("&") 1240 .build(); 1241 assertEquals(2, url.querySize()); 1242 assertEquals("", url.queryParameterName(0)); 1243 assertEquals(null, url.queryParameterValue(0)); 1244 assertEquals("", url.queryParameterName(1)); 1245 assertEquals(null, url.queryParameterValue(1)); 1246 } 1247 1248 @Test public void removeAllDoesNotRemoveQueryIfNoParametersWereRemoved() throws Exception { 1249 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1250 .query("") 1251 .removeAllQueryParameters("a") 1252 .build(); 1253 assertEquals("http://host/?", url.toString()); 1254 } 1255 1256 @Test public void queryParametersWithoutValues() throws Exception { 1257 HttpUrl url = HttpUrl.parse("http://host/?foo&bar&baz"); 1258 assertEquals(3, url.querySize()); 1259 assertEquals(new LinkedHashSet<>(Arrays.asList("foo", "bar", "baz")), 1260 url.queryParameterNames()); 1261 assertEquals(null, url.queryParameterValue(0)); 1262 assertEquals(null, url.queryParameterValue(1)); 1263 assertEquals(null, url.queryParameterValue(2)); 1264 assertEquals(singletonList((String) null), url.queryParameterValues("foo")); 1265 assertEquals(singletonList((String) null), url.queryParameterValues("bar")); 1266 assertEquals(singletonList((String) null), url.queryParameterValues("baz")); 1267 } 1268 1269 @Test public void queryParametersWithEmptyValues() throws Exception { 1270 HttpUrl url = HttpUrl.parse("http://host/?foo=&bar=&baz="); 1271 assertEquals(3, url.querySize()); 1272 assertEquals(new LinkedHashSet<>(Arrays.asList("foo", "bar", "baz")), 1273 url.queryParameterNames()); 1274 assertEquals("", url.queryParameterValue(0)); 1275 assertEquals("", url.queryParameterValue(1)); 1276 assertEquals("", url.queryParameterValue(2)); 1277 assertEquals(singletonList(""), url.queryParameterValues("foo")); 1278 assertEquals(singletonList(""), url.queryParameterValues("bar")); 1279 assertEquals(singletonList(""), url.queryParameterValues("baz")); 1280 } 1281 1282 @Test public void queryParametersWithRepeatedName() throws Exception { 1283 HttpUrl url = HttpUrl.parse("http://host/?foo[]=1&foo[]=2&foo[]=3"); 1284 assertEquals(3, url.querySize()); 1285 assertEquals(Collections.singleton("foo[]"), url.queryParameterNames()); 1286 assertEquals("1", url.queryParameterValue(0)); 1287 assertEquals("2", url.queryParameterValue(1)); 1288 assertEquals("3", url.queryParameterValue(2)); 1289 assertEquals(Arrays.asList("1", "2", "3"), url.queryParameterValues("foo[]")); 1290 } 1291 1292 @Test public void queryParameterLookupWithNonCanonicalEncoding() throws Exception { 1293 HttpUrl url = HttpUrl.parse("http://host/?%6d=m&+=%20"); 1294 assertEquals("m", url.queryParameterName(0)); 1295 assertEquals(" ", url.queryParameterName(1)); 1296 assertEquals("m", url.queryParameter("m")); 1297 assertEquals(" ", url.queryParameter(" ")); 1298 } 1299 1300 @Test public void roundTripBuilder() throws Exception { 1301 HttpUrl url = new HttpUrl.Builder() 1302 .scheme("http") 1303 .username("%") 1304 .password("%") 1305 .host("host") 1306 .addPathSegment("%") 1307 .query("%") 1308 .fragment("%") 1309 .build(); 1310 assertEquals("http://%25:%25@host/%25?%25#%25", url.toString()); 1311 assertEquals("http://%25:%25@host/%25?%25#%25", url.newBuilder().build().toString()); 1312 assertEquals("http://%25:%25@host/%25?%25", url.resolve("").toString()); 1313 } 1314 1315 /** 1316 * Although HttpUrl prefers percent-encodings in uppercase, it should preserve the exact 1317 * structure of the original encoding. 1318 */ 1319 @Test public void rawEncodingRetained() throws Exception { 1320 String urlString = "http://%6d%6D:%6d%6D@host/%6d%6D?%6d%6D#%6d%6D"; 1321 HttpUrl url = HttpUrl.parse(urlString); 1322 assertEquals("%6d%6D", url.encodedUsername()); 1323 assertEquals("%6d%6D", url.encodedPassword()); 1324 assertEquals("/%6d%6D", url.encodedPath()); 1325 assertEquals(Arrays.asList("%6d%6D"), url.encodedPathSegments()); 1326 assertEquals("%6d%6D", url.encodedQuery()); 1327 assertEquals("%6d%6D", url.encodedFragment()); 1328 assertEquals(urlString, url.toString()); 1329 assertEquals(urlString, url.newBuilder().build().toString()); 1330 assertEquals("http://%6d%6D:%6d%6D@host/%6d%6D?%6d%6D", url.resolve("").toString()); 1331 } 1332 1333 @Test public void clearFragment() throws Exception { 1334 HttpUrl url = HttpUrl.parse("http://host/#fragment") 1335 .newBuilder() 1336 .fragment(null) 1337 .build(); 1338 assertEquals("http://host/", url.toString()); 1339 assertEquals(null, url.fragment()); 1340 assertEquals(null, url.encodedFragment()); 1341 } 1342 1343 @Test public void clearEncodedFragment() throws Exception { 1344 HttpUrl url = HttpUrl.parse("http://host/#fragment") 1345 .newBuilder() 1346 .encodedFragment(null) 1347 .build(); 1348 assertEquals("http://host/", url.toString()); 1349 assertEquals(null, url.fragment()); 1350 assertEquals(null, url.encodedFragment()); 1351 } 1352} 1353