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