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