URLConnectionTest.java revision 2503556d17b28c7b4e6e514540a77df1627857d0
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.internal.http.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 = new MockWebServer(); 84 private HttpResponseCache cache; 85 private String hostName; 86 87 @Override protected void setUp() throws Exception { 88 super.setUp(); 89 hostName = server.getHostName(); 90 } 91 92 @Override protected void tearDown() throws Exception { 93 ResponseCache.setDefault(null); 94 Authenticator.setDefault(null); 95 System.clearProperty("proxyHost"); 96 System.clearProperty("proxyPort"); 97 System.clearProperty("http.proxyHost"); 98 System.clearProperty("http.proxyPort"); 99 System.clearProperty("https.proxyHost"); 100 System.clearProperty("https.proxyPort"); 101 server.shutdown(); 102 if (cache != null) { 103 cache.getCache().delete(); 104 } 105 super.tearDown(); 106 } 107 108 public void testRequestHeaders() throws IOException, InterruptedException { 109 server.enqueue(new MockResponse()); 110 server.play(); 111 112 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 113 urlConnection.addRequestProperty("D", "e"); 114 urlConnection.addRequestProperty("D", "f"); 115 assertEquals("f", urlConnection.getRequestProperty("D")); 116 assertEquals("f", urlConnection.getRequestProperty("d")); 117 Map<String, List<String>> requestHeaders = urlConnection.getRequestProperties(); 118 assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("D"))); 119 assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("d"))); 120 try { 121 requestHeaders.put("G", Arrays.asList("h")); 122 fail("Modified an unmodifiable view."); 123 } catch (UnsupportedOperationException expected) { 124 } 125 try { 126 requestHeaders.get("D").add("i"); 127 fail("Modified an unmodifiable view."); 128 } catch (UnsupportedOperationException expected) { 129 } 130 try { 131 urlConnection.setRequestProperty(null, "j"); 132 fail(); 133 } catch (NullPointerException expected) { 134 } 135 try { 136 urlConnection.addRequestProperty(null, "k"); 137 fail(); 138 } catch (NullPointerException expected) { 139 } 140 urlConnection.setRequestProperty("NullValue", null); // should fail silently! 141 assertNull(urlConnection.getRequestProperty("NullValue")); 142 urlConnection.addRequestProperty("AnotherNullValue", null); // should fail silently! 143 assertNull(urlConnection.getRequestProperty("AnotherNullValue")); 144 145 urlConnection.getResponseCode(); 146 RecordedRequest request = server.takeRequest(); 147 assertContains(request.getHeaders(), "D: e"); 148 assertContains(request.getHeaders(), "D: f"); 149 assertContainsNoneMatching(request.getHeaders(), "NullValue.*"); 150 assertContainsNoneMatching(request.getHeaders(), "AnotherNullValue.*"); 151 assertContainsNoneMatching(request.getHeaders(), "G:.*"); 152 assertContainsNoneMatching(request.getHeaders(), "null:.*"); 153 154 try { 155 urlConnection.addRequestProperty("N", "o"); 156 fail("Set header after connect"); 157 } catch (IllegalStateException expected) { 158 } 159 try { 160 urlConnection.setRequestProperty("P", "q"); 161 fail("Set header after connect"); 162 } catch (IllegalStateException expected) { 163 } 164 try { 165 urlConnection.getRequestProperties(); 166 fail(); 167 } catch (IllegalStateException expected) { 168 } 169 } 170 171 public void testGetRequestPropertyReturnsLastValue() throws Exception { 172 server.play(); 173 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 174 urlConnection.addRequestProperty("A", "value1"); 175 urlConnection.addRequestProperty("A", "value2"); 176 assertEquals("value2", urlConnection.getRequestProperty("A")); 177 } 178 179 public void testResponseHeaders() throws IOException, InterruptedException { 180 server.enqueue(new MockResponse() 181 .setStatus("HTTP/1.0 200 Fantastic") 182 .addHeader("A: c") 183 .addHeader("B: d") 184 .addHeader("A: e") 185 .setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8)); 186 server.play(); 187 188 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 189 assertEquals(200, urlConnection.getResponseCode()); 190 assertEquals("Fantastic", urlConnection.getResponseMessage()); 191 assertEquals("HTTP/1.0 200 Fantastic", urlConnection.getHeaderField(null)); 192 Map<String, List<String>> responseHeaders = urlConnection.getHeaderFields(); 193 assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null)); 194 assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("A"))); 195 assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("a"))); 196 try { 197 responseHeaders.put("N", Arrays.asList("o")); 198 fail("Modified an unmodifiable view."); 199 } catch (UnsupportedOperationException expected) { 200 } 201 try { 202 responseHeaders.get("A").add("f"); 203 fail("Modified an unmodifiable view."); 204 } catch (UnsupportedOperationException expected) { 205 } 206 assertEquals("A", urlConnection.getHeaderFieldKey(0)); 207 assertEquals("c", urlConnection.getHeaderField(0)); 208 assertEquals("B", urlConnection.getHeaderFieldKey(1)); 209 assertEquals("d", urlConnection.getHeaderField(1)); 210 assertEquals("A", urlConnection.getHeaderFieldKey(2)); 211 assertEquals("e", urlConnection.getHeaderField(2)); 212 } 213 214 public void testGetErrorStreamOnSuccessfulRequest() throws Exception { 215 server.enqueue(new MockResponse().setBody("A")); 216 server.play(); 217 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 218 assertNull(connection.getErrorStream()); 219 } 220 221 public void testGetErrorStreamOnUnsuccessfulRequest() throws Exception { 222 server.enqueue(new MockResponse().setResponseCode(404).setBody("A")); 223 server.play(); 224 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 225 assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE)); 226 } 227 228 // Check that if we don't read to the end of a response, the next request on the 229 // recycled connection doesn't get the unread tail of the first request's response. 230 // http://code.google.com/p/android/issues/detail?id=2939 231 public void test_2939() throws Exception { 232 MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8); 233 234 server.enqueue(response); 235 server.enqueue(response); 236 server.play(); 237 238 assertContent("ABCDE", server.getUrl("/").openConnection(), 5); 239 assertContent("ABCDE", server.getUrl("/").openConnection(), 5); 240 } 241 242 // Check that we recognize a few basic mime types by extension. 243 // http://code.google.com/p/android/issues/detail?id=10100 244 public void test_10100() throws Exception { 245 assertEquals("image/jpeg", URLConnection.guessContentTypeFromName("someFile.jpg")); 246 assertEquals("application/pdf", URLConnection.guessContentTypeFromName("stuff.pdf")); 247 } 248 249 public void testConnectionsArePooled() throws Exception { 250 MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR"); 251 252 server.enqueue(response); 253 server.enqueue(response); 254 server.enqueue(response); 255 server.play(); 256 257 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/foo").openConnection()); 258 assertEquals(0, server.takeRequest().getSequenceNumber()); 259 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/bar?baz=quux").openConnection()); 260 assertEquals(1, server.takeRequest().getSequenceNumber()); 261 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/z").openConnection()); 262 assertEquals(2, server.takeRequest().getSequenceNumber()); 263 } 264 265 public void testChunkedConnectionsArePooled() throws Exception { 266 MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5); 267 268 server.enqueue(response); 269 server.enqueue(response); 270 server.enqueue(response); 271 server.play(); 272 273 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/foo").openConnection()); 274 assertEquals(0, server.takeRequest().getSequenceNumber()); 275 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/bar?baz=quux").openConnection()); 276 assertEquals(1, server.takeRequest().getSequenceNumber()); 277 assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/z").openConnection()); 278 assertEquals(2, server.takeRequest().getSequenceNumber()); 279 } 280 281 /** 282 * Test that connections are added to the pool as soon as the response has 283 * been consumed. 284 */ 285 public void testConnectionsArePooledWithoutExplicitDisconnect() throws Exception { 286 server.enqueue(new MockResponse().setBody("ABC")); 287 server.enqueue(new MockResponse().setBody("DEF")); 288 server.play(); 289 290 URLConnection connection1 = server.getUrl("/").openConnection(); 291 assertEquals("ABC", readAscii(connection1.getInputStream(), Integer.MAX_VALUE)); 292 assertEquals(0, server.takeRequest().getSequenceNumber()); 293 URLConnection connection2 = server.getUrl("/").openConnection(); 294 assertEquals("DEF", readAscii(connection2.getInputStream(), Integer.MAX_VALUE)); 295 assertEquals(1, server.takeRequest().getSequenceNumber()); 296 } 297 298 public void testServerClosesSocket() throws Exception { 299 testServerClosesSocket(DISCONNECT_AT_END); 300 } 301 302 public void testServerShutdownInput() throws Exception { 303 testServerClosesSocket(SHUTDOWN_INPUT_AT_END); 304 } 305 306 private void testServerClosesSocket(SocketPolicy socketPolicy) throws Exception { 307 server.enqueue(new MockResponse() 308 .setBody("This connection won't pool properly") 309 .setSocketPolicy(socketPolicy)); 310 server.enqueue(new MockResponse().setBody("This comes after a busted connection")); 311 server.play(); 312 313 assertContent("This connection won't pool properly", server.getUrl("/a").openConnection()); 314 assertEquals(0, server.takeRequest().getSequenceNumber()); 315 assertContent("This comes after a busted connection", server.getUrl("/b").openConnection()); 316 // sequence number 0 means the HTTP socket connection was not reused 317 assertEquals(0, server.takeRequest().getSequenceNumber()); 318 } 319 320 public void testServerShutdownOutput() throws Exception { 321 // This test causes MockWebServer to log a "connection failed" stack trace 322 server.enqueue(new MockResponse() 323 .setBody("Output shutdown after this response") 324 .setSocketPolicy(SHUTDOWN_OUTPUT_AT_END)); 325 server.enqueue(new MockResponse().setBody("This response will fail to write")); 326 server.enqueue(new MockResponse().setBody("This comes after a busted connection")); 327 server.play(); 328 329 assertContent("Output shutdown after this response", server.getUrl("/a").openConnection()); 330 assertEquals(0, server.takeRequest().getSequenceNumber()); 331 assertContent("This comes after a busted connection", server.getUrl("/b").openConnection()); 332 assertEquals(1, server.takeRequest().getSequenceNumber()); 333 assertEquals(0, server.takeRequest().getSequenceNumber()); 334 } 335 336 public void testRetryableRequestBodyAfterBrokenConnection() throws Exception { 337 server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END)); 338 server.enqueue(new MockResponse().setBody("def")); 339 server.play(); 340 341 assertContent("abc", server.getUrl("/a").openConnection()); 342 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection(); 343 connection.setDoOutput(true); 344 OutputStream out = connection.getOutputStream(); 345 out.write(new byte[] {1, 2, 3}); 346 out.close(); 347 assertContent("def", connection); 348 } 349 350 public void testNonRetryableRequestBodyAfterBrokenConnection() throws Exception { 351 server.enqueue(new MockResponse().setBody("abc").setSocketPolicy(DISCONNECT_AT_END)); 352 server.enqueue(new MockResponse().setBody("def")); 353 server.play(); 354 355 assertContent("abc", server.getUrl("/a").openConnection()); 356 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/b").openConnection(); 357 connection.setDoOutput(true); 358 connection.setFixedLengthStreamingMode(3); 359 OutputStream out = connection.getOutputStream(); 360 out.write(new byte[] {1, 2, 3}); 361 out.close(); 362 try { 363 connection.getInputStream(); 364 fail(); 365 } catch (IOException expected) { 366 } 367 } 368 369 enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS } 370 371 public void test_chunkedUpload_byteByByte() throws Exception { 372 doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE); 373 } 374 375 public void test_chunkedUpload_smallBuffers() throws Exception { 376 doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS); 377 } 378 379 public void test_chunkedUpload_largeBuffers() throws Exception { 380 doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS); 381 } 382 383 public void test_fixedLengthUpload_byteByByte() throws Exception { 384 doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE); 385 } 386 387 public void test_fixedLengthUpload_smallBuffers() throws Exception { 388 doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS); 389 } 390 391 public void test_fixedLengthUpload_largeBuffers() throws Exception { 392 doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS); 393 } 394 395 private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception { 396 int n = 512*1024; 397 server.setBodyLimit(0); 398 server.enqueue(new MockResponse()); 399 server.play(); 400 401 HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection(); 402 conn.setDoOutput(true); 403 conn.setRequestMethod("POST"); 404 if (uploadKind == TransferKind.CHUNKED) { 405 conn.setChunkedStreamingMode(-1); 406 } else { 407 conn.setFixedLengthStreamingMode(n); 408 } 409 OutputStream out = conn.getOutputStream(); 410 if (writeKind == WriteKind.BYTE_BY_BYTE) { 411 for (int i = 0; i < n; ++i) { 412 out.write('x'); 413 } 414 } else { 415 byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64*1024]; 416 Arrays.fill(buf, (byte) 'x'); 417 for (int i = 0; i < n; i += buf.length) { 418 out.write(buf, 0, Math.min(buf.length, n - i)); 419 } 420 } 421 out.close(); 422 assertEquals(200, conn.getResponseCode()); 423 RecordedRequest request = server.takeRequest(); 424 assertEquals(n, request.getBodySize()); 425 if (uploadKind == TransferKind.CHUNKED) { 426 assertTrue(request.getChunkSizes().size() > 0); 427 } else { 428 assertTrue(request.getChunkSizes().isEmpty()); 429 } 430 } 431 432 public void testGetResponseCodeNoResponseBody() throws Exception { 433 server.enqueue(new MockResponse() 434 .addHeader("abc: def")); 435 server.play(); 436 437 URL url = server.getUrl("/"); 438 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 439 conn.setDoInput(false); 440 assertEquals("def", conn.getHeaderField("abc")); 441 assertEquals(200, conn.getResponseCode()); 442 try { 443 conn.getInputStream(); 444 fail(); 445 } catch (ProtocolException expected) { 446 } 447 } 448 449 public void testConnectViaHttps() throws IOException, InterruptedException { 450 TestSSLContext testSSLContext = TestSSLContext.create(); 451 452 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 453 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 454 server.play(); 455 456 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); 457 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 458 459 assertContent("this response comes via HTTPS", connection); 460 461 RecordedRequest request = server.takeRequest(); 462 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 463 assertEquals("TLSv1", request.getSslProtocol()); 464 } 465 466 public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException { 467 TestSSLContext testSSLContext = TestSSLContext.create(); 468 SSLSocketFactory clientSocketFactory = testSSLContext.clientContext.getSocketFactory(); 469 470 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 471 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 472 server.enqueue(new MockResponse().setBody("another response via HTTPS")); 473 server.play(); 474 475 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 476 connection.setSSLSocketFactory(clientSocketFactory); 477 assertContent("this response comes via HTTPS", connection); 478 479 connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 480 connection.setSSLSocketFactory(clientSocketFactory); 481 assertContent("another response via HTTPS", connection); 482 483 assertEquals(0, server.takeRequest().getSequenceNumber()); 484 assertEquals(1, server.takeRequest().getSequenceNumber()); 485 } 486 487 public void testConnectViaHttpsReusingConnectionsDifferentFactories() 488 throws IOException, InterruptedException { 489 TestSSLContext testSSLContext = TestSSLContext.create(); 490 491 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 492 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 493 server.enqueue(new MockResponse().setBody("another response via HTTPS")); 494 server.play(); 495 496 // install a custom SSL socket factory so the server can be authorized 497 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 498 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 499 assertContent("this response comes via HTTPS", connection); 500 501 connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 502 try { 503 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 504 fail("without an SSL socket factory, the connection should fail"); 505 } catch (SSLException expected) { 506 } 507 } 508 509 public void testConnectViaHttpsWithSSLFallback() throws IOException, InterruptedException { 510 TestSSLContext testSSLContext = TestSSLContext.create(); 511 512 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 513 server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); 514 server.enqueue(new MockResponse().setBody("this response comes via SSL")); 515 server.play(); 516 517 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); 518 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 519 520 assertContent("this response comes via SSL", connection); 521 522 RecordedRequest request = server.takeRequest(); 523 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 524 } 525 526 /** 527 * Verify that we don't retry connections on certificate verification errors. 528 * 529 * http://code.google.com/p/android/issues/detail?id=13178 530 */ 531 public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException { 532 TestSSLContext testSSLContext = TestSSLContext.create(TestKeyStore.getClientCA2(), 533 TestKeyStore.getServer()); 534 535 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 536 server.enqueue(new MockResponse()); // unused 537 server.play(); 538 539 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection(); 540 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 541 try { 542 connection.getInputStream(); 543 fail(); 544 } catch (SSLHandshakeException expected) { 545 assertTrue(expected.getCause() instanceof CertificateException); 546 } 547 assertEquals(0, server.getRequestCount()); 548 } 549 550 public void testConnectViaProxyUsingProxyArg() throws Exception { 551 testConnectViaProxy(ProxyConfig.CREATE_ARG); 552 } 553 554 public void testConnectViaProxyUsingProxySystemProperty() throws Exception { 555 testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY); 556 } 557 558 public void testConnectViaProxyUsingHttpProxySystemProperty() throws Exception { 559 testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 560 } 561 562 private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception { 563 MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy"); 564 server.enqueue(mockResponse); 565 server.play(); 566 567 URL url = new URL("http://android.com/foo"); 568 HttpURLConnection connection = proxyConfig.connect(server, url); 569 assertContent("this response comes via a proxy", connection); 570 571 RecordedRequest request = server.takeRequest(); 572 assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine()); 573 assertContains(request.getHeaders(), "Host: android.com"); 574 } 575 576 public void testContentDisagreesWithContentLengthHeader() throws IOException { 577 server.enqueue(new MockResponse() 578 .setBody("abc\r\nYOU SHOULD NOT SEE THIS") 579 .clearHeaders() 580 .addHeader("Content-Length: 3")); 581 server.play(); 582 583 assertContent("abc", server.getUrl("/").openConnection()); 584 } 585 586 public void testContentDisagreesWithChunkedHeader() throws IOException { 587 MockResponse mockResponse = new MockResponse(); 588 mockResponse.setChunkedBody("abc", 3); 589 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 590 bytesOut.write(mockResponse.getBody()); 591 bytesOut.write("\r\nYOU SHOULD NOT SEE THIS".getBytes()); 592 mockResponse.setBody(bytesOut.toByteArray()); 593 mockResponse.clearHeaders(); 594 mockResponse.addHeader("Transfer-encoding: chunked"); 595 596 server.enqueue(mockResponse); 597 server.play(); 598 599 assertContent("abc", server.getUrl("/").openConnection()); 600 } 601 602 public void testConnectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception { 603 testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY); 604 } 605 606 public void testConnectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception { 607 // https should not use http proxy 608 testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 609 } 610 611 private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception { 612 TestSSLContext testSSLContext = TestSSLContext.create(); 613 614 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 615 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 616 server.play(); 617 618 URL url = server.getUrl("/foo"); 619 HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); 620 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 621 622 assertContent("this response comes via HTTPS", connection); 623 624 RecordedRequest request = server.takeRequest(); 625 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 626 } 627 628 629 public void testConnectViaHttpProxyToHttpsUsingProxyArg() throws Exception { 630 testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG); 631 } 632 633 /** 634 * We weren't honoring all of the appropriate proxy system properties when 635 * connecting via HTTPS. http://b/3097518 636 */ 637 public void testConnectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception { 638 testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY); 639 } 640 641 public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception { 642 testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY); 643 } 644 645 /** 646 * We were verifying the wrong hostname when connecting to an HTTPS site 647 * through a proxy. http://b/3097277 648 */ 649 private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { 650 TestSSLContext testSSLContext = TestSSLContext.create(); 651 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 652 653 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 654 server.enqueue(new MockResponse() 655 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 656 .clearHeaders()); 657 server.enqueue(new MockResponse().setBody("this response comes via a secure proxy")); 658 server.play(); 659 660 URL url = new URL("https://android.com/foo"); 661 HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); 662 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 663 connection.setHostnameVerifier(hostnameVerifier); 664 665 assertContent("this response comes via a secure proxy", connection); 666 667 RecordedRequest connect = server.takeRequest(); 668 assertEquals("Connect line failure on proxy", 669 "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); 670 assertContains(connect.getHeaders(), "Host: android.com"); 671 672 RecordedRequest get = server.takeRequest(); 673 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 674 assertContains(get.getHeaders(), "Host: android.com"); 675 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 676 } 677 678 679 /** 680 * Tolerate bad https proxy response when using HttpResponseCache. http://b/6754912 681 */ 682 public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception { 683 ProxyConfig proxyConfig = ProxyConfig.PROXY_SYSTEM_PROPERTY; 684 685 TestSSLContext testSSLContext = TestSSLContext.create(); 686 687 initResponseCache(); 688 689 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 690 server.enqueue(new MockResponse() 691 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 692 .clearHeaders() 693 .setBody("bogus proxy connect response content")); // Key to reproducing b/6754912 694 server.play(); 695 696 URL url = new URL("https://android.com/foo"); 697 HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url); 698 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 699 700 try { 701 connection.connect(); 702 fail(); 703 } catch (IOException expected) { 704 // Thrown when the connect causes SSLSocket.startHandshake() to throw 705 // when it sees the "bogus proxy connect response content" 706 // instead of a ServerHello handshake message. 707 } 708 709 RecordedRequest connect = server.takeRequest(); 710 assertEquals("Connect line failure on proxy", 711 "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); 712 assertContains(connect.getHeaders(), "Host: android.com"); 713 } 714 715 private void initResponseCache() throws IOException { 716 String tmp = System.getProperty("java.io.tmpdir"); 717 File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID()); 718 cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE); 719 ResponseCache.setDefault(cache); 720 } 721 722 /** 723 * Test which headers are sent unencrypted to the HTTP proxy. 724 */ 725 public void testProxyConnectIncludesProxyHeadersOnly() 726 throws IOException, InterruptedException { 727 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 728 TestSSLContext testSSLContext = TestSSLContext.create(); 729 730 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 731 server.enqueue(new MockResponse() 732 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 733 .clearHeaders()); 734 server.enqueue(new MockResponse().setBody("encrypted response from the origin server")); 735 server.play(); 736 737 URL url = new URL("https://android.com/foo"); 738 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( 739 server.toProxyAddress()); 740 connection.addRequestProperty("Private", "Secret"); 741 connection.addRequestProperty("Proxy-Authorization", "bar"); 742 connection.addRequestProperty("User-Agent", "baz"); 743 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 744 connection.setHostnameVerifier(hostnameVerifier); 745 assertContent("encrypted response from the origin server", connection); 746 747 RecordedRequest connect = server.takeRequest(); 748 assertContainsNoneMatching(connect.getHeaders(), "Private.*"); 749 assertContains(connect.getHeaders(), "Proxy-Authorization: bar"); 750 assertContains(connect.getHeaders(), "User-Agent: baz"); 751 assertContains(connect.getHeaders(), "Host: android.com"); 752 assertContains(connect.getHeaders(), "Proxy-Connection: Keep-Alive"); 753 754 RecordedRequest get = server.takeRequest(); 755 assertContains(get.getHeaders(), "Private: Secret"); 756 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 757 } 758 759 public void testProxyAuthenticateOnConnect() throws Exception { 760 Authenticator.setDefault(new SimpleAuthenticator()); 761 TestSSLContext testSSLContext = TestSSLContext.create(); 762 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 763 server.enqueue(new MockResponse() 764 .setResponseCode(407) 765 .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); 766 server.enqueue(new MockResponse() 767 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 768 .clearHeaders()); 769 server.enqueue(new MockResponse().setBody("A")); 770 server.play(); 771 772 URL url = new URL("https://android.com/foo"); 773 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( 774 server.toProxyAddress()); 775 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 776 connection.setHostnameVerifier(new RecordingHostnameVerifier()); 777 assertContent("A", connection); 778 779 RecordedRequest connect1 = server.takeRequest(); 780 assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); 781 assertContainsNoneMatching(connect1.getHeaders(), "Proxy\\-Authorization.*"); 782 783 RecordedRequest connect2 = server.takeRequest(); 784 assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); 785 assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " 786 + SimpleAuthenticator.BASE_64_CREDENTIALS); 787 788 RecordedRequest get = server.takeRequest(); 789 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 790 assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*"); 791 } 792 793 // Don't disconnect after building a tunnel with CONNECT 794 // http://code.google.com/p/android/issues/detail?id=37221 795 public void testProxyWithConnectionClose() throws IOException { 796 TestSSLContext testSSLContext = TestSSLContext.create(); 797 server.useHttps(testSSLContext.serverContext.getSocketFactory(), true); 798 server.enqueue(new MockResponse() 799 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 800 .clearHeaders()); 801 server.enqueue(new MockResponse().setBody("this response comes via a proxy")); 802 server.play(); 803 804 URL url = new URL("https://android.com/foo"); 805 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection( 806 server.toProxyAddress()); 807 connection.setRequestProperty("Connection", "close"); 808 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 809 connection.setHostnameVerifier(new RecordingHostnameVerifier()); 810 811 assertContent("this response comes via a proxy", connection); 812 } 813 814 public void testDisconnectedConnection() throws IOException { 815 server.enqueue(new MockResponse().setBody("ABCDEFGHIJKLMNOPQR")); 816 server.play(); 817 818 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 819 InputStream in = connection.getInputStream(); 820 assertEquals('A', (char) in.read()); 821 connection.disconnect(); 822 try { 823 in.read(); 824 fail("Expected a connection closed exception"); 825 } catch (IOException expected) { 826 } 827 } 828 829 public void testDisconnectBeforeConnect() throws IOException { 830 server.enqueue(new MockResponse().setBody("A")); 831 server.play(); 832 833 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 834 connection.disconnect(); 835 836 assertContent("A", connection); 837 assertEquals(200, connection.getResponseCode()); 838 } 839 840 public void testDisconnectAfterOnlyResponseCodeCausesNoCloseGuardWarning() throws IOException { 841 CloseGuardGuard guard = new CloseGuardGuard(); 842 try { 843 server.enqueue(new MockResponse() 844 .setBody(gzip("ABCABCABC".getBytes("UTF-8"))) 845 .addHeader("Content-Encoding: gzip")); 846 server.play(); 847 848 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 849 assertEquals(200, connection.getResponseCode()); 850 connection.disconnect(); 851 connection = null; 852 assertFalse(guard.wasCloseGuardCalled()); 853 } finally { 854 guard.close(); 855 } 856 } 857 858 public static class CloseGuardGuard implements Closeable, CloseGuard.Reporter { 859 private final CloseGuard.Reporter oldReporter = CloseGuard.getReporter(); 860 861 private AtomicBoolean closeGuardCalled = new AtomicBoolean(); 862 863 public CloseGuardGuard() { 864 CloseGuard.setReporter(this); 865 } 866 867 @Override public void report(String message, Throwable allocationSite) { 868 oldReporter.report(message, allocationSite); 869 closeGuardCalled.set(true); 870 } 871 872 public boolean wasCloseGuardCalled() { 873 FinalizationTester.induceFinalization(); 874 close(); 875 return closeGuardCalled.get(); 876 } 877 878 @Override public void close() { 879 CloseGuard.setReporter(oldReporter); 880 } 881 882 } 883 884 public void testDefaultRequestProperty() throws Exception { 885 URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A"); 886 assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty")); 887 } 888 889 /** 890 * Reads {@code count} characters from the stream. If the stream is 891 * exhausted before {@code count} characters can be read, the remaining 892 * characters are returned and the stream is closed. 893 */ 894 private String readAscii(InputStream in, int count) throws IOException { 895 StringBuilder result = new StringBuilder(); 896 for (int i = 0; i < count; i++) { 897 int value = in.read(); 898 if (value == -1) { 899 in.close(); 900 break; 901 } 902 result.append((char) value); 903 } 904 return result.toString(); 905 } 906 907 public void testMarkAndResetWithContentLengthHeader() throws IOException { 908 testMarkAndReset(TransferKind.FIXED_LENGTH); 909 } 910 911 public void testMarkAndResetWithChunkedEncoding() throws IOException { 912 testMarkAndReset(TransferKind.CHUNKED); 913 } 914 915 public void testMarkAndResetWithNoLengthHeaders() throws IOException { 916 testMarkAndReset(TransferKind.END_OF_STREAM); 917 } 918 919 private void testMarkAndReset(TransferKind transferKind) throws IOException { 920 MockResponse response = new MockResponse(); 921 transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024); 922 server.enqueue(response); 923 server.enqueue(response); 924 server.play(); 925 926 InputStream in = server.getUrl("/").openConnection().getInputStream(); 927 assertFalse("This implementation claims to support mark().", in.markSupported()); 928 in.mark(5); 929 assertEquals("ABCDE", readAscii(in, 5)); 930 try { 931 in.reset(); 932 fail(); 933 } catch (IOException expected) { 934 } 935 assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE)); 936 assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", server.getUrl("/").openConnection()); 937 } 938 939 /** 940 * We've had a bug where we forget the HTTP response when we see response 941 * code 401. This causes a new HTTP request to be issued for every call into 942 * the URLConnection. 943 */ 944 public void testUnauthorizedResponseHandling() throws IOException { 945 MockResponse response = new MockResponse() 946 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 947 .setResponseCode(401) // UNAUTHORIZED 948 .setBody("Unauthorized"); 949 server.enqueue(response); 950 server.enqueue(response); 951 server.enqueue(response); 952 server.play(); 953 954 URL url = server.getUrl("/"); 955 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 956 957 assertEquals(401, conn.getResponseCode()); 958 assertEquals(401, conn.getResponseCode()); 959 assertEquals(401, conn.getResponseCode()); 960 assertEquals(1, server.getRequestCount()); 961 } 962 963 public void testNonHexChunkSize() throws IOException { 964 server.enqueue(new MockResponse() 965 .setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n") 966 .clearHeaders() 967 .addHeader("Transfer-encoding: chunked")); 968 server.play(); 969 970 URLConnection connection = server.getUrl("/").openConnection(); 971 try { 972 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 973 fail(); 974 } catch (IOException e) { 975 } 976 } 977 978 public void testMissingChunkBody() throws IOException { 979 server.enqueue(new MockResponse() 980 .setBody("5") 981 .clearHeaders() 982 .addHeader("Transfer-encoding: chunked") 983 .setSocketPolicy(DISCONNECT_AT_END)); 984 server.play(); 985 986 URLConnection connection = server.getUrl("/").openConnection(); 987 try { 988 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 989 fail(); 990 } catch (IOException e) { 991 } 992 } 993 994 /** 995 * This test checks whether connections are gzipped by default. This 996 * behavior in not required by the API, so a failure of this test does not 997 * imply a bug in the implementation. 998 */ 999 public void testGzipEncodingEnabledByDefault() throws IOException, InterruptedException { 1000 server.enqueue(new MockResponse() 1001 .setBody(gzip("ABCABCABC".getBytes("UTF-8"))) 1002 .addHeader("Content-Encoding: gzip")); 1003 server.play(); 1004 1005 URLConnection connection = server.getUrl("/").openConnection(); 1006 assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1007 assertNull(connection.getContentEncoding()); 1008 assertEquals(-1, connection.getContentLength()); 1009 1010 RecordedRequest request = server.takeRequest(); 1011 assertContains(request.getHeaders(), "Accept-Encoding: gzip"); 1012 } 1013 1014 public void testClientConfiguredGzipContentEncoding() throws Exception { 1015 byte[] bodyBytes = gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8")); 1016 server.enqueue(new MockResponse() 1017 .setBody(bodyBytes) 1018 .addHeader("Content-Encoding: gzip") 1019 .addHeader("Content-Length: " + bodyBytes.length)); 1020 server.play(); 1021 1022 URLConnection connection = server.getUrl("/").openConnection(); 1023 connection.addRequestProperty("Accept-Encoding", "gzip"); 1024 InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream()); 1025 assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE)); 1026 assertEquals(bodyBytes.length, connection.getContentLength()); 1027 1028 RecordedRequest request = server.takeRequest(); 1029 assertContains(request.getHeaders(), "Accept-Encoding: gzip"); 1030 } 1031 1032 public void testGzipAndConnectionReuseWithFixedLength() throws Exception { 1033 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH); 1034 } 1035 1036 public void testGzipAndConnectionReuseWithChunkedEncoding() throws Exception { 1037 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED); 1038 } 1039 1040 public void testClientConfiguredCustomContentEncoding() throws Exception { 1041 server.enqueue(new MockResponse() 1042 .setBody("ABCDE") 1043 .addHeader("Content-Encoding: custom")); 1044 server.play(); 1045 1046 URLConnection connection = server.getUrl("/").openConnection(); 1047 connection.addRequestProperty("Accept-Encoding", "custom"); 1048 assertEquals("ABCDE", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1049 1050 RecordedRequest request = server.takeRequest(); 1051 assertContains(request.getHeaders(), "Accept-Encoding: custom"); 1052 } 1053 1054 /** 1055 * Test a bug where gzip input streams weren't exhausting the input stream, 1056 * which corrupted the request that followed. 1057 * http://code.google.com/p/android/issues/detail?id=7059 1058 */ 1059 private void testClientConfiguredGzipContentEncodingAndConnectionReuse( 1060 TransferKind transferKind) throws Exception { 1061 MockResponse responseOne = new MockResponse(); 1062 responseOne.addHeader("Content-Encoding: gzip"); 1063 transferKind.setBody(responseOne, gzip("one (gzipped)".getBytes("UTF-8")), 5); 1064 server.enqueue(responseOne); 1065 MockResponse responseTwo = new MockResponse(); 1066 transferKind.setBody(responseTwo, "two (identity)", 5); 1067 server.enqueue(responseTwo); 1068 server.play(); 1069 1070 URLConnection connection = server.getUrl("/").openConnection(); 1071 connection.addRequestProperty("Accept-Encoding", "gzip"); 1072 InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream()); 1073 assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE)); 1074 assertEquals(0, server.takeRequest().getSequenceNumber()); 1075 1076 connection = server.getUrl("/").openConnection(); 1077 assertEquals("two (identity)", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1078 assertEquals(1, server.takeRequest().getSequenceNumber()); 1079 } 1080 1081 /** 1082 * Test that HEAD requests don't have a body regardless of the response 1083 * headers. http://code.google.com/p/android/issues/detail?id=24672 1084 */ 1085 public void testHeadAndContentLength() throws Exception { 1086 server.enqueue(new MockResponse() 1087 .clearHeaders() 1088 .addHeader("Content-Length: 100")); 1089 server.enqueue(new MockResponse().setBody("A")); 1090 server.play(); 1091 1092 HttpURLConnection connection1 = (HttpURLConnection) server.getUrl("/").openConnection(); 1093 connection1.setRequestMethod("HEAD"); 1094 assertEquals("100", connection1.getHeaderField("Content-Length")); 1095 assertContent("", connection1); 1096 1097 HttpURLConnection connection2 = (HttpURLConnection) server.getUrl("/").openConnection(); 1098 assertEquals("A", readAscii(connection2.getInputStream(), Integer.MAX_VALUE)); 1099 1100 assertEquals(0, server.takeRequest().getSequenceNumber()); 1101 assertEquals(1, server.takeRequest().getSequenceNumber()); 1102 } 1103 1104 /** 1105 * Obnoxiously test that the chunk sizes transmitted exactly equal the 1106 * requested data+chunk header size. Although setChunkedStreamingMode() 1107 * isn't specific about whether the size applies to the data or the 1108 * complete chunk, the RI interprets it as a complete chunk. 1109 */ 1110 public void testSetChunkedStreamingMode() throws IOException, InterruptedException { 1111 server.enqueue(new MockResponse()); 1112 server.play(); 1113 1114 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 1115 urlConnection.setChunkedStreamingMode(8); 1116 urlConnection.setDoOutput(true); 1117 OutputStream outputStream = urlConnection.getOutputStream(); 1118 outputStream.write("ABCDEFGHIJKLMNOPQ".getBytes("US-ASCII")); 1119 assertEquals(200, urlConnection.getResponseCode()); 1120 1121 RecordedRequest request = server.takeRequest(); 1122 assertEquals("ABCDEFGHIJKLMNOPQ", new String(request.getBody(), "US-ASCII")); 1123 assertEquals(Arrays.asList(3, 3, 3, 3, 3, 2), request.getChunkSizes()); 1124 } 1125 1126 public void testAuthenticateWithFixedLengthStreaming() throws Exception { 1127 testAuthenticateWithStreamingPost(StreamingMode.FIXED_LENGTH); 1128 } 1129 1130 public void testAuthenticateWithChunkedStreaming() throws Exception { 1131 testAuthenticateWithStreamingPost(StreamingMode.CHUNKED); 1132 } 1133 1134 private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) throws Exception { 1135 MockResponse pleaseAuthenticate = new MockResponse() 1136 .setResponseCode(401) 1137 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1138 .setBody("Please authenticate."); 1139 server.enqueue(pleaseAuthenticate); 1140 server.play(); 1141 1142 Authenticator.setDefault(new SimpleAuthenticator()); 1143 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1144 connection.setDoOutput(true); 1145 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1146 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1147 connection.setFixedLengthStreamingMode(requestBody.length); 1148 } else if (streamingMode == StreamingMode.CHUNKED) { 1149 connection.setChunkedStreamingMode(0); 1150 } 1151 OutputStream outputStream = connection.getOutputStream(); 1152 outputStream.write(requestBody); 1153 outputStream.close(); 1154 try { 1155 connection.getInputStream(); 1156 fail(); 1157 } catch (HttpRetryException expected) { 1158 } 1159 1160 // no authorization header for the request... 1161 RecordedRequest request = server.takeRequest(); 1162 assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*"); 1163 assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); 1164 } 1165 1166 public void testSetValidRequestMethod() throws Exception { 1167 server.play(); 1168 assertValidRequestMethod("GET"); 1169 assertValidRequestMethod("DELETE"); 1170 assertValidRequestMethod("HEAD"); 1171 assertValidRequestMethod("OPTIONS"); 1172 assertValidRequestMethod("POST"); 1173 assertValidRequestMethod("PUT"); 1174 assertValidRequestMethod("TRACE"); 1175 } 1176 1177 private void assertValidRequestMethod(String requestMethod) throws Exception { 1178 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1179 connection.setRequestMethod(requestMethod); 1180 assertEquals(requestMethod, connection.getRequestMethod()); 1181 } 1182 1183 public void testSetInvalidRequestMethodLowercase() throws Exception { 1184 server.play(); 1185 assertInvalidRequestMethod("get"); 1186 } 1187 1188 public void testSetInvalidRequestMethodConnect() throws Exception { 1189 server.play(); 1190 assertInvalidRequestMethod("CONNECT"); 1191 } 1192 1193 private void assertInvalidRequestMethod(String requestMethod) throws Exception { 1194 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1195 try { 1196 connection.setRequestMethod(requestMethod); 1197 fail(); 1198 } catch (ProtocolException expected) { 1199 } 1200 } 1201 1202 public void testCannotSetNegativeFixedLengthStreamingMode() throws Exception { 1203 server.play(); 1204 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1205 try { 1206 connection.setFixedLengthStreamingMode(-2); 1207 fail(); 1208 } catch (IllegalArgumentException expected) { 1209 } 1210 } 1211 1212 public void testCanSetNegativeChunkedStreamingMode() throws Exception { 1213 server.play(); 1214 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1215 connection.setChunkedStreamingMode(-2); 1216 } 1217 1218 public void testCannotSetFixedLengthStreamingModeAfterConnect() throws Exception { 1219 server.enqueue(new MockResponse().setBody("A")); 1220 server.play(); 1221 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1222 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1223 try { 1224 connection.setFixedLengthStreamingMode(1); 1225 fail(); 1226 } catch (IllegalStateException expected) { 1227 } 1228 } 1229 1230 public void testCannotSetChunkedStreamingModeAfterConnect() throws Exception { 1231 server.enqueue(new MockResponse().setBody("A")); 1232 server.play(); 1233 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1234 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1235 try { 1236 connection.setChunkedStreamingMode(1); 1237 fail(); 1238 } catch (IllegalStateException expected) { 1239 } 1240 } 1241 1242 public void testCannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception { 1243 server.play(); 1244 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1245 connection.setChunkedStreamingMode(1); 1246 try { 1247 connection.setFixedLengthStreamingMode(1); 1248 fail(); 1249 } catch (IllegalStateException expected) { 1250 } 1251 } 1252 1253 public void testCannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception { 1254 server.play(); 1255 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1256 connection.setFixedLengthStreamingMode(1); 1257 try { 1258 connection.setChunkedStreamingMode(1); 1259 fail(); 1260 } catch (IllegalStateException expected) { 1261 } 1262 } 1263 1264 public void testSecureFixedLengthStreaming() throws Exception { 1265 testSecureStreamingPost(StreamingMode.FIXED_LENGTH); 1266 } 1267 1268 public void testSecureChunkedStreaming() throws Exception { 1269 testSecureStreamingPost(StreamingMode.CHUNKED); 1270 } 1271 1272 /** 1273 * Users have reported problems using HTTPS with streaming request bodies. 1274 * http://code.google.com/p/android/issues/detail?id=12860 1275 */ 1276 private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception { 1277 TestSSLContext testSSLContext = TestSSLContext.create(); 1278 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1279 server.enqueue(new MockResponse().setBody("Success!")); 1280 server.play(); 1281 1282 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 1283 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 1284 connection.setDoOutput(true); 1285 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1286 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1287 connection.setFixedLengthStreamingMode(requestBody.length); 1288 } else if (streamingMode == StreamingMode.CHUNKED) { 1289 connection.setChunkedStreamingMode(0); 1290 } 1291 OutputStream outputStream = connection.getOutputStream(); 1292 outputStream.write(requestBody); 1293 outputStream.close(); 1294 assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1295 1296 RecordedRequest request = server.takeRequest(); 1297 assertEquals("POST / HTTP/1.1", request.getRequestLine()); 1298 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1299 assertEquals(Collections.<Integer>emptyList(), request.getChunkSizes()); 1300 } else if (streamingMode == StreamingMode.CHUNKED) { 1301 assertEquals(Arrays.asList(4), request.getChunkSizes()); 1302 } 1303 assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); 1304 } 1305 1306 enum StreamingMode { 1307 FIXED_LENGTH, CHUNKED 1308 } 1309 1310 public void testAuthenticateWithPost() throws Exception { 1311 MockResponse pleaseAuthenticate = new MockResponse() 1312 .setResponseCode(401) 1313 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1314 .setBody("Please authenticate."); 1315 // fail auth three times... 1316 server.enqueue(pleaseAuthenticate); 1317 server.enqueue(pleaseAuthenticate); 1318 server.enqueue(pleaseAuthenticate); 1319 // ...then succeed the fourth time 1320 server.enqueue(new MockResponse().setBody("Successful auth!")); 1321 server.play(); 1322 1323 Authenticator.setDefault(new SimpleAuthenticator()); 1324 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1325 connection.setDoOutput(true); 1326 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1327 OutputStream outputStream = connection.getOutputStream(); 1328 outputStream.write(requestBody); 1329 outputStream.close(); 1330 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1331 1332 // no authorization header for the first request... 1333 RecordedRequest request = server.takeRequest(); 1334 assertContainsNoneMatching(request.getHeaders(), "Authorization: .*"); 1335 1336 // ...but the three requests that follow include an authorization header 1337 for (int i = 0; i < 3; i++) { 1338 request = server.takeRequest(); 1339 assertEquals("POST / HTTP/1.1", request.getRequestLine()); 1340 assertContains(request.getHeaders(), "Authorization: Basic " 1341 + SimpleAuthenticator.BASE_64_CREDENTIALS); 1342 assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody())); 1343 } 1344 } 1345 1346 public void testAuthenticateWithGet() throws Exception { 1347 MockResponse pleaseAuthenticate = new MockResponse() 1348 .setResponseCode(401) 1349 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1350 .setBody("Please authenticate."); 1351 // fail auth three times... 1352 server.enqueue(pleaseAuthenticate); 1353 server.enqueue(pleaseAuthenticate); 1354 server.enqueue(pleaseAuthenticate); 1355 // ...then succeed the fourth time 1356 server.enqueue(new MockResponse().setBody("Successful auth!")); 1357 server.play(); 1358 1359 SimpleAuthenticator authenticator = new SimpleAuthenticator(); 1360 Authenticator.setDefault(authenticator); 1361 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1362 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1363 assertEquals(Authenticator.RequestorType.SERVER, authenticator.requestorType); 1364 assertEquals(server.getPort(), authenticator.requestingPort); 1365 assertEquals(InetAddress.getByName(server.getHostName()), authenticator.requestingSite); 1366 assertEquals("protected area", authenticator.requestingPrompt); 1367 assertEquals("http", authenticator.requestingProtocol); 1368 assertEquals("Basic", authenticator.requestingScheme); 1369 1370 // no authorization header for the first request... 1371 RecordedRequest request = server.takeRequest(); 1372 assertContainsNoneMatching(request.getHeaders(), "Authorization: .*"); 1373 1374 // ...but the three requests that follow requests include an authorization header 1375 for (int i = 0; i < 3; i++) { 1376 request = server.takeRequest(); 1377 assertEquals("GET / HTTP/1.1", request.getRequestLine()); 1378 assertContains(request.getHeaders(), "Authorization: Basic " 1379 + SimpleAuthenticator.BASE_64_CREDENTIALS); 1380 } 1381 } 1382 1383 // http://code.google.com/p/android/issues/detail?id=19081 1384 public void testAuthenticateWithCommaSeparatedAuthenticationMethods() throws Exception { 1385 server.enqueue(new MockResponse() 1386 .setResponseCode(401) 1387 .addHeader("WWW-Authenticate: Scheme1 realm=\"a\", Scheme2 realm=\"b\", " 1388 + "Scheme3 realm=\"c\"") 1389 .setBody("Please authenticate.")); 1390 server.enqueue(new MockResponse().setBody("Successful auth!")); 1391 server.play(); 1392 1393 SimpleAuthenticator authenticator = new SimpleAuthenticator(); 1394 authenticator.expectedPrompt = "b"; 1395 Authenticator.setDefault(authenticator); 1396 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1397 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1398 1399 assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*"); 1400 assertContains(server.takeRequest().getHeaders(), 1401 "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS); 1402 assertEquals("Scheme2", authenticator.requestingScheme); 1403 } 1404 1405 public void testAuthenticateWithMultipleAuthenticationHeaders() throws Exception { 1406 server.enqueue(new MockResponse() 1407 .setResponseCode(401) 1408 .addHeader("WWW-Authenticate: Scheme1 realm=\"a\"") 1409 .addHeader("WWW-Authenticate: Scheme2 realm=\"b\"") 1410 .addHeader("WWW-Authenticate: Scheme3 realm=\"c\"") 1411 .setBody("Please authenticate.")); 1412 server.enqueue(new MockResponse().setBody("Successful auth!")); 1413 server.play(); 1414 1415 SimpleAuthenticator authenticator = new SimpleAuthenticator(); 1416 authenticator.expectedPrompt = "b"; 1417 Authenticator.setDefault(authenticator); 1418 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1419 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1420 1421 assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*"); 1422 assertContains(server.takeRequest().getHeaders(), 1423 "Authorization: Scheme2 " + SimpleAuthenticator.BASE_64_CREDENTIALS); 1424 assertEquals("Scheme2", authenticator.requestingScheme); 1425 } 1426 1427 public void testRedirectedWithChunkedEncoding() throws Exception { 1428 testRedirected(TransferKind.CHUNKED, true); 1429 } 1430 1431 public void testRedirectedWithContentLengthHeader() throws Exception { 1432 testRedirected(TransferKind.FIXED_LENGTH, true); 1433 } 1434 1435 public void testRedirectedWithNoLengthHeaders() throws Exception { 1436 testRedirected(TransferKind.END_OF_STREAM, false); 1437 } 1438 1439 private void testRedirected(TransferKind transferKind, boolean reuse) throws Exception { 1440 MockResponse response = new MockResponse() 1441 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1442 .addHeader("Location: /foo"); 1443 transferKind.setBody(response, "This page has moved!", 10); 1444 server.enqueue(response); 1445 server.enqueue(new MockResponse().setBody("This is the new location!")); 1446 server.play(); 1447 1448 URLConnection connection = server.getUrl("/").openConnection(); 1449 assertEquals("This is the new location!", 1450 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1451 1452 RecordedRequest first = server.takeRequest(); 1453 assertEquals("GET / HTTP/1.1", first.getRequestLine()); 1454 RecordedRequest retry = server.takeRequest(); 1455 assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); 1456 if (reuse) { 1457 assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); 1458 } 1459 } 1460 1461 public void testRedirectedOnHttps() throws IOException, InterruptedException { 1462 TestSSLContext testSSLContext = TestSSLContext.create(); 1463 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1464 server.enqueue(new MockResponse() 1465 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1466 .addHeader("Location: /foo") 1467 .setBody("This page has moved!")); 1468 server.enqueue(new MockResponse().setBody("This is the new location!")); 1469 server.play(); 1470 1471 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 1472 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 1473 assertEquals("This is the new location!", 1474 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1475 1476 RecordedRequest first = server.takeRequest(); 1477 assertEquals("GET / HTTP/1.1", first.getRequestLine()); 1478 RecordedRequest retry = server.takeRequest(); 1479 assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); 1480 assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); 1481 } 1482 1483 public void testNotRedirectedFromHttpsToHttp() throws IOException, InterruptedException { 1484 TestSSLContext testSSLContext = TestSSLContext.create(); 1485 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1486 server.enqueue(new MockResponse() 1487 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1488 .addHeader("Location: http://anyhost/foo") 1489 .setBody("This page has moved!")); 1490 server.play(); 1491 1492 HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection(); 1493 connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory()); 1494 assertEquals("This page has moved!", 1495 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1496 } 1497 1498 public void testNotRedirectedFromHttpToHttps() throws IOException, InterruptedException { 1499 server.enqueue(new MockResponse() 1500 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1501 .addHeader("Location: https://anyhost/foo") 1502 .setBody("This page has moved!")); 1503 server.play(); 1504 1505 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1506 assertEquals("This page has moved!", 1507 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1508 } 1509 1510 public void testRedirectToAnotherOriginServer() throws Exception { 1511 MockWebServer server2 = new MockWebServer(); 1512 server2.enqueue(new MockResponse().setBody("This is the 2nd server!")); 1513 server2.play(); 1514 1515 server.enqueue(new MockResponse() 1516 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1517 .addHeader("Location: " + server2.getUrl("/").toString()) 1518 .setBody("This page has moved!")); 1519 server.enqueue(new MockResponse().setBody("This is the first server again!")); 1520 server.play(); 1521 1522 URLConnection connection = server.getUrl("/").openConnection(); 1523 assertEquals("This is the 2nd server!", 1524 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1525 assertEquals(server2.getUrl("/"), connection.getURL()); 1526 1527 // make sure the first server was careful to recycle the connection 1528 assertEquals("This is the first server again!", 1529 readAscii(server.getUrl("/").openStream(), Integer.MAX_VALUE)); 1530 1531 RecordedRequest first = server.takeRequest(); 1532 assertContains(first.getHeaders(), "Host: " + hostName + ":" + server.getPort()); 1533 RecordedRequest second = server2.takeRequest(); 1534 assertContains(second.getHeaders(), "Host: " + hostName + ":" + server2.getPort()); 1535 RecordedRequest third = server.takeRequest(); 1536 assertEquals("Expected connection reuse", 1, third.getSequenceNumber()); 1537 1538 server2.shutdown(); 1539 } 1540 1541 public void testResponse300MultipleChoiceWithPost() throws Exception { 1542 // Chrome doesn't follow the redirect, but Firefox and the RI both do 1543 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE); 1544 } 1545 1546 public void testResponse301MovedPermanentlyWithPost() throws Exception { 1547 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM); 1548 } 1549 1550 public void testResponse302MovedTemporarilyWithPost() throws Exception { 1551 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP); 1552 } 1553 1554 public void testResponse303SeeOtherWithPost() throws Exception { 1555 testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER); 1556 } 1557 1558 private void testResponseRedirectedWithPost(int redirectCode) throws Exception { 1559 server.enqueue(new MockResponse() 1560 .setResponseCode(redirectCode) 1561 .addHeader("Location: /page2") 1562 .setBody("This page has moved!")); 1563 server.enqueue(new MockResponse().setBody("Page 2")); 1564 server.play(); 1565 1566 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/page1").openConnection(); 1567 connection.setDoOutput(true); 1568 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1569 OutputStream outputStream = connection.getOutputStream(); 1570 outputStream.write(requestBody); 1571 outputStream.close(); 1572 assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1573 assertTrue(connection.getDoOutput()); 1574 1575 RecordedRequest page1 = server.takeRequest(); 1576 assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine()); 1577 assertEquals(Arrays.toString(requestBody), Arrays.toString(page1.getBody())); 1578 1579 RecordedRequest page2 = server.takeRequest(); 1580 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 1581 } 1582 1583 public void testResponse305UseProxy() throws Exception { 1584 server.play(); 1585 server.enqueue(new MockResponse() 1586 .setResponseCode(HttpURLConnection.HTTP_USE_PROXY) 1587 .addHeader("Location: " + server.getUrl("/")) 1588 .setBody("This page has moved!")); 1589 server.enqueue(new MockResponse().setBody("Proxy Response")); 1590 1591 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/foo").openConnection(); 1592 // Fails on the RI, which gets "Proxy Response" 1593 assertEquals("This page has moved!", 1594 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1595 1596 RecordedRequest page1 = server.takeRequest(); 1597 assertEquals("GET /foo HTTP/1.1", page1.getRequestLine()); 1598 assertEquals(1, server.getRequestCount()); 1599 } 1600 1601 public void testHttpsWithCustomTrustManager() throws Exception { 1602 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 1603 RecordingTrustManager trustManager = new RecordingTrustManager(); 1604 SSLContext sc = SSLContext.getInstance("TLS"); 1605 sc.init(null, new TrustManager[] { trustManager }, new java.security.SecureRandom()); 1606 1607 HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); 1608 HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); 1609 SSLSocketFactory defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 1610 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 1611 try { 1612 TestSSLContext testSSLContext = TestSSLContext.create(); 1613 server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); 1614 server.enqueue(new MockResponse().setBody("ABC")); 1615 server.enqueue(new MockResponse().setBody("DEF")); 1616 server.enqueue(new MockResponse().setBody("GHI")); 1617 server.play(); 1618 1619 URL url = server.getUrl("/"); 1620 assertEquals("ABC", readAscii(url.openStream(), Integer.MAX_VALUE)); 1621 assertEquals("DEF", readAscii(url.openStream(), Integer.MAX_VALUE)); 1622 assertEquals("GHI", readAscii(url.openStream(), Integer.MAX_VALUE)); 1623 1624 assertEquals(Arrays.asList("verify " + hostName), hostnameVerifier.calls); 1625 assertEquals(Arrays.asList("checkServerTrusted [" 1626 + "CN=" + hostName + " 1, " 1627 + "CN=Test Intermediate Certificate Authority 1, " 1628 + "CN=Test Root Certificate Authority 1" 1629 + "] RSA"), 1630 trustManager.calls); 1631 } finally { 1632 HttpsURLConnection.setDefaultHostnameVerifier(defaultHostnameVerifier); 1633 HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory); 1634 } 1635 } 1636 1637 /** 1638 * Test that the timeout period is honored. The timeout may be doubled! 1639 * HttpURLConnection will wait the full timeout for each of the server's IP 1640 * addresses. This is typically one IPv4 address and one IPv6 address. 1641 */ 1642 public void testConnectTimeouts() throws IOException { 1643 StuckServer ss = new StuckServer(false); 1644 int serverPort = ss.getLocalPort(); 1645 URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection(); 1646 int timeout = 1000; 1647 urlConnection.setConnectTimeout(timeout); 1648 long start = System.currentTimeMillis(); 1649 try { 1650 urlConnection.getInputStream(); 1651 fail(); 1652 } catch (SocketTimeoutException expected) { 1653 long elapsed = System.currentTimeMillis() - start; 1654 int attempts = InetAddress.getAllByName("localhost").length; // one per IP address 1655 assertTrue("timeout=" +timeout + ", elapsed=" + elapsed + ", attempts=" + attempts, 1656 Math.abs((attempts * timeout) - elapsed) < 500); 1657 } finally { 1658 ss.close(); 1659 } 1660 } 1661 1662 public void testReadTimeouts() throws IOException { 1663 /* 1664 * This relies on the fact that MockWebServer doesn't close the 1665 * connection after a response has been sent. This causes the client to 1666 * try to read more bytes than are sent, which results in a timeout. 1667 */ 1668 MockResponse timeout = new MockResponse() 1669 .setBody("ABC") 1670 .clearHeaders() 1671 .addHeader("Content-Length: 4"); 1672 server.enqueue(timeout); 1673 server.enqueue(new MockResponse().setBody("unused")); // to keep the server alive 1674 server.play(); 1675 1676 URLConnection urlConnection = server.getUrl("/").openConnection(); 1677 urlConnection.setReadTimeout(1000); 1678 InputStream in = urlConnection.getInputStream(); 1679 assertEquals('A', in.read()); 1680 assertEquals('B', in.read()); 1681 assertEquals('C', in.read()); 1682 try { 1683 in.read(); // if Content-Length was accurate, this would return -1 immediately 1684 fail(); 1685 } catch (SocketTimeoutException expected) { 1686 } 1687 } 1688 1689 public void testSetChunkedEncodingAsRequestProperty() throws IOException, InterruptedException { 1690 server.enqueue(new MockResponse()); 1691 server.play(); 1692 1693 HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); 1694 urlConnection.setRequestProperty("Transfer-encoding", "chunked"); 1695 urlConnection.setDoOutput(true); 1696 urlConnection.getOutputStream().write("ABC".getBytes("UTF-8")); 1697 assertEquals(200, urlConnection.getResponseCode()); 1698 1699 RecordedRequest request = server.takeRequest(); 1700 assertEquals("ABC", new String(request.getBody(), "UTF-8")); 1701 } 1702 1703 public void testConnectionCloseInRequest() throws IOException, InterruptedException { 1704 server.enqueue(new MockResponse()); // server doesn't honor the connection: close header! 1705 server.enqueue(new MockResponse()); 1706 server.play(); 1707 1708 HttpURLConnection a = (HttpURLConnection) server.getUrl("/").openConnection(); 1709 a.setRequestProperty("Connection", "close"); 1710 assertEquals(200, a.getResponseCode()); 1711 1712 HttpURLConnection b = (HttpURLConnection) server.getUrl("/").openConnection(); 1713 assertEquals(200, b.getResponseCode()); 1714 1715 assertEquals(0, server.takeRequest().getSequenceNumber()); 1716 assertEquals("When connection: close is used, each request should get its own connection", 1717 0, server.takeRequest().getSequenceNumber()); 1718 } 1719 1720 public void testConnectionCloseInResponse() throws IOException, InterruptedException { 1721 server.enqueue(new MockResponse().addHeader("Connection: close")); 1722 server.enqueue(new MockResponse()); 1723 server.play(); 1724 1725 HttpURLConnection a = (HttpURLConnection) server.getUrl("/").openConnection(); 1726 assertEquals(200, a.getResponseCode()); 1727 1728 HttpURLConnection b = (HttpURLConnection) server.getUrl("/").openConnection(); 1729 assertEquals(200, b.getResponseCode()); 1730 1731 assertEquals(0, server.takeRequest().getSequenceNumber()); 1732 assertEquals("When connection: close is used, each request should get its own connection", 1733 0, server.takeRequest().getSequenceNumber()); 1734 } 1735 1736 public void testConnectionCloseWithRedirect() throws IOException, InterruptedException { 1737 MockResponse response = new MockResponse() 1738 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1739 .addHeader("Location: /foo") 1740 .addHeader("Connection: close"); 1741 server.enqueue(response); 1742 server.enqueue(new MockResponse().setBody("This is the new location!")); 1743 server.play(); 1744 1745 URLConnection connection = server.getUrl("/").openConnection(); 1746 assertEquals("This is the new location!", 1747 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1748 1749 assertEquals(0, server.takeRequest().getSequenceNumber()); 1750 assertEquals("When connection: close is used, each request should get its own connection", 1751 0, server.takeRequest().getSequenceNumber()); 1752 } 1753 1754 public void testResponseCodeDisagreesWithHeaders() throws IOException, InterruptedException { 1755 server.enqueue(new MockResponse() 1756 .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT) 1757 .setBody("This body is not allowed!")); 1758 server.play(); 1759 1760 URLConnection connection = server.getUrl("/").openConnection(); 1761 assertEquals("This body is not allowed!", 1762 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1763 } 1764 1765 public void testSingleByteReadIsSigned() throws IOException { 1766 server.enqueue(new MockResponse().setBody(new byte[] { -2, -1 })); 1767 server.play(); 1768 1769 URLConnection connection = server.getUrl("/").openConnection(); 1770 InputStream in = connection.getInputStream(); 1771 assertEquals(254, in.read()); 1772 assertEquals(255, in.read()); 1773 assertEquals(-1, in.read()); 1774 } 1775 1776 public void testFlushAfterStreamTransmittedWithChunkedEncoding() throws IOException { 1777 testFlushAfterStreamTransmitted(TransferKind.CHUNKED); 1778 } 1779 1780 public void testFlushAfterStreamTransmittedWithFixedLength() throws IOException { 1781 testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH); 1782 } 1783 1784 public void testFlushAfterStreamTransmittedWithNoLengthHeaders() throws IOException { 1785 testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM); 1786 } 1787 1788 /** 1789 * We explicitly permit apps to close the upload stream even after it has 1790 * been transmitted. We also permit flush so that buffered streams can 1791 * do a no-op flush when they are closed. http://b/3038470 1792 */ 1793 private void testFlushAfterStreamTransmitted(TransferKind transferKind) throws IOException { 1794 server.enqueue(new MockResponse().setBody("abc")); 1795 server.play(); 1796 1797 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1798 connection.setDoOutput(true); 1799 byte[] upload = "def".getBytes("UTF-8"); 1800 1801 if (transferKind == TransferKind.CHUNKED) { 1802 connection.setChunkedStreamingMode(0); 1803 } else if (transferKind == TransferKind.FIXED_LENGTH) { 1804 connection.setFixedLengthStreamingMode(upload.length); 1805 } 1806 1807 OutputStream out = connection.getOutputStream(); 1808 out.write(upload); 1809 assertEquals("abc", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1810 1811 out.flush(); // dubious but permitted 1812 try { 1813 out.write("ghi".getBytes("UTF-8")); 1814 fail(); 1815 } catch (IOException expected) { 1816 } 1817 } 1818 1819 public void testGetHeadersThrows() throws IOException { 1820 server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); 1821 server.play(); 1822 1823 HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection(); 1824 try { 1825 connection.getInputStream(); 1826 fail(); 1827 } catch (IOException expected) { 1828 } 1829 1830 try { 1831 connection.getInputStream(); 1832 fail(); 1833 } catch (IOException expected) { 1834 } 1835 } 1836 1837 public void testGetKeepAlive() throws Exception { 1838 MockWebServer server = new MockWebServer(); 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