1781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller/*
2781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * Copyright (C) 2015 Square, Inc.
3781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller *
4781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * you may not use this file except in compliance with the License.
6781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * You may obtain a copy of the License at
7781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller *
8781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller *
10781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * Unless required by applicable law or agreed to in writing, software
11781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * See the License for the specific language governing permissions and
14781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * limitations under the License.
15781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller */
16781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerpackage com.squareup.okhttp;
17781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
18781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport com.squareup.okhttp.UrlComponentEncodingTester.Component;
19781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport com.squareup.okhttp.UrlComponentEncodingTester.Encoding;
20781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport java.util.Arrays;
21781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport org.junit.Test;
22781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
23781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport static org.junit.Assert.assertEquals;
24781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
25781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerpublic final class HttpUrlTest {
26781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void parseTrimsAsciiWhitespace() throws Exception {
27781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl expected = HttpUrl.parse("http://host/");
28781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(expected, HttpUrl.parse("http://host/\f\n\t \r")); // Leading.
29781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(expected, HttpUrl.parse("\r\n\f \thttp://host/")); // Trailing.
30781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(expected, HttpUrl.parse(" http://host/ ")); // Both.
31781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(expected, HttpUrl.parse("    http://host/    ")); // Both.
32781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(expected, HttpUrl.parse("http://host/").resolve("   "));
33781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(expected, HttpUrl.parse("http://host/").resolve("  .  "));
34781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
35781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
36781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void parseDoesNotTrimOtherWhitespaceCharacters() throws Exception {
37781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // Whitespace characters list from Google's Guava team: http://goo.gl/IcR9RD
38781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%0B", HttpUrl.parse("http://h/\u000b").path()); // line tabulation
39781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%1C", HttpUrl.parse("http://h/\u001c").path()); // information separator 4
40781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%1D", HttpUrl.parse("http://h/\u001d").path()); // information separator 3
41781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%1E", HttpUrl.parse("http://h/\u001e").path()); // information separator 2
42781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%1F", HttpUrl.parse("http://h/\u001f").path()); // information separator 1
43781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%C2%85", HttpUrl.parse("http://h/\u0085").path()); // next line
44781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%C2%A0", HttpUrl.parse("http://h/\u00a0").path()); // non-breaking space
45781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E1%9A%80", HttpUrl.parse("http://h/\u1680").path()); // ogham space mark
46781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E1%A0%8E", HttpUrl.parse("http://h/\u180e").path()); // mongolian vowel separator
47781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%80", HttpUrl.parse("http://h/\u2000").path()); // en quad
48781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%81", HttpUrl.parse("http://h/\u2001").path()); // em quad
49781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%82", HttpUrl.parse("http://h/\u2002").path()); // en space
50781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%83", HttpUrl.parse("http://h/\u2003").path()); // em space
51781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%84", HttpUrl.parse("http://h/\u2004").path()); // three-per-em space
52781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%85", HttpUrl.parse("http://h/\u2005").path()); // four-per-em space
53781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%86", HttpUrl.parse("http://h/\u2006").path()); // six-per-em space
54781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%87", HttpUrl.parse("http://h/\u2007").path()); // figure space
55781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%88", HttpUrl.parse("http://h/\u2008").path()); // punctuation space
56781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%89", HttpUrl.parse("http://h/\u2009").path()); // thin space
57781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%8A", HttpUrl.parse("http://h/\u200a").path()); // hair space
58781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%8B", HttpUrl.parse("http://h/\u200b").path()); // zero-width space
59781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%8C", HttpUrl.parse("http://h/\u200c").path()); // zero-width non-joiner
60781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%8D", HttpUrl.parse("http://h/\u200d").path()); // zero-width joiner
61781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%8E", HttpUrl.parse("http://h/\u200e").path()); // left-to-right mark
62781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%8F", HttpUrl.parse("http://h/\u200f").path()); // right-to-left mark
63781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%A8", HttpUrl.parse("http://h/\u2028").path()); // line separator
64781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%A9", HttpUrl.parse("http://h/\u2029").path()); // paragraph separator
65781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%80%AF", HttpUrl.parse("http://h/\u202f").path()); // narrow non-breaking space
66781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E2%81%9F", HttpUrl.parse("http://h/\u205f").path()); // medium mathematical space
67781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%E3%80%80", HttpUrl.parse("http://h/\u3000").path()); // ideographic space
68781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
69781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
70781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void scheme() throws Exception {
71781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host/"));
72781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("Http://host/"));
73781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host/"));
74781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("HTTP://host/"));
75781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("https://host/"), HttpUrl.parse("https://host/"));
76781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("https://host/"), HttpUrl.parse("HTTPS://host/"));
77781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("httpp://host/"));
78781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("0ttp://host/"));
79781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("ht+tp://host/"));
80781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("ht.tp://host/"));
81781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("ht-tp://host/"));
82781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("ht1tp://host/"));
83781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("httpss://host/"));
84781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
85781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
86781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void parseNoScheme() throws Exception {
87781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("//host"));
88781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("/path"));
89781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("path"));
90781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("?query"));
91781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("#fragment"));
92781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
93781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
94781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void resolveNoScheme() throws Exception {
95781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://host/a/b");
96781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host2/"), base.resolve("//host2"));
97781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("/path"));
98781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/path"), base.resolve("path"));
99781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b?query"), base.resolve("?query"));
100781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b#fragment"), base.resolve("#fragment"));
101781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b"), base.resolve(""));
102781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("\\path"));
103781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
104781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
105781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void resolveUnsupportedScheme() throws Exception {
106781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://a/");
107781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, base.resolve("ftp://b"));
108781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, base.resolve("ht+tp://b"));
109781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, base.resolve("ht-tp://b"));
110781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, base.resolve("ht.tp://b"));
111781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
112781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
113781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void resolveSchemeLikePath() throws Exception {
114781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://a/");
115781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/http//b/"), base.resolve("http//b/"));
116781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/ht+tp//b/"), base.resolve("ht+tp//b/"));
117781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/ht-tp//b/"), base.resolve("ht-tp//b/"));
118781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/ht.tp//b/"), base.resolve("ht.tp//b/"));
119781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
120781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
121781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void parseAuthoritySlashCountDoesntMatter() throws Exception {
122781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:host/path"));
123781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/host/path"));
124781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\host/path"));
125781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://host/path"));
126781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\/host/path"));
127781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/\\host/path"));
128781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\\\host/path"));
129781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:///host/path"));
130781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\//host/path"));
131781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/\\/host/path"));
132781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://\\host/path"));
133781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\\\/host/path"));
134781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:/\\\\host/path"));
135781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:\\\\\\host/path"));
136781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http:////host/path"));
137781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
138781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
139781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void resolveAuthoritySlashCountDoesntMatterWithDifferentScheme() throws Exception {
140781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("https://a/b/c");
141781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:host/path"));
142781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/host/path"));
143781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\host/path"));
144781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://host/path"));
145781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\/host/path"));
146781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\host/path"));
147781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\host/path"));
148781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:///host/path"));
149781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\//host/path"));
150781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\/host/path"));
151781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://\\host/path"));
152781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\/host/path"));
153781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\\\host/path"));
154781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\\\host/path"));
155781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:////host/path"));
156781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
157781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
158781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void resolveAuthoritySlashCountMattersWithSameScheme() throws Exception {
159781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://a/b/c");
160781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/b/host/path"), base.resolve("http:host/path"));
161781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/host/path"), base.resolve("http:/host/path"));
162781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://a/host/path"), base.resolve("http:\\host/path"));
163781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://host/path"));
164781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\/host/path"));
165781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\host/path"));
166781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\host/path"));
167781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:///host/path"));
168781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\//host/path"));
169781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\/host/path"));
170781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http://\\host/path"));
171781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\/host/path"));
172781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:/\\\\host/path"));
173781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:\\\\\\host/path"));
174781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), base.resolve("http:////host/path"));
175781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
176781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
177781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void username() throws Exception {
178781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://@host/path"));
179781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://user@host/path"), HttpUrl.parse("http://user@host/path"));
180781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
181781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
182781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void authorityWithMultipleAtSigns() throws Exception {
183781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://foo%40bar@baz/path"),
184781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://foo@bar@baz/path"));
185781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://foo:pass1%40bar%3Apass2@baz/path"),
186781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://foo:pass1@bar:pass2@baz/path"));
187781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
188781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
189781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void usernameAndPassword() throws Exception {
190781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://username:password@host/path"),
191781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://username:password@host/path"));
192781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://username@host/path"),
193781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://username:@host/path"));
194781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
195781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
196781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void passwordWithEmptyUsername() throws Exception {
197781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // Chrome doesn't mind, but Firefox rejects URLs with empty usernames and non-empty passwords.
198781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/path"), HttpUrl.parse("http://:@host/path"));
199781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("password%40", HttpUrl.parse("http://:password@@host/path").password());
200781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
201781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
202781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void unprintableCharactersArePercentEncoded() throws Exception {
203781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%00", HttpUrl.parse("http://host/\u0000").path());
204781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%08", HttpUrl.parse("http://host/\u0008").path());
205781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("/%EF%BF%BD", HttpUrl.parse("http://host/\ufffd").path());
206781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
207781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
208781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void usernameCharacters() throws Exception {
209781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    new UrlComponentEncodingTester()
210781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.PERCENT, '[', ']', '{', '}', '|', '^', '\'', ';', '=', '@')
211781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.SKIP, ':', '/', '\\', '?', '#')
212781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .test(Component.USER);
213781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
214781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
215781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void passwordCharacters() throws Exception {
216781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    new UrlComponentEncodingTester()
217781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.PERCENT, '[', ']', '{', '}', '|', '^', '\'', ':', ';', '=', '@')
218781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.SKIP, '/', '\\', '?', '#')
219781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .test(Component.PASSWORD);
220781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
221781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
222781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void hostContainsIllegalCharacter() throws Exception {
223781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://\n/"));
224781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http:// /"));
225781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://%20/"));
226781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
227781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
228781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void hostIpv6() throws Exception {
229781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // Square braces are absent from host()...
230781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("::1", HttpUrl.parse("http://[::1]/").host());
231781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
232781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // ... but they're included in toString().
233781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("http://[::1]/", HttpUrl.parse("http://[::1]/").toString());
234781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
235781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // IPv6 colons don't interfere with port numbers or passwords.
236781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(8080, HttpUrl.parse("http://[::1]:8080/").port());
237781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("password", HttpUrl.parse("http://user:password@[::1]/").password());
238781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("::1", HttpUrl.parse("http://user:password@[::1]:8080/").host());
239781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
240781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // Permit the contents of IPv6 addresses to be percent-encoded...
241781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("::1", HttpUrl.parse("http://[%3A%3A%31]/").host());
242781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
243781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // Including the Square braces themselves! (This is what Chrome does.)
244781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("::1", HttpUrl.parse("http://%5B%3A%3A1%5D/").host());
245781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
246781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
247781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void port() throws Exception {
248781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), HttpUrl.parse("http://host:80/"));
249781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host:99/"), HttpUrl.parse("http://host:99/"));
250781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(65535, HttpUrl.parse("http://host:65535/").port());
251781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://host:0/"));
252781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://host:65536/"));
253781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://host:-1/"));
254781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://host:a/"));
255781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://host:%39%39/"));
256781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
257781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
258781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void pathCharacters() throws Exception {
259781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    new UrlComponentEncodingTester()
260781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.PERCENT, '^', '{', '}', '|')
261781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.SKIP, '\\', '?', '#')
262781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .test(Component.PATH);
263781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
264781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
265781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void queryCharacters() throws Exception {
266781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    new UrlComponentEncodingTester()
267781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.IDENTITY, '?', '`')
268781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.PERCENT, '\'')
269781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.SKIP, '#')
270781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .test(Component.QUERY);
271781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
272781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
273781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void fragmentCharacters() throws Exception {
274781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    new UrlComponentEncodingTester()
275781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .override(Encoding.IDENTITY, ' ', '"', '#', '<', '>', '?', '`')
276781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        .test(Component.FRAGMENT);
277781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // TODO(jwilson): don't percent-encode non-ASCII characters. (But do encode control characters!)
278781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
279781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
280781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void relativePath() throws Exception {
281781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://host/a/b/c");
282781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("d/e/f"));
283781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("../../d/e/f"));
284781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".."));
285781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), base.resolve("../.."));
286781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), base.resolve("../../.."));
287781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("."));
288781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("././.."));
289781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/c/"), base.resolve("c/d/../e/../"));
290781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/..e/"), base.resolve("..e/"));
291781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/e/f../"), base.resolve("e/f../"));
292781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2E."));
293781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".%2E"));
294781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2E%2E"));
295781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2e."));
296781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve(".%2e"));
297781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/"), base.resolve("%2e%2e"));
298781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("%2E"));
299781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/"), base.resolve("%2e"));
300781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
301781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
302781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void pathWithBackslash() throws Exception {
303781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://host/a/b/c");
304781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("d\\e\\f"));
305781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("../..\\d\\e\\f"));
306781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/"), base.resolve("..\\.."));
307781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
308781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
309781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void relativePathWithSameScheme() throws Exception {
310781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    HttpUrl base = HttpUrl.parse("http://host/a/b/c");
311781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/a/b/d/e/f"), base.resolve("http:d/e/f"));
312781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(HttpUrl.parse("http://host/d/e/f"), base.resolve("http:../../d/e/f"));
313781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
314781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
315781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void decodeUsername() {
316781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("user", HttpUrl.parse("http://user@host/").decodeUsername());
317781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("\uD83C\uDF69", HttpUrl.parse("http://%F0%9F%8D%A9@host/").decodeUsername());
318781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
319781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
320781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void decodePassword() {
321781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("password", HttpUrl.parse("http://user:password@host/").decodePassword());
322781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(null, HttpUrl.parse("http://user:@host/").decodePassword());
323781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals("\uD83C\uDF69", HttpUrl.parse("http://user:%F0%9F%8D%A9@host/").decodePassword());
324781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
325781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
326781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void decodeSlashCharacterInDecodedPathSegment() {
327781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a/b/c"),
328781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a%2Fb%2Fc").decodePathSegments());
329781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
330781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
331781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void decodeEmptyPathSegments() {
332781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList(""),
333781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/").decodePathSegments());
334781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
335781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
336781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void percentDecode() throws Exception {
337781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("\u0000"),
338781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/%00").decodePathSegments());
339781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a", "\u2603", "c"),
340781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a/%E2%98%83/c").decodePathSegments());
341781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a", "\uD83C\uDF69", "c"),
342781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a/%F0%9F%8D%A9/c").decodePathSegments());
343781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a", "b", "c"),
344781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a/%62/c").decodePathSegments());
345781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a", "z", "c"),
346781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a/%7A/c").decodePathSegments());
347781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a", "z", "c"),
348781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a/%7a/c").decodePathSegments());
349781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
350781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
351781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void malformedPercentEncoding() {
352781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a%f", "b"),
353781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a%f/b").decodePathSegments());
354781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("%", "b"),
355781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/%/b").decodePathSegments());
356781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("%"),
357781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/%").decodePathSegments());
358781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
359781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller
360781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  @Test public void malformedUtf8Encoding() {
361781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    // Replace a partial UTF-8 sequence with the Unicode replacement character.
362781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller    assertEquals(Arrays.asList("a", "\ufffdx", "c"),
363781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller        HttpUrl.parse("http://host/a/%E2%98x/c").decodePathSegments());
364781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller  }
365781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller}
366