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 /** The builder permits square braces but does not require them. */ 426 @Test public void hostIPv6Builder() throws Exception { 427 HttpUrl base = HttpUrl.parse("http://example.com/"); 428 assertEquals("http://[::1]/", base.newBuilder().host("[::1]").build().toString()); 429 assertEquals("http://[::1]/", base.newBuilder().host("[::0001]").build().toString()); 430 assertEquals("http://[::1]/", base.newBuilder().host("::1").build().toString()); 431 assertEquals("http://[::1]/", base.newBuilder().host("::0001").build().toString()); 432 } 433 434 @Test public void hostIpv4CanonicalForm() throws Exception { 435 assertEquals("255.255.255.255", HttpUrl.parse("http://255.255.255.255/").host()); 436 assertEquals("1.2.3.4", HttpUrl.parse("http://1.2.3.4/").host()); 437 assertEquals("0.0.0.0", HttpUrl.parse("http://0.0.0.0/").host()); 438 } 439 440 @Ignore("java.net.IDN strips trailing trailing dots on Java 7, but not on Java 8.") 441 @Test public void hostWithTrailingDot() throws Exception { 442 assertEquals("host.", HttpUrl.parse("http://host./").host()); 443 } 444 445 @Test public void port() throws Exception { 446 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host:80/")); 447 assertEquals(HttpUrl.parse("http://host:99/"), HttpUrl.parse("http://host:99/")); 448 assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host:/")); 449 assertEquals(65535, HttpUrl.parse("http://host:65535/").port()); 450 assertEquals(null, HttpUrl.parse("http://host:0/")); 451 assertEquals(null, HttpUrl.parse("http://host:65536/")); 452 assertEquals(null, HttpUrl.parse("http://host:-1/")); 453 assertEquals(null, HttpUrl.parse("http://host:a/")); 454 assertEquals(null, HttpUrl.parse("http://host:%39%39/")); 455 } 456 457 @Test public void pathCharacters() throws Exception { 458 new UrlComponentEncodingTester() 459 .override(Encoding.PERCENT, '^', '{', '}', '|') 460 .override(Encoding.SKIP, '\\', '?', '#') 461 .skipForUri('%', '[', ']') 462 .test(Component.PATH); 463 } 464 465 @Test public void queryCharacters() throws Exception { 466 new UrlComponentEncodingTester() 467 .override(Encoding.IDENTITY, '?', '`') 468 // ANDROID-CHANGED: http://b/30405333 469 // .override(Encoding.PERCENT, '\'') 470 .override(Encoding.IDENTITY, '\'') 471 // ANDROID-CHANGED end. 472 .override(Encoding.SKIP, '#', '+') 473 .skipForUri('%', '\\', '^', '`', '{', '|', '}') 474 .test(Component.QUERY); 475 } 476 477 @Test public void fragmentCharacters() throws Exception { 478 new UrlComponentEncodingTester() 479 .override(Encoding.IDENTITY, ' ', '"', '#', '<', '>', '?', '`') 480 .skipForUri('%', ' ', '"', '#', '<', '>', '\\', '^', '`', '{', '|', '}') 481 .identityForNonAscii() 482 .test(Component.FRAGMENT); 483 } 484 485 @Test public void fragmentNonAscii() throws Exception { 486 HttpUrl url = HttpUrl.parse("http://host/#Σ"); 487 assertEquals("http://host/#Σ", url.toString()); 488 assertEquals("Σ", url.fragment()); 489 assertEquals("Σ", url.encodedFragment()); 490 assertEquals("http://host/#Σ", url.uri().toString()); 491 } 492 493 @Test public void fragmentNonAsciiThatOffendsJavaNetUri() throws Exception { 494 HttpUrl url = HttpUrl.parse("http://host/#\u0080"); 495 assertEquals("http://host/#\u0080", url.toString()); 496 assertEquals("\u0080", url.fragment()); 497 assertEquals("\u0080", url.encodedFragment()); 498 assertEquals(new URI("http://host/#"), url.uri()); // Control characters may be stripped! 499 } 500 501 @Test public void fragmentPercentEncodedNonAscii() throws Exception { 502 HttpUrl url = HttpUrl.parse("http://host/#%C2%80"); 503 assertEquals("http://host/#%C2%80", url.toString()); 504 assertEquals("\u0080", url.fragment()); 505 assertEquals("%C2%80", url.encodedFragment()); 506 assertEquals("http://host/#%C2%80", url.uri().toString()); 507 } 508 509 @Test public void fragmentPercentEncodedPartialCodePoint() throws Exception { 510 HttpUrl url = HttpUrl.parse("http://host/#%80"); 511 assertEquals("http://host/#%80", url.toString()); 512 assertEquals("\ufffd", url.fragment()); // Unicode replacement character. 513 assertEquals("%80", url.encodedFragment()); 514 assertEquals("http://host/#%80", url.uri().toString()); 515 } 516 517 @Test public void relativePath() throws Exception { 518 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 519 assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("d/e/f")); 520 assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("../../d/e/f")); 521 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("..")); 522 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../..")); 523 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../..")); 524 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve(".")); 525 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("././..")); 526 assertEquals(HttpUrl.parse("http://host/a/b/c/"), base.resolve("c/d/../e/../")); 527 assertEquals(HttpUrl.parse("http://host/a/b/..e/"), base.resolve("..e/")); 528 assertEquals(HttpUrl.parse("http://host/a/b/e/f../"), base.resolve("e/f../")); 529 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2E.")); 530 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".%2E")); 531 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2E%2E")); 532 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2e.")); 533 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".%2e")); 534 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2e%2e")); 535 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("%2E")); 536 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("%2e")); 537 } 538 539 @Test public void relativePathWithTrailingSlash() throws Exception { 540 HttpUrl base = HttpUrl.parse("http://host/a/b/c/"); 541 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("..")); 542 assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("../")); 543 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("../..")); 544 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("../../")); 545 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../..")); 546 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../")); 547 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../..")); 548 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../../")); 549 assertEquals(HttpUrl.parse("http://host/a"), base.resolve("../../../../a")); 550 assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../../../a/..")); 551 assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("../../../../a/b/..")); 552 } 553 554 @Test public void pathWithBackslash() throws Exception { 555 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 556 assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("d\\e\\f")); 557 assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("../..\\d\\e\\f")); 558 assertEquals(HttpUrl.parse("http://host/"), base.resolve("..\\..")); 559 } 560 561 @Test public void relativePathWithSameScheme() throws Exception { 562 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 563 assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("http:d/e/f")); 564 assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("http:../../d/e/f")); 565 } 566 567 @Test public void decodeUsername() { 568 assertEquals("user", HttpUrl.parse("http://user@host/").username()); 569 assertEquals("\uD83C\uDF69", HttpUrl.parse("http://%F0%9F%8D%A9@host/").username()); 570 } 571 572 @Test public void decodePassword() { 573 assertEquals("password", HttpUrl.parse("http://user:password@host/").password()); 574 assertEquals("", HttpUrl.parse("http://user:@host/").password()); 575 assertEquals("\uD83C\uDF69", HttpUrl.parse("http://user:%F0%9F%8D%A9@host/").password()); 576 } 577 578 @Test public void decodeSlashCharacterInDecodedPathSegment() { 579 assertEquals(Arrays.asList("a/b/c"), 580 HttpUrl.parse("http://host/a%2Fb%2Fc").pathSegments()); 581 } 582 583 @Test public void decodeEmptyPathSegments() { 584 assertEquals(Arrays.asList(""), 585 HttpUrl.parse("http://host/").pathSegments()); 586 } 587 588 @Test public void percentDecode() throws Exception { 589 assertEquals(Arrays.asList("\u0000"), 590 HttpUrl.parse("http://host/%00").pathSegments()); 591 assertEquals(Arrays.asList("a", "\u2603", "c"), 592 HttpUrl.parse("http://host/a/%E2%98%83/c").pathSegments()); 593 assertEquals(Arrays.asList("a", "\uD83C\uDF69", "c"), 594 HttpUrl.parse("http://host/a/%F0%9F%8D%A9/c").pathSegments()); 595 assertEquals(Arrays.asList("a", "b", "c"), 596 HttpUrl.parse("http://host/a/%62/c").pathSegments()); 597 assertEquals(Arrays.asList("a", "z", "c"), 598 HttpUrl.parse("http://host/a/%7A/c").pathSegments()); 599 assertEquals(Arrays.asList("a", "z", "c"), 600 HttpUrl.parse("http://host/a/%7a/c").pathSegments()); 601 } 602 603 @Test public void malformedPercentEncoding() { 604 assertEquals(Arrays.asList("a%f", "b"), 605 HttpUrl.parse("http://host/a%f/b").pathSegments()); 606 assertEquals(Arrays.asList("%", "b"), 607 HttpUrl.parse("http://host/%/b").pathSegments()); 608 assertEquals(Arrays.asList("%"), 609 HttpUrl.parse("http://host/%").pathSegments()); 610 assertEquals(Arrays.asList("%00"), 611 HttpUrl.parse("http://github.com/%%30%30").pathSegments()); 612 } 613 614 @Test public void malformedUtf8Encoding() { 615 // Replace a partial UTF-8 sequence with the Unicode replacement character. 616 assertEquals(Arrays.asList("a", "\ufffdx", "c"), 617 HttpUrl.parse("http://host/a/%E2%98x/c").pathSegments()); 618 } 619 620 @Test public void incompleteUrlComposition() throws Exception { 621 try { 622 new HttpUrl.Builder().scheme("http").build(); 623 fail(); 624 } catch (IllegalStateException expected) { 625 assertEquals("host == null", expected.getMessage()); 626 } 627 try { 628 new HttpUrl.Builder().host("host").build(); 629 fail(); 630 } catch (IllegalStateException expected) { 631 assertEquals("scheme == null", expected.getMessage()); 632 } 633 } 634 635 @Test public void minimalUrlComposition() throws Exception { 636 HttpUrl url = new HttpUrl.Builder().scheme("http").host("host").build(); 637 assertEquals("http://host/", url.toString()); 638 assertEquals("http", url.scheme()); 639 assertEquals("", url.username()); 640 assertEquals("", url.password()); 641 assertEquals("host", url.host()); 642 assertEquals(80, url.port()); 643 assertEquals("/", url.encodedPath()); 644 assertEquals(null, url.query()); 645 assertEquals(null, url.fragment()); 646 } 647 648 @Test public void fullUrlComposition() throws Exception { 649 HttpUrl url = new HttpUrl.Builder() 650 .scheme("http") 651 .username("username") 652 .password("password") 653 .host("host") 654 .port(8080) 655 .addPathSegment("path") 656 .query("query") 657 .fragment("fragment") 658 .build(); 659 assertEquals("http://username:password@host:8080/path?query#fragment", url.toString()); 660 assertEquals("http", url.scheme()); 661 assertEquals("username", url.username()); 662 assertEquals("password", url.password()); 663 assertEquals("host", url.host()); 664 assertEquals(8080, url.port()); 665 assertEquals("/path", url.encodedPath()); 666 assertEquals("query", url.query()); 667 assertEquals("fragment", url.fragment()); 668 } 669 670 @Test public void changingSchemeChangesDefaultPort() throws Exception { 671 assertEquals(443, HttpUrl.parse("http://example.com") 672 .newBuilder() 673 .scheme("https") 674 .build().port()); 675 676 assertEquals(80, HttpUrl.parse("https://example.com") 677 .newBuilder() 678 .scheme("http") 679 .build().port()); 680 681 assertEquals(1234, HttpUrl.parse("https://example.com:1234") 682 .newBuilder() 683 .scheme("http") 684 .build().port()); 685 } 686 687 @Test public void composeEncodesWhitespace() throws Exception { 688 HttpUrl url = new HttpUrl.Builder() 689 .scheme("http") 690 .username("a\r\n\f\t b") 691 .password("c\r\n\f\t d") 692 .host("host") 693 .addPathSegment("e\r\n\f\t f") 694 .query("g\r\n\f\t h") 695 .fragment("i\r\n\f\t j") 696 .build(); 697 assertEquals("http://a%0D%0A%0C%09%20b:c%0D%0A%0C%09%20d@host" 698 + "/e%0D%0A%0C%09%20f?g%0D%0A%0C%09%20h#i%0D%0A%0C%09 j", url.toString()); 699 assertEquals("a\r\n\f\t b", url.username()); 700 assertEquals("c\r\n\f\t d", url.password()); 701 assertEquals("e\r\n\f\t f", url.pathSegments().get(0)); 702 assertEquals("g\r\n\f\t h", url.query()); 703 assertEquals("i\r\n\f\t j", url.fragment()); 704 } 705 706 @Test public void composeFromUnencodedComponents() throws Exception { 707 HttpUrl url = new HttpUrl.Builder() 708 .scheme("http") 709 .username("a:\u0001@/\\?#%b") 710 .password("c:\u0001@/\\?#%d") 711 .host("ef") 712 .port(8080) 713 .addPathSegment("g:\u0001@/\\?#%h") 714 .query("i:\u0001@/\\?#%j") 715 .fragment("k:\u0001@/\\?#%l") 716 .build(); 717 assertEquals("http://a%3A%01%40%2F%5C%3F%23%25b:c%3A%01%40%2F%5C%3F%23%25d@ef:8080/" 718 + "g:%01@%2F%5C%3F%23%25h?i:%01@/\\?%23%25j#k:%01@/\\?#%25l", url.toString()); 719 assertEquals("http", url.scheme()); 720 assertEquals("a:\u0001@/\\?#%b", url.username()); 721 assertEquals("c:\u0001@/\\?#%d", url.password()); 722 assertEquals(Arrays.asList("g:\u0001@/\\?#%h"), url.pathSegments()); 723 assertEquals("i:\u0001@/\\?#%j", url.query()); 724 assertEquals("k:\u0001@/\\?#%l", url.fragment()); 725 assertEquals("a%3A%01%40%2F%5C%3F%23%25b", url.encodedUsername()); 726 assertEquals("c%3A%01%40%2F%5C%3F%23%25d", url.encodedPassword()); 727 assertEquals("/g:%01@%2F%5C%3F%23%25h", url.encodedPath()); 728 assertEquals("i:%01@/\\?%23%25j", url.encodedQuery()); 729 assertEquals("k:%01@/\\?#%25l", url.encodedFragment()); 730 } 731 732 @Test public void composeFromEncodedComponents() throws Exception { 733 HttpUrl url = new HttpUrl.Builder() 734 .scheme("http") 735 .encodedUsername("a:\u0001@/\\?#%25b") 736 .encodedPassword("c:\u0001@/\\?#%25d") 737 .host("ef") 738 .port(8080) 739 .addEncodedPathSegment("g:\u0001@/\\?#%25h") 740 .encodedQuery("i:\u0001@/\\?#%25j") 741 .encodedFragment("k:\u0001@/\\?#%25l") 742 .build(); 743 assertEquals("http://a%3A%01%40%2F%5C%3F%23%25b:c%3A%01%40%2F%5C%3F%23%25d@ef:8080/" 744 + "g:%01@%2F%5C%3F%23%25h?i:%01@/\\?%23%25j#k:%01@/\\?#%25l", url.toString()); 745 assertEquals("http", url.scheme()); 746 assertEquals("a:\u0001@/\\?#%b", url.username()); 747 assertEquals("c:\u0001@/\\?#%d", url.password()); 748 assertEquals(Arrays.asList("g:\u0001@/\\?#%h"), url.pathSegments()); 749 assertEquals("i:\u0001@/\\?#%j", url.query()); 750 assertEquals("k:\u0001@/\\?#%l", url.fragment()); 751 assertEquals("a%3A%01%40%2F%5C%3F%23%25b", url.encodedUsername()); 752 assertEquals("c%3A%01%40%2F%5C%3F%23%25d", url.encodedPassword()); 753 assertEquals("/g:%01@%2F%5C%3F%23%25h", url.encodedPath()); 754 assertEquals("i:%01@/\\?%23%25j", url.encodedQuery()); 755 assertEquals("k:%01@/\\?#%25l", url.encodedFragment()); 756 } 757 758 @Test public void composeWithEncodedPath() throws Exception { 759 HttpUrl url = new HttpUrl.Builder() 760 .scheme("http") 761 .host("host") 762 .encodedPath("/a%2Fb/c") 763 .build(); 764 assertEquals("http://host/a%2Fb/c", url.toString()); 765 assertEquals("/a%2Fb/c", url.encodedPath()); 766 assertEquals(Arrays.asList("a/b", "c"), url.pathSegments()); 767 } 768 769 @Test public void composeMixingPathSegments() throws Exception { 770 HttpUrl url = new HttpUrl.Builder() 771 .scheme("http") 772 .host("host") 773 .encodedPath("/a%2fb/c") 774 .addPathSegment("d%25e") 775 .addEncodedPathSegment("f%25g") 776 .build(); 777 assertEquals("http://host/a%2fb/c/d%2525e/f%25g", url.toString()); 778 assertEquals("/a%2fb/c/d%2525e/f%25g", url.encodedPath()); 779 assertEquals(Arrays.asList("a%2fb", "c", "d%2525e", "f%25g"), url.encodedPathSegments()); 780 assertEquals(Arrays.asList("a/b", "c", "d%25e", "f%g"), url.pathSegments()); 781 } 782 783 @Test public void composeWithAddSegment() throws Exception { 784 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 785 assertEquals("/a/b/c/", base.newBuilder().addPathSegment("").build().encodedPath()); 786 assertEquals("/a/b/c/d", 787 base.newBuilder().addPathSegment("").addPathSegment("d").build().encodedPath()); 788 assertEquals("/a/b/", base.newBuilder().addPathSegment("..").build().encodedPath()); 789 assertEquals("/a/b/", base.newBuilder().addPathSegment("").addPathSegment("..").build() 790 .encodedPath()); 791 assertEquals("/a/b/c/", base.newBuilder().addPathSegment("").addPathSegment("").build() 792 .encodedPath()); 793 } 794 795 @Test public void pathSize() throws Exception { 796 assertEquals(1, HttpUrl.parse("http://host/").pathSize()); 797 assertEquals(3, HttpUrl.parse("http://host/a/b/c").pathSize()); 798 } 799 800 @Test public void addPathSegmentDotDoesNothing() throws Exception { 801 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 802 assertEquals("/a/b/c", base.newBuilder().addPathSegment(".").build().encodedPath()); 803 } 804 805 @Test public void addPathSegmentEncodes() throws Exception { 806 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 807 assertEquals("/a/b/c/%252e", 808 base.newBuilder().addPathSegment("%2e").build().encodedPath()); 809 assertEquals("/a/b/c/%252e%252e", 810 base.newBuilder().addPathSegment("%2e%2e").build().encodedPath()); 811 } 812 813 @Test public void addPathSegmentDotDotPopsDirectory() throws Exception { 814 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 815 assertEquals("/a/b/", base.newBuilder().addPathSegment("..").build().encodedPath()); 816 } 817 818 @Test public void addPathSegmentDotAndIgnoredCharacter() throws Exception { 819 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 820 assertEquals("/a/b/c/.%0A", base.newBuilder().addPathSegment(".\n").build().encodedPath()); 821 } 822 823 @Test public void addEncodedPathSegmentDotAndIgnoredCharacter() throws Exception { 824 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 825 assertEquals("/a/b/c", base.newBuilder().addEncodedPathSegment(".\n").build().encodedPath()); 826 } 827 828 @Test public void addEncodedPathSegmentDotDotAndIgnoredCharacter() throws Exception { 829 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 830 assertEquals("/a/b/", base.newBuilder().addEncodedPathSegment("..\n").build().encodedPath()); 831 } 832 833 @Test public void setPathSegment() throws Exception { 834 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 835 assertEquals("/d/b/c", base.newBuilder().setPathSegment(0, "d").build().encodedPath()); 836 assertEquals("/a/d/c", base.newBuilder().setPathSegment(1, "d").build().encodedPath()); 837 assertEquals("/a/b/d", base.newBuilder().setPathSegment(2, "d").build().encodedPath()); 838 } 839 840 @Test public void setPathSegmentEncodes() throws Exception { 841 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 842 assertEquals("/%2525/b/c", base.newBuilder().setPathSegment(0, "%25").build().encodedPath()); 843 assertEquals("/.%0A/b/c", base.newBuilder().setPathSegment(0, ".\n").build().encodedPath()); 844 assertEquals("/%252e/b/c", base.newBuilder().setPathSegment(0, "%2e").build().encodedPath()); 845 } 846 847 @Test public void setPathSegmentAcceptsEmpty() throws Exception { 848 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 849 assertEquals("//b/c", base.newBuilder().setPathSegment(0, "").build().encodedPath()); 850 assertEquals("/a/b/", base.newBuilder().setPathSegment(2, "").build().encodedPath()); 851 } 852 853 @Test public void setPathSegmentRejectsDot() 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 setPathSegmentRejectsDotDot() throws Exception { 863 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 864 try { 865 base.newBuilder().setPathSegment(0, ".."); 866 fail(); 867 } catch (IllegalArgumentException expected) { 868 } 869 } 870 871 @Test public void setPathSegmentWithSlash() throws Exception { 872 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 873 HttpUrl url = base.newBuilder().setPathSegment(1, "/").build(); 874 assertEquals("/a/%2F/c", url.encodedPath()); 875 } 876 877 @Test public void setPathSegmentOutOfBounds() throws Exception { 878 try { 879 new HttpUrl.Builder().setPathSegment(1, "a"); 880 fail(); 881 } catch (IndexOutOfBoundsException expected) { 882 } 883 } 884 885 @Test public void setEncodedPathSegmentEncodes() throws Exception { 886 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 887 assertEquals("/%25/b/c", 888 base.newBuilder().setEncodedPathSegment(0, "%25").build().encodedPath()); 889 } 890 891 @Test public void setEncodedPathSegmentRejectsDot() throws Exception { 892 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 893 try { 894 base.newBuilder().setEncodedPathSegment(0, "."); 895 fail(); 896 } catch (IllegalArgumentException expected) { 897 } 898 } 899 900 @Test public void setEncodedPathSegmentRejectsDotAndIgnoredCharacter() throws Exception { 901 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 902 try { 903 base.newBuilder().setEncodedPathSegment(0, ".\n"); 904 fail(); 905 } catch (IllegalArgumentException expected) { 906 } 907 } 908 909 @Test public void setEncodedPathSegmentRejectsDotDot() throws Exception { 910 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 911 try { 912 base.newBuilder().setEncodedPathSegment(0, ".."); 913 fail(); 914 } catch (IllegalArgumentException expected) { 915 } 916 } 917 918 @Test public void setEncodedPathSegmentRejectsDotDotAndIgnoredCharacter() throws Exception { 919 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 920 try { 921 base.newBuilder().setEncodedPathSegment(0, "..\n"); 922 fail(); 923 } catch (IllegalArgumentException expected) { 924 } 925 } 926 927 @Test public void setEncodedPathSegmentWithSlash() throws Exception { 928 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 929 HttpUrl url = base.newBuilder().setEncodedPathSegment(1, "/").build(); 930 assertEquals("/a/%2F/c", url.encodedPath()); 931 } 932 933 @Test public void setEncodedPathSegmentOutOfBounds() throws Exception { 934 try { 935 new HttpUrl.Builder().setEncodedPathSegment(1, "a"); 936 fail(); 937 } catch (IndexOutOfBoundsException expected) { 938 } 939 } 940 941 @Test public void removePathSegment() throws Exception { 942 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 943 HttpUrl url = base.newBuilder() 944 .removePathSegment(0) 945 .build(); 946 assertEquals("/b/c", url.encodedPath()); 947 } 948 949 @Test public void removePathSegmentDoesntRemovePath() throws Exception { 950 HttpUrl base = HttpUrl.parse("http://host/a/b/c"); 951 HttpUrl url = base.newBuilder() 952 .removePathSegment(0) 953 .removePathSegment(0) 954 .removePathSegment(0) 955 .build(); 956 assertEquals(Arrays.asList(""), url.pathSegments()); 957 assertEquals("/", url.encodedPath()); 958 } 959 960 @Test public void removePathSegmentOutOfBounds() throws Exception { 961 try { 962 new HttpUrl.Builder().removePathSegment(1); 963 fail(); 964 } catch (IndexOutOfBoundsException expected) { 965 } 966 } 967 968 @Test public void toJavaNetUrl() throws Exception { 969 HttpUrl httpUrl = HttpUrl.parse("http://username:password@host/path?query#fragment"); 970 URL javaNetUrl = httpUrl.url(); 971 assertEquals("http://username:password@host/path?query#fragment", javaNetUrl.toString()); 972 } 973 974 @Test public void toUri() throws Exception { 975 HttpUrl httpUrl = HttpUrl.parse("http://username:password@host/path?query#fragment"); 976 URI uri = httpUrl.uri(); 977 assertEquals("http://username:password@host/path?query#fragment", uri.toString()); 978 } 979 980 @Test public void toUriSpecialQueryCharacters() throws Exception { 981 HttpUrl httpUrl = HttpUrl.parse("http://host/?d=abc!@[]^`{}|\\"); 982 URI uri = httpUrl.uri(); 983 assertEquals("http://host/?d=abc!@[]%5E%60%7B%7D%7C%5C", uri.toString()); 984 } 985 986 @Test public void toUriWithUsernameNoPassword() throws Exception { 987 HttpUrl httpUrl = new HttpUrl.Builder() 988 .scheme("http") 989 .username("user") 990 .host("host") 991 .build(); 992 assertEquals("http://user@host/", httpUrl.toString()); 993 assertEquals("http://user@host/", httpUrl.uri().toString()); 994 } 995 996 @Test public void toUriUsernameSpecialCharacters() throws Exception { 997 HttpUrl url = new HttpUrl.Builder() 998 .scheme("http") 999 .host("host") 1000 .username("=[]:;\"~|?#@^/$%*") 1001 .build(); 1002 assertEquals("http://%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", url.toString()); 1003 assertEquals("http://%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", url.uri().toString()); 1004 } 1005 1006 @Test public void toUriPasswordSpecialCharacters() throws Exception { 1007 HttpUrl url = new HttpUrl.Builder() 1008 .scheme("http") 1009 .host("host") 1010 .username("user") 1011 .password("=[]:;\"~|?#@^/$%*") 1012 .build(); 1013 assertEquals("http://user:%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", url.toString()); 1014 assertEquals("http://user:%3D%5B%5D%3A%3B%22~%7C%3F%23%40%5E%2F$%25*@host/", 1015 url.uri().toString()); 1016 } 1017 1018 @Test public void toUriPathSpecialCharacters() throws Exception { 1019 HttpUrl url = new HttpUrl.Builder() 1020 .scheme("http") 1021 .host("host") 1022 .addPathSegment("=[]:;\"~|?#@^/$%*") 1023 .build(); 1024 assertEquals("http://host/=[]:;%22~%7C%3F%23@%5E%2F$%25*", url.toString()); 1025 assertEquals("http://host/=%5B%5D:;%22~%7C%3F%23@%5E%2F$%25*", url.uri().toString()); 1026 } 1027 1028 @Test public void toUriQueryParameterNameSpecialCharacters() throws Exception { 1029 HttpUrl url = new HttpUrl.Builder() 1030 .scheme("http") 1031 .host("host") 1032 .addQueryParameter("=[]:;\"~|?#@^/$%*", "a") 1033 .build(); 1034 assertEquals("http://host/?%3D[]:;%22~|?%23@^/$%25*=a", url.toString()); 1035 assertEquals("http://host/?%3D[]:;%22~%7C?%23@%5E/$%25*=a", url.uri().toString()); 1036 } 1037 1038 @Test public void toUriQueryParameterValueSpecialCharacters() throws Exception { 1039 HttpUrl url = new HttpUrl.Builder() 1040 .scheme("http") 1041 .host("host") 1042 .addQueryParameter("a", "=[]:;\"~|?#@^/$%*") 1043 .build(); 1044 assertEquals("http://host/?a=%3D[]:;%22~|?%23@^/$%25*", url.toString()); 1045 assertEquals("http://host/?a=%3D[]:;%22~%7C?%23@%5E/$%25*", url.uri().toString()); 1046 } 1047 1048 @Test public void toUriQueryValueSpecialCharacters() throws Exception { 1049 HttpUrl url = new HttpUrl.Builder() 1050 .scheme("http") 1051 .host("host") 1052 .query("=[]:;\"~|?#@^/$%*") 1053 .build(); 1054 assertEquals("http://host/?=[]:;%22~|?%23@^/$%25*", url.toString()); 1055 assertEquals("http://host/?=[]:;%22~%7C?%23@%5E/$%25*", url.uri().toString()); 1056 } 1057 1058 @Test public void toUriFragmentSpecialCharacters() throws Exception { 1059 HttpUrl url = new HttpUrl.Builder() 1060 .scheme("http") 1061 .host("host") 1062 .fragment("=[]:;\"~|?#@^/$%*") 1063 .build(); 1064 assertEquals("http://host/#=[]:;\"~|?#@^/$%25*", url.toString()); 1065 assertEquals("http://host/#=[]:;%22~%7C?%23@%5E/$%25*", url.uri().toString()); 1066 } 1067 1068 @Test public void toUriWithControlCharacters() throws Exception { 1069 // Percent-encoded in the path. 1070 assertEquals(new URI("http://host/a%00b"), HttpUrl.parse("http://host/a\u0000b").uri()); 1071 assertEquals(new URI("http://host/a%C2%80b"), HttpUrl.parse("http://host/a\u0080b").uri()); 1072 assertEquals(new URI("http://host/a%C2%9Fb"), HttpUrl.parse("http://host/a\u009fb").uri()); 1073 // Percent-encoded in the query. 1074 assertEquals(new URI("http://host/?a%00b"), HttpUrl.parse("http://host/?a\u0000b").uri()); 1075 assertEquals(new URI("http://host/?a%C2%80b"), HttpUrl.parse("http://host/?a\u0080b").uri()); 1076 assertEquals(new URI("http://host/?a%C2%9Fb"), HttpUrl.parse("http://host/?a\u009fb").uri()); 1077 // Stripped from the fragment. 1078 assertEquals(new URI("http://host/#a%00b"), HttpUrl.parse("http://host/#a\u0000b").uri()); 1079 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u0080b").uri()); 1080 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u009fb").uri()); 1081 } 1082 1083 @Test public void toUriWithSpaceCharacters() throws Exception { 1084 // Percent-encoded in the path. 1085 assertEquals(new URI("http://host/a%0Bb"), HttpUrl.parse("http://host/a\u000bb").uri()); 1086 assertEquals(new URI("http://host/a%20b"), HttpUrl.parse("http://host/a b").uri()); 1087 assertEquals(new URI("http://host/a%E2%80%89b"), HttpUrl.parse("http://host/a\u2009b").uri()); 1088 assertEquals(new URI("http://host/a%E3%80%80b"), HttpUrl.parse("http://host/a\u3000b").uri()); 1089 // Percent-encoded in the query. 1090 assertEquals(new URI("http://host/?a%0Bb"), HttpUrl.parse("http://host/?a\u000bb").uri()); 1091 assertEquals(new URI("http://host/?a%20b"), HttpUrl.parse("http://host/?a b").uri()); 1092 assertEquals(new URI("http://host/?a%E2%80%89b"), HttpUrl.parse("http://host/?a\u2009b").uri()); 1093 assertEquals(new URI("http://host/?a%E3%80%80b"), HttpUrl.parse("http://host/?a\u3000b").uri()); 1094 // Stripped from the fragment. 1095 assertEquals(new URI("http://host/#a%0Bb"), HttpUrl.parse("http://host/#a\u000bb").uri()); 1096 assertEquals(new URI("http://host/#a%20b"), HttpUrl.parse("http://host/#a b").uri()); 1097 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u2009b").uri()); 1098 assertEquals(new URI("http://host/#ab"), HttpUrl.parse("http://host/#a\u3000b").uri()); 1099 } 1100 1101 @Test public void toUriWithNonHexPercentEscape() throws Exception { 1102 assertEquals(new URI("http://host/%25xx"), HttpUrl.parse("http://host/%xx").uri()); 1103 } 1104 1105 @Test public void toUriWithTruncatedPercentEscape() throws Exception { 1106 assertEquals(new URI("http://host/%25a"), HttpUrl.parse("http://host/%a").uri()); 1107 assertEquals(new URI("http://host/%25"), HttpUrl.parse("http://host/%").uri()); 1108 } 1109 1110 @Test public void fromJavaNetUrl() throws Exception { 1111 URL javaNetUrl = new URL("http://username:password@host/path?query#fragment"); 1112 HttpUrl httpUrl = HttpUrl.get(javaNetUrl); 1113 assertEquals("http://username:password@host/path?query#fragment", httpUrl.toString()); 1114 } 1115 1116 // ANDROID-BEGIN 1117 @Ignore // Android's URL implementation does not support mailto: 1118 // ANDROID-END 1119 @Test public void fromJavaNetUrlUnsupportedScheme() throws Exception { 1120 URL javaNetUrl = new URL("mailto:user@example.com"); 1121 assertEquals(null, HttpUrl.get(javaNetUrl)); 1122 } 1123 1124 @Test public void fromUri() throws Exception { 1125 URI uri = new URI("http://username:password@host/path?query#fragment"); 1126 HttpUrl httpUrl = HttpUrl.get(uri); 1127 assertEquals("http://username:password@host/path?query#fragment", httpUrl.toString()); 1128 } 1129 1130 @Test public void fromUriUnsupportedScheme() throws Exception { 1131 URI uri = new URI("mailto:user@example.com"); 1132 assertEquals(null, HttpUrl.get(uri)); 1133 } 1134 1135 @Test public void fromUriPartial() throws Exception { 1136 URI uri = new URI("/path"); 1137 assertEquals(null, HttpUrl.get(uri)); 1138 } 1139 1140 @Test public void fromJavaNetUrl_checked() throws Exception { 1141 HttpUrl httpUrl = HttpUrl.getChecked("http://username:password@host/path?query#fragment"); 1142 assertEquals("http://username:password@host/path?query#fragment", httpUrl.toString()); 1143 } 1144 1145 @Test public void fromJavaNetUrlUnsupportedScheme_checked() throws Exception { 1146 try { 1147 HttpUrl.getChecked("mailto:user@example.com"); 1148 fail(); 1149 } catch (MalformedURLException e) { 1150 } 1151 } 1152 1153 @Test public void fromJavaNetUrlBadHost_checked() throws Exception { 1154 try { 1155 HttpUrl.getChecked("http://hostw ithspace/"); 1156 fail(); 1157 } catch (UnknownHostException expected) { 1158 } 1159 } 1160 1161 @Test public void composeQueryWithComponents() throws Exception { 1162 HttpUrl base = HttpUrl.parse("http://host/"); 1163 HttpUrl url = base.newBuilder().addQueryParameter("a+=& b", "c+=& d").build(); 1164 assertEquals("http://host/?a%2B%3D%26%20b=c%2B%3D%26%20d", url.toString()); 1165 assertEquals("c+=& d", url.queryParameterValue(0)); 1166 assertEquals("a+=& b", url.queryParameterName(0)); 1167 assertEquals("c+=& d", url.queryParameter("a+=& b")); 1168 assertEquals(Collections.singleton("a+=& b"), url.queryParameterNames()); 1169 assertEquals(singletonList("c+=& d"), url.queryParameterValues("a+=& b")); 1170 assertEquals(1, url.querySize()); 1171 assertEquals("a+=& b=c+=& d", url.query()); // Ambiguous! (Though working as designed.) 1172 assertEquals("a%2B%3D%26%20b=c%2B%3D%26%20d", url.encodedQuery()); 1173 } 1174 1175 @Test public void composeQueryWithEncodedComponents() throws Exception { 1176 HttpUrl base = HttpUrl.parse("http://host/"); 1177 HttpUrl url = base.newBuilder().addEncodedQueryParameter("a+=& b", "c+=& d").build(); 1178 assertEquals("http://host/?a+%3D%26%20b=c+%3D%26%20d", url.toString()); 1179 assertEquals("c =& d", url.queryParameter("a =& b")); 1180 } 1181 1182 @Test public void composeQueryRemoveQueryParameter() throws Exception { 1183 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1184 .addQueryParameter("a+=& b", "c+=& d") 1185 .removeAllQueryParameters("a+=& b") 1186 .build(); 1187 assertEquals("http://host/", url.toString()); 1188 assertEquals(null, url.queryParameter("a+=& b")); 1189 } 1190 1191 @Test public void composeQueryRemoveEncodedQueryParameter() throws Exception { 1192 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1193 .addEncodedQueryParameter("a+=& b", "c+=& d") 1194 .removeAllEncodedQueryParameters("a+=& b") 1195 .build(); 1196 assertEquals("http://host/", url.toString()); 1197 assertEquals(null, url.queryParameter("a =& b")); 1198 } 1199 1200 @Test public void composeQuerySetQueryParameter() throws Exception { 1201 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1202 .addQueryParameter("a+=& b", "c+=& d") 1203 .setQueryParameter("a+=& b", "ef") 1204 .build(); 1205 assertEquals("http://host/?a%2B%3D%26%20b=ef", url.toString()); 1206 assertEquals("ef", url.queryParameter("a+=& b")); 1207 } 1208 1209 @Test public void composeQuerySetEncodedQueryParameter() throws Exception { 1210 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1211 .addEncodedQueryParameter("a+=& b", "c+=& d") 1212 .setEncodedQueryParameter("a+=& b", "ef") 1213 .build(); 1214 assertEquals("http://host/?a+%3D%26%20b=ef", url.toString()); 1215 assertEquals("ef", url.queryParameter("a =& b")); 1216 } 1217 1218 @Test public void composeQueryMultipleEncodedValuesForParameter() throws Exception { 1219 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1220 .addQueryParameter("a+=& b", "c+=& d") 1221 .addQueryParameter("a+=& b", "e+=& f") 1222 .build(); 1223 assertEquals("http://host/?a%2B%3D%26%20b=c%2B%3D%26%20d&a%2B%3D%26%20b=e%2B%3D%26%20f", 1224 url.toString()); 1225 assertEquals(2, url.querySize()); 1226 assertEquals(Collections.singleton("a+=& b"), url.queryParameterNames()); 1227 assertEquals(Arrays.asList("c+=& d", "e+=& f"), url.queryParameterValues("a+=& b")); 1228 } 1229 1230 @Test public void absentQueryIsZeroNameValuePairs() throws Exception { 1231 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1232 .query(null) 1233 .build(); 1234 assertEquals(0, url.querySize()); 1235 } 1236 1237 @Test public void emptyQueryIsSingleNameValuePairWithEmptyKey() throws Exception { 1238 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1239 .query("") 1240 .build(); 1241 assertEquals(1, url.querySize()); 1242 assertEquals("", url.queryParameterName(0)); 1243 assertEquals(null, url.queryParameterValue(0)); 1244 } 1245 1246 @Test public void ampersandQueryIsTwoNameValuePairsWithEmptyKeys() throws Exception { 1247 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1248 .query("&") 1249 .build(); 1250 assertEquals(2, url.querySize()); 1251 assertEquals("", url.queryParameterName(0)); 1252 assertEquals(null, url.queryParameterValue(0)); 1253 assertEquals("", url.queryParameterName(1)); 1254 assertEquals(null, url.queryParameterValue(1)); 1255 } 1256 1257 @Test public void removeAllDoesNotRemoveQueryIfNoParametersWereRemoved() throws Exception { 1258 HttpUrl url = HttpUrl.parse("http://host/").newBuilder() 1259 .query("") 1260 .removeAllQueryParameters("a") 1261 .build(); 1262 assertEquals("http://host/?", url.toString()); 1263 } 1264 1265 @Test public void queryParametersWithoutValues() throws Exception { 1266 HttpUrl url = HttpUrl.parse("http://host/?foo&bar&baz"); 1267 assertEquals(3, url.querySize()); 1268 assertEquals(new LinkedHashSet<>(Arrays.asList("foo", "bar", "baz")), 1269 url.queryParameterNames()); 1270 assertEquals(null, url.queryParameterValue(0)); 1271 assertEquals(null, url.queryParameterValue(1)); 1272 assertEquals(null, url.queryParameterValue(2)); 1273 assertEquals(singletonList((String) null), url.queryParameterValues("foo")); 1274 assertEquals(singletonList((String) null), url.queryParameterValues("bar")); 1275 assertEquals(singletonList((String) null), url.queryParameterValues("baz")); 1276 } 1277 1278 @Test public void queryParametersWithEmptyValues() throws Exception { 1279 HttpUrl url = HttpUrl.parse("http://host/?foo=&bar=&baz="); 1280 assertEquals(3, url.querySize()); 1281 assertEquals(new LinkedHashSet<>(Arrays.asList("foo", "bar", "baz")), 1282 url.queryParameterNames()); 1283 assertEquals("", url.queryParameterValue(0)); 1284 assertEquals("", url.queryParameterValue(1)); 1285 assertEquals("", url.queryParameterValue(2)); 1286 assertEquals(singletonList(""), url.queryParameterValues("foo")); 1287 assertEquals(singletonList(""), url.queryParameterValues("bar")); 1288 assertEquals(singletonList(""), url.queryParameterValues("baz")); 1289 } 1290 1291 @Test public void queryParametersWithRepeatedName() throws Exception { 1292 HttpUrl url = HttpUrl.parse("http://host/?foo[]=1&foo[]=2&foo[]=3"); 1293 assertEquals(3, url.querySize()); 1294 assertEquals(Collections.singleton("foo[]"), url.queryParameterNames()); 1295 assertEquals("1", url.queryParameterValue(0)); 1296 assertEquals("2", url.queryParameterValue(1)); 1297 assertEquals("3", url.queryParameterValue(2)); 1298 assertEquals(Arrays.asList("1", "2", "3"), url.queryParameterValues("foo[]")); 1299 } 1300 1301 @Test public void queryParameterLookupWithNonCanonicalEncoding() throws Exception { 1302 HttpUrl url = HttpUrl.parse("http://host/?%6d=m&+=%20"); 1303 assertEquals("m", url.queryParameterName(0)); 1304 assertEquals(" ", url.queryParameterName(1)); 1305 assertEquals("m", url.queryParameter("m")); 1306 assertEquals(" ", url.queryParameter(" ")); 1307 } 1308 1309 @Test public void roundTripBuilder() throws Exception { 1310 HttpUrl url = new HttpUrl.Builder() 1311 .scheme("http") 1312 .username("%") 1313 .password("%") 1314 .host("host") 1315 .addPathSegment("%") 1316 .query("%") 1317 .fragment("%") 1318 .build(); 1319 assertEquals("http://%25:%25@host/%25?%25#%25", url.toString()); 1320 assertEquals("http://%25:%25@host/%25?%25#%25", url.newBuilder().build().toString()); 1321 assertEquals("http://%25:%25@host/%25?%25", url.resolve("").toString()); 1322 } 1323 1324 /** 1325 * Although HttpUrl prefers percent-encodings in uppercase, it should preserve the exact 1326 * structure of the original encoding. 1327 */ 1328 @Test public void rawEncodingRetained() throws Exception { 1329 String urlString = "http://%6d%6D:%6d%6D@host/%6d%6D?%6d%6D#%6d%6D"; 1330 HttpUrl url = HttpUrl.parse(urlString); 1331 assertEquals("%6d%6D", url.encodedUsername()); 1332 assertEquals("%6d%6D", url.encodedPassword()); 1333 assertEquals("/%6d%6D", url.encodedPath()); 1334 assertEquals(Arrays.asList("%6d%6D"), url.encodedPathSegments()); 1335 assertEquals("%6d%6D", url.encodedQuery()); 1336 assertEquals("%6d%6D", url.encodedFragment()); 1337 assertEquals(urlString, url.toString()); 1338 assertEquals(urlString, url.newBuilder().build().toString()); 1339 assertEquals("http://%6d%6D:%6d%6D@host/%6d%6D?%6d%6D", url.resolve("").toString()); 1340 } 1341 1342 @Test public void clearFragment() throws Exception { 1343 HttpUrl url = HttpUrl.parse("http://host/#fragment") 1344 .newBuilder() 1345 .fragment(null) 1346 .build(); 1347 assertEquals("http://host/", url.toString()); 1348 assertEquals(null, url.fragment()); 1349 assertEquals(null, url.encodedFragment()); 1350 } 1351 1352 @Test public void clearEncodedFragment() throws Exception { 1353 HttpUrl url = HttpUrl.parse("http://host/#fragment") 1354 .newBuilder() 1355 .encodedFragment(null) 1356 .build(); 1357 assertEquals("http://host/", url.toString()); 1358 assertEquals(null, url.fragment()); 1359 assertEquals(null, url.encodedFragment()); 1360 } 1361} 1362