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