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