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