URLConnectionTest.java revision 6d41a7cc3cb4cc684c8bece69ddc55954812ad6e
1/* 2 * Copyright (C) 2009 The Android Open Source Project 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 */ 16 17package libcore.java.net; 18 19import com.google.mockwebserver.MockResponse; 20import com.google.mockwebserver.MockWebServer; 21import com.google.mockwebserver.RecordedRequest; 22import com.google.mockwebserver.SocketPolicy; 23import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END; 24import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_START; 25import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END; 26import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; 27import dalvik.system.CloseGuard; 28import java.io.ByteArrayOutputStream; 29import java.io.Closeable; 30import java.io.File; 31import java.io.IOException; 32import java.io.InputStream; 33import java.io.OutputStream; 34import java.net.Authenticator; 35import java.net.CacheRequest; 36import java.net.CacheResponse; 37import java.net.ConnectException; 38import java.net.HttpRetryException; 39import java.net.HttpURLConnection; 40import java.net.InetAddress; 41import java.net.PasswordAuthentication; 42import java.net.ProtocolException; 43import java.net.Proxy; 44import java.net.ResponseCache; 45import java.net.SocketTimeoutException; 46import java.net.URI; 47import java.net.URL; 48import java.net.URLConnection; 49import java.net.UnknownHostException; 50import java.security.cert.CertificateException; 51import java.security.cert.X509Certificate; 52import java.text.DateFormat; 53import java.text.SimpleDateFormat; 54import java.util.ArrayList; 55import java.util.Arrays; 56import java.util.Collections; 57import java.util.Date; 58import java.util.HashSet; 59import java.util.Iterator; 60import java.util.List; 61import java.util.Locale; 62import java.util.Map; 63import java.util.Set; 64import java.util.TimeZone; 65import java.util.UUID; 66import java.util.concurrent.TimeUnit; 67import java.util.concurrent.atomic.AtomicBoolean; 68import java.util.concurrent.atomic.AtomicReference; 69import java.util.zip.GZIPInputStream; 70import java.util.zip.GZIPOutputStream; 71import javax.net.ssl.HostnameVerifier; 72import javax.net.ssl.HttpsURLConnection; 73import javax.net.ssl.SSLContext; 74import javax.net.ssl.SSLException; 75import javax.net.ssl.SSLHandshakeException; 76import javax.net.ssl.SSLSession; 77import javax.net.ssl.SSLSocketFactory; 78import javax.net.ssl.TrustManager; 79import javax.net.ssl.X509TrustManager; 80import junit.framework.TestCase; 81import libcore.java.lang.ref.FinalizationTester; 82import libcore.java.security.TestKeyStore; 83import libcore.javax.net.ssl.TestSSLContext; 84import libcore.net.http.HttpResponseCache; 85import tests.net.StuckServer; 86 87public final class URLConnectionTest extends TestCase { 88 private MockWebServer server = new MockWebServer(); 89 private HttpResponseCache cache; 90 private String hostName; 91 92 @Override protected void setUp() throws Exception { 93 super.setUp(); 94 hostName = server.getHostName(); 95 } 96 97 @Override protected void tearDown() throws Exception { 98 ResponseCache.setDefault(null); 99 Authenticator.setDefault(null); 100 System.clearProperty("proxyHost"); 101 System.clearProperty("proxyPort"); 102 System.clearProperty("http.proxyHost"); 103 System.clearProperty("http.proxyPort"); 104 System.clearProperty("https.proxyHost"); 105 System.clearProperty("https.proxyPort"); 106 server.shutdown(); 107 if (cache != null) { 108 cache.getCache().delete(); 109 } 110 super.tearDown(); 111 } 112 113 public void testRequestHeaders() throws IOException, InterruptedException { 114 server.enqueue(new MockResponse()); 115 server.play(); 116 117 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 118 urlConnection.addRequestProperty("D", "e"); 119 urlConnection.addRequestProperty("D", "f"); 120 assertEquals("f", urlConnection.getRequestProperty("D")); 121 assertEquals("f", urlConnection.getRequestProperty("d")); 122 Map<String, List<String>> requestHeaders = urlConnection.getRequestProperties(); 123 assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("D"))); 124 assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("d"))); 125 try { 126 requestHeaders.put("G", Arrays.asList("h")); 127 fail("Modified an unmodifiable view."); 128 } catch (UnsupportedOperationException expected) { 129 } 130 try { 131 requestHeaders.get("D").add("i"); 132 fail("Modified an unmodifiable view."); 133 } catch (UnsupportedOperationException expected) { 134 } 135 try { 136 urlConnection.setRequestProperty(null, "j"); 137 fail(); 138 } catch (NullPointerException expected) { 139 } 140 try { 141 urlConnection.addRequestProperty(null, "k"); 142 fail(); 143 } catch (NullPointerException expected) { 144 } 145 urlConnection.setRequestProperty("NullValue", null); // should fail silently! 146 assertNull(urlConnection.getRequestProperty("NullValue")); 147 urlConnection.addRequestProperty("AnotherNullValue", null); // should fail silently! 148 assertNull(urlConnection.getRequestProperty("AnotherNullValue")); 149 150 urlConnection.getResponseCode(); 151 RecordedRequest request = server.takeRequest(); 152 assertContains(request.getHeaders(), "D: e"); 153 assertContains(request.getHeaders(), "D: f"); 154 assertContainsNoneMatching(request.getHeaders(), "NullValue.*"); 155 assertContainsNoneMatching(request.getHeaders(), "AnotherNullValue.*"); 156 assertContainsNoneMatching(request.getHeaders(), "G:.*"); 157 assertContainsNoneMatching(request.getHeaders(), "null:.*"); 158 159 try { 160 urlConnection.addRequestProperty("N", "o"); 161 fail("Set header after connect"); 162 } catch (IllegalStateException expected) { 163 } 164 try { 165 urlConnection.setRequestProperty("P", "q"); 166 fail("Set header after connect"); 167 } catch (IllegalStateException expected) { 168 } 169 try { 170 urlConnection.getRequestProperties(); 171 fail(); 172 } catch (IllegalStateException expected) { 173 } 174 } 175 176 public void testGetRequestPropertyReturnsLastValue() throws Exception { 177 server.play(); 178 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 179 urlConnection.addRequestProperty("A", "value1"); 180 urlConnection.addRequestProperty("A", "value2"); 181 assertEquals("value2", urlConnection.getRequestProperty("A")); 182 } 183 184 public void testResponseHeaders() throws IOException, InterruptedException { 185 server.enqueue(new MockResponse() 186 .setStatus("HTTP/1.0 200 Fantastic") 187 .addHeader("A: c") 188 .addHeader("B: d") 189 .addHeader("A: e") 190 .setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8)); 191 server.play(); 192 193 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 194 assertEquals(200, urlConnection.getResponseCode()); 195 assertEquals("Fantastic", urlConnection.getResponseMessage()); 196 assertEquals("HTTP/1.0 200 Fantastic", urlConnection.getHeaderField(null)); 197 Map<String, List<String>> responseHeaders = urlConnection.getHeaderFields(); 198 assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null)); 199 assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("A"))); 200 assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("a"))); 201 try { 202 responseHeaders.put("N", Arrays.asList("o")); 203 fail("Modified an unmodifiable view."); 204 } catch (UnsupportedOperationException expected) { 205 } 206 try { 207 responseHeaders.get("A").add("f"); 208 fail("Modified an unmodifiable view."); 209 } catch (UnsupportedOperationException expected) { 210 } 211 assertEquals("A", urlConnection.getHeaderFieldKey(0)); 212 assertEquals("c", urlConnection.getHeaderField(0)); 213 assertEquals("B", urlConnection.getHeaderFieldKey(1)); 214 assertEquals("d", urlConnection.getHeaderField(1)); 215 assertEquals("A", urlConnection.getHeaderFieldKey(2)); 216 assertEquals("e", urlConnection.getHeaderField(2)); 217 } 218 219 public void testGetErrorStreamOnSuccessfulRequest() throws Exception { 220 server.enqueue(new MockResponse().setBody("A")); 221 server.play(); 222 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 223 assertNull(connection.getErrorStream()); 224 } 225 226 public void testGetErrorStreamOnUnsuccessfulRequest() throws Exception { 227 server.enqueue(new MockResponse().setResponseCode(404).setBody("A")); 228 server.play(); 229 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 230 assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE)); 231 } 232 233 // Check that if we don't read to the end of a response, the next request on the 234 // recycled connection doesn't get the unread tail of the first request's response. 235 // http://code.google.com/p/android/issues/detail?id=2939 236 public void test_2939() throws Exception { 237 MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8); 238 239 server.enqueue(response); 240 server.enqueue(response); 241 server.play(); 242 243 assertContent("ABCDE", server.getUrl("/").openConnection(), 5); 244 assertContent("ABCDE", server.getUrl("/").openConnection(), 5); 245 } 246 247 // Check that we recognize a few basic mime types by extension. 248 // http://code.google.com/p/android/issues/detail?id=10100 249 public void test_10100() throws Exception { 250 assertEquals("image/jpeg", URLConnection.guessContentTypeFromName("someFile.jpg")); 251 assertEquals("application/pdf", URLConnection.guessContentTypeFromName("stuff.pdf")); 252 } 253 254 public void testConnectionsArePooled() throws Exception { 255 MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR"); 256 257 server.enqueue(response); 258 server.enqueue(response); 259 server.enqueue(response); 260 server.play(); 261 262 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/foo").openConnection()); 263 assertEquals(0, server.takeRequest().getSequenceNumber()); 264 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/bar?baz=quux").openConnection()); 265 assertEquals(1, server.takeRequest().getSequenceNumber()); 266 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/z").openConnection()); 267 assertEquals(2, server.takeRequest().getSequenceNumber()); 268 } 269 270 public void testChunkedConnectionsArePooled() throws Exception { 271 MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5); 272 273 server.enqueue(response); 274 server.enqueue(response); 275 server.enqueue(response); 276 server.play(); 277 278 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/foo").openConnection()); 279 assertEquals(0, server.takeRequest().getSequenceNumber()); 280 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/bar?baz=quux").openConnection()); 281 assertEquals(1, server.takeRequest().getSequenceNumber()); 282 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/z").openConnection()); 283 assertEquals(2, server.takeRequest().getSequenceNumber()); 284 } 285 286 /** 287 * Test that connections are added to the pool as soon as the response has 288 * been consumed. 289 */ 290 public void testConnectionsArePooledWithoutExplicitDisconnect() throws Exception { 291 server.enqueue(new MockResponse().setBody("ABC")); 292 server.enqueue(new MockResponse().setBody("DEF")); 293 server.play(); 294 295 URLConnection connection1 = server.getUrl("/").openConnection(); 296 assertEquals("ABC", readAscii(connection1.getInputStream(), Integer.MAX_VALUE)); 297 assertEquals(0, server.takeRequest().getSequenceNumber()); 298 URLConnection connection2 = server.getUrl("/").openConnection(); 299 assertEquals("DEF", readAscii(connection2.getInputStream(), Integer.MAX_VALUE)); 300 assertEquals(1, server.takeRequest().getSequenceNumber()); 301 } 302 303 public void testServerClosesSocket() throws Exception { 304 testServerClosesSocket(DISCONNECT_AT_END); 305 } 306 307 public void testServerShutdownInput() throws Exception { 308 testServerClosesSocket(SHUTDOWN_INPUT_AT_END); 309 } 310 311 private void testServerClosesSocket(SocketPolicy socketPolicy) throws Exception { 312 server.enqueue(new MockResponse() 313 .setBody("This connection won't pool properly") 314 .setSocketPolicy(socketPolicy)); 315 server.enqueue(new MockResponse().setBody("This comes after a busted connection")); 316 server.play(); 317 318 assertContent("This connection won't pool properly", server.getUrl("/a").openConnection()); 319 assertEquals(0, server.takeRequest().getSequenceNumber()); 320 assertContent("This comes after a busted connection", server.getUrl("/b").openConnection()); 321 // sequence number 0 means the HTTP socket connection was not reused 322 assertEquals(0, server.takeRequest().getSequenceNumber()); 323 } 324 325 public void testServerShutdownOutput() throws Exception { 326 // This test causes MockWebServer to log a "connection failed" stack trace 327 server.enqueue(new MockResponse() 328 .setBody("Output shutdown after this response") 329 .setSocketPolicy(SHUTDOWN_OUTPUT_AT_END)); 330 server.enqueue(new MockResponse().setBody("This response will fail to write")); 331 server.enqueue(new MockResponse().setBody("This comes after a busted connection")); 332 server.play(); 333 334 assertContent("Output shutdown after this response", server.getUrl("/a").openConnection()); 335 assertEquals(0, server.takeRequest().getSequenceNumber()); 336 assertContent("This comes after a busted connection", server.getUrl("/b").openConnection()); 337 assertEquals(1, server.takeRequest().getSequenceNumber()); 338 assertEquals(0, server.takeRequest().getSequenceNumber()); 339 } 340 341 public void testRetryableRequestBodyAfterBrokenConnection() throws Exception { 342 server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END)); 343 server.enqueue(new MockResponse().setBody("def")); 344 server.play(); 345 346 assertContent("abc", server.getUrl("/a").openConnection()); 347 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection(); 348 connection.setDoOutput(true); 349 OutputStream out = connection.getOutputStream(); 350 out.write(new byte[] {1, 2, 3}); 351 out.close(); 352 assertContent("def", connection); 353 } 354 355 public void testNonRetryableRequestBodyAfterBrokenConnection() throws Exception { 356 server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END)); 357 server.enqueue(new MockResponse().setBody("def")); 358 server.play(); 359 360 assertContent("abc", server.getUrl("/a").openConnection()); 361 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection(); 362 connection.setDoOutput(true); 363 connection.setFixedLengthStreamingMode(3); 364 OutputStream out = connection.getOutputStream(); 365 out.write(new byte[] {1, 2, 3}); 366 out.close(); 367 try { 368 connection.getInputStream(); 369 fail(); 370 } catch (IOException expected) { 371 } 372 } 373 374 enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS } 375 376 public void test_chunkedUpload_byteByByte() throws Exception { 377 doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE); 378 } 379 380 public void test_chunkedUpload_smallBuffers() throws Exception { 381 doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS); 382 } 383 384 public void test_chunkedUpload_largeBuffers() throws Exception { 385 doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS); 386 } 387 388 public void test_fixedLengthUpload_byteByByte() throws Exception { 389 doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE); 390 } 391 392 public void test_fixedLengthUpload_smallBuffers() throws Exception { 393 doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS); 394 } 395 396 public void test_fixedLengthUpload_largeBuffers() throws Exception { 397 doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS); 398 } 399 400 private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception { 401 int n = 512*1024; 402 server.setBodyLimit(0); 403 server.enqueue(new MockResponse()); 404 server.play(); 405 406 HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection(); 407 conn.setDoOutput(true); 408 conn.setRequestMethod("POST"); 409 if (uploadKind == TransferKind.CHUNKED) { 410 conn.setChunkedStreamingMode(-1); 411 } else { 412 conn.setFixedLengthStreamingMode(n); 413 } 414 OutputStream out = conn.getOutputStream(); 415 if (writeKind == WriteKind.BYTE_BY_BYTE) { 416 for (int i = 0; i < n; ++i) { 417 out.write('x'); 418 } 419 } else { 420 byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64*1024]; 421 Arrays.fill(buf, (byte) 'x'); 422 for (int i = 0; i < n; i += buf.length) { 423 out.write(buf, 0, Math.min(buf.length, n - i)); 424 } 425 } 426 out.close(); 427 assertEquals(200, conn.getResponseCode()); 428 RecordedRequest request = server.takeRequest(); 429 assertEquals(n, request.getBodySize()); 430 if (uploadKind == TransferKind.CHUNKED) { 431 assertTrue(request.getChunkSizes().size() > 0); 432 } else { 433 assertTrue(request.getChunkSizes().isEmpty()); 434 } 435 } 436 437 public void testGetResponseCodeNoResponseBody() throws Exception { 438 server.enqueue(new MockResponse() 439 .addHeader("abc: def")); 440 server.play(); 441 442 URL url = server.getUrl("/"); 443 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 444 conn.setDoInput(false); 445 assertEquals("def", conn.getHeaderField("abc")); 446 assertEquals(200, conn.getResponseCode()); 447 try { 448 conn.getInputStream(); 449 fail(); 450 } catch (ProtocolException expected) { 451 } 452 } 453 454 public void testConnectViaHttps() throws IOException, InterruptedException { 455 TestSSLContext testSSLContext = TestSSLContext.create(); 456 457 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 458 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 459 server.play(); 460 461 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); 462 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 463 464 assertContent("this response comes via HTTPS", connection); 465 466 RecordedRequest request = server.takeRequest(); 467 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 468 assertEquals("TLSv1", request.getSslProtocol()); 469 } 470 471 public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException { 472 TestSSLContext testSSLContext = TestSSLContext.create(); 473 SSLSocketFactory clientSocketFactory = testSSLContext.clientContext.getSocketFactory(); 474 475 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 476 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 477 server.enqueue(new MockResponse().setBody("another response via HTTPS")); 478 server.play(); 479 480 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 481 connection.setSSLSocketFactory(clientSocketFactory); 482 assertContent("this response comes via HTTPS", connection); 483 484 connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 485 connection.setSSLSocketFactory(clientSocketFactory); 486 assertContent("another response via HTTPS", connection); 487 488 assertEquals(0, server.takeRequest().getSequenceNumber()); 489 assertEquals(1, server.takeRequest().getSequenceNumber()); 490 } 491 492 public void testConnectViaHttpsReusingConnectionsDifferentFactories() 493 throws IOException, InterruptedException { 494 TestSSLContext testSSLContext = TestSSLContext.create(); 495 496 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 497 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 498 server.enqueue(new MockResponse().setBody("another response via HTTPS")); 499 server.play(); 500 501 // install a custom SSL socket factory so the server can be authorized 502 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 503 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 504 assertContent("this response comes via HTTPS", connection); 505 506 connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 507 try { 508 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 509 fail("without an SSL socket factory, the connection should fail"); 510 } catch (SSLException expected) { 511 } 512 } 513 514 public void testConnectViaHttpsWithSSLFallback() throws IOException, InterruptedException { 515 TestSSLContext testSSLContext = TestSSLContext.create(); 516 517 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 518 server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); 519 server.enqueue(new MockResponse().setBody("this response comes via SSL")); 520 server.play(); 521 522 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); 523 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 524 525 assertContent("this response comes via SSL", connection); 526 527 RecordedRequest request = server.takeRequest(); 528 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 529 } 530 531 /** 532 * Verify that we don't retry connections on certificate verification errors. 533 * 534 * http://code.google.com/p/android/issues/detail?id=13178 535 */ 536 public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException { 537 TestSSLContext testSSLContext = TestSSLContext.create(TestKeyStore.getClientCA2(), 538 TestKeyStore.getServer()); 539 540 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 541 server.enqueue(new MockResponse()); // unused 542 server.play(); 543 544 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); 545 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 546 try { 547 connection.getInputStream(); 548 fail(); 549 } catch (SSLHandshakeException expected) { 550 assertTrue(expected.getCause() instanceof CertificateException); 551 } 552 assertEquals(0, server.getRequestCount()); 553 } 554 555 public void testConnectViaProxyUsingProxyArg() throws Exception { 556 testConnectViaProxy(ProxyConfig.CREATE_ARG); 557 } 558 559 public void testConnectViaProxyUsingProxySystemProperty() throws Exception { 560 testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY); 561 } 562 563 public void testConnectViaProxyUsingHttpProxySystemProperty() throws Exception { 564 testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 565 } 566 567 private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception { 568 MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy"); 569 server.enqueue(mockResponse); 570 server.play(); 571 572 URL url = new URL("http://android.com/foo"); 573 HttpURLConnection connection = proxyConfig.connect(server, url); 574 assertContent("this response comes via a proxy", connection); 575 576 RecordedRequest request = server.takeRequest(); 577 assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine()); 578 assertContains(request.getHeaders(), "Host: android.com"); 579 } 580 581 public void testContentDisagreesWithContentLengthHeader() throws IOException { 582 server.enqueue(new MockResponse() 583 .setBody("abc\r\nYOU SHOULD NOT SEE THIS") 584 .clearHeaders() 585 .addHeader("Content-Length: 3")); 586 server.play(); 587 588 assertContent("abc", server.getUrl("/").openConnection()); 589 } 590 591 public void testContentDisagreesWithChunkedHeader() throws IOException { 592 MockResponse mockResponse = new MockResponse(); 593 mockResponse.setChunkedBody("abc", 3); 594 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 595 bytesOut.write(mockResponse.getBody()); 596 bytesOut.write("\r\nYOU SHOULD NOT SEE THIS".getBytes()); 597 mockResponse.setBody(bytesOut.toByteArray()); 598 mockResponse.clearHeaders(); 599 mockResponse.addHeader("Transfer-encoding: chunked"); 600 601 server.enqueue(mockResponse); 602 server.play(); 603 604 assertContent("abc", server.getUrl("/").openConnection()); 605 } 606 607 public void testConnectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception { 608 testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY); 609 } 610 611 public void testConnectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception { 612 // https should not use http proxy 613 testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 614 } 615 616 private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception { 617 TestSSLContext testSSLContext = TestSSLContext.create(); 618 619 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 620 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 621 server.play(); 622 623 URL url = server.getUrl("/foo"); 624 HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); 625 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 626 627 assertContent("this response comes via HTTPS", connection); 628 629 RecordedRequest request = server.takeRequest(); 630 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 631 } 632 633 634 public void testConnectViaHttpProxyToHttpsUsingProxyArg() throws Exception { 635 testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG); 636 } 637 638 /** 639 * We weren't honoring all of the appropriate proxy system properties when 640 * connecting via HTTPS. http://b/3097518 641 */ 642 public void testConnectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception { 643 testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY); 644 } 645 646 public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception { 647 testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY); 648 } 649 650 /** 651 * We were verifying the wrong hostname when connecting to an HTTPS site 652 * through a proxy. http://b/3097277 653 */ 654 private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { 655 TestSSLContext testSSLContext = TestSSLContext.create(); 656 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 657 658 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 659 server.enqueue(new MockResponse() 660 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 661 .clearHeaders()); 662 server.enqueue(new MockResponse().setBody("this response comes via a secure proxy")); 663 server.play(); 664 665 URL url = new URL("https://android.com/foo"); 666 HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); 667 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 668 connection.setHostnameVerifier(hostnameVerifier); 669 670 assertContent("this response comes via a secure proxy", connection); 671 672 RecordedRequest connect = server.takeRequest(); 673 assertEquals("Connect line failure on proxy", 674 "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); 675 assertContains(connect.getHeaders(), "Host: android.com"); 676 677 RecordedRequest get = server.takeRequest(); 678 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 679 assertContains(get.getHeaders(), "Host: android.com"); 680 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 681 } 682 683 684 /** 685 * Tolerate bad https proxy response when using HttpResponseCache. http://b/6754912 686 */ 687 public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception { 688 ProxyConfig proxyConfig = ProxyConfig.PROXY_SYSTEM_PROPERTY; 689 690 TestSSLContext testSSLContext = TestSSLContext.create(); 691 692 initResponseCache(); 693 694 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 695 server.enqueue(new MockResponse() 696 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 697 .clearHeaders() 698 .setBody("bogus proxy connect response content")); // Key to reproducing b/6754912 699 server.play(); 700 701 URL url = new URL("https://android.com/foo"); 702 HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); 703 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 704 705 try { 706 connection.connect(); 707 fail(); 708 } catch (IOException expected) { 709 // Thrown when the connect causes SSLSocket.startHandshake() to throw 710 // when it sees the "bogus proxy connect response content" 711 // instead of a ServerHello handshake message. 712 } 713 714 RecordedRequest connect = server.takeRequest(); 715 assertEquals("Connect line failure on proxy", 716 "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); 717 assertContains(connect.getHeaders(), "Host: android.com"); 718 } 719 720 private void initResponseCache() throws IOException { 721 String tmp = System.getProperty("java.io.tmpdir"); 722 File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID()); 723 cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE); 724 ResponseCache.setDefault(cache); 725 } 726 727 /** 728 * Test which headers are sent unencrypted to the HTTP proxy. 729 */ 730 public void testProxyConnectIncludesProxyHeadersOnly() 731 throws IOException, InterruptedException { 732 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 733 TestSSLContext testSSLContext = TestSSLContext.create(); 734 735 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 736 server.enqueue(new MockResponse() 737 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 738 .clearHeaders()); 739 server.enqueue(new MockResponse().setBody("encrypted response from the origin server")); 740 server.play(); 741 742 URL url = new URL("https://android.com/foo"); 743 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( 744 server.toProxyAddress()); 745 connection.addRequestProperty("Private", "Secret"); 746 connection.addRequestProperty("Proxy-Authorization", "bar"); 747 connection.addRequestProperty("User-Agent", "baz"); 748 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 749 connection.setHostnameVerifier(hostnameVerifier); 750 assertContent("encrypted response from the origin server", connection); 751 752 RecordedRequest connect = server.takeRequest(); 753 assertContainsNoneMatching(connect.getHeaders(), "Private.*"); 754 assertContains(connect.getHeaders(), "Proxy-Authorization: bar"); 755 assertContains(connect.getHeaders(), "User-Agent: baz"); 756 assertContains(connect.getHeaders(), "Host: android.com"); 757 assertContains(connect.getHeaders(), "Proxy-Connection: Keep-Alive"); 758 759 RecordedRequest get = server.takeRequest(); 760 assertContains(get.getHeaders(), "Private: Secret"); 761 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 762 } 763 764 public void testProxyAuthenticateOnConnect() throws Exception { 765 Authenticator.setDefault(new SimpleAuthenticator()); 766 TestSSLContext testSSLContext = TestSSLContext.create(); 767 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 768 server.enqueue(new MockResponse() 769 .setResponseCode(407) 770 .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); 771 server.enqueue(new MockResponse() 772 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 773 .clearHeaders()); 774 server.enqueue(new MockResponse().setBody("A")); 775 server.play(); 776 777 URL url = new URL("https://android.com/foo"); 778 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( 779 server.toProxyAddress()); 780 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 781 connection.setHostnameVerifier(new RecordingHostnameVerifier()); 782 assertContent("A", connection); 783 784 RecordedRequest connect1 = server.takeRequest(); 785 assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); 786 assertContainsNoneMatching(connect1.getHeaders(), "Proxy\\-Authorization.*"); 787 788 RecordedRequest connect2 = server.takeRequest(); 789 assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); 790 assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " 791 + SimpleAuthenticator.BASE_64_CREDENTIALS); 792 793 RecordedRequest get = server.takeRequest(); 794 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 795 assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*"); 796 } 797 798 public void testDisconnectedConnection() throws IOException { 799 server.enqueue(new MockResponse().setBody("ABCDEFGHIJKLMNOPQR")); 800 server.play(); 801 802 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 803 InputStream in = connection.getInputStream(); 804 assertEquals('A', (char) in.read()); 805 connection.disconnect(); 806 try { 807 in.read(); 808 fail("Expected a connection closed exception"); 809 } catch (IOException expected) { 810 } 811 } 812 813 public void testDisconnectBeforeConnect() throws IOException { 814 server.enqueue(new MockResponse().setBody("A")); 815 server.play(); 816 817 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 818 connection.disconnect(); 819 820 assertContent("A", connection); 821 assertEquals(200, connection.getResponseCode()); 822 } 823 824 public void testDisconnectAfterOnlyResponseCodeCausesNoCloseGuardWarning() throws IOException { 825 CloseGuardGuard guard = new CloseGuardGuard(); 826 try { 827 server.enqueue(new MockResponse() 828 .setBody(gzip("ABCABCABC".getBytes("UTF-8"))) 829 .addHeader("Content-Encoding: gzip")); 830 server.play(); 831 832 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 833 assertEquals(200, connection.getResponseCode()); 834 connection.disconnect(); 835 connection = null; 836 assertFalse(guard.wasCloseGuardCalled()); 837 } finally { 838 guard.close(); 839 } 840 } 841 842 public static class CloseGuardGuard implements Closeable, CloseGuard.Reporter { 843 private final CloseGuard.Reporter oldReporter = CloseGuard.getReporter(); 844 845 private AtomicBoolean closeGuardCalled = new AtomicBoolean(); 846 847 public CloseGuardGuard() { 848 CloseGuard.setReporter(this); 849 } 850 851 @Override public void report(String message, Throwable allocationSite) { 852 oldReporter.report(message, allocationSite); 853 closeGuardCalled.set(true); 854 } 855 856 public boolean wasCloseGuardCalled() { 857 FinalizationTester.induceFinalization(); 858 close(); 859 return closeGuardCalled.get(); 860 } 861 862 @Override public void close() { 863 CloseGuard.setReporter(oldReporter); 864 } 865 866 } 867 868 public void testDefaultRequestProperty() throws Exception { 869 URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A"); 870 assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty")); 871 } 872 873 /** 874 * Reads {@code count} characters from the stream. If the stream is 875 * exhausted before {@code count} characters can be read, the remaining 876 * characters are returned and the stream is closed. 877 */ 878 private String readAscii(InputStream in, int count) throws IOException { 879 StringBuilder result = new StringBuilder(); 880 for (int i = 0; i < count; i++) { 881 int value = in.read(); 882 if (value == -1) { 883 in.close(); 884 break; 885 } 886 result.append((char) value); 887 } 888 return result.toString(); 889 } 890 891 public void testMarkAndResetWithContentLengthHeader() throws IOException { 892 testMarkAndReset(TransferKind.FIXED_LENGTH); 893 } 894 895 public void testMarkAndResetWithChunkedEncoding() throws IOException { 896 testMarkAndReset(TransferKind.CHUNKED); 897 } 898 899 public void testMarkAndResetWithNoLengthHeaders() throws IOException { 900 testMarkAndReset(TransferKind.END_OF_STREAM); 901 } 902 903 private void testMarkAndReset(TransferKind transferKind) throws IOException { 904 MockResponse response = new MockResponse(); 905 transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024); 906 server.enqueue(response); 907 server.enqueue(response); 908 server.play(); 909 910 InputStream in = server.getUrl("/").openConnection().getInputStream(); 911 assertFalse("This implementation claims to support mark().", in.markSupported()); 912 in.mark(5); 913 assertEquals("ABCDE", readAscii(in, 5)); 914 try { 915 in.reset(); 916 fail(); 917 } catch (IOException expected) { 918 } 919 assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE)); 920 assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", server.getUrl("/").openConnection()); 921 } 922 923 /** 924 * We've had a bug where we forget the HTTP response when we see response 925 * code 401. This causes a new HTTP request to be issued for every call into 926 * the URLConnection. 927 */ 928 public void testUnauthorizedResponseHandling() throws IOException { 929 MockResponse response = new MockResponse() 930 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 931 .setResponseCode(401) // UNAUTHORIZED 932 .setBody("Unauthorized"); 933 server.enqueue(response); 934 server.enqueue(response); 935 server.enqueue(response); 936 server.play(); 937 938 URL url = server.getUrl("/"); 939 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 940 941 assertEquals(401, conn.getResponseCode()); 942 assertEquals(401, conn.getResponseCode()); 943 assertEquals(401, conn.getResponseCode()); 944 assertEquals(1, server.getRequestCount()); 945 } 946 947 public void testNonHexChunkSize() throws IOException { 948 server.enqueue(new MockResponse() 949 .setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n") 950 .clearHeaders() 951 .addHeader("Transfer-encoding: chunked")); 952 server.play(); 953 954 URLConnection connection = server.getUrl("/").openConnection(); 955 try { 956 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 957 fail(); 958 } catch (IOException e) { 959 } 960 } 961 962 public void testMissingChunkBody() throws IOException { 963 server.enqueue(new MockResponse() 964 .setBody("5") 965 .clearHeaders() 966 .addHeader("Transfer-encoding: chunked") 967 .setSocketPolicy(DISCONNECT_AT_END)); 968 server.play(); 969 970 URLConnection connection = server.getUrl("/").openConnection(); 971 try { 972 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 973 fail(); 974 } catch (IOException e) { 975 } 976 } 977 978 /** 979 * This test checks whether connections are gzipped by default. This 980 * behavior in not required by the API, so a failure of this test does not 981 * imply a bug in the implementation. 982 */ 983 public void testGzipEncodingEnabledByDefault() throws IOException, InterruptedException { 984 server.enqueue(new MockResponse() 985 .setBody(gzip("ABCABCABC".getBytes("UTF-8"))) 986 .addHeader("Content-Encoding: gzip")); 987 server.play(); 988 989 URLConnection connection = server.getUrl("/").openConnection(); 990 assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 991 assertNull(connection.getContentEncoding()); 992 assertEquals(-1, connection.getContentLength()); 993 994 RecordedRequest request = server.takeRequest(); 995 assertContains(request.getHeaders(), "Accept-Encoding: gzip"); 996 } 997 998 public void testClientConfiguredGzipContentEncoding() throws Exception { 999 byte[] bodyBytes = gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8")); 1000 server.enqueue(new MockResponse() 1001 .setBody(bodyBytes) 1002 .addHeader("Content-Encoding: gzip") 1003 .addHeader("Content-Length: " + bodyBytes.length)); 1004 server.play(); 1005 1006 URLConnection connection = server.getUrl("/").openConnection(); 1007 connection.addRequestProperty("Accept-Encoding", "gzip"); 1008 InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream()); 1009 assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE)); 1010 assertEquals(bodyBytes.length, connection.getContentLength()); 1011 1012 RecordedRequest request = server.takeRequest(); 1013 assertContains(request.getHeaders(), "Accept-Encoding: gzip"); 1014 } 1015 1016 public void testGzipAndConnectionReuseWithFixedLength() throws Exception { 1017 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH); 1018 } 1019 1020 public void testGzipAndConnectionReuseWithChunkedEncoding() throws Exception { 1021 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED); 1022 } 1023 1024 public void testClientConfiguredCustomContentEncoding() throws Exception { 1025 server.enqueue(new MockResponse() 1026 .setBody("ABCDE") 1027 .addHeader("Content-Encoding: custom")); 1028 server.play(); 1029 1030 URLConnection connection = server.getUrl("/").openConnection(); 1031 connection.addRequestProperty("Accept-Encoding", "custom"); 1032 assertEquals("ABCDE", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1033 1034 RecordedRequest request = server.takeRequest(); 1035 assertContains(request.getHeaders(), "Accept-Encoding: custom"); 1036 } 1037 1038 /** 1039 * Test a bug where gzip input streams weren't exhausting the input stream, 1040 * which corrupted the request that followed. 1041 * http://code.google.com/p/android/issues/detail?id=7059 1042 */ 1043 private void testClientConfiguredGzipContentEncodingAndConnectionReuse( 1044 TransferKind transferKind) throws Exception { 1045 MockResponse responseOne = new MockResponse(); 1046 responseOne.addHeader("Content-Encoding: gzip"); 1047 transferKind.setBody(responseOne, gzip("one (gzipped)".getBytes("UTF-8")), 5); 1048 server.enqueue(responseOne); 1049 MockResponse responseTwo = new MockResponse(); 1050 transferKind.setBody(responseTwo, "two (identity)", 5); 1051 server.enqueue(responseTwo); 1052 server.play(); 1053 1054 URLConnection connection = server.getUrl("/").openConnection(); 1055 connection.addRequestProperty("Accept-Encoding", "gzip"); 1056 InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream()); 1057 assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE)); 1058 assertEquals(0, server.takeRequest().getSequenceNumber()); 1059 1060 connection = server.getUrl("/").openConnection(); 1061 assertEquals("two (identity)", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1062 assertEquals(1, server.takeRequest().getSequenceNumber()); 1063 } 1064 1065 /** 1066 * Test that HEAD requests don't have a body regardless of the response 1067 * headers. http://code.google.com/p/android/issues/detail?id=24672 1068 */ 1069 public void testHeadAndContentLength() throws Exception { 1070 server.enqueue(new MockResponse() 1071 .clearHeaders() 1072 .addHeader("Content-Length: 100")); 1073 server.enqueue(new MockResponse().setBody("A")); 1074 server.play(); 1075 1076 HttpURLConnection connection1 = (HttpURLConnection) server.getUrl("/").openConnection(); 1077 connection1.setRequestMethod("HEAD"); 1078 assertEquals("100", connection1.getHeaderField("Content-Length")); 1079 assertContent("", connection1); 1080 1081 HttpURLConnection connection2 = (HttpURLConnection) server.getUrl("/").openConnection(); 1082 assertEquals("A", readAscii(connection2.getInputStream(), Integer.MAX_VALUE)); 1083 1084 assertEquals(0, server.takeRequest().getSequenceNumber()); 1085 assertEquals(1, server.takeRequest().getSequenceNumber()); 1086 } 1087 1088 /** 1089 * Obnoxiously test that the chunk sizes transmitted exactly equal the 1090 * requested data+chunk header size. Although setChunkedStreamingMode() 1091 * isn't specific about whether the size applies to the data or the 1092 * complete chunk, the RI interprets it as a complete chunk. 1093 */ 1094 public void testSetChunkedStreamingMode() throws IOException, InterruptedException { 1095 server.enqueue(new MockResponse()); 1096 server.play(); 1097 1098 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 1099 urlConnection.setChunkedStreamingMode(8); 1100 urlConnection.setDoOutput(true); 1101 OutputStream outputStream = urlConnection.getOutputStream(); 1102 outputStream.write("ABCDEFGHIJKLMNOPQ".getBytes("US-ASCII")); 1103 assertEquals(200, urlConnection.getResponseCode()); 1104 1105 RecordedRequest request = server.takeRequest(); 1106 assertEquals("ABCDEFGHIJKLMNOPQ", new String(request.getBody(), "US-ASCII")); 1107 assertEquals(Arrays.asList(3, 3, 3, 3, 3, 2), request.getChunkSizes()); 1108 } 1109 1110 public void testAuthenticateWithFixedLengthStreaming() throws Exception { 1111 testAuthenticateWithStreamingPost(StreamingMode.FIXED_LENGTH); 1112 } 1113 1114 public void testAuthenticateWithChunkedStreaming() throws Exception { 1115 testAuthenticateWithStreamingPost(StreamingMode.CHUNKED); 1116 } 1117 1118 private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) throws Exception { 1119 MockResponse pleaseAuthenticate = new MockResponse() 1120 .setResponseCode(401) 1121 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1122 .setBody("Please authenticate."); 1123 server.enqueue(pleaseAuthenticate); 1124 server.play(); 1125 1126 Authenticator.setDefault(new SimpleAuthenticator()); 1127 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1128 connection.setDoOutput(true); 1129 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1130 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1131 connection.setFixedLengthStreamingMode(requestBody.length); 1132 } else if (streamingMode == StreamingMode.CHUNKED) { 1133 connection.setChunkedStreamingMode(0); 1134 } 1135 OutputStream outputStream = connection.getOutputStream(); 1136 outputStream.write(requestBody); 1137 outputStream.close(); 1138 try { 1139 connection.getInputStream(); 1140 fail(); 1141 } catch (HttpRetryException expected) { 1142 } 1143 1144 // no authorization header for the request... 1145 RecordedRequest request = server.takeRequest(); 1146 assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*"); 1147 assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); 1148 } 1149 1150 public void testSetValidRequestMethod() throws Exception { 1151 server.play(); 1152 assertValidRequestMethod("GET"); 1153 assertValidRequestMethod("DELETE"); 1154 assertValidRequestMethod("HEAD"); 1155 assertValidRequestMethod("OPTIONS"); 1156 assertValidRequestMethod("POST"); 1157 assertValidRequestMethod("PUT"); 1158 assertValidRequestMethod("TRACE"); 1159 } 1160 1161 private void assertValidRequestMethod(String requestMethod) throws Exception { 1162 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1163 connection.setRequestMethod(requestMethod); 1164 assertEquals(requestMethod, connection.getRequestMethod()); 1165 } 1166 1167 public void testSetInvalidRequestMethodLowercase() throws Exception { 1168 server.play(); 1169 assertInvalidRequestMethod("get"); 1170 } 1171 1172 public void testSetInvalidRequestMethodConnect() throws Exception { 1173 server.play(); 1174 assertInvalidRequestMethod("CONNECT"); 1175 } 1176 1177 private void assertInvalidRequestMethod(String requestMethod) throws Exception { 1178 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1179 try { 1180 connection.setRequestMethod(requestMethod); 1181 fail(); 1182 } catch (ProtocolException expected) { 1183 } 1184 } 1185 1186 public void testCannotSetNegativeFixedLengthStreamingMode() throws Exception { 1187 server.play(); 1188 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1189 try { 1190 connection.setFixedLengthStreamingMode(-2); 1191 fail(); 1192 } catch (IllegalArgumentException expected) { 1193 } 1194 } 1195 1196 public void testCanSetNegativeChunkedStreamingMode() throws Exception { 1197 server.play(); 1198 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1199 connection.setChunkedStreamingMode(-2); 1200 } 1201 1202 public void testCannotSetFixedLengthStreamingModeAfterConnect() throws Exception { 1203 server.enqueue(new MockResponse().setBody("A")); 1204 server.play(); 1205 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1206 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1207 try { 1208 connection.setFixedLengthStreamingMode(1); 1209 fail(); 1210 } catch (IllegalStateException expected) { 1211 } 1212 } 1213 1214 public void testCannotSetChunkedStreamingModeAfterConnect() throws Exception { 1215 server.enqueue(new MockResponse().setBody("A")); 1216 server.play(); 1217 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1218 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1219 try { 1220 connection.setChunkedStreamingMode(1); 1221 fail(); 1222 } catch (IllegalStateException expected) { 1223 } 1224 } 1225 1226 public void testCannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception { 1227 server.play(); 1228 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1229 connection.setChunkedStreamingMode(1); 1230 try { 1231 connection.setFixedLengthStreamingMode(1); 1232 fail(); 1233 } catch (IllegalStateException expected) { 1234 } 1235 } 1236 1237 public void testCannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception { 1238 server.play(); 1239 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1240 connection.setFixedLengthStreamingMode(1); 1241 try { 1242 connection.setChunkedStreamingMode(1); 1243 fail(); 1244 } catch (IllegalStateException expected) { 1245 } 1246 } 1247 1248 public void testSecureFixedLengthStreaming() throws Exception { 1249 testSecureStreamingPost(StreamingMode.FIXED_LENGTH); 1250 } 1251 1252 public void testSecureChunkedStreaming() throws Exception { 1253 testSecureStreamingPost(StreamingMode.CHUNKED); 1254 } 1255 1256 /** 1257 * Users have reported problems using HTTPS with streaming request bodies. 1258 * http://code.google.com/p/android/issues/detail?id=12860 1259 */ 1260 private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception { 1261 TestSSLContext testSSLContext = TestSSLContext.create(); 1262 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1263 server.enqueue(new MockResponse().setBody("Success!")); 1264 server.play(); 1265 1266 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 1267 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 1268 connection.setDoOutput(true); 1269 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1270 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1271 connection.setFixedLengthStreamingMode(requestBody.length); 1272 } else if (streamingMode == StreamingMode.CHUNKED) { 1273 connection.setChunkedStreamingMode(0); 1274 } 1275 OutputStream outputStream = connection.getOutputStream(); 1276 outputStream.write(requestBody); 1277 outputStream.close(); 1278 assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1279 1280 RecordedRequest request = server.takeRequest(); 1281 assertEquals("POST / HTTP/1.1", request.getRequestLine()); 1282 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1283 assertEquals(Collections.<Integer>emptyList(), request.getChunkSizes()); 1284 } else if (streamingMode == StreamingMode.CHUNKED) { 1285 assertEquals(Arrays.asList(4), request.getChunkSizes()); 1286 } 1287 assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); 1288 } 1289 1290 enum StreamingMode { 1291 FIXED_LENGTH, CHUNKED 1292 } 1293 1294 public void testAuthenticateWithPost() throws Exception { 1295 MockResponse pleaseAuthenticate = new MockResponse() 1296 .setResponseCode(401) 1297 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1298 .setBody("Please authenticate."); 1299 // fail auth three times... 1300 server.enqueue(pleaseAuthenticate); 1301 server.enqueue(pleaseAuthenticate); 1302 server.enqueue(pleaseAuthenticate); 1303 // ...then succeed the fourth time 1304 server.enqueue(new MockResponse().setBody("Successful auth!")); 1305 server.play(); 1306 1307 Authenticator.setDefault(new SimpleAuthenticator()); 1308 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1309 connection.setDoOutput(true); 1310 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1311 OutputStream outputStream = connection.getOutputStream(); 1312 outputStream.write(requestBody); 1313 outputStream.close(); 1314 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1315 1316 // no authorization header for the first request... 1317 RecordedRequest request = server.takeRequest(); 1318 assertContainsNoneMatching(request.getHeaders(), "Authorization: .*"); 1319 1320 // ...but the three requests that follow include an authorization header 1321 for (int i = 0; i < 3; i++) { 1322 request = server.takeRequest(); 1323 assertEquals("POST / HTTP/1.1", request.getRequestLine()); 1324 assertContains(request.getHeaders(), "Authorization: Basic " 1325 + SimpleAuthenticator.BASE_64_CREDENTIALS); 1326 assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); 1327 } 1328 } 1329 1330 public void testAuthenticateWithGet() throws Exception { 1331 MockResponse pleaseAuthenticate = new MockResponse() 1332 .setResponseCode(401) 1333 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1334 .setBody("Please authenticate."); 1335 // fail auth three times... 1336 server.enqueue(pleaseAuthenticate); 1337 server.enqueue(pleaseAuthenticate); 1338 server.enqueue(pleaseAuthenticate); 1339 // ...then succeed the fourth time 1340 server.enqueue(new MockResponse().setBody("Successful auth!")); 1341 server.play(); 1342 1343 SimpleAuthenticator authenticator = new SimpleAuthenticator(); 1344 Authenticator.setDefault(authenticator); 1345 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1346 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1347 assertEquals(Authenticator.RequestorType.SERVER, authenticator.requestorType); 1348 assertEquals(server.getPort(), authenticator.requestingPort); 1349 assertEquals(InetAddress.getByName(server.getHostName()), authenticator.requestingSite); 1350 assertEquals("protected area", authenticator.requestingPrompt); 1351 assertEquals("http", authenticator.requestingProtocol); 1352 assertEquals("Basic", authenticator.requestingScheme); 1353 1354 // no authorization header for the first request... 1355 RecordedRequest request = server.takeRequest(); 1356 assertContainsNoneMatching(request.getHeaders(), "Authorization: .*"); 1357 1358 // ...but the three requests that follow requests include an authorization header 1359 for (int i = 0; i < 3; i++) { 1360 request = server.takeRequest(); 1361 assertEquals("GET / HTTP/1.1", request.getRequestLine()); 1362 assertContains(request.getHeaders(), "Authorization: Basic " 1363 + SimpleAuthenticator.BASE_64_CREDENTIALS); 1364 } 1365 } 1366 1367 // http://code.google.com/p/android/issues/detail?id=19081 1368 public void testAuthenticateWithCommaSeparatedAuthenticationMethods() throws Exception { 1369 server.enqueue(new MockResponse() 1370 .setResponseCode(401) 1371 .addHeader("WWW-Authenticate: Scheme1 realm=\"a\", Scheme2 realm=\"b\", " 1372 + "Scheme3 realm=\"c\"") 1373 .setBody("Please authenticate.")); 1374 server.enqueue(new MockResponse().setBody("Successful auth!")); 1375 server.play(); 1376 1377 SimpleAuthenticator authenticator = new SimpleAuthenticator(); 1378 authenticator.expectedPrompt = "b"; 1379 Authenticator.setDefault(authenticator); 1380 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1381 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1382 1383 assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*"); 1384 assertContains(server.takeRequest().getHeaders(), 1385 "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS); 1386 assertEquals("Scheme2", authenticator.requestingScheme); 1387 } 1388 1389 public void testAuthenticateWithMultipleAuthenticationHeaders() throws Exception { 1390 server.enqueue(new MockResponse() 1391 .setResponseCode(401) 1392 .addHeader("WWW-Authenticate: Scheme1 realm=\"a\"") 1393 .addHeader("WWW-Authenticate: Scheme2 realm=\"b\"") 1394 .addHeader("WWW-Authenticate: Scheme3 realm=\"c\"") 1395 .setBody("Please authenticate.")); 1396 server.enqueue(new MockResponse().setBody("Successful auth!")); 1397 server.play(); 1398 1399 SimpleAuthenticator authenticator = new SimpleAuthenticator(); 1400 authenticator.expectedPrompt = "b"; 1401 Authenticator.setDefault(authenticator); 1402 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1403 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1404 1405 assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*"); 1406 assertContains(server.takeRequest().getHeaders(), 1407 "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS); 1408 assertEquals("Scheme2", authenticator.requestingScheme); 1409 } 1410 1411 public void testRedirectedWithChunkedEncoding() throws Exception { 1412 testRedirected(TransferKind.CHUNKED, true); 1413 } 1414 1415 public void testRedirectedWithContentLengthHeader() throws Exception { 1416 testRedirected(TransferKind.FIXED_LENGTH, true); 1417 } 1418 1419 public void testRedirectedWithNoLengthHeaders() throws Exception { 1420 testRedirected(TransferKind.END_OF_STREAM, false); 1421 } 1422 1423 private void testRedirected(TransferKind transferKind, boolean reuse) throws Exception { 1424 MockResponse response = new MockResponse() 1425 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1426 .addHeader("Location: /foo"); 1427 transferKind.setBody(response, "This page has moved!", 10); 1428 server.enqueue(response); 1429 server.enqueue(new MockResponse().setBody("This is the new location!")); 1430 server.play(); 1431 1432 URLConnection connection = server.getUrl("/").openConnection(); 1433 assertEquals("This is the new location!", 1434 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1435 1436 RecordedRequest first = server.takeRequest(); 1437 assertEquals("GET / HTTP/1.1", first.getRequestLine()); 1438 RecordedRequest retry = server.takeRequest(); 1439 assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); 1440 if (reuse) { 1441 assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); 1442 } 1443 } 1444 1445 public void testRedirectedOnHttps() throws IOException, InterruptedException { 1446 TestSSLContext testSSLContext = TestSSLContext.create(); 1447 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1448 server.enqueue(new MockResponse() 1449 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1450 .addHeader("Location: /foo") 1451 .setBody("This page has moved!")); 1452 server.enqueue(new MockResponse().setBody("This is the new location!")); 1453 server.play(); 1454 1455 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 1456 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 1457 assertEquals("This is the new location!", 1458 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1459 1460 RecordedRequest first = server.takeRequest(); 1461 assertEquals("GET / HTTP/1.1", first.getRequestLine()); 1462 RecordedRequest retry = server.takeRequest(); 1463 assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); 1464 assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); 1465 } 1466 1467 public void testNotRedirectedFromHttpsToHttp() throws IOException, InterruptedException { 1468 TestSSLContext testSSLContext = TestSSLContext.create(); 1469 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1470 server.enqueue(new MockResponse() 1471 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1472 .addHeader("Location: http://anyhost/foo") 1473 .setBody("This page has moved!")); 1474 server.play(); 1475 1476 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 1477 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 1478 assertEquals("This page has moved!", 1479 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1480 } 1481 1482 public void testNotRedirectedFromHttpToHttps() throws IOException, InterruptedException { 1483 server.enqueue(new MockResponse() 1484 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1485 .addHeader("Location: https://anyhost/foo") 1486 .setBody("This page has moved!")); 1487 server.play(); 1488 1489 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1490 assertEquals("This page has moved!", 1491 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1492 } 1493 1494 public void testRedirectToAnotherOriginServer() throws Exception { 1495 MockWebServer server2 = new MockWebServer(); 1496 server2.enqueue(new MockResponse().setBody("This is the 2nd server!")); 1497 server2.play(); 1498 1499 server.enqueue(new MockResponse() 1500 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1501 .addHeader("Location: " + server2.getUrl("/").toString()) 1502 .setBody("This page has moved!")); 1503 server.enqueue(new MockResponse().setBody("This is the first server again!")); 1504 server.play(); 1505 1506 URLConnection connection = server.getUrl("/").openConnection(); 1507 assertEquals("This is the 2nd server!", 1508 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1509 assertEquals(server2.getUrl("/"), connection.getURL()); 1510 1511 // make sure the first server was careful to recycle the connection 1512 assertEquals("This is the first server again!", 1513 readAscii(server.getUrl("/").openStream(), Integer.MAX_VALUE)); 1514 1515 RecordedRequest first = server.takeRequest(); 1516 assertContains(first.getHeaders(), "Host: " + hostName + ":" + server.getPort()); 1517 RecordedRequest second = server2.takeRequest(); 1518 assertContains(second.getHeaders(), "Host: " + hostName + ":" + server2.getPort()); 1519 RecordedRequest third = server.takeRequest(); 1520 assertEquals("Expected connection reuse", 1, third.getSequenceNumber()); 1521 1522 server2.shutdown(); 1523 } 1524 1525 public void testResponse300MultipleChoiceWithPost() throws Exception { 1526 // Chrome doesn't follow the redirect, but Firefox and the RI both do 1527 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE); 1528 } 1529 1530 public void testResponse301MovedPermanentlyWithPost() throws Exception { 1531 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM); 1532 } 1533 1534 public void testResponse302MovedTemporarilyWithPost() throws Exception { 1535 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP); 1536 } 1537 1538 public void testResponse303SeeOtherWithPost() throws Exception { 1539 testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER); 1540 } 1541 1542 private void testResponseRedirectedWithPost(int redirectCode) throws Exception { 1543 server.enqueue(new MockResponse() 1544 .setResponseCode(redirectCode) 1545 .addHeader("Location: /page2") 1546 .setBody("This page has moved!")); 1547 server.enqueue(new MockResponse().setBody("Page 2")); 1548 server.play(); 1549 1550 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/page1").openConnection(); 1551 connection.setDoOutput(true); 1552 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1553 OutputStream outputStream = connection.getOutputStream(); 1554 outputStream.write(requestBody); 1555 outputStream.close(); 1556 assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1557 assertTrue(connection.getDoOutput()); 1558 1559 RecordedRequest page1 = server.takeRequest(); 1560 assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine()); 1561 assertEquals(Arrays.toString(requestBody), Arrays.toString(page1.getBody())); 1562 1563 RecordedRequest page2 = server.takeRequest(); 1564 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 1565 } 1566 1567 public void testResponse305UseProxy() throws Exception { 1568 server.play(); 1569 server.enqueue(new MockResponse() 1570 .setResponseCode(HttpURLConnection.HTTP_USE_PROXY) 1571 .addHeader("Location: " + server.getUrl("/")) 1572 .setBody("This page has moved!")); 1573 server.enqueue(new MockResponse().setBody("Proxy Response")); 1574 1575 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/foo").openConnection(); 1576 // Fails on the RI, which gets "Proxy Response" 1577 assertEquals("This page has moved!", 1578 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1579 1580 RecordedRequest page1 = server.takeRequest(); 1581 assertEquals("GET /foo HTTP/1.1", page1.getRequestLine()); 1582 assertEquals(1, server.getRequestCount()); 1583 } 1584 1585 public void testHttpsWithCustomTrustManager() throws Exception { 1586 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 1587 RecordingTrustManager trustManager = new RecordingTrustManager(); 1588 SSLContext sc = SSLContext.getInstance("TLS"); 1589 sc.init(null, new TrustManager[] { trustManager }, new java.security.SecureRandom()); 1590 1591 HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); 1592 HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); 1593 SSLSocketFactory defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 1594 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 1595 try { 1596 TestSSLContext testSSLContext = TestSSLContext.create(); 1597 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1598 server.enqueue(new MockResponse().setBody("ABC")); 1599 server.enqueue(new MockResponse().setBody("DEF")); 1600 server.enqueue(new MockResponse().setBody("GHI")); 1601 server.play(); 1602 1603 URL url = server.getUrl("/"); 1604 assertEquals("ABC", readAscii(url.openStream(), Integer.MAX_VALUE)); 1605 assertEquals("DEF", readAscii(url.openStream(), Integer.MAX_VALUE)); 1606 assertEquals("GHI", readAscii(url.openStream(), Integer.MAX_VALUE)); 1607 1608 assertEquals(Arrays.asList("verify " + hostName), hostnameVerifier.calls); 1609 assertEquals(Arrays.asList("checkServerTrusted [" 1610 + "CN=" + hostName + " 1, " 1611 + "CN=Test Intermediate Certificate Authority 1, " 1612 + "CN=Test Root Certificate Authority 1" 1613 + "] RSA"), 1614 trustManager.calls); 1615 } finally { 1616 HttpsURLConnection.setDefaultHostnameVerifier(defaultHostnameVerifier); 1617 HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory); 1618 } 1619 } 1620 1621 /** 1622 * Test that the timeout period is honored. The timeout may be doubled! 1623 * HttpURLConnection will wait the full timeout for each of the server's IP 1624 * addresses. This is typically one IPv4 address and one IPv6 address. 1625 */ 1626 public void testConnectTimeouts() throws IOException { 1627 StuckServer ss = new StuckServer(); 1628 int serverPort = ss.getLocalPort(); 1629 URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection(); 1630 int timeout = 1000; 1631 urlConnection.setConnectTimeout(timeout); 1632 long start = System.currentTimeMillis(); 1633 try { 1634 urlConnection.getInputStream(); 1635 fail(); 1636 } catch (SocketTimeoutException expected) { 1637 long elapsed = System.currentTimeMillis() - start; 1638 int attempts = InetAddress.getAllByName("localhost").length; // one per IP address 1639 assertTrue("timeout=" +timeout + ", elapsed=" + elapsed + ", attempts=" + attempts, 1640 Math.abs((attempts * timeout) - elapsed) < 500); 1641 } finally { 1642 ss.close(); 1643 } 1644 } 1645 1646 public void testReadTimeouts() throws IOException { 1647 /* 1648 * This relies on the fact that MockWebServer doesn't close the 1649 * connection after a response has been sent. This causes the client to 1650 * try to read more bytes than are sent, which results in a timeout. 1651 */ 1652 MockResponse timeout = new MockResponse() 1653 .setBody("ABC") 1654 .clearHeaders() 1655 .addHeader("Content-Length: 4"); 1656 server.enqueue(timeout); 1657 server.enqueue(new MockResponse().setBody("unused")); // to keep the server alive 1658 server.play(); 1659 1660 URLConnection urlConnection = server.getUrl("/").openConnection(); 1661 urlConnection.setReadTimeout(1000); 1662 InputStream in = urlConnection.getInputStream(); 1663 assertEquals('A', in.read()); 1664 assertEquals('B', in.read()); 1665 assertEquals('C', in.read()); 1666 try { 1667 in.read(); // if Content-Length was accurate, this would return -1 immediately 1668 fail(); 1669 } catch (SocketTimeoutException expected) { 1670 } 1671 } 1672 1673 public void testSetChunkedEncodingAsRequestProperty() throws IOException, InterruptedException { 1674 server.enqueue(new MockResponse()); 1675 server.play(); 1676 1677 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 1678 urlConnection.setRequestProperty("Transfer-encoding", "chunked"); 1679 urlConnection.setDoOutput(true); 1680 urlConnection.getOutputStream().write("ABC".getBytes("UTF-8")); 1681 assertEquals(200, urlConnection.getResponseCode()); 1682 1683 RecordedRequest request = server.takeRequest(); 1684 assertEquals("ABC", new String(request.getBody(), "UTF-8")); 1685 } 1686 1687 public void testConnectionCloseInRequest() throws IOException, InterruptedException { 1688 server.enqueue(new MockResponse()); // server doesn't honor the connection: close header! 1689 server.enqueue(new MockResponse()); 1690 server.play(); 1691 1692 HttpURLConnection a = (HttpURLConnection) server.getUrl("/").openConnection(); 1693 a.setRequestProperty("Connection", "close"); 1694 assertEquals(200, a.getResponseCode()); 1695 1696 HttpURLConnection b = (HttpURLConnection) server.getUrl("/").openConnection(); 1697 assertEquals(200, b.getResponseCode()); 1698 1699 assertEquals(0, server.takeRequest().getSequenceNumber()); 1700 assertEquals("When connection: close is used, each request should get its own connection", 1701 0, server.takeRequest().getSequenceNumber()); 1702 } 1703 1704 public void testConnectionCloseInResponse() throws IOException, InterruptedException { 1705 server.enqueue(new MockResponse().addHeader("Connection: close")); 1706 server.enqueue(new MockResponse()); 1707 server.play(); 1708 1709 HttpURLConnection a = (HttpURLConnection) server.getUrl("/").openConnection(); 1710 assertEquals(200, a.getResponseCode()); 1711 1712 HttpURLConnection b = (HttpURLConnection) server.getUrl("/").openConnection(); 1713 assertEquals(200, b.getResponseCode()); 1714 1715 assertEquals(0, server.takeRequest().getSequenceNumber()); 1716 assertEquals("When connection: close is used, each request should get its own connection", 1717 0, server.takeRequest().getSequenceNumber()); 1718 } 1719 1720 public void testConnectionCloseWithRedirect() throws IOException, InterruptedException { 1721 MockResponse response = new MockResponse() 1722 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1723 .addHeader("Location: /foo") 1724 .addHeader("Connection: close"); 1725 server.enqueue(response); 1726 server.enqueue(new MockResponse().setBody("This is the new location!")); 1727 server.play(); 1728 1729 URLConnection connection = server.getUrl("/").openConnection(); 1730 assertEquals("This is the new location!", 1731 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1732 1733 assertEquals(0, server.takeRequest().getSequenceNumber()); 1734 assertEquals("When connection: close is used, each request should get its own connection", 1735 0, server.takeRequest().getSequenceNumber()); 1736 } 1737 1738 public void testResponseCodeDisagreesWithHeaders() throws IOException, InterruptedException { 1739 server.enqueue(new MockResponse() 1740 .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT) 1741 .setBody("This body is not allowed!")); 1742 server.play(); 1743 1744 URLConnection connection = server.getUrl("/").openConnection(); 1745 assertEquals("This body is not allowed!", 1746 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1747 } 1748 1749 public void testSingleByteReadIsSigned() throws IOException { 1750 server.enqueue(new MockResponse().setBody(new byte[] { -2, -1 })); 1751 server.play(); 1752 1753 URLConnection connection = server.getUrl("/").openConnection(); 1754 InputStream in = connection.getInputStream(); 1755 assertEquals(254, in.read()); 1756 assertEquals(255, in.read()); 1757 assertEquals(-1, in.read()); 1758 } 1759 1760 public void testFlushAfterStreamTransmittedWithChunkedEncoding() throws IOException { 1761 testFlushAfterStreamTransmitted(TransferKind.CHUNKED); 1762 } 1763 1764 public void testFlushAfterStreamTransmittedWithFixedLength() throws IOException { 1765 testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH); 1766 } 1767 1768 public void testFlushAfterStreamTransmittedWithNoLengthHeaders() throws IOException { 1769 testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM); 1770 } 1771 1772 /** 1773 * We explicitly permit apps to close the upload stream even after it has 1774 * been transmitted. We also permit flush so that buffered streams can 1775 * do a no-op flush when they are closed. http://b/3038470 1776 */ 1777 private void testFlushAfterStreamTransmitted(TransferKind transferKind) throws IOException { 1778 server.enqueue(new MockResponse().setBody("abc")); 1779 server.play(); 1780 1781 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1782 connection.setDoOutput(true); 1783 byte[] upload = "def".getBytes("UTF-8"); 1784 1785 if (transferKind == TransferKind.CHUNKED) { 1786 connection.setChunkedStreamingMode(0); 1787 } else if (transferKind == TransferKind.FIXED_LENGTH) { 1788 connection.setFixedLengthStreamingMode(upload.length); 1789 } 1790 1791 OutputStream out = connection.getOutputStream(); 1792 out.write(upload); 1793 assertEquals("abc", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1794 1795 out.flush(); // dubious but permitted 1796 try { 1797 out.write("ghi".getBytes("UTF-8")); 1798 fail(); 1799 } catch (IOException expected) { 1800 } 1801 } 1802 1803 public void testGetHeadersThrows() throws IOException { 1804 server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); 1805 server.play(); 1806 1807 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1808 try { 1809 connection.getInputStream(); 1810 fail(); 1811 } catch (IOException expected) { 1812 } 1813 1814 try { 1815 connection.getInputStream(); 1816 fail(); 1817 } catch (IOException expected) { 1818 } 1819 } 1820 1821 public void testGetKeepAlive() throws Exception { 1822 MockWebServer server = new MockWebServer(); 1823 server.enqueue(new MockResponse().setBody("ABC")); 1824 server.play(); 1825 1826 // The request should work once and then fail 1827 URLConnection connection = server.getUrl("").openConnection(); 1828 InputStream input = connection.getInputStream(); 1829 assertEquals("ABC", readAscii(input, Integer.MAX_VALUE)); 1830 input.close(); 1831 try { 1832 server.getUrl("").openConnection().getInputStream(); 1833 fail(); 1834 } catch (ConnectException expected) { 1835 } 1836 } 1837 1838 /** 1839 * This test goes through the exhaustive set of interesting ASCII characters 1840 * because most of those characters are interesting in some way according to 1841 * RFC 2396 and RFC 2732. http://b/1158780 1842 */ 1843 public void testLenientUrlToUri() throws Exception { 1844 // alphanum 1845 testUrlToUriMapping("abzABZ09", "abzABZ09", "abzABZ09", "abzABZ09", "abzABZ09"); 1846 1847 // control characters 1848 testUrlToUriMapping("\u0001", "%01", "%01", "%01", "%01"); 1849 testUrlToUriMapping("\u001f", "%1F", "%1F", "%1F", "%1F"); 1850 1851 // ascii characters 1852 testUrlToUriMapping("%20", "%20", "%20", "%20", "%20"); 1853 testUrlToUriMapping("%20", "%20", "%20", "%20", "%20"); 1854 testUrlToUriMapping(" ", "%20", "%20", "%20", "%20"); 1855 testUrlToUriMapping("!", "!", "!", "!", "!"); 1856 testUrlToUriMapping("\"", "%22", "%22", "%22", "%22"); 1857 testUrlToUriMapping("#", null, null, null, "%23"); 1858 testUrlToUriMapping("$", "$", "$", "$", "$"); 1859 testUrlToUriMapping("&", "&", "&", "&", "&"); 1860 testUrlToUriMapping("'", "'", "'", "'", "'"); 1861 testUrlToUriMapping("(", "(", "(", "(", "("); 1862 testUrlToUriMapping(")", ")", ")", ")", ")"); 1863 testUrlToUriMapping("*", "*", "*", "*", "*"); 1864 testUrlToUriMapping("+", "+", "+", "+", "+"); 1865 testUrlToUriMapping(",", ",", ",", ",", ","); 1866 testUrlToUriMapping("-", "-", "-", "-", "-"); 1867 testUrlToUriMapping(".", ".", ".", ".", "."); 1868 testUrlToUriMapping("/", null, "/", "/", "/"); 1869 testUrlToUriMapping(":", null, ":", ":", ":"); 1870 testUrlToUriMapping(";", ";", ";", ";", ";"); 1871 testUrlToUriMapping("<", "%3C", "%3C", "%3C", "%3C"); 1872 testUrlToUriMapping("=", "=", "=", "=", "="); 1873 testUrlToUriMapping(">", "%3E", "%3E", "%3E", "%3E"); 1874 testUrlToUriMapping("?", null, null, "?", "?"); 1875 testUrlToUriMapping("@", "@", "@", "@", "@"); 1876 testUrlToUriMapping("[", null, "%5B", null, "%5B"); 1877 testUrlToUriMapping("\\", "%5C", "%5C", "%5C", "%5C"); 1878 testUrlToUriMapping("]", null, "%5D", null, "%5D"); 1879 testUrlToUriMapping("^", "%5E", "%5E", "%5E", "%5E"); 1880 testUrlToUriMapping("_", "_", "_", "_", "_"); 1881 testUrlToUriMapping("`", "%60", "%60", "%60", "%60"); 1882 testUrlToUriMapping("{", "%7B", "%7B", "%7B", "%7B"); 1883 testUrlToUriMapping("|", "%7C", "%7C", "%7C", "%7C"); 1884 testUrlToUriMapping("}", "%7D", "%7D", "%7D", "%7D"); 1885 testUrlToUriMapping("~", "~", "~", "~", "~"); 1886 testUrlToUriMapping("~", "~", "~", "~", "~"); 1887 testUrlToUriMapping("\u007f", "%7F", "%7F", "%7F", "%7F"); 1888 1889 // beyond ascii 1890 testUrlToUriMapping("\u0080", "%C2%80", "%C2%80", "%C2%80", "%C2%80"); 1891 testUrlToUriMapping("\u20ac", "\u20ac", "\u20ac", "\u20ac", "\u20ac"); 1892 testUrlToUriMapping("\ud842\udf9f", 1893 "\ud842\udf9f", "\ud842\udf9f", "\ud842\udf9f", "\ud842\udf9f"); 1894 } 1895 1896 public void testLenientUrlToUriNul() throws Exception { 1897 testUrlToUriMapping("\u0000", "%00", "%00", "%00", "%00"); // RI fails this 1898 } 1899 1900 private void testUrlToUriMapping(String string, String asAuthority, String asFile, 1901 String asQuery, String asFragment) throws Exception { 1902 if (asAuthority != null) { 1903 assertEquals("http://host" + asAuthority + ".tld/", 1904 backdoorUrlToUri(new URL("http://host" + string + ".tld/")).toString()); 1905 } 1906 if (asFile != null) { 1907 assertEquals("http://host.tld/file" + asFile + "/", 1908 backdoorUrlToUri(new URL("http://host.tld/file" + string + "/")).toString()); 1909 } 1910 if (asQuery != null) { 1911 assertEquals("http://host.tld/file?q" + asQuery + "=x", 1912 backdoorUrlToUri(new URL("http://host.tld/file?q" + string + "=x")).toString()); 1913 } 1914 assertEquals("http://host.tld/file#" + asFragment + "-x", 1915 backdoorUrlToUri(new URL("http://host.tld/file#" + asFragment + "-x")).toString()); 1916 } 1917 1918 /** 1919 * Exercises HttpURLConnection to convert URL to a URI. Unlike URL#toURI, 1920 * HttpURLConnection recovers from URLs with unescaped but unsupported URI 1921 * characters like '{' and '|' by escaping these characters. 1922 */ 1923 private URI backdoorUrlToUri(URL url) throws Exception { 1924 final AtomicReference<URI> uriReference = new AtomicReference<URI>(); 1925 1926 ResponseCache.setDefault(new ResponseCache() { 1927 @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException { 1928 return null; 1929 } 1930 @Override public CacheResponse get(URI uri, String requestMethod, 1931 Map<String, List<String>> requestHeaders) throws IOException { 1932 uriReference.set(uri); 1933 throw new UnsupportedOperationException(); 1934 } 1935 }); 1936 1937 try { 1938 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 1939 connection.getResponseCode(); 1940 } catch (Exception expected) { 1941 } 1942 1943 return uriReference.get(); 1944 } 1945 1946 /** 1947 * Don't explode if the cache returns a null body. http://b/3373699 1948 */ 1949 public void testResponseCacheReturnsNullOutputStream() throws Exception { 1950 final AtomicBoolean aborted = new AtomicBoolean(); 1951 ResponseCache.setDefault(new ResponseCache() { 1952 @Override public CacheResponse get(URI uri, String requestMethod, 1953 Map<String, List<String>> requestHeaders) throws IOException { 1954 return null; 1955 } 1956 @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException { 1957 return new CacheRequest() { 1958 @Override public void abort() { 1959 aborted.set(true); 1960 } 1961 @Override public OutputStream getBody() throws IOException { 1962 return null; 1963 } 1964 }; 1965 } 1966 }); 1967 1968 server.enqueue(new MockResponse().setBody("abcdef")); 1969 server.play(); 1970 1971 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1972 InputStream in = connection.getInputStream(); 1973 assertEquals("abc", readAscii(in, 3)); 1974 in.close(); 1975 assertFalse(aborted.get()); // The best behavior is ambiguous, but RI 6 doesn't abort here 1976 } 1977 1978 1979 /** 1980 * http://code.google.com/p/android/issues/detail?id=14562 1981 */ 1982 public void testReadAfterLastByte() throws Exception { 1983 server.enqueue(new MockResponse() 1984 .setBody("ABC") 1985 .clearHeaders() 1986 .addHeader("Connection: close") 1987 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)); 1988 server.play(); 1989 1990 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1991 InputStream in = connection.getInputStream(); 1992 assertEquals("ABC", readAscii(in, 3)); 1993 assertEquals(-1, in.read()); 1994 assertEquals(-1, in.read()); // throws IOException in Gingerbread 1995 } 1996 1997 public void testGetContent() throws Exception { 1998 server.enqueue(new MockResponse().setBody("A")); 1999 server.play(); 2000 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2001 InputStream in = (InputStream) connection.getContent(); 2002 assertEquals("A", readAscii(in, Integer.MAX_VALUE)); 2003 } 2004 2005 public void testGetContentOfType() throws Exception { 2006 server.enqueue(new MockResponse().setBody("A")); 2007 server.play(); 2008 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2009 try { 2010 connection.getContent(null); 2011 fail(); 2012 } catch (NullPointerException expected) { 2013 } 2014 try { 2015 connection.getContent(new Class[] { null }); 2016 fail(); 2017 } catch (NullPointerException expected) { 2018 } 2019 assertNull(connection.getContent(new Class[] { getClass() })); 2020 connection.disconnect(); 2021 } 2022 2023 public void testGetOutputStreamOnGetFails() throws Exception { 2024 server.enqueue(new MockResponse()); 2025 server.play(); 2026 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2027 try { 2028 connection.getOutputStream(); 2029 fail(); 2030 } catch (ProtocolException expected) { 2031 } 2032 } 2033 2034 public void testGetOutputAfterGetInputStreamFails() throws Exception { 2035 server.enqueue(new MockResponse()); 2036 server.play(); 2037 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2038 connection.setDoOutput(true); 2039 try { 2040 connection.getInputStream(); 2041 connection.getOutputStream(); 2042 fail(); 2043 } catch (ProtocolException expected) { 2044 } 2045 } 2046 2047 public void testSetDoOutputOrDoInputAfterConnectFails() throws Exception { 2048 server.enqueue(new MockResponse()); 2049 server.play(); 2050 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2051 connection.connect(); 2052 try { 2053 connection.setDoOutput(true); 2054 fail(); 2055 } catch (IllegalStateException expected) { 2056 } 2057 try { 2058 connection.setDoInput(true); 2059 fail(); 2060 } catch (IllegalStateException expected) { 2061 } 2062 connection.disconnect(); 2063 } 2064 2065 public void testClientSendsContentLength() throws Exception { 2066 server.enqueue(new MockResponse().setBody("A")); 2067 server.play(); 2068 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2069 connection.setDoOutput(true); 2070 OutputStream out = connection.getOutputStream(); 2071 out.write(new byte[] { 'A', 'B', 'C' }); 2072 out.close(); 2073 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2074 RecordedRequest request = server.takeRequest(); 2075 assertContains(request.getHeaders(), "Content-Length: 3"); 2076 } 2077 2078 public void testGetContentLengthConnects() throws Exception { 2079 server.enqueue(new MockResponse().setBody("ABC")); 2080 server.play(); 2081 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2082 assertEquals(3, connection.getContentLength()); 2083 connection.disconnect(); 2084 } 2085 2086 public void testGetContentTypeConnects() throws Exception { 2087 server.enqueue(new MockResponse() 2088 .addHeader("Content-Type: text/plain") 2089 .setBody("ABC")); 2090 server.play(); 2091 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2092 assertEquals("text/plain", connection.getContentType()); 2093 connection.disconnect(); 2094 } 2095 2096 public void testGetContentEncodingConnects() throws Exception { 2097 server.enqueue(new MockResponse() 2098 .addHeader("Content-Encoding: identity") 2099 .setBody("ABC")); 2100 server.play(); 2101 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 2102 assertEquals("identity", connection.getContentEncoding()); 2103 connection.disconnect(); 2104 } 2105 2106 // http://b/4361656 2107 public void testUrlContainsQueryButNoPath() throws Exception { 2108 server.enqueue(new MockResponse().setBody("A")); 2109 server.play(); 2110 URL url = new URL("http", server.getHostName(), server.getPort(), "?query"); 2111 assertEquals("A", readAscii(url.openConnection().getInputStream(), Integer.MAX_VALUE)); 2112 RecordedRequest request = server.takeRequest(); 2113 assertEquals("GET /?query HTTP/1.1", request.getRequestLine()); 2114 } 2115 2116 // http://code.google.com/p/android/issues/detail?id=20442 2117 public void testInputStreamAvailableWithChunkedEncoding() throws Exception { 2118 testInputStreamAvailable(TransferKind.CHUNKED); 2119 } 2120 2121 public void testInputStreamAvailableWithContentLengthHeader() throws Exception { 2122 testInputStreamAvailable(TransferKind.FIXED_LENGTH); 2123 } 2124 2125 public void testInputStreamAvailableWithNoLengthHeaders() throws Exception { 2126 testInputStreamAvailable(TransferKind.END_OF_STREAM); 2127 } 2128 2129 private void testInputStreamAvailable(TransferKind transferKind) throws IOException { 2130 String body = "ABCDEFGH"; 2131 MockResponse response = new MockResponse(); 2132 transferKind.setBody(response, body, 4); 2133 server.enqueue(response); 2134 server.play(); 2135 URLConnection connection = server.getUrl("/").openConnection(); 2136 InputStream in = connection.getInputStream(); 2137 for (int i = 0; i < body.length(); i++) { 2138 assertTrue(in.available() >= 0); 2139 assertEquals(body.charAt(i), in.read()); 2140 } 2141 assertEquals(0, in.available()); 2142 assertEquals(-1, in.read()); 2143 } 2144 2145 // http://code.google.com/p/android/issues/detail?id=28095 2146 public void testInvalidIpv4Address() throws Exception { 2147 try { 2148 URI uri = new URI("http://1111.111.111.111/index.html"); 2149 uri.toURL().openConnection().connect(); 2150 fail(); 2151 } catch (UnknownHostException expected) { 2152 } 2153 } 2154 2155 // http://code.google.com/p/android/issues/detail?id=16895 2156 public void testUrlWithSpaceInHost() throws Exception { 2157 URLConnection urlConnection = new URL("http://and roid.com/").openConnection(); 2158 try { 2159 urlConnection.getInputStream(); 2160 fail(); 2161 } catch (UnknownHostException expected) { 2162 } 2163 } 2164 2165 public void testUrlWithSpaceInHostViaHttpProxy() throws Exception { 2166 server.enqueue(new MockResponse()); 2167 server.play(); 2168 URLConnection urlConnection = new URL("http://and roid.com/") 2169 .openConnection(server.toProxyAddress()); 2170 try { 2171 urlConnection.getInputStream(); 2172 fail(); // the RI makes a bogus proxy request for "GET http://and roid.com/ HTTP/1.1" 2173 } catch (UnknownHostException expected) { 2174 } 2175 } 2176 2177 public void testSslFallback() throws Exception { 2178 TestSSLContext testSSLContext = TestSSLContext.create(); 2179 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 2180 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 2181 server.enqueue(new MockResponse().setBody("This required a 2nd handshake")); 2182 server.play(); 2183 2184 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 2185 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 2186 assertEquals("This required a 2nd handshake", 2187 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2188 2189 RecordedRequest first = server.takeRequest(); 2190 assertEquals(0, first.getSequenceNumber()); 2191 RecordedRequest retry = server.takeRequest(); 2192 assertEquals(0, retry.getSequenceNumber()); 2193 assertEquals("SSLv3", retry.getSslProtocol()); 2194 } 2195 2196 public void testInspectSslBeforeConnect() throws Exception { 2197 TestSSLContext testSSLContext = TestSSLContext.create(); 2198 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 2199 server.enqueue(new MockResponse()); 2200 server.play(); 2201 2202 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 2203 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 2204 assertNotNull(connection.getHostnameVerifier()); 2205 try { 2206 connection.getLocalCertificates(); 2207 fail(); 2208 } catch (IllegalStateException expected) { 2209 } 2210 try { 2211 connection.getServerCertificates(); 2212 fail(); 2213 } catch (IllegalStateException expected) { 2214 } 2215 try { 2216 connection.getCipherSuite(); 2217 fail(); 2218 } catch (IllegalStateException expected) { 2219 } 2220 try { 2221 connection.getPeerPrincipal(); 2222 fail(); 2223 } catch (IllegalStateException expected) { 2224 } 2225 } 2226 2227 /** 2228 * Test that we can inspect the SSL session after connect(). 2229 * http://code.google.com/p/android/issues/detail?id=24431 2230 */ 2231 public void testInspectSslAfterConnect() throws Exception { 2232 TestSSLContext testSSLContext = TestSSLContext.create(); 2233 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 2234 server.enqueue(new MockResponse()); 2235 server.play(); 2236 2237 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 2238 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 2239 connection.connect(); 2240 assertNotNull(connection.getHostnameVerifier()); 2241 assertNull(connection.getLocalCertificates()); 2242 assertNotNull(connection.getServerCertificates()); 2243 assertNotNull(connection.getCipherSuite()); 2244 assertNotNull(connection.getPeerPrincipal()); 2245 } 2246 2247 /** 2248 * Returns a gzipped copy of {@code bytes}. 2249 */ 2250 public byte[] gzip(byte[] bytes) throws IOException { 2251 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 2252 OutputStream gzippedOut = new GZIPOutputStream(bytesOut); 2253 gzippedOut.write(bytes); 2254 gzippedOut.close(); 2255 return bytesOut.toByteArray(); 2256 } 2257 2258 /** 2259 * Reads at most {@code limit} characters from {@code in} and asserts that 2260 * content equals {@code expected}. 2261 */ 2262 private void assertContent(String expected, URLConnection connection, int limit) 2263 throws IOException { 2264 connection.connect(); 2265 assertEquals(expected, readAscii(connection.getInputStream(), limit)); 2266 ((HttpURLConnection) connection).disconnect(); 2267 } 2268 2269 private void assertContent(String expected, URLConnection connection) throws IOException { 2270 assertContent(expected, connection, Integer.MAX_VALUE); 2271 } 2272 2273 private void assertContains(List<String> headers, String header) { 2274 assertTrue(headers.toString(), headers.contains(header)); 2275 } 2276 2277 private void assertContainsNoneMatching(List<String> headers, String pattern) { 2278 for (String header : headers) { 2279 if (header.matches(pattern)) { 2280 fail("Header " + header + " matches " + pattern); 2281 } 2282 } 2283 } 2284 2285 private Set<String> newSet(String... elements) { 2286 return new HashSet<String>(Arrays.asList(elements)); 2287 } 2288 2289 enum TransferKind { 2290 CHUNKED() { 2291 @Override void setBody(MockResponse response, byte[] content, int chunkSize) 2292 throws IOException { 2293 response.setChunkedBody(content, chunkSize); 2294 } 2295 }, 2296 FIXED_LENGTH() { 2297 @Override void setBody(MockResponse response, byte[] content, int chunkSize) { 2298 response.setBody(content); 2299 } 2300 }, 2301 END_OF_STREAM() { 2302 @Override void setBody(MockResponse response, byte[] content, int chunkSize) { 2303 response.setBody(content); 2304 response.setSocketPolicy(DISCONNECT_AT_END); 2305 for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) { 2306 if (h.next().startsWith("Content-Length:")) { 2307 h.remove(); 2308 break; 2309 } 2310 } 2311 } 2312 }; 2313 2314 abstract void setBody(MockResponse response, byte[] content, int chunkSize) 2315 throws IOException; 2316 2317 void setBody(MockResponse response, String content, int chunkSize) throws IOException { 2318 setBody(response, content.getBytes("UTF-8"), chunkSize); 2319 } 2320 } 2321 2322 enum ProxyConfig { 2323 NO_PROXY() { 2324 @Override public HttpURLConnection connect(MockWebServer server, URL url) 2325 throws IOException { 2326 return (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); 2327 } 2328 }, 2329 2330 CREATE_ARG() { 2331 @Override public HttpURLConnection connect(MockWebServer server, URL url) 2332 throws IOException { 2333 return (HttpURLConnection) url.openConnection(server.toProxyAddress()); 2334 } 2335 }, 2336 2337 PROXY_SYSTEM_PROPERTY() { 2338 @Override public HttpURLConnection connect(MockWebServer server, URL url) 2339 throws IOException { 2340 System.setProperty("proxyHost", "localhost"); 2341 System.setProperty("proxyPort", Integer.toString(server.getPort())); 2342 return (HttpURLConnection) url.openConnection(); 2343 } 2344 }, 2345 2346 HTTP_PROXY_SYSTEM_PROPERTY() { 2347 @Override public HttpURLConnection connect(MockWebServer server, URL url) 2348 throws IOException { 2349 System.setProperty("http.proxyHost", "localhost"); 2350 System.setProperty("http.proxyPort", Integer.toString(server.getPort())); 2351 return (HttpURLConnection) url.openConnection(); 2352 } 2353 }, 2354 2355 HTTPS_PROXY_SYSTEM_PROPERTY() { 2356 @Override public HttpURLConnection connect(MockWebServer server, URL url) 2357 throws IOException { 2358 System.setProperty("https.proxyHost", "localhost"); 2359 System.setProperty("https.proxyPort", Integer.toString(server.getPort())); 2360 return (HttpURLConnection) url.openConnection(); 2361 } 2362 }; 2363 2364 public abstract HttpURLConnection connect(MockWebServer server, URL url) throws IOException; 2365 } 2366 2367 private static class RecordingTrustManager implements X509TrustManager { 2368 private final List<String> calls = new ArrayList<String>(); 2369 2370 public X509Certificate[] getAcceptedIssuers() { 2371 calls.add("getAcceptedIssuers"); 2372 return new X509Certificate[] {}; 2373 } 2374 2375 public void checkClientTrusted(X509Certificate[] chain, String authType) 2376 throws CertificateException { 2377 calls.add("checkClientTrusted " + certificatesToString(chain) + " " + authType); 2378 } 2379 2380 public void checkServerTrusted(X509Certificate[] chain, String authType) 2381 throws CertificateException { 2382 calls.add("checkServerTrusted " + certificatesToString(chain) + " " + authType); 2383 } 2384 2385 private String certificatesToString(X509Certificate[] certificates) { 2386 List<String> result = new ArrayList<String>(); 2387 for (X509Certificate certificate : certificates) { 2388 result.add(certificate.getSubjectDN() + " " + certificate.getSerialNumber()); 2389 } 2390 return result.toString(); 2391 } 2392 } 2393 2394 private static class RecordingHostnameVerifier implements HostnameVerifier { 2395 private final List<String> calls = new ArrayList<String>(); 2396 2397 public boolean verify(String hostname, SSLSession session) { 2398 calls.add("verify " + hostname); 2399 return true; 2400 } 2401 } 2402 2403 private static class SimpleAuthenticator extends Authenticator { 2404 /** base64("username:password") */ 2405 private static final String BASE_64_CREDENTIALS = "dXNlcm5hbWU6cGFzc3dvcmQ="; 2406 2407 private String expectedPrompt; 2408 private RequestorType requestorType; 2409 private int requestingPort; 2410 private InetAddress requestingSite; 2411 private String requestingPrompt; 2412 private String requestingProtocol; 2413 private String requestingScheme; 2414 2415 protected PasswordAuthentication getPasswordAuthentication() { 2416 requestorType = getRequestorType(); 2417 requestingPort = getRequestingPort(); 2418 requestingSite = getRequestingSite(); 2419 requestingPrompt = getRequestingPrompt(); 2420 requestingProtocol = getRequestingProtocol(); 2421 requestingScheme = getRequestingScheme(); 2422 return (expectedPrompt == null || expectedPrompt.equals(requestingPrompt)) 2423 ? new PasswordAuthentication("username", "password".toCharArray()) 2424 : null; 2425 } 2426 } 2427} 2428