1 package org.jsoup.integration; 2 3 import org.jsoup.Connection; 4 import org.jsoup.Jsoup; 5 import org.jsoup.UnsupportedMimeTypeException; 6 import org.jsoup.helper.W3CDom; 7 import org.jsoup.internal.StringUtil; 8 import org.jsoup.nodes.Document; 9 import org.jsoup.nodes.FormElement; 10 import org.jsoup.parser.HtmlTreeBuilder; 11 import org.jsoup.parser.Parser; 12 import org.jsoup.parser.XmlTreeBuilder; 13 import org.junit.jupiter.api.Disabled; 14 import org.junit.jupiter.api.Test; 15 16 import java.io.File; 17 import java.io.FileInputStream; 18 import java.io.IOException; 19 import java.net.ConnectException; 20 import java.net.InetSocketAddress; 21 import java.net.Proxy; 22 import java.net.URL; 23 import java.util.List; 24 25 import static org.junit.jupiter.api.Assertions.*; 26 27 /** 28 Tests the URL connection. Not enabled by default, so tests don't require network connection. 29 30 @author Jonathan Hedley, [email protected] */ 31 @Disabled // ignored by default so tests don't require network access. comment out to enable. 32 // todo: rebuild these into a local Jetty test server, so not reliant on the vagaries of the internet. 33 public class UrlConnectTest { 34 private static final String WEBSITE_WITH_INVALID_CERTIFICATE = "https://certs.cac.washington.edu/CAtest/"; 35 private static final String WEBSITE_WITH_SNI = "https://jsoup.org/"; 36 public static String browserUa = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"; 37 38 @Test fetchBaidu()39 public void fetchBaidu() throws IOException { 40 Connection.Response res = Jsoup.connect("http://www.baidu.com/").timeout(10*1000).execute(); 41 Document doc = res.parse(); 42 43 assertEquals("GBK", doc.outputSettings().charset().displayName()); 44 assertEquals("GBK", res.charset()); 45 assert(res.hasCookie("BAIDUID")); 46 assertEquals("text/html;charset=gbk", res.contentType()); 47 } 48 49 @Test exceptOnUnknownContentType()50 public void exceptOnUnknownContentType() { 51 String url = "http://direct.jsoup.org/rez/osi_logo.png"; // not text/* but image/png, should throw 52 boolean threw = false; 53 try { 54 Document doc = Jsoup.parse(new URL(url), 3000); 55 } catch (UnsupportedMimeTypeException e) { 56 threw = true; 57 assertEquals("org.jsoup.UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/xhtml+xml. Mimetype=image/png, URL=http://direct.jsoup.org/rez/osi_logo.png", e.toString()); 58 assertEquals(url, e.getUrl()); 59 assertEquals("image/png", e.getMimeType()); 60 } catch (IOException e) { 61 } 62 assertTrue(threw); 63 } 64 65 @Test ignoresContentTypeIfSoConfigured()66 public void ignoresContentTypeIfSoConfigured() throws IOException { 67 Document doc = Jsoup.connect("https://jsoup.org/rez/osi_logo.png").ignoreContentType(true).get(); 68 assertEquals("", doc.title()); // this will cause an ugly parse tree 69 } 70 ihVal(String key, Document doc)71 private static String ihVal(String key, Document doc) { 72 return doc.select("th:contains("+key+") + td").first().text(); 73 } 74 75 @Test followsTempRedirect()76 public void followsTempRedirect() throws IOException { 77 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302.pl"); // http://jsoup.org 78 Document doc = con.get(); 79 assertTrue(doc.title().contains("jsoup")); 80 } 81 82 @Test followsNewTempRedirect()83 public void followsNewTempRedirect() throws IOException { 84 Connection con = Jsoup.connect("http://direct.infohound.net/tools/307.pl"); // http://jsoup.org 85 Document doc = con.get(); 86 assertTrue(doc.title().contains("jsoup")); 87 assertEquals("https://jsoup.org/", con.response().url().toString()); 88 } 89 90 @Test postRedirectsFetchWithGet()91 public void postRedirectsFetchWithGet() throws IOException { 92 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302.pl") 93 .data("Argument", "Riposte") 94 .method(Connection.Method.POST); 95 Connection.Response res = con.execute(); 96 assertEquals("https://jsoup.org/", res.url().toExternalForm()); 97 assertEquals(Connection.Method.GET, res.method()); 98 } 99 100 @Test followsRedirectToHttps()101 public void followsRedirectToHttps() throws IOException { 102 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302-secure.pl"); // https://www.google.com 103 con.data("id", "5"); 104 Document doc = con.get(); 105 assertTrue(doc.title().contains("Google")); 106 } 107 108 @Test followsRelativeRedirect()109 public void followsRelativeRedirect() throws IOException { 110 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302-rel.pl"); // to /tidy/ 111 Document doc = con.post(); 112 assertTrue(doc.title().contains("HTML Tidy Online")); 113 } 114 115 @Test followsRelativeDotRedirect()116 public void followsRelativeDotRedirect() throws IOException { 117 // redirects to "./ok.html", should resolve to http://direct.infohound.net/tools/ok.html 118 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302-rel-dot.pl"); // to ./ok.html 119 Document doc = con.post(); 120 assertTrue(doc.title().contains("OK")); 121 assertEquals(doc.location(), "http://direct.infohound.net/tools/ok.html"); 122 } 123 124 @Test followsRelativeDotRedirect2()125 public void followsRelativeDotRedirect2() throws IOException { 126 //redirects to "esportspenedes.cat/./ep/index.php", should resolve to "esportspenedes.cat/ep/index.php" 127 Connection con = Jsoup.connect("http://esportspenedes.cat") // note lack of trailing / - server should redir to / first, then to ./ep/...; but doesn't' 128 .timeout(10000); 129 Document doc = con.post(); 130 assertEquals(doc.location(), "http://esportspenedes.cat/ep/index.php"); 131 } 132 133 @Test followsRedirectsWithWithespaces()134 public void followsRedirectsWithWithespaces() throws IOException { 135 Connection con = Jsoup.connect("http://tinyurl.com/kgofxl8"); // to http://www.google.com/?q=white spaces 136 Document doc = con.get(); 137 assertTrue(doc.title().contains("Google")); 138 } 139 140 @Test gracefullyHandleBrokenLocationRedirect()141 public void gracefullyHandleBrokenLocationRedirect() throws IOException { 142 Connection con = Jsoup.connect("http://aag-ye.com"); // has Location: http:/temp/AAG_New/en/index.php 143 con.get(); // would throw exception on error 144 assertTrue(true); 145 } 146 147 @Test ignores500tExceptionIfSoConfigured()148 public void ignores500tExceptionIfSoConfigured() throws IOException { 149 Connection con = Jsoup.connect("http://direct.infohound.net/tools/500.pl").ignoreHttpErrors(true); 150 Connection.Response res = con.execute(); 151 Document doc = res.parse(); 152 assertEquals(500, res.statusCode()); 153 assertEquals("Application Error", res.statusMessage()); 154 assertEquals("Woops", doc.select("h1").first().text()); 155 } 156 157 @Test ignores500WithNoContentExceptionIfSoConfigured()158 public void ignores500WithNoContentExceptionIfSoConfigured() throws IOException { 159 Connection con = Jsoup.connect("http://direct.infohound.net/tools/500-no-content.pl").ignoreHttpErrors(true); 160 Connection.Response res = con.execute(); 161 Document doc = res.parse(); 162 assertEquals(500, res.statusCode()); 163 assertEquals("Application Error", res.statusMessage()); 164 } 165 166 @Test ignores200WithNoContentExceptionIfSoConfigured()167 public void ignores200WithNoContentExceptionIfSoConfigured() throws IOException { 168 Connection con = Jsoup.connect("http://direct.infohound.net/tools/200-no-content.pl").ignoreHttpErrors(true); 169 Connection.Response res = con.execute(); 170 Document doc = res.parse(); 171 assertEquals(200, res.statusCode()); 172 assertEquals("All Good", res.statusMessage()); 173 } 174 175 @Test handles200WithNoContent()176 public void handles200WithNoContent() throws IOException { 177 Connection con = Jsoup 178 .connect("http://direct.infohound.net/tools/200-no-content.pl") 179 .userAgent(browserUa); 180 Connection.Response res = con.execute(); 181 Document doc = res.parse(); 182 assertEquals(200, res.statusCode()); 183 184 con = Jsoup 185 .connect("http://direct.infohound.net/tools/200-no-content.pl") 186 .parser(Parser.xmlParser()) 187 .userAgent(browserUa); 188 res = con.execute(); 189 doc = res.parse(); 190 assertEquals(200, res.statusCode()); 191 } 192 193 @Test doesntRedirectIfSoConfigured()194 public void doesntRedirectIfSoConfigured() throws IOException { 195 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302.pl").followRedirects(false); 196 Connection.Response res = con.execute(); 197 assertEquals(302, res.statusCode()); 198 assertEquals("http://jsoup.org", res.header("Location")); 199 } 200 201 @Test redirectsResponseCookieToNextResponse()202 public void redirectsResponseCookieToNextResponse() throws IOException { 203 Connection con = Jsoup.connect("http://direct.infohound.net/tools/302-cookie.pl"); 204 Connection.Response res = con.execute(); 205 assertEquals("asdfg123", res.cookie("token")); // confirms that cookies set on 1st hit are presented in final result 206 Document doc = res.parse(); 207 assertEquals("token=asdfg123; uid=jhy", ihVal("HTTP_COOKIE", doc)); // confirms that redirected hit saw cookie 208 } 209 210 @Test maximumRedirects()211 public void maximumRedirects() { 212 boolean threw = false; 213 try { 214 Document doc = Jsoup.connect("http://direct.infohound.net/tools/loop.pl").get(); 215 } catch (IOException e) { 216 assertTrue(e.getMessage().contains("Too many redirects")); 217 threw = true; 218 } 219 assertTrue(threw); 220 } 221 222 @Test handlesDodgyCharset()223 public void handlesDodgyCharset() throws IOException { 224 // tests that when we get back "UFT8", that it is recognised as unsupported, and falls back to default instead 225 String url = "http://direct.infohound.net/tools/bad-charset.pl"; 226 Connection.Response res = Jsoup.connect(url).execute(); 227 assertEquals("text/html; charset=UFT8", res.header("Content-Type")); // from the header 228 assertNull(res.charset()); // tried to get from header, not supported, so returns null 229 Document doc = res.parse(); // would throw an error if charset unsupported 230 assertTrue(doc.text().contains("Hello!")); 231 assertEquals("UTF-8", res.charset()); // set from default on parse 232 } 233 234 /** 235 * Verify that security disabling feature works properly. 236 * <p/> 237 * 1. try to hit url with invalid certificate and evaluate that exception is thrown 238 * 239 * @throws Exception 240 */ 241 @Test testUnsafeFail()242 public void testUnsafeFail() throws Exception { 243 String url = WEBSITE_WITH_INVALID_CERTIFICATE; 244 assertThrows(IOException.class, () -> Jsoup.connect(url).execute()); 245 } 246 247 248 /** 249 * Verify that requests to websites with SNI fail on jdk 1.6 250 * <p/> 251 * read for more details: 252 * http://en.wikipedia.org/wiki/Server_Name_Indication 253 * 254 * Test is ignored independent from others as it requires JDK 1.6 255 * @throws Exception 256 */ 257 @Test testSNIFail()258 public void testSNIFail() throws Exception { 259 assertThrows(IOException.class, () -> Jsoup.connect(WEBSITE_WITH_SNI).execute()); 260 } 261 262 @Test shouldWorkForCharsetInExtraAttribute()263 public void shouldWorkForCharsetInExtraAttribute() throws IOException { 264 Connection.Response res = Jsoup.connect("https://www.creditmutuel.com/groupe/fr/").execute(); 265 Document doc = res.parse(); // would throw an error if charset unsupported 266 assertEquals("ISO-8859-1", res.charset()); 267 } 268 269 // The following tests were added to test specific domains if they work. All code paths 270 // which make the following test green are tested in other unit or integration tests, so the following lines 271 // could be deleted 272 273 @Test shouldSelectFirstCharsetOnWeirdMultileCharsetsInMetaTags()274 public void shouldSelectFirstCharsetOnWeirdMultileCharsetsInMetaTags() throws IOException { 275 Connection.Response res = Jsoup.connect("http://aamo.info/").execute(); 276 res.parse(); // would throw an error if charset unsupported 277 assertEquals("ISO-8859-1", res.charset()); 278 } 279 280 @Test shouldParseBrokenHtml5MetaCharsetTagCorrectly()281 public void shouldParseBrokenHtml5MetaCharsetTagCorrectly() throws IOException { 282 Connection.Response res = Jsoup.connect("http://9kuhkep.net").execute(); 283 res.parse(); // would throw an error if charset unsupported 284 assertEquals("UTF-8", res.charset()); 285 } 286 287 @Test shouldEmptyMetaCharsetCorrectly()288 public void shouldEmptyMetaCharsetCorrectly() throws IOException { 289 Connection.Response res = Jsoup.connect("http://aastmultimedia.com").execute(); 290 res.parse(); // would throw an error if charset unsupported 291 assertEquals("UTF-8", res.charset()); 292 } 293 294 @Test shouldWorkForDuplicateCharsetInTag()295 public void shouldWorkForDuplicateCharsetInTag() throws IOException { 296 Connection.Response res = Jsoup.connect("http://aaptsdassn.org").execute(); 297 Document doc = res.parse(); // would throw an error if charset unsupported 298 assertEquals("ISO-8859-1", res.charset()); 299 } 300 301 @Test handles201Created()302 public void handles201Created() throws IOException { 303 Document doc = Jsoup.connect("http://direct.infohound.net/tools/201.pl").get(); // 201, location=jsoup 304 assertEquals("https://jsoup.org/", doc.location()); 305 } 306 307 /* 308 Proxy tests. Assumes local proxy running on 8888, without system propery set (so that specifying it is required). 309 */ 310 311 @Test fetchViaHttpProxy()312 public void fetchViaHttpProxy() throws IOException { 313 String url = "https://jsoup.org"; 314 Proxy proxy = new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved("localhost", 8888)); 315 Document doc = Jsoup.connect(url).proxy(proxy).get(); 316 assertTrue(doc.title().contains("jsoup")); 317 } 318 319 @Test fetchViaHttpProxySetByArgument()320 public void fetchViaHttpProxySetByArgument() throws IOException { 321 String url = "https://jsoup.org"; 322 Document doc = Jsoup.connect(url).proxy("localhost", 8888).get(); 323 assertTrue(doc.title().contains("jsoup")); 324 } 325 326 @Test invalidProxyFails()327 public void invalidProxyFails() { 328 boolean caught = false; 329 String url = "https://jsoup.org"; 330 try { 331 Document doc = Jsoup.connect(url).proxy("localhost", 8889).get(); 332 } catch (IOException e) { 333 caught = e instanceof ConnectException; 334 } 335 assertTrue(caught); 336 } 337 338 @Test proxyGetAndSet()339 public void proxyGetAndSet() throws IOException { 340 String url = "https://jsoup.org"; 341 Proxy proxy = new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved("localhost", 8889)); // invalid 342 final Connection con = Jsoup.connect(url).proxy(proxy); 343 344 assert con.request().proxy() == proxy; 345 con.request().proxy(null); // disable 346 Document doc = con.get(); 347 assertTrue(doc.title().contains("jsoup")); // would fail if actually went via proxy 348 } 349 350 @Test throwsIfRequestBodyForGet()351 public void throwsIfRequestBodyForGet() throws IOException { 352 boolean caught = false; 353 String url = "https://jsoup.org"; 354 try { 355 Document doc = Jsoup.connect(url).requestBody("fail").get(); 356 } catch (IllegalArgumentException e) { 357 caught = true; 358 } 359 assertTrue(caught); 360 } 361 362 @Test canSpecifyResponseCharset()363 public void canSpecifyResponseCharset() throws IOException { 364 // both these docs have <80> in there as euro/control char depending on charset 365 String noCharsetUrl = "http://direct.infohound.net/tools/Windows-1252-nocharset.html"; 366 String charsetUrl = "http://direct.infohound.net/tools/Windows-1252-charset.html"; 367 368 // included in meta 369 Connection.Response res1 = Jsoup.connect(charsetUrl).execute(); 370 assertNull(res1.charset()); // not set in headers 371 final Document doc1 = res1.parse(); 372 assertEquals("windows-1252", doc1.charset().displayName()); // but determined at parse time 373 assertEquals("Cost is €100", doc1.select("p").text()); 374 assertTrue(doc1.text().contains("€")); 375 376 // no meta, no override 377 Connection.Response res2 = Jsoup.connect(noCharsetUrl).execute(); 378 assertNull(res2.charset()); // not set in headers 379 final Document doc2 = res2.parse(); 380 assertEquals("UTF-8", doc2.charset().displayName()); // so defaults to utf-8 381 assertEquals("Cost is �100", doc2.select("p").text()); 382 assertTrue(doc2.text().contains("�")); 383 384 // no meta, let's override 385 Connection.Response res3 = Jsoup.connect(noCharsetUrl).execute(); 386 assertNull(res3.charset()); // not set in headers 387 res3.charset("windows-1252"); 388 assertEquals("windows-1252", res3.charset()); // read back 389 final Document doc3 = res3.parse(); 390 assertEquals("windows-1252", doc3.charset().displayName()); // from override 391 assertEquals("Cost is €100", doc3.select("p").text()); 392 assertTrue(doc3.text().contains("€")); 393 } 394 395 @Test handlesUnescapedRedirects()396 public void handlesUnescapedRedirects() throws IOException { 397 // URL locations should be url safe (ascii) but are often not, so we should try to guess 398 // in this case the location header is utf-8, but defined in spec as iso8859, so detect, convert, encode 399 String url = "https://direct.infohound.net/tools/302-utf.pl"; 400 String urlEscaped = "https://direct.infohound.net/tools/test%F0%9F%92%A9.html"; 401 402 Connection.Response res = Jsoup.connect(url).execute(); 403 Document doc = res.parse(); 404 assertEquals(doc.body().text(), "\uD83D\uDCA9!"); 405 assertEquals(doc.location(), urlEscaped); 406 407 Connection.Response res2 = Jsoup.connect(url).followRedirects(false).execute(); 408 assertEquals("/tools/test\uD83D\uDCA9.html", res2.header("Location")); 409 // if we didn't notice it was utf8, would look like: Location: /tools/testð©.html 410 } 411 handlesEscapesInRedirecct()412 @Test public void handlesEscapesInRedirecct() throws IOException { 413 Document doc = Jsoup.connect("https://direct.infohound.net/tools/302-escaped.pl").get(); 414 assertEquals("https://direct.infohound.net/tools/q.pl?q=one%20two", doc.location()); 415 416 doc = Jsoup.connect("https://direct.infohound.net/tools/302-white.pl").get(); 417 assertEquals("https://direct.infohound.net/tools/q.pl?q=one+two", doc.location()); 418 } 419 420 @Test handlesUt8fInUrl()421 public void handlesUt8fInUrl() throws IOException { 422 String url = "https://direct.infohound.net/tools/test\uD83D\uDCA9.html"; 423 String urlEscaped = "https://direct.infohound.net/tools/test%F0%9F%92%A9.html"; 424 425 Connection.Response res = Jsoup.connect(url).execute(); 426 Document doc = res.parse(); 427 assertEquals("\uD83D\uDCA9!", doc.body().text()); 428 assertEquals(urlEscaped, doc.location()); 429 } 430 431 @Test inWildUtfRedirect()432 public void inWildUtfRedirect() throws IOException { 433 Connection.Response res = Jsoup.connect("http://brabantn.ws/Q4F").execute(); 434 Document doc = res.parse(); 435 assertEquals( 436 "http://www.omroepbrabant.nl/?news/2474781303/Gestrande+ree+in+Oss+niet+verdoofd,+maar+doodgeschoten+%E2%80%98Dit+kan+gewoon+niet,+bizar%E2%80%99+[VIDEO].aspx", 437 doc.location() 438 ); 439 } 440 441 @Test inWildUtfRedirect2()442 public void inWildUtfRedirect2() throws IOException { 443 Connection.Response res = Jsoup.connect("https://ssl.souq.com/sa-en/2724288604627/s").execute(); 444 Document doc = res.parse(); 445 assertEquals( 446 "https://saudi.souq.com/sa-en/%D8%AE%D8%B2%D9%86%D8%A9-%D8%A2%D9%85%D9%86%D8%A9-3-%D8%B7%D8%A8%D9%82%D8%A7%D8%AA-%D8%A8%D9%86%D8%B8%D8%A7%D9%85-%D9%82%D9%81%D9%84-%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-bsd11523-6831477/i/?ctype=dsrch", 447 doc.location() 448 ); 449 } 450 handlesEscapedRedirectUrls()451 @Test public void handlesEscapedRedirectUrls() throws IOException { 452 String url = "http://www.altalex.com/documents/news/2016/12/06/questioni-civilistiche-conseguenti-alla-depenalizzazione"; 453 // sends: Location:http://shop.wki.it/shared/sso/sso.aspx?sso=&url=http%3a%2f%2fwww.altalex.com%2fsession%2fset%2f%3freturnurl%3dhttp%253a%252f%252fwww.altalex.com%253a80%252fdocuments%252fnews%252f2016%252f12%252f06%252fquestioni-civilistiche-conseguenti-alla-depenalizzazione 454 // then to: http://www.altalex.com/session/set/?returnurl=http%3a%2f%2fwww.altalex.com%3a80%2fdocuments%2fnews%2f2016%2f12%2f06%2fquestioni-civilistiche-conseguenti-alla-depenalizzazione&sso=RDRG6T684G4AK2E7U591UGR923 455 // then : http://www.altalex.com:80/documents/news/2016/12/06/questioni-civilistiche-conseguenti-alla-depenalizzazione 456 457 // bug is that jsoup goes to 458 // GET /shared/sso/sso.aspx?sso=&url=http%253a%252f%252fwww.altalex.com%252fsession%252fset%252f%253freturnurl%253dhttp%25253a%25252f%25252fwww.altalex.com%25253a80%25252fdocuments%25252fnews%25252f2016%25252f12%25252f06%25252fquestioni-civilistiche-conseguenti-alla-depenalizzazione HTTP/1.1 459 // i.e. double escaped 460 461 Connection.Response res = Jsoup.connect(url) 462 .proxy("localhost", 8888) 463 .execute(); 464 Document doc = res.parse(); 465 assertEquals(200, res.statusCode()); 466 } 467 handlesUnicodeInQuery()468 @Test public void handlesUnicodeInQuery() throws IOException { 469 Document doc = Jsoup.connect("https://www.google.pl/search?q=gąska").get(); 470 assertEquals("gąska - Szukaj w Google", doc.title()); 471 472 doc = Jsoup.connect("http://mov-world.net/archiv/TV/A/%23No.Title/").get(); 473 assertEquals("Index of /archiv/TV/A/%23No.Title", doc.title()); 474 } 475 handlesSuperDeepPage()476 @Test public void handlesSuperDeepPage() throws IOException { 477 // https://github.com/jhy/jsoup/issues/955 478 479 long start = System.currentTimeMillis(); 480 String url = "http://sv.stargate.wikia.com/wiki/M2J"; 481 Document doc = Jsoup.connect(url).get(); 482 assertEquals("M2J | Sv.stargate Wiki | FANDOM powered by Wikia", doc.title()); 483 assertEquals(110160, doc.select("dd").size()); 484 // those are all <dl><dd> stacked in each other. wonder how that got generated? 485 assertTrue(System.currentTimeMillis() - start < 1000); 486 } 487 488 @Test public void handles966() throws IOException { 489 // http://szshb.nxszs.gov.cn/ 490 // https://github.com/jhy/jsoup/issues/966 491 492 Document doc = Jsoup.connect("http://szshb.nxszs.gov.cn/").get(); 493 494 assertEquals("石嘴山市环境保护局", doc.title()); 495 } 496 497 @Test public void canRequestIdn() throws IOException { 498 String url = "https://räksmörgås.josefsson.org/"; 499 Document doc = Jsoup.connect(url).get(); 500 501 assertEquals("https://xn--rksmrgs-5wao1o.josefsson.org/", doc.location()); 502 assertTrue(doc.title().contains("Räksmörgås.josefßon.org")); 503 } 504 505 } 506