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