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