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 17 package com.squareup.okhttp; 18 19 import com.squareup.okhttp.internal.Internal; 20 import com.squareup.okhttp.internal.Platform; 21 import com.squareup.okhttp.internal.RecordingAuthenticator; 22 import com.squareup.okhttp.internal.RecordingOkAuthenticator; 23 import com.squareup.okhttp.internal.SingleInetAddressDns; 24 import com.squareup.okhttp.internal.SslContextBuilder; 25 import com.squareup.okhttp.internal.Util; 26 import com.squareup.okhttp.internal.Version; 27 import com.squareup.okhttp.mockwebserver.MockResponse; 28 import com.squareup.okhttp.mockwebserver.MockWebServer; 29 import com.squareup.okhttp.mockwebserver.RecordedRequest; 30 import com.squareup.okhttp.mockwebserver.SocketPolicy; 31 import com.squareup.okhttp.mockwebserver.SocketShutdownListener; 32 import com.squareup.okhttp.testing.RecordingHostnameVerifier; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.OutputStream; 36 import java.net.Authenticator; 37 import java.net.ConnectException; 38 import java.net.HttpRetryException; 39 import java.net.HttpURLConnection; 40 import java.net.InetAddress; 41 import java.net.ProtocolException; 42 import java.net.Proxy; 43 import java.net.ProxySelector; 44 import java.net.ServerSocket; 45 import java.net.Socket; 46 import java.net.SocketAddress; 47 import java.net.SocketTimeoutException; 48 import java.net.URI; 49 import java.net.URL; 50 import java.net.URLConnection; 51 import java.net.UnknownHostException; 52 import java.security.SecureRandom; 53 import java.security.cert.CertificateException; 54 import java.security.cert.X509Certificate; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.Collections; 58 import java.util.EnumSet; 59 import java.util.LinkedHashSet; 60 import java.util.List; 61 import java.util.Map; 62 import java.util.Random; 63 import java.util.Set; 64 import java.util.concurrent.TimeUnit; 65 import java.util.zip.GZIPInputStream; 66 import javax.net.ServerSocketFactory; 67 import javax.net.SocketFactory; 68 import javax.net.ssl.HttpsURLConnection; 69 import javax.net.ssl.SSLContext; 70 import javax.net.ssl.SSLException; 71 import javax.net.ssl.SSLHandshakeException; 72 import javax.net.ssl.SSLSocketFactory; 73 import javax.net.ssl.TrustManager; 74 import javax.net.ssl.X509TrustManager; 75 import okio.Buffer; 76 import okio.BufferedSink; 77 import okio.GzipSink; 78 import okio.Okio; 79 import org.junit.After; 80 import org.junit.Before; 81 import org.junit.Ignore; 82 import org.junit.Rule; 83 import org.junit.Test; 84 import org.junit.rules.TemporaryFolder; 85 86 import static com.squareup.okhttp.internal.Util.UTF_8; 87 import static com.squareup.okhttp.internal.http.OkHeaders.SELECTED_PROTOCOL; 88 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_PERM_REDIRECT; 89 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT; 90 import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END; 91 import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START; 92 import static com.squareup.okhttp.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END; 93 import static com.squareup.okhttp.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; 94 import static java.util.concurrent.TimeUnit.MILLISECONDS; 95 import static java.util.concurrent.TimeUnit.NANOSECONDS; 96 import static org.junit.Assert.assertEquals; 97 import static org.junit.Assert.assertFalse; 98 import static org.junit.Assert.assertNotNull; 99 import static org.junit.Assert.assertNull; 100 import static org.junit.Assert.assertTrue; 101 import static org.junit.Assert.fail; 102 103 /** Android's URLConnectionTest. */ 104 public final class URLConnectionTest { 105 @Rule public final MockWebServer server = new MockWebServer(); 106 @Rule public final MockWebServer server2 = new MockWebServer(); 107 @Rule public final TemporaryFolder tempDir = new TemporaryFolder(); 108 109 // Android-added: Use TLS 1.3 and 1.2 for testing 110 private static final ConnectionSpec TLS_SPEC_1_3 = 111 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 112 .tlsVersions(TlsVersion.TLS_1_3) 113 .build(); 114 115 private static final ConnectionSpec TLS_SPEC_1_2 = 116 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 117 .tlsVersions(TlsVersion.TLS_1_2) 118 .build(); 119 120 private static final List<ConnectionSpec> TLS_SPEC_NO_V1 121 = Arrays.asList(TLS_SPEC_1_3, TLS_SPEC_1_2); 122 123 private SSLContext sslContext = SslContextBuilder.localhost(); 124 private OkUrlFactory client; 125 private HttpURLConnection connection; 126 private Cache cache; 127 setUp()128 @Before public void setUp() throws Exception { 129 server.setProtocolNegotiationEnabled(false); 130 client = new OkUrlFactory(new OkHttpClient()); 131 } 132 tearDown()133 @After public void tearDown() throws Exception { 134 Authenticator.setDefault(null); 135 System.clearProperty("proxyHost"); 136 System.clearProperty("proxyPort"); 137 System.clearProperty("http.agent"); 138 System.clearProperty("http.proxyHost"); 139 System.clearProperty("http.proxyPort"); 140 System.clearProperty("https.proxyHost"); 141 System.clearProperty("https.proxyPort"); 142 if (cache != null) { 143 cache.delete(); 144 } 145 } 146 requestHeaders()147 @Test public void requestHeaders() throws IOException, InterruptedException { 148 server.enqueue(new MockResponse()); 149 150 connection = client.open(server.getUrl("/")); 151 connection.addRequestProperty("D", "e"); 152 connection.addRequestProperty("D", "f"); 153 assertEquals("f", connection.getRequestProperty("D")); 154 assertEquals("f", connection.getRequestProperty("d")); 155 Map<String, List<String>> requestHeaders = connection.getRequestProperties(); 156 assertEquals(newSet("e", "f"), new LinkedHashSet<>(requestHeaders.get("D"))); 157 assertEquals(newSet("e", "f"), new LinkedHashSet<>(requestHeaders.get("d"))); 158 try { 159 requestHeaders.put("G", Arrays.asList("h")); 160 fail("Modified an unmodifiable view."); 161 } catch (UnsupportedOperationException expected) { 162 } 163 try { 164 requestHeaders.get("D").add("i"); 165 fail("Modified an unmodifiable view."); 166 } catch (UnsupportedOperationException expected) { 167 } 168 try { 169 connection.setRequestProperty(null, "j"); 170 fail(); 171 } catch (NullPointerException expected) { 172 } 173 try { 174 connection.addRequestProperty(null, "k"); 175 fail(); 176 } catch (NullPointerException expected) { 177 } 178 connection.setRequestProperty("NullValue", null); 179 assertNull(connection.getRequestProperty("NullValue")); 180 connection.addRequestProperty("AnotherNullValue", null); 181 assertNull(connection.getRequestProperty("AnotherNullValue")); 182 183 connection.getResponseCode(); 184 RecordedRequest request = server.takeRequest(); 185 assertEquals(Arrays.asList("e", "f"), request.getHeaders().values("D")); 186 assertNull(request.getHeader("NullValue")); 187 assertNull(request.getHeader("AnotherNullValue")); 188 assertNull(request.getHeader("G")); 189 assertNull(request.getHeader("null")); 190 191 try { 192 connection.addRequestProperty("N", "o"); 193 fail("Set header after connect"); 194 } catch (IllegalStateException expected) { 195 } 196 try { 197 connection.setRequestProperty("P", "q"); 198 fail("Set header after connect"); 199 } catch (IllegalStateException expected) { 200 } 201 try { 202 connection.getRequestProperties(); 203 fail(); 204 } catch (IllegalStateException expected) { 205 } 206 } 207 getRequestPropertyReturnsLastValue()208 @Test public void getRequestPropertyReturnsLastValue() throws Exception { 209 connection = client.open(server.getUrl("/")); 210 connection.addRequestProperty("A", "value1"); 211 connection.addRequestProperty("A", "value2"); 212 assertEquals("value2", connection.getRequestProperty("A")); 213 } 214 responseHeaders()215 @Test public void responseHeaders() throws IOException, InterruptedException { 216 server.enqueue(new MockResponse().setStatus("HTTP/1.0 200 Fantastic") 217 .addHeader("A: c") 218 .addHeader("B: d") 219 .addHeader("A: e") 220 .setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8)); 221 222 connection = client.open(server.getUrl("/")); 223 assertEquals(200, connection.getResponseCode()); 224 assertEquals("Fantastic", connection.getResponseMessage()); 225 assertEquals("HTTP/1.0 200 Fantastic", connection.getHeaderField(null)); 226 Map<String, List<String>> responseHeaders = connection.getHeaderFields(); 227 assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null)); 228 assertEquals(newSet("c", "e"), new LinkedHashSet<>(responseHeaders.get("A"))); 229 assertEquals(newSet("c", "e"), new LinkedHashSet<>(responseHeaders.get("a"))); 230 try { 231 responseHeaders.put("N", Arrays.asList("o")); 232 fail("Modified an unmodifiable view."); 233 } catch (UnsupportedOperationException expected) { 234 } 235 try { 236 responseHeaders.get("A").add("f"); 237 fail("Modified an unmodifiable view."); 238 } catch (UnsupportedOperationException expected) { 239 } 240 assertEquals("A", connection.getHeaderFieldKey(0)); 241 assertEquals("c", connection.getHeaderField(0)); 242 assertEquals("B", connection.getHeaderFieldKey(1)); 243 assertEquals("d", connection.getHeaderField(1)); 244 assertEquals("A", connection.getHeaderFieldKey(2)); 245 assertEquals("e", connection.getHeaderField(2)); 246 connection.getInputStream().close(); 247 } 248 serverSendsInvalidResponseHeaders()249 @Test public void serverSendsInvalidResponseHeaders() throws Exception { 250 server.enqueue(new MockResponse().setStatus("HTP/1.1 200 OK")); 251 252 connection = client.open(server.getUrl("/")); 253 try { 254 connection.getResponseCode(); 255 fail(); 256 } catch (IOException expected) { 257 } 258 } 259 serverSendsInvalidCodeTooLarge()260 @Test public void serverSendsInvalidCodeTooLarge() throws Exception { 261 server.enqueue(new MockResponse().setStatus("HTTP/1.1 2147483648 OK")); 262 263 connection = client.open(server.getUrl("/")); 264 try { 265 connection.getResponseCode(); 266 fail(); 267 } catch (IOException expected) { 268 } 269 } 270 serverSendsInvalidCodeNotANumber()271 @Test public void serverSendsInvalidCodeNotANumber() throws Exception { 272 server.enqueue(new MockResponse().setStatus("HTTP/1.1 00a OK")); 273 274 connection = client.open(server.getUrl("/")); 275 try { 276 connection.getResponseCode(); 277 fail(); 278 } catch (IOException expected) { 279 } 280 } 281 serverSendsUnnecessaryWhitespace()282 @Test public void serverSendsUnnecessaryWhitespace() throws Exception { 283 server.enqueue(new MockResponse().setStatus(" HTTP/1.1 2147483648 OK")); 284 285 connection = client.open(server.getUrl("/")); 286 try { 287 connection.getResponseCode(); 288 fail(); 289 } catch (IOException expected) { 290 } 291 } 292 connectRetriesUntilConnectedOrFailed()293 @Test public void connectRetriesUntilConnectedOrFailed() throws Exception { 294 URL url = server.getUrl("/foo"); 295 server.shutdown(); 296 297 connection = client.open(url); 298 try { 299 connection.connect(); 300 fail(); 301 } catch (IOException expected) { 302 } 303 } 304 requestBodySurvivesRetriesWithFixedLength()305 @Test public void requestBodySurvivesRetriesWithFixedLength() throws Exception { 306 testRequestBodySurvivesRetries(TransferKind.FIXED_LENGTH); 307 } 308 requestBodySurvivesRetriesWithChunkedStreaming()309 @Test public void requestBodySurvivesRetriesWithChunkedStreaming() throws Exception { 310 testRequestBodySurvivesRetries(TransferKind.CHUNKED); 311 } 312 requestBodySurvivesRetriesWithBufferedBody()313 @Test public void requestBodySurvivesRetriesWithBufferedBody() throws Exception { 314 testRequestBodySurvivesRetries(TransferKind.END_OF_STREAM); 315 } 316 testRequestBodySurvivesRetries(TransferKind transferKind)317 private void testRequestBodySurvivesRetries(TransferKind transferKind) throws Exception { 318 server.enqueue(new MockResponse().setBody("abc")); 319 320 // Use a misconfigured proxy to guarantee that the request is retried. 321 FakeProxySelector proxySelector = new FakeProxySelector(); 322 proxySelector.proxies.add(server2.toProxyAddress()); 323 client.client().setProxySelector(proxySelector); 324 server2.shutdown(); 325 326 connection = client.open(server.getUrl("/def")); 327 connection.setDoOutput(true); 328 transferKind.setForRequest(connection, 4); 329 connection.getOutputStream().write("body".getBytes("UTF-8")); 330 assertContent("abc", connection); 331 332 assertEquals("body", server.takeRequest().getBody().readUtf8()); 333 } 334 getErrorStreamOnSuccessfulRequest()335 @Test public void getErrorStreamOnSuccessfulRequest() throws Exception { 336 server.enqueue(new MockResponse().setBody("A")); 337 connection = client.open(server.getUrl("/")); 338 assertNull(connection.getErrorStream()); 339 connection.getInputStream().close(); 340 } 341 getErrorStreamOnUnsuccessfulRequest()342 @Test public void getErrorStreamOnUnsuccessfulRequest() throws Exception { 343 server.enqueue(new MockResponse().setResponseCode(404).setBody("A")); 344 connection = client.open(server.getUrl("/")); 345 assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE)); 346 } 347 348 // Check that if we don't read to the end of a response, the next request on the 349 // recycled connection doesn't get the unread tail of the first request's response. 350 // http://code.google.com/p/android/issues/detail?id=2939 bug2939()351 @Test public void bug2939() throws Exception { 352 MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8); 353 354 server.enqueue(response); 355 server.enqueue(response); 356 357 HttpURLConnection c1 = client.open(server.getUrl("/")); 358 assertContent("ABCDE", c1, 5); 359 HttpURLConnection c2 = client.open(server.getUrl("/")); 360 assertContent("ABCDE", c2, 5); 361 362 c1.getInputStream().close(); 363 c2.getInputStream().close(); 364 } 365 366 // Check that we recognize a few basic mime types by extension. 367 // http://code.google.com/p/android/issues/detail?id=10100 bug10100()368 @Test public void bug10100() throws Exception { 369 assertEquals("image/jpeg", URLConnection.guessContentTypeFromName("someFile.jpg")); 370 assertEquals("application/pdf", URLConnection.guessContentTypeFromName("stuff.pdf")); 371 } 372 connectionsArePooled()373 @Test public void connectionsArePooled() throws Exception { 374 MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR"); 375 376 server.enqueue(response); 377 server.enqueue(response); 378 server.enqueue(response); 379 380 assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/foo"))); 381 assertEquals(0, server.takeRequest().getSequenceNumber()); 382 assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/bar?baz=quux"))); 383 assertEquals(1, server.takeRequest().getSequenceNumber()); 384 assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/z"))); 385 assertEquals(2, server.takeRequest().getSequenceNumber()); 386 } 387 chunkedConnectionsArePooled()388 @Test public void chunkedConnectionsArePooled() throws Exception { 389 MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5); 390 391 server.enqueue(response); 392 server.enqueue(response); 393 server.enqueue(response); 394 395 assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/foo"))); 396 assertEquals(0, server.takeRequest().getSequenceNumber()); 397 assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/bar?baz=quux"))); 398 assertEquals(1, server.takeRequest().getSequenceNumber()); 399 assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/z"))); 400 assertEquals(2, server.takeRequest().getSequenceNumber()); 401 } 402 serverClosesSocket()403 @Test public void serverClosesSocket() throws Exception { 404 testServerClosesOutput(DISCONNECT_AT_END); 405 } 406 serverShutdownInput()407 @Test public void serverShutdownInput() throws Exception { 408 testServerClosesOutput(SHUTDOWN_INPUT_AT_END); 409 } 410 411 @Test 412 @Ignore("TODO(b/333847678 - diagnose and fix flake") serverShutdownOutput()413 public void serverShutdownOutput() throws Exception { 414 testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END); 415 } 416 invalidHost()417 @Test public void invalidHost() throws Exception { 418 // Note that 1234.1.1.1 is an invalid host in a URI, but URL isn't as strict. 419 URL url = new URL("http://1234.1.1.1/index.html"); 420 HttpURLConnection connection = client.open(url); 421 try { 422 connection.connect(); 423 fail(); 424 } catch (UnknownHostException expected) { 425 } 426 } 427 testServerClosesOutput(SocketPolicy socketPolicy)428 private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception { 429 SocketShutdownListener shutdownListener = new SocketShutdownListener(); 430 server.enqueue(new MockResponse().setBody("This connection won't pool properly") 431 .setSocketPolicy(socketPolicy) 432 .setSocketShutdownListener(shutdownListener)); 433 MockResponse responseAfter = new MockResponse().setBody("This comes after a busted connection"); 434 server.enqueue(responseAfter); 435 server.enqueue(responseAfter); // Enqueue 2x because the broken connection may be reused. 436 437 HttpURLConnection connection1 = client.open(server.getUrl("/a")); 438 connection1.setReadTimeout(100); 439 assertContent("This connection won't pool properly", connection1); 440 assertEquals(0, server.takeRequest().getSequenceNumber()); 441 442 shutdownListener.waitForSocketShutdown(); 443 444 HttpURLConnection connection2 = client.open(server.getUrl("/b")); 445 connection2.setReadTimeout(100); 446 assertContent("This comes after a busted connection", connection2); 447 448 // Check that a fresh connection was created, either immediately or after attempting reuse. 449 RecordedRequest requestAfter = server.takeRequest(); 450 if (server.getRequestCount() == 3) { 451 requestAfter = server.takeRequest(); // The failure consumed a response. 452 } 453 // sequence number 0 means the HTTP socket connection was not reused 454 assertEquals(0, requestAfter.getSequenceNumber()); 455 } 456 457 enum WriteKind {BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS} 458 chunkedUpload_byteByByte()459 @Test public void chunkedUpload_byteByByte() throws Exception { 460 doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE); 461 } 462 chunkedUpload_smallBuffers()463 @Test public void chunkedUpload_smallBuffers() throws Exception { 464 doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS); 465 } 466 chunkedUpload_largeBuffers()467 @Test public void chunkedUpload_largeBuffers() throws Exception { 468 doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS); 469 } 470 fixedLengthUpload_byteByByte()471 @Test public void fixedLengthUpload_byteByByte() throws Exception { 472 doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE); 473 } 474 fixedLengthUpload_smallBuffers()475 @Test public void fixedLengthUpload_smallBuffers() throws Exception { 476 doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS); 477 } 478 fixedLengthUpload_largeBuffers()479 @Test public void fixedLengthUpload_largeBuffers() throws Exception { 480 doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS); 481 } 482 doUpload(TransferKind uploadKind, WriteKind writeKind)483 private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception { 484 int n = 512 * 1024; 485 server.setBodyLimit(0); 486 server.enqueue(new MockResponse()); 487 488 HttpURLConnection conn = client.open(server.getUrl("/")); 489 conn.setDoOutput(true); 490 conn.setRequestMethod("POST"); 491 if (uploadKind == TransferKind.CHUNKED) { 492 conn.setChunkedStreamingMode(-1); 493 } else { 494 conn.setFixedLengthStreamingMode(n); 495 } 496 OutputStream out = conn.getOutputStream(); 497 if (writeKind == WriteKind.BYTE_BY_BYTE) { 498 for (int i = 0; i < n; ++i) { 499 out.write('x'); 500 } 501 } else { 502 byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64 * 1024]; 503 Arrays.fill(buf, (byte) 'x'); 504 for (int i = 0; i < n; i += buf.length) { 505 out.write(buf, 0, Math.min(buf.length, n - i)); 506 } 507 } 508 out.close(); 509 assertEquals(200, conn.getResponseCode()); 510 RecordedRequest request = server.takeRequest(); 511 assertEquals(n, request.getBodySize()); 512 if (uploadKind == TransferKind.CHUNKED) { 513 assertTrue(request.getChunkSizes().size() > 0); 514 } else { 515 assertTrue(request.getChunkSizes().isEmpty()); 516 } 517 } 518 getResponseCodeNoResponseBody()519 @Test public void getResponseCodeNoResponseBody() throws Exception { 520 server.enqueue(new MockResponse().addHeader("abc: def")); 521 522 URL url = server.getUrl("/"); 523 HttpURLConnection conn = client.open(url); 524 conn.setDoInput(false); 525 assertEquals("def", conn.getHeaderField("abc")); 526 assertEquals(200, conn.getResponseCode()); 527 try { 528 conn.getInputStream(); 529 fail(); 530 } catch (ProtocolException expected) { 531 } 532 } 533 connectViaHttps()534 @Test public void connectViaHttps() throws Exception { 535 server.useHttps(sslContext.getSocketFactory(), false); 536 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 537 538 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 539 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 540 connection = client.open(server.getUrl("/foo")); 541 542 assertContent("this response comes via HTTPS", connection); 543 544 RecordedRequest request = server.takeRequest(); 545 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 546 } 547 inspectHandshakeThroughoutRequestLifecycle()548 @Test public void inspectHandshakeThroughoutRequestLifecycle() throws Exception { 549 server.useHttps(sslContext.getSocketFactory(), false); 550 server.enqueue(new MockResponse()); 551 552 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 553 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 554 555 HttpsURLConnection httpsConnection = (HttpsURLConnection) client.open(server.getUrl("/foo")); 556 557 // Prior to calling connect(), getting the cipher suite is forbidden. 558 try { 559 httpsConnection.getCipherSuite(); 560 fail(); 561 } catch (IllegalStateException expected) { 562 } 563 564 // Calling connect establishes a handshake... 565 httpsConnection.connect(); 566 assertNotNull(httpsConnection.getCipherSuite()); 567 568 // ...which remains after we read the response body... 569 assertContent("", httpsConnection); 570 assertNotNull(httpsConnection.getCipherSuite()); 571 572 // ...and after we disconnect. 573 httpsConnection.disconnect(); 574 assertNotNull(httpsConnection.getCipherSuite()); 575 } 576 connectViaHttpsReusingConnections()577 @Test public void connectViaHttpsReusingConnections() throws IOException, InterruptedException { 578 server.useHttps(sslContext.getSocketFactory(), false); 579 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 580 server.enqueue(new MockResponse().setBody("another response via HTTPS")); 581 582 // The pool will only reuse sockets if the SSL socket factories are the same. 583 SSLSocketFactory clientSocketFactory = sslContext.getSocketFactory(); 584 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 585 586 client.client().setSslSocketFactory(clientSocketFactory); 587 client.client().setHostnameVerifier(hostnameVerifier); 588 connection = client.open(server.getUrl("/")); 589 assertContent("this response comes via HTTPS", connection); 590 591 connection = client.open(server.getUrl("/")); 592 assertContent("another response via HTTPS", connection); 593 594 assertEquals(0, server.takeRequest().getSequenceNumber()); 595 assertEquals(1, server.takeRequest().getSequenceNumber()); 596 } 597 connectViaHttpsReusingConnectionsDifferentFactories()598 @Test public void connectViaHttpsReusingConnectionsDifferentFactories() 599 throws IOException, InterruptedException { 600 server.useHttps(sslContext.getSocketFactory(), false); 601 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 602 server.enqueue(new MockResponse().setBody("another response via HTTPS")); 603 604 // install a custom SSL socket factory so the server can be authorized 605 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 606 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 607 HttpURLConnection connection1 = client.open(server.getUrl("/")); 608 assertContent("this response comes via HTTPS", connection1); 609 610 client.client().setSslSocketFactory(null); 611 HttpURLConnection connection2 = client.open(server.getUrl("/")); 612 try { 613 readAscii(connection2.getInputStream(), Integer.MAX_VALUE); 614 fail("without an SSL socket factory, the connection should fail"); 615 } catch (SSLException expected) { 616 } 617 } 618 connectViaHttpsWithSSLFallback()619 @Test public void connectViaHttpsWithSSLFallback() throws Exception { 620 server.useHttps(sslContext.getSocketFactory(), false); 621 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 622 server.enqueue(new MockResponse().setBody("this response comes via SSL")); 623 624 suppressTlsFallbackScsv(client.client()); 625 client.client().setConnectionSpecs(TLS_SPEC_NO_V1); 626 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 627 connection = client.open(server.getUrl("/foo")); 628 629 assertContent("this response comes via SSL", connection); 630 631 RecordedRequest request = server.takeRequest(); 632 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 633 assertEquals(TlsVersion.TLS_1_2, request.getTlsVersion()); 634 } 635 connectViaHttpsWithSSLFallbackFailuresRecorded()636 @Test public void connectViaHttpsWithSSLFallbackFailuresRecorded() throws Exception { 637 server.useHttps(sslContext.getSocketFactory(), false); 638 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 639 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE)); 640 641 suppressTlsFallbackScsv(client.client()); 642 client.client().setConnectionSpecs(TLS_SPEC_NO_V1); 643 client.client().setDns(new SingleInetAddressDns()); 644 645 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 646 connection = client.open(server.getUrl("/foo")); 647 648 try { 649 connection.getResponseCode(); 650 fail(); 651 } catch (IOException expected) { 652 assertEquals(1, expected.getSuppressed().length); 653 } 654 } 655 656 /** 657 * When a pooled connection fails, don't blame the route. Otherwise pooled 658 * connection failures can cause unnecessary SSL fallbacks. 659 * 660 * https://github.com/square/okhttp/issues/515 661 */ sslFallbackNotUsedWhenRecycledConnectionFails()662 @Test public void sslFallbackNotUsedWhenRecycledConnectionFails() throws Exception { 663 SocketShutdownListener shutdownListener = new SocketShutdownListener(); 664 server.useHttps(sslContext.getSocketFactory(), false); 665 server.enqueue(new MockResponse() 666 .setBody("abc") 667 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END) 668 .setSocketShutdownListener(shutdownListener)); 669 server.enqueue(new MockResponse().setBody("def")); 670 671 suppressTlsFallbackScsv(client.client()); 672 client.client().setConnectionSpecs(TLS_SPEC_NO_V1); 673 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 674 675 assertContent("abc", client.open(server.getUrl("/"))); 676 677 shutdownListener.waitForSocketShutdown(); 678 679 assertContent("def", client.open(server.getUrl("/"))); 680 681 Set<TlsVersion> tlsVersions = 682 EnumSet.of(TlsVersion.TLS_1_3); 683 684 RecordedRequest request1 = server.takeRequest(); 685 assertTrue(tlsVersions.contains(request1.getTlsVersion())); 686 687 RecordedRequest request2 = server.takeRequest(); 688 assertTrue(tlsVersions.contains(request2.getTlsVersion())); 689 } 690 691 /** 692 * Verify that we don't retry connections on certificate verification errors. 693 * 694 * http://code.google.com/p/android/issues/detail?id=13178 695 */ connectViaHttpsToUntrustedServer()696 @Test public void connectViaHttpsToUntrustedServer() throws IOException, InterruptedException { 697 server.useHttps(sslContext.getSocketFactory(), false); 698 server.enqueue(new MockResponse()); // unused 699 700 connection = client.open(server.getUrl("/foo")); 701 try { 702 connection.getInputStream(); 703 fail(); 704 } catch (SSLHandshakeException expected) { 705 assertTrue(expected.getCause() instanceof CertificateException); 706 } 707 assertEquals(0, server.getRequestCount()); 708 } 709 connectViaProxyUsingProxyArg()710 @Test public void connectViaProxyUsingProxyArg() throws Exception { 711 testConnectViaProxy(ProxyConfig.CREATE_ARG); 712 } 713 connectViaProxyUsingProxySystemProperty()714 @Test public void connectViaProxyUsingProxySystemProperty() throws Exception { 715 testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY); 716 } 717 connectViaProxyUsingHttpProxySystemProperty()718 @Test public void connectViaProxyUsingHttpProxySystemProperty() throws Exception { 719 testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 720 } 721 testConnectViaProxy(ProxyConfig proxyConfig)722 private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception { 723 MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy"); 724 server.enqueue(mockResponse); 725 726 URL url = new URL("http://android.com/foo"); 727 connection = proxyConfig.connect(server, client, url); 728 assertContent("this response comes via a proxy", connection); 729 assertTrue(connection.usingProxy()); 730 731 RecordedRequest request = server.takeRequest(); 732 assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine()); 733 assertEquals("android.com", request.getHeader("Host")); 734 } 735 contentDisagreesWithContentLengthHeaderBodyTooLong()736 @Test public void contentDisagreesWithContentLengthHeaderBodyTooLong() throws IOException { 737 server.enqueue(new MockResponse().setBody("abc\r\nYOU SHOULD NOT SEE THIS") 738 .clearHeaders() 739 .addHeader("Content-Length: 3")); 740 assertContent("abc", client.open(server.getUrl("/"))); 741 } 742 contentDisagreesWithContentLengthHeaderBodyTooShort()743 @Test public void contentDisagreesWithContentLengthHeaderBodyTooShort() throws IOException { 744 server.enqueue(new MockResponse().setBody("abc") 745 .setHeader("Content-Length", "5") 746 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)); 747 try { 748 readAscii(client.open(server.getUrl("/")).getInputStream(), 5); 749 fail(); 750 } catch (ProtocolException expected) { 751 } 752 } 753 testConnectViaSocketFactory(boolean useHttps)754 public void testConnectViaSocketFactory(boolean useHttps) throws IOException { 755 SocketFactory uselessSocketFactory = new SocketFactory() { 756 public Socket createSocket() { throw new IllegalArgumentException("useless"); } 757 public Socket createSocket(InetAddress host, int port) { return null; } 758 public Socket createSocket(InetAddress address, int port, InetAddress localAddress, 759 int localPort) { return null; } 760 public Socket createSocket(String host, int port) { return null; } 761 public Socket createSocket(String host, int port, InetAddress localHost, int localPort) { 762 return null; 763 } 764 }; 765 766 if (useHttps) { 767 server.useHttps(sslContext.getSocketFactory(), false); 768 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 769 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 770 } 771 772 server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK")); 773 774 client.client().setSocketFactory(uselessSocketFactory); 775 connection = client.open(server.getUrl("/")); 776 try { 777 connection.getResponseCode(); 778 fail(); 779 } catch (IllegalArgumentException expected) { 780 } 781 782 client.client().setSocketFactory(SocketFactory.getDefault()); 783 connection = client.open(server.getUrl("/")); 784 assertEquals(200, connection.getResponseCode()); 785 } 786 connectHttpViaSocketFactory()787 @Test public void connectHttpViaSocketFactory() throws Exception { 788 testConnectViaSocketFactory(false); 789 } 790 connectHttpsViaSocketFactory()791 @Test public void connectHttpsViaSocketFactory() throws Exception { 792 testConnectViaSocketFactory(true); 793 } 794 contentDisagreesWithChunkedHeaderBodyTooLong()795 @Test public void contentDisagreesWithChunkedHeaderBodyTooLong() throws IOException { 796 MockResponse mockResponse = new MockResponse(); 797 mockResponse.setChunkedBody("abc", 3); 798 Buffer buffer = mockResponse.getBody(); 799 buffer.writeUtf8("\r\nYOU SHOULD NOT SEE THIS"); 800 mockResponse.setBody(buffer); 801 mockResponse.clearHeaders(); 802 mockResponse.addHeader("Transfer-encoding: chunked"); 803 804 server.enqueue(mockResponse); 805 806 assertContent("abc", client.open(server.getUrl("/"))); 807 } 808 contentDisagreesWithChunkedHeaderBodyTooShort()809 @Test public void contentDisagreesWithChunkedHeaderBodyTooShort() throws IOException { 810 MockResponse mockResponse = new MockResponse(); 811 mockResponse.setChunkedBody("abcde", 5); 812 813 Buffer truncatedBody = new Buffer(); 814 Buffer fullBody = mockResponse.getBody(); 815 truncatedBody.write(fullBody, fullBody.indexOf((byte) 'e')); 816 mockResponse.setBody(truncatedBody); 817 818 mockResponse.clearHeaders(); 819 mockResponse.addHeader("Transfer-encoding: chunked"); 820 mockResponse.setSocketPolicy(SocketPolicy.DISCONNECT_AT_END); 821 822 server.enqueue(mockResponse); 823 824 try { 825 readAscii(client.open(server.getUrl("/")).getInputStream(), 5); 826 fail(); 827 } catch (ProtocolException expected) { 828 } 829 } 830 connectViaHttpProxyToHttpsUsingProxyArgWithNoProxy()831 @Test public void connectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception { 832 testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY); 833 } 834 connectViaHttpProxyToHttpsUsingHttpProxySystemProperty()835 @Test public void connectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception { 836 // https should not use http proxy 837 testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY); 838 } 839 testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig)840 private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception { 841 server.useHttps(sslContext.getSocketFactory(), false); 842 server.enqueue(new MockResponse().setBody("this response comes via HTTPS")); 843 844 URL url = server.getUrl("/foo"); 845 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 846 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 847 connection = proxyConfig.connect(server, client, url); 848 849 assertContent("this response comes via HTTPS", connection); 850 851 RecordedRequest request = server.takeRequest(); 852 assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); 853 } 854 connectViaHttpProxyToHttpsUsingProxyArg()855 @Test public void connectViaHttpProxyToHttpsUsingProxyArg() throws Exception { 856 testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG); 857 } 858 859 /** 860 * We weren't honoring all of the appropriate proxy system properties when 861 * connecting via HTTPS. http://b/3097518 862 */ connectViaHttpProxyToHttpsUsingProxySystemProperty()863 @Test public void connectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception { 864 testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY); 865 } 866 connectViaHttpProxyToHttpsUsingHttpsProxySystemProperty()867 @Test public void connectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception { 868 testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY); 869 } 870 871 /** 872 * We were verifying the wrong hostname when connecting to an HTTPS site 873 * through a proxy. http://b/3097277 874 */ testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig)875 private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception { 876 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 877 878 server.useHttps(sslContext.getSocketFactory(), true); 879 server.enqueue( 880 new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders()); 881 server.enqueue(new MockResponse().setBody("this response comes via a secure proxy")); 882 883 URL url = new URL("https://android.com/foo"); 884 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 885 client.client().setHostnameVerifier(hostnameVerifier); 886 connection = proxyConfig.connect(server, client, url); 887 888 assertContent("this response comes via a secure proxy", connection); 889 890 RecordedRequest connect = server.takeRequest(); 891 assertEquals("Connect line failure on proxy", "CONNECT android.com:443 HTTP/1.1", 892 connect.getRequestLine()); 893 assertEquals("android.com:443", connect.getHeader("Host")); 894 895 RecordedRequest get = server.takeRequest(); 896 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 897 assertEquals("android.com", get.getHeader("Host")); 898 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 899 } 900 901 /** Tolerate bad https proxy response when using HttpResponseCache. Android bug 6754912. */ connectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache()902 @Test public void connectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception { 903 initResponseCache(); 904 905 server.useHttps(sslContext.getSocketFactory(), true); 906 // The inclusion of a body in the response to a CONNECT is key to reproducing b/6754912. 907 MockResponse badProxyResponse = new MockResponse() 908 .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END) 909 .setBody("bogus proxy connect response content"); 910 server.enqueue(badProxyResponse); 911 server.enqueue(new MockResponse().setBody("response")); 912 913 // Configure a single IP address for the host and a single configuration, so we only need one 914 // failure to fail permanently. 915 client.client().setDns(new SingleInetAddressDns()); 916 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 917 client.client().setConnectionSpecs(Util.immutableList(ConnectionSpec.MODERN_TLS)); 918 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 919 client.client().setProxy(server.toProxyAddress()); 920 921 URL url = new URL("https://android.com/foo"); 922 connection = client.open(url); 923 assertContent("response", connection); 924 925 RecordedRequest connect = server.takeRequest(); 926 assertEquals("CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); 927 assertEquals("android.com:443", connect.getHeader("Host")); 928 } 929 initResponseCache()930 private void initResponseCache() throws IOException { 931 cache = new Cache(tempDir.getRoot(), Integer.MAX_VALUE); 932 client.client().setCache(cache); 933 } 934 935 /** Test which headers are sent unencrypted to the HTTP proxy. */ proxyConnectIncludesProxyHeadersOnly()936 @Test public void proxyConnectIncludesProxyHeadersOnly() 937 throws IOException, InterruptedException { 938 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 939 940 server.useHttps(sslContext.getSocketFactory(), true); 941 server.enqueue( 942 new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders()); 943 server.enqueue(new MockResponse().setBody("encrypted response from the origin server")); 944 945 client.client().setProxy(server.toProxyAddress()); 946 947 URL url = new URL("https://android.com/foo"); 948 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 949 client.client().setHostnameVerifier(hostnameVerifier); 950 connection = client.open(url); 951 connection.addRequestProperty("Private", "Secret"); 952 connection.addRequestProperty("Proxy-Authorization", "bar"); 953 connection.addRequestProperty("User-Agent", "baz"); 954 assertContent("encrypted response from the origin server", connection); 955 956 RecordedRequest connect = server.takeRequest(); 957 assertNull(connect.getHeader("Private")); 958 assertNull(connect.getHeader("Proxy-Authorization")); 959 assertEquals(Version.userAgent(), connect.getHeader("User-Agent")); 960 assertEquals("android.com:443", connect.getHeader("Host")); 961 assertEquals("Keep-Alive", connect.getHeader("Proxy-Connection")); 962 963 RecordedRequest get = server.takeRequest(); 964 assertEquals("Secret", get.getHeader("Private")); 965 assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls); 966 } 967 proxyAuthenticateOnConnect()968 @Test public void proxyAuthenticateOnConnect() throws Exception { 969 Authenticator.setDefault(new RecordingAuthenticator()); 970 server.useHttps(sslContext.getSocketFactory(), true); 971 server.enqueue(new MockResponse().setResponseCode(407) 972 .addHeader("Proxy-Authenticate: Basic realm=\"localhost\"")); 973 server.enqueue( 974 new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders()); 975 server.enqueue(new MockResponse().setBody("A")); 976 977 client.client().setProxy(server.toProxyAddress()); 978 979 URL url = new URL("https://android.com/foo"); 980 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 981 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 982 connection = client.open(url); 983 assertContent("A", connection); 984 985 RecordedRequest connect1 = server.takeRequest(); 986 assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine()); 987 assertNull(connect1.getHeader("Proxy-Authorization")); 988 989 RecordedRequest connect2 = server.takeRequest(); 990 assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine()); 991 assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS, 992 connect2.getHeader("Proxy-Authorization")); 993 994 RecordedRequest get = server.takeRequest(); 995 assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); 996 assertNull(get.getHeader("Proxy-Authorization")); 997 } 998 999 // Don't disconnect after building a tunnel with CONNECT 1000 // http://code.google.com/p/android/issues/detail?id=37221 proxyWithConnectionClose()1001 @Test public void proxyWithConnectionClose() throws IOException { 1002 server.useHttps(sslContext.getSocketFactory(), true); 1003 server.enqueue( 1004 new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders()); 1005 server.enqueue(new MockResponse().setBody("this response comes via a proxy")); 1006 1007 client.client().setProxy(server.toProxyAddress()); 1008 1009 URL url = new URL("https://android.com/foo"); 1010 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1011 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1012 connection = client.open(url); 1013 connection.setRequestProperty("Connection", "close"); 1014 1015 assertContent("this response comes via a proxy", connection); 1016 } 1017 proxyWithConnectionReuse()1018 @Test public void proxyWithConnectionReuse() throws IOException { 1019 SSLSocketFactory socketFactory = sslContext.getSocketFactory(); 1020 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 1021 1022 server.useHttps(socketFactory, true); 1023 server.enqueue( 1024 new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders()); 1025 server.enqueue(new MockResponse().setBody("response 1")); 1026 server.enqueue(new MockResponse().setBody("response 2")); 1027 1028 client.client().setProxy(server.toProxyAddress()); 1029 1030 URL url = new URL("https://android.com/foo"); 1031 client.client().setSslSocketFactory(socketFactory); 1032 client.client().setHostnameVerifier(hostnameVerifier); 1033 assertContent("response 1", client.open(url)); 1034 assertContent("response 2", client.open(url)); 1035 } 1036 disconnectedConnection()1037 @Test public void disconnectedConnection() throws IOException { 1038 server.enqueue(new MockResponse() 1039 .throttleBody(2, 100, TimeUnit.MILLISECONDS) 1040 .setBody("ABCD")); 1041 1042 connection = client.open(server.getUrl("/")); 1043 InputStream in = connection.getInputStream(); 1044 assertEquals('A', (char) in.read()); 1045 connection.disconnect(); 1046 try { 1047 // Reading 'B' may succeed if it's buffered. 1048 in.read(); 1049 1050 // But 'C' shouldn't be buffered (the response is throttled) and this should fail. 1051 in.read(); 1052 fail("Expected a connection closed exception"); 1053 } catch (IOException expected) { 1054 } 1055 in.close(); 1056 } 1057 disconnectBeforeConnect()1058 @Test public void disconnectBeforeConnect() throws IOException { 1059 server.enqueue(new MockResponse().setBody("A")); 1060 1061 connection = client.open(server.getUrl("/")); 1062 connection.disconnect(); 1063 assertContent("A", connection); 1064 assertEquals(200, connection.getResponseCode()); 1065 } 1066 defaultRequestProperty()1067 @SuppressWarnings("deprecation") @Test public void defaultRequestProperty() throws Exception { 1068 URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A"); 1069 assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty")); 1070 } 1071 1072 /** 1073 * Reads {@code count} characters from the stream. If the stream is 1074 * exhausted before {@code count} characters can be read, the remaining 1075 * characters are returned and the stream is closed. 1076 */ readAscii(InputStream in, int count)1077 private String readAscii(InputStream in, int count) throws IOException { 1078 StringBuilder result = new StringBuilder(); 1079 for (int i = 0; i < count; i++) { 1080 int value = in.read(); 1081 if (value == -1) { 1082 in.close(); 1083 break; 1084 } 1085 result.append((char) value); 1086 } 1087 return result.toString(); 1088 } 1089 markAndResetWithContentLengthHeader()1090 @Test public void markAndResetWithContentLengthHeader() throws IOException { 1091 testMarkAndReset(TransferKind.FIXED_LENGTH); 1092 } 1093 markAndResetWithChunkedEncoding()1094 @Test public void markAndResetWithChunkedEncoding() throws IOException { 1095 testMarkAndReset(TransferKind.CHUNKED); 1096 } 1097 markAndResetWithNoLengthHeaders()1098 @Test public void markAndResetWithNoLengthHeaders() throws IOException { 1099 testMarkAndReset(TransferKind.END_OF_STREAM); 1100 } 1101 testMarkAndReset(TransferKind transferKind)1102 private void testMarkAndReset(TransferKind transferKind) throws IOException { 1103 MockResponse response = new MockResponse(); 1104 transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024); 1105 server.enqueue(response); 1106 server.enqueue(response); 1107 1108 InputStream in = client.open(server.getUrl("/")).getInputStream(); 1109 assertFalse("This implementation claims to support mark().", in.markSupported()); 1110 in.mark(5); 1111 assertEquals("ABCDE", readAscii(in, 5)); 1112 try { 1113 in.reset(); 1114 fail(); 1115 } catch (IOException expected) { 1116 } 1117 assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE)); 1118 in.close(); 1119 assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", client.open(server.getUrl("/"))); 1120 } 1121 1122 /** 1123 * We've had a bug where we forget the HTTP response when we see response 1124 * code 401. This causes a new HTTP request to be issued for every call into 1125 * the URLConnection. 1126 */ unauthorizedResponseHandling()1127 @Test public void unauthorizedResponseHandling() throws IOException { 1128 MockResponse response = new MockResponse().addHeader("WWW-Authenticate: challenge") 1129 .setResponseCode(401) // UNAUTHORIZED 1130 .setBody("Unauthorized"); 1131 server.enqueue(response); 1132 server.enqueue(response); 1133 server.enqueue(response); 1134 1135 URL url = server.getUrl("/"); 1136 HttpURLConnection conn = client.open(url); 1137 1138 assertEquals(401, conn.getResponseCode()); 1139 assertEquals(401, conn.getResponseCode()); 1140 assertEquals(401, conn.getResponseCode()); 1141 assertEquals(1, server.getRequestCount()); 1142 conn.getErrorStream().close(); 1143 } 1144 nonHexChunkSize()1145 @Test public void nonHexChunkSize() throws IOException { 1146 server.enqueue(new MockResponse().setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n") 1147 .clearHeaders() 1148 .addHeader("Transfer-encoding: chunked")); 1149 1150 URLConnection connection = client.open(server.getUrl("/")); 1151 try { 1152 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 1153 fail(); 1154 } catch (IOException e) { 1155 } 1156 connection.getInputStream().close(); 1157 } 1158 malformedChunkSize()1159 @Test public void malformedChunkSize() throws IOException { 1160 server.enqueue(new MockResponse().setBody("5:x\r\nABCDE\r\n0\r\n\r\n") 1161 .clearHeaders() 1162 .addHeader("Transfer-encoding: chunked")); 1163 1164 URLConnection connection = client.open(server.getUrl("/")); 1165 try { 1166 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 1167 fail(); 1168 } catch (IOException e) { 1169 } finally { 1170 connection.getInputStream().close(); 1171 } 1172 } 1173 extensionAfterChunkSize()1174 @Test public void extensionAfterChunkSize() throws IOException { 1175 server.enqueue(new MockResponse().setBody("5;x\r\nABCDE\r\n0\r\n\r\n") 1176 .clearHeaders() 1177 .addHeader("Transfer-encoding: chunked")); 1178 1179 HttpURLConnection connection = client.open(server.getUrl("/")); 1180 assertContent("ABCDE", connection); 1181 } 1182 missingChunkBody()1183 @Test public void missingChunkBody() throws IOException { 1184 server.enqueue(new MockResponse().setBody("5") 1185 .clearHeaders() 1186 .addHeader("Transfer-encoding: chunked") 1187 .setSocketPolicy(DISCONNECT_AT_END)); 1188 1189 URLConnection connection = client.open(server.getUrl("/")); 1190 try { 1191 readAscii(connection.getInputStream(), Integer.MAX_VALUE); 1192 fail(); 1193 } catch (IOException e) { 1194 } finally { 1195 connection.getInputStream().close(); 1196 } 1197 } 1198 1199 /** 1200 * This test checks whether connections are gzipped by default. This 1201 * behavior in not required by the API, so a failure of this test does not 1202 * imply a bug in the implementation. 1203 */ gzipEncodingEnabledByDefault()1204 @Test public void gzipEncodingEnabledByDefault() throws IOException, InterruptedException { 1205 server.enqueue(new MockResponse() 1206 .setBody(gzip("ABCABCABC")) 1207 .addHeader("Content-Encoding: gzip")); 1208 1209 URLConnection connection = client.open(server.getUrl("/")); 1210 assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1211 assertNull(connection.getContentEncoding()); 1212 assertEquals(-1, connection.getContentLength()); 1213 1214 RecordedRequest request = server.takeRequest(); 1215 assertEquals("gzip", request.getHeader("Accept-Encoding")); 1216 } 1217 clientConfiguredGzipContentEncoding()1218 @Test public void clientConfiguredGzipContentEncoding() throws Exception { 1219 Buffer bodyBytes = gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 1220 server.enqueue(new MockResponse() 1221 .setBody(bodyBytes) 1222 .addHeader("Content-Encoding: gzip")); 1223 1224 URLConnection connection = client.open(server.getUrl("/")); 1225 connection.addRequestProperty("Accept-Encoding", "gzip"); 1226 InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream()); 1227 assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE)); 1228 assertEquals(bodyBytes.size(), connection.getContentLength()); 1229 1230 RecordedRequest request = server.takeRequest(); 1231 assertEquals("gzip", request.getHeader("Accept-Encoding")); 1232 } 1233 gzipAndConnectionReuseWithFixedLength()1234 @Test public void gzipAndConnectionReuseWithFixedLength() throws Exception { 1235 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, false); 1236 } 1237 gzipAndConnectionReuseWithChunkedEncoding()1238 @Test public void gzipAndConnectionReuseWithChunkedEncoding() throws Exception { 1239 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, false); 1240 } 1241 gzipAndConnectionReuseWithFixedLengthAndTls()1242 @Test public void gzipAndConnectionReuseWithFixedLengthAndTls() throws Exception { 1243 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, true); 1244 } 1245 gzipAndConnectionReuseWithChunkedEncodingAndTls()1246 @Test public void gzipAndConnectionReuseWithChunkedEncodingAndTls() throws Exception { 1247 testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, true); 1248 } 1249 clientConfiguredCustomContentEncoding()1250 @Test public void clientConfiguredCustomContentEncoding() throws Exception { 1251 server.enqueue(new MockResponse().setBody("ABCDE").addHeader("Content-Encoding: custom")); 1252 1253 URLConnection connection = client.open(server.getUrl("/")); 1254 connection.addRequestProperty("Accept-Encoding", "custom"); 1255 assertEquals("ABCDE", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1256 1257 RecordedRequest request = server.takeRequest(); 1258 assertEquals("custom", request.getHeader("Accept-Encoding")); 1259 } 1260 1261 /** 1262 * Test a bug where gzip input streams weren't exhausting the input stream, 1263 * which corrupted the request that followed or prevented connection reuse. 1264 * http://code.google.com/p/android/issues/detail?id=7059 1265 * http://code.google.com/p/android/issues/detail?id=38817 1266 */ testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind transferKind, boolean tls)1267 private void testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind transferKind, 1268 boolean tls) throws Exception { 1269 if (tls) { 1270 SSLSocketFactory socketFactory = sslContext.getSocketFactory(); 1271 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 1272 server.useHttps(socketFactory, false); 1273 client.client().setSslSocketFactory(socketFactory); 1274 client.client().setHostnameVerifier(hostnameVerifier); 1275 } 1276 1277 MockResponse responseOne = new MockResponse(); 1278 responseOne.addHeader("Content-Encoding: gzip"); 1279 transferKind.setBody(responseOne, gzip("one (gzipped)"), 5); 1280 server.enqueue(responseOne); 1281 MockResponse responseTwo = new MockResponse(); 1282 transferKind.setBody(responseTwo, "two (identity)", 5); 1283 server.enqueue(responseTwo); 1284 1285 HttpURLConnection connection1 = client.open(server.getUrl("/")); 1286 connection1.addRequestProperty("Accept-Encoding", "gzip"); 1287 InputStream gunzippedIn = new GZIPInputStream(connection1.getInputStream()); 1288 assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE)); 1289 assertEquals(0, server.takeRequest().getSequenceNumber()); 1290 1291 HttpURLConnection connection2 = client.open(server.getUrl("/")); 1292 assertEquals("two (identity)", readAscii(connection2.getInputStream(), Integer.MAX_VALUE)); 1293 assertEquals(1, server.takeRequest().getSequenceNumber()); 1294 } 1295 transparentGzipWorksAfterExceptionRecovery()1296 @Test public void transparentGzipWorksAfterExceptionRecovery() throws Exception { 1297 SocketShutdownListener shutdownListener = new SocketShutdownListener(); 1298 server.enqueue(new MockResponse() 1299 .setBody("a") 1300 .setSocketPolicy(SHUTDOWN_INPUT_AT_END) 1301 .setSocketShutdownListener(shutdownListener)); 1302 server.enqueue(new MockResponse() 1303 .addHeader("Content-Encoding: gzip") 1304 .setBody(gzip("b"))); 1305 1306 // Seed the pool with a bad connection. 1307 assertContent("a", client.open(server.getUrl("/"))); 1308 1309 shutdownListener.waitForSocketShutdown(); 1310 1311 // This connection will need to be recovered. When it is, transparent gzip should still work! 1312 assertContent("b", client.open(server.getUrl("/"))); 1313 1314 assertEquals(0, server.takeRequest().getSequenceNumber()); 1315 assertEquals(0, server.takeRequest().getSequenceNumber()); // Connection is not pooled. 1316 } 1317 endOfStreamResponseIsNotPooled()1318 @Test public void endOfStreamResponseIsNotPooled() throws Exception { 1319 server.enqueue(new MockResponse() 1320 .setBody("{}") 1321 .clearHeaders() 1322 .setSocketPolicy(DISCONNECT_AT_END)); 1323 1324 ConnectionPool pool = ConnectionPool.getDefault(); 1325 pool.evictAll(); 1326 client.client().setConnectionPool(pool); 1327 1328 HttpURLConnection connection = client.open(server.getUrl("/")); 1329 assertContent("{}", connection); 1330 assertEquals(0, client.client().getConnectionPool().getIdleConnectionCount()); 1331 } 1332 earlyDisconnectDoesntHarmPoolingWithChunkedEncoding()1333 @Test public void earlyDisconnectDoesntHarmPoolingWithChunkedEncoding() throws Exception { 1334 testEarlyDisconnectDoesntHarmPooling(TransferKind.CHUNKED); 1335 } 1336 earlyDisconnectDoesntHarmPoolingWithFixedLengthEncoding()1337 @Test public void earlyDisconnectDoesntHarmPoolingWithFixedLengthEncoding() throws Exception { 1338 testEarlyDisconnectDoesntHarmPooling(TransferKind.FIXED_LENGTH); 1339 } 1340 testEarlyDisconnectDoesntHarmPooling(TransferKind transferKind)1341 private void testEarlyDisconnectDoesntHarmPooling(TransferKind transferKind) throws Exception { 1342 MockResponse response1 = new MockResponse(); 1343 transferKind.setBody(response1, "ABCDEFGHIJK", 1024); 1344 server.enqueue(response1); 1345 1346 MockResponse response2 = new MockResponse(); 1347 transferKind.setBody(response2, "LMNOPQRSTUV", 1024); 1348 server.enqueue(response2); 1349 1350 HttpURLConnection connection1 = client.open(server.getUrl("/")); 1351 InputStream in1 = connection1.getInputStream(); 1352 assertEquals("ABCDE", readAscii(in1, 5)); 1353 in1.close(); 1354 connection1.disconnect(); 1355 1356 HttpURLConnection connection2 = client.open(server.getUrl("/")); 1357 InputStream in2 = connection2.getInputStream(); 1358 assertEquals("LMNOP", readAscii(in2, 5)); 1359 in2.close(); 1360 connection2.disconnect(); 1361 1362 assertEquals(0, server.takeRequest().getSequenceNumber()); 1363 assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection is pooled! 1364 } 1365 streamDiscardingIsTimely()1366 @Test public void streamDiscardingIsTimely() throws Exception { 1367 // This response takes at least a full second to serve: 10,000 bytes served 100 bytes at a time. 1368 server.enqueue(new MockResponse() 1369 .setBody(new Buffer().write(new byte[10000])) 1370 .throttleBody(100, 10, MILLISECONDS)); 1371 server.enqueue(new MockResponse().setBody("A")); 1372 1373 long startNanos = System.nanoTime(); 1374 URLConnection connection1 = client.open(server.getUrl("/")); 1375 InputStream in = connection1.getInputStream(); 1376 in.close(); 1377 long elapsedNanos = System.nanoTime() - startNanos; 1378 long elapsedMillis = NANOSECONDS.toMillis(elapsedNanos); 1379 1380 // If we're working correctly, this should be greater than 100ms, but less than double that. 1381 // Previously we had a bug where we would download the entire response body as long as no 1382 // individual read took longer than 100ms. 1383 assertTrue(String.format("Time to close: %sms", elapsedMillis), elapsedMillis < 500); 1384 1385 // Do another request to confirm that the discarded connection was not pooled. 1386 assertContent("A", client.open(server.getUrl("/"))); 1387 1388 assertEquals(0, server.takeRequest().getSequenceNumber()); 1389 assertEquals(0, server.takeRequest().getSequenceNumber()); // Connection is not pooled. 1390 } 1391 1392 @Test public void setChunkedStreamingMode() throws IOException, InterruptedException { 1393 server.enqueue(new MockResponse()); 1394 1395 String body = "ABCDEFGHIJKLMNOPQ"; 1396 connection = client.open(server.getUrl("/")); 1397 connection.setChunkedStreamingMode(0); // OkHttp does not honor specific chunk sizes. 1398 connection.setDoOutput(true); 1399 OutputStream outputStream = connection.getOutputStream(); 1400 outputStream.write(body.getBytes("US-ASCII")); 1401 assertEquals(200, connection.getResponseCode()); 1402 connection.getInputStream().close(); 1403 1404 RecordedRequest request = server.takeRequest(); 1405 assertEquals(body, request.getBody().readUtf8()); 1406 assertEquals(Arrays.asList(body.length()), request.getChunkSizes()); 1407 } 1408 1409 @Test public void authenticateWithFixedLengthStreaming() throws Exception { 1410 testAuthenticateWithStreamingPost(StreamingMode.FIXED_LENGTH); 1411 } 1412 1413 @Test public void authenticateWithChunkedStreaming() throws Exception { 1414 testAuthenticateWithStreamingPost(StreamingMode.CHUNKED); 1415 } 1416 1417 private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) throws Exception { 1418 MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401) 1419 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1420 .setBody("Please authenticate."); 1421 server.enqueue(pleaseAuthenticate); 1422 1423 Authenticator.setDefault(new RecordingAuthenticator()); 1424 connection = client.open(server.getUrl("/")); 1425 connection.setDoOutput(true); 1426 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1427 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1428 connection.setFixedLengthStreamingMode(requestBody.length); 1429 } else if (streamingMode == StreamingMode.CHUNKED) { 1430 connection.setChunkedStreamingMode(0); 1431 } 1432 OutputStream outputStream = connection.getOutputStream(); 1433 outputStream.write(requestBody); 1434 outputStream.close(); 1435 try { 1436 connection.getInputStream(); 1437 fail(); 1438 } catch (HttpRetryException expected) { 1439 } 1440 1441 // no authorization header for the request... 1442 RecordedRequest request = server.takeRequest(); 1443 assertNull(request.getHeader("Authorization")); 1444 assertEquals("ABCD", request.getBody().readUtf8()); 1445 } 1446 1447 @Test public void postBodyRetransmittedAfterAuthorizationFail() throws Exception { 1448 postBodyRetransmittedAfterAuthorizationFail("abc"); 1449 } 1450 1451 @Test public void postBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception { 1452 enableProtocol(Protocol.SPDY_3); 1453 postBodyRetransmittedAfterAuthorizationFail("abc"); 1454 } 1455 1456 @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception { 1457 enableProtocol(Protocol.HTTP_2); 1458 postBodyRetransmittedAfterAuthorizationFail("abc"); 1459 } 1460 1461 /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131 */ 1462 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail() throws Exception { 1463 postBodyRetransmittedAfterAuthorizationFail(""); 1464 } 1465 1466 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception { 1467 enableProtocol(Protocol.SPDY_3); 1468 postBodyRetransmittedAfterAuthorizationFail(""); 1469 } 1470 1471 @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception { 1472 enableProtocol(Protocol.HTTP_2); 1473 postBodyRetransmittedAfterAuthorizationFail(""); 1474 } 1475 1476 private void postBodyRetransmittedAfterAuthorizationFail(String body) throws Exception { 1477 server.enqueue(new MockResponse().setResponseCode(401)); 1478 server.enqueue(new MockResponse()); 1479 1480 String credential = Credentials.basic("jesse", "secret"); 1481 client.client().setAuthenticator(new RecordingOkAuthenticator(credential)); 1482 1483 connection = client.open(server.getUrl("/")); 1484 connection.setDoOutput(true); 1485 OutputStream outputStream = connection.getOutputStream(); 1486 outputStream.write(body.getBytes("UTF-8")); 1487 outputStream.close(); 1488 assertEquals(200, connection.getResponseCode()); 1489 1490 RecordedRequest recordedRequest1 = server.takeRequest(); 1491 assertEquals("POST", recordedRequest1.getMethod()); 1492 assertEquals(body, recordedRequest1.getBody().readUtf8()); 1493 assertNull(recordedRequest1.getHeader("Authorization")); 1494 1495 RecordedRequest recordedRequest2 = server.takeRequest(); 1496 assertEquals("POST", recordedRequest2.getMethod()); 1497 assertEquals(body, recordedRequest2.getBody().readUtf8()); 1498 assertEquals(credential, recordedRequest2.getHeader("Authorization")); 1499 } 1500 1501 @Test public void nonStandardAuthenticationScheme() throws Exception { 1502 List<String> calls = authCallsForHeader("WWW-Authenticate: Foo"); 1503 assertEquals(Collections.<String>emptyList(), calls); 1504 } 1505 1506 @Test public void nonStandardAuthenticationSchemeWithRealm() throws Exception { 1507 List<String> calls = authCallsForHeader("WWW-Authenticate: Foo realm=\"Bar\""); 1508 assertEquals(0, calls.size()); 1509 } 1510 1511 // Digest auth is currently unsupported. Test that digest requests should fail reasonably. 1512 // http://code.google.com/p/android/issues/detail?id=11140 1513 @Test public void digestAuthentication() throws Exception { 1514 List<String> calls = authCallsForHeader("WWW-Authenticate: Digest " 1515 + "realm=\"testrealm@host.com\", qop=\"auth,auth-int\", " 1516 + "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", " 1517 + "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""); 1518 assertEquals(0, calls.size()); 1519 } 1520 1521 @Test public void allAttributesSetInServerAuthenticationCallbacks() throws Exception { 1522 List<String> calls = authCallsForHeader("WWW-Authenticate: Basic realm=\"Bar\""); 1523 assertEquals(1, calls.size()); 1524 URL url = server.getUrl("/"); 1525 String call = calls.get(0); 1526 assertTrue(call, call.contains("host=" + url.getHost())); 1527 assertTrue(call, call.contains("port=" + url.getPort())); 1528 assertTrue(call, call.contains("site=" + url.getHost())); 1529 assertTrue(call, call.contains("url=" + url)); 1530 assertTrue(call, call.contains("type=" + Authenticator.RequestorType.SERVER)); 1531 assertTrue(call, call.contains("prompt=Bar")); 1532 assertTrue(call, call.contains("protocol=http")); 1533 assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI. 1534 } 1535 1536 @Test public void allAttributesSetInProxyAuthenticationCallbacks() throws Exception { 1537 List<String> calls = authCallsForHeader("Proxy-Authenticate: Basic realm=\"Bar\""); 1538 assertEquals(1, calls.size()); 1539 URL url = server.getUrl("/"); 1540 String call = calls.get(0); 1541 assertTrue(call, call.contains("host=" + url.getHost())); 1542 assertTrue(call, call.contains("port=" + url.getPort())); 1543 assertTrue(call, call.contains("site=" + url.getHost())); 1544 assertTrue(call, call.contains("url=http://android.com")); 1545 assertTrue(call, call.contains("type=" + Authenticator.RequestorType.PROXY)); 1546 assertTrue(call, call.contains("prompt=Bar")); 1547 assertTrue(call, call.contains("protocol=http")); 1548 assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI. 1549 } 1550 1551 private List<String> authCallsForHeader(String authHeader) throws IOException { 1552 boolean proxy = authHeader.startsWith("Proxy-"); 1553 int responseCode = proxy ? 407 : 401; 1554 RecordingAuthenticator authenticator = new RecordingAuthenticator(null); 1555 Authenticator.setDefault(authenticator); 1556 MockResponse pleaseAuthenticate = new MockResponse() 1557 .setResponseCode(responseCode) 1558 .addHeader(authHeader) 1559 .setBody("Please authenticate."); 1560 server.enqueue(pleaseAuthenticate); 1561 1562 if (proxy) { 1563 client.client().setProxy(server.toProxyAddress()); 1564 connection = client.open(new URL("http://android.com")); 1565 } else { 1566 connection = client.open(server.getUrl("/")); 1567 } 1568 assertEquals(responseCode, connection.getResponseCode()); 1569 connection.getErrorStream().close(); 1570 return authenticator.calls; 1571 } 1572 1573 @Test public void setValidRequestMethod() throws Exception { 1574 assertValidRequestMethod("GET"); 1575 assertValidRequestMethod("DELETE"); 1576 assertValidRequestMethod("HEAD"); 1577 assertValidRequestMethod("OPTIONS"); 1578 assertValidRequestMethod("POST"); 1579 assertValidRequestMethod("PUT"); 1580 assertValidRequestMethod("TRACE"); 1581 assertValidRequestMethod("PATCH"); 1582 } 1583 1584 private void assertValidRequestMethod(String requestMethod) throws Exception { 1585 connection = client.open(server.getUrl("/")); 1586 connection.setRequestMethod(requestMethod); 1587 assertEquals(requestMethod, connection.getRequestMethod()); 1588 } 1589 1590 @Test public void setInvalidRequestMethodLowercase() throws Exception { 1591 assertInvalidRequestMethod("get"); 1592 } 1593 1594 @Test public void setInvalidRequestMethodConnect() throws Exception { 1595 assertInvalidRequestMethod("CONNECT"); 1596 } 1597 1598 private void assertInvalidRequestMethod(String requestMethod) throws Exception { 1599 connection = client.open(server.getUrl("/")); 1600 try { 1601 connection.setRequestMethod(requestMethod); 1602 fail(); 1603 } catch (ProtocolException expected) { 1604 } 1605 } 1606 1607 @Test public void shoutcast() throws Exception { 1608 server.enqueue(new MockResponse().setStatus("ICY 200 OK") 1609 // .addHeader("HTTP/1.0 200 OK") 1610 .addHeader("Accept-Ranges: none") 1611 .addHeader("Content-Type: audio/mpeg") 1612 .addHeader("icy-br:128") 1613 .addHeader("ice-audio-info: bitrate=128;samplerate=44100;channels=2") 1614 .addHeader("icy-br:128") 1615 .addHeader("icy-description:Rock") 1616 .addHeader("icy-genre:riders") 1617 .addHeader("icy-name:A2RRock") 1618 .addHeader("icy-pub:1") 1619 .addHeader("icy-url:http://www.A2Rradio.com") 1620 .addHeader("Server: Icecast 2.3.3-kh8") 1621 .addHeader("Cache-Control: no-cache") 1622 .addHeader("Pragma: no-cache") 1623 .addHeader("Expires: Mon, 26 Jul 1997 05:00:00 GMT") 1624 .addHeader("icy-metaint:16000") 1625 .setBody("mp3 data")); 1626 connection = client.open(server.getUrl("/")); 1627 assertEquals(200, connection.getResponseCode()); 1628 assertEquals("OK", connection.getResponseMessage()); 1629 assertContent("mp3 data", connection); 1630 } 1631 1632 @Test public void cannotSetNegativeFixedLengthStreamingMode() throws Exception { 1633 connection = client.open(server.getUrl("/")); 1634 try { 1635 connection.setFixedLengthStreamingMode(-2); 1636 fail(); 1637 } catch (IllegalArgumentException expected) { 1638 } 1639 } 1640 1641 @Test public void canSetNegativeChunkedStreamingMode() throws Exception { 1642 connection = client.open(server.getUrl("/")); 1643 connection.setChunkedStreamingMode(-2); 1644 } 1645 1646 @Test public void cannotSetFixedLengthStreamingModeAfterConnect() throws Exception { 1647 server.enqueue(new MockResponse().setBody("A")); 1648 connection = client.open(server.getUrl("/")); 1649 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1650 try { 1651 connection.setFixedLengthStreamingMode(1); 1652 fail(); 1653 } catch (IllegalStateException expected) { 1654 } 1655 } 1656 1657 @Test public void cannotSetChunkedStreamingModeAfterConnect() throws Exception { 1658 server.enqueue(new MockResponse().setBody("A")); 1659 connection = client.open(server.getUrl("/")); 1660 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1661 try { 1662 connection.setChunkedStreamingMode(1); 1663 fail(); 1664 } catch (IllegalStateException expected) { 1665 } 1666 } 1667 1668 @Test public void cannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception { 1669 connection = client.open(server.getUrl("/")); 1670 connection.setChunkedStreamingMode(1); 1671 try { 1672 connection.setFixedLengthStreamingMode(1); 1673 fail(); 1674 } catch (IllegalStateException expected) { 1675 } 1676 } 1677 1678 @Test public void cannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception { 1679 connection = client.open(server.getUrl("/")); 1680 connection.setFixedLengthStreamingMode(1); 1681 try { 1682 connection.setChunkedStreamingMode(1); 1683 fail(); 1684 } catch (IllegalStateException expected) { 1685 } 1686 } 1687 1688 @Test public void secureFixedLengthStreaming() throws Exception { 1689 testSecureStreamingPost(StreamingMode.FIXED_LENGTH); 1690 } 1691 1692 @Test public void secureChunkedStreaming() throws Exception { 1693 testSecureStreamingPost(StreamingMode.CHUNKED); 1694 } 1695 1696 /** 1697 * Users have reported problems using HTTPS with streaming request bodies. 1698 * http://code.google.com/p/android/issues/detail?id=12860 1699 */ 1700 private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception { 1701 server.useHttps(sslContext.getSocketFactory(), false); 1702 server.enqueue(new MockResponse().setBody("Success!")); 1703 1704 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1705 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1706 connection = client.open(server.getUrl("/")); 1707 connection.setDoOutput(true); 1708 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1709 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1710 connection.setFixedLengthStreamingMode(requestBody.length); 1711 } else if (streamingMode == StreamingMode.CHUNKED) { 1712 connection.setChunkedStreamingMode(0); 1713 } 1714 OutputStream outputStream = connection.getOutputStream(); 1715 outputStream.write(requestBody); 1716 outputStream.close(); 1717 assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1718 1719 RecordedRequest request = server.takeRequest(); 1720 assertEquals("POST / HTTP/1.1", request.getRequestLine()); 1721 if (streamingMode == StreamingMode.FIXED_LENGTH) { 1722 assertEquals(Collections.<Integer>emptyList(), request.getChunkSizes()); 1723 } else if (streamingMode == StreamingMode.CHUNKED) { 1724 assertEquals(Arrays.asList(4), request.getChunkSizes()); 1725 } 1726 assertEquals("ABCD", request.getBody().readUtf8()); 1727 } 1728 1729 enum StreamingMode { 1730 FIXED_LENGTH, CHUNKED 1731 } 1732 1733 @Test public void authenticateWithPost() throws Exception { 1734 MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401) 1735 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1736 .setBody("Please authenticate."); 1737 // fail auth three times... 1738 server.enqueue(pleaseAuthenticate); 1739 server.enqueue(pleaseAuthenticate); 1740 server.enqueue(pleaseAuthenticate); 1741 // ...then succeed the fourth time 1742 server.enqueue(new MockResponse().setBody("Successful auth!")); 1743 1744 Authenticator.setDefault(new RecordingAuthenticator()); 1745 connection = client.open(server.getUrl("/")); 1746 connection.setDoOutput(true); 1747 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 1748 OutputStream outputStream = connection.getOutputStream(); 1749 outputStream.write(requestBody); 1750 outputStream.close(); 1751 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1752 1753 // no authorization header for the first request... 1754 RecordedRequest request = server.takeRequest(); 1755 assertNull(request.getHeader("Authorization")); 1756 1757 // ...but the three requests that follow include an authorization header 1758 for (int i = 0; i < 3; i++) { 1759 request = server.takeRequest(); 1760 assertEquals("POST / HTTP/1.1", request.getRequestLine()); 1761 assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS, 1762 request.getHeader("Authorization")); 1763 assertEquals("ABCD", request.getBody().readUtf8()); 1764 } 1765 } 1766 1767 @Test public void authenticateWithGet() throws Exception { 1768 MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401) 1769 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1770 .setBody("Please authenticate."); 1771 // fail auth three times... 1772 server.enqueue(pleaseAuthenticate); 1773 server.enqueue(pleaseAuthenticate); 1774 server.enqueue(pleaseAuthenticate); 1775 // ...then succeed the fourth time 1776 server.enqueue(new MockResponse().setBody("Successful auth!")); 1777 1778 Authenticator.setDefault(new RecordingAuthenticator()); 1779 connection = client.open(server.getUrl("/")); 1780 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1781 1782 // no authorization header for the first request... 1783 RecordedRequest request = server.takeRequest(); 1784 assertNull(request.getHeader("Authorization")); 1785 1786 // ...but the three requests that follow requests include an authorization header 1787 for (int i = 0; i < 3; i++) { 1788 request = server.takeRequest(); 1789 assertEquals("GET / HTTP/1.1", request.getRequestLine()); 1790 assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS, 1791 request.getHeader("Authorization")); 1792 } 1793 } 1794 1795 /** https://code.google.com/p/android/issues/detail?id=74026 */ 1796 @Test public void authenticateWithGetAndTransparentGzip() throws Exception { 1797 MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401) 1798 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 1799 .setBody("Please authenticate."); 1800 // fail auth three times... 1801 server.enqueue(pleaseAuthenticate); 1802 server.enqueue(pleaseAuthenticate); 1803 server.enqueue(pleaseAuthenticate); 1804 // ...then succeed the fourth time 1805 MockResponse successfulResponse = new MockResponse() 1806 .addHeader("Content-Encoding", "gzip") 1807 .setBody(gzip("Successful auth!")); 1808 server.enqueue(successfulResponse); 1809 1810 Authenticator.setDefault(new RecordingAuthenticator()); 1811 connection = client.open(server.getUrl("/")); 1812 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1813 1814 // no authorization header for the first request... 1815 RecordedRequest request = server.takeRequest(); 1816 assertNull(request.getHeader("Authorization")); 1817 1818 // ...but the three requests that follow requests include an authorization header 1819 for (int i = 0; i < 3; i++) { 1820 request = server.takeRequest(); 1821 assertEquals("GET / HTTP/1.1", request.getRequestLine()); 1822 assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS, 1823 request.getHeader("Authorization")); 1824 } 1825 } 1826 1827 /** https://github.com/square/okhttp/issues/342 */ 1828 @Test public void authenticateRealmUppercase() throws Exception { 1829 server.enqueue(new MockResponse().setResponseCode(401) 1830 .addHeader("wWw-aUtHeNtIcAtE: bAsIc rEaLm=\"pRoTeCtEd aReA\"") 1831 .setBody("Please authenticate.")); 1832 server.enqueue(new MockResponse().setBody("Successful auth!")); 1833 1834 Authenticator.setDefault(new RecordingAuthenticator()); 1835 connection = client.open(server.getUrl("/")); 1836 assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1837 } 1838 1839 @Test public void redirectedWithChunkedEncoding() throws Exception { 1840 testRedirected(TransferKind.CHUNKED, true); 1841 } 1842 1843 @Test public void redirectedWithContentLengthHeader() throws Exception { 1844 testRedirected(TransferKind.FIXED_LENGTH, true); 1845 } 1846 1847 @Test public void redirectedWithNoLengthHeaders() throws Exception { 1848 testRedirected(TransferKind.END_OF_STREAM, false); 1849 } 1850 1851 private void testRedirected(TransferKind transferKind, boolean reuse) throws Exception { 1852 MockResponse response = new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1853 .addHeader("Location: /foo"); 1854 transferKind.setBody(response, "This page has moved!", 10); 1855 server.enqueue(response); 1856 server.enqueue(new MockResponse().setBody("This is the new location!")); 1857 1858 URLConnection connection = client.open(server.getUrl("/")); 1859 assertEquals("This is the new location!", 1860 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1861 1862 RecordedRequest first = server.takeRequest(); 1863 assertEquals("GET / HTTP/1.1", first.getRequestLine()); 1864 RecordedRequest retry = server.takeRequest(); 1865 assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); 1866 if (reuse) { 1867 assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); 1868 } 1869 } 1870 1871 @Test public void redirectedOnHttps() throws IOException, InterruptedException { 1872 server.useHttps(sslContext.getSocketFactory(), false); 1873 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1874 .addHeader("Location: /foo") 1875 .setBody("This page has moved!")); 1876 server.enqueue(new MockResponse().setBody("This is the new location!")); 1877 1878 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1879 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1880 connection = client.open(server.getUrl("/")); 1881 assertEquals("This is the new location!", 1882 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1883 1884 RecordedRequest first = server.takeRequest(); 1885 assertEquals("GET / HTTP/1.1", first.getRequestLine()); 1886 RecordedRequest retry = server.takeRequest(); 1887 assertEquals("GET /foo HTTP/1.1", retry.getRequestLine()); 1888 assertEquals("Expected connection reuse", 1, retry.getSequenceNumber()); 1889 } 1890 1891 @Test public void notRedirectedFromHttpsToHttp() throws IOException, InterruptedException { 1892 server.useHttps(sslContext.getSocketFactory(), false); 1893 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1894 .addHeader("Location: http://anyhost/foo") 1895 .setBody("This page has moved!")); 1896 1897 client.client().setFollowSslRedirects(false); 1898 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1899 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1900 connection = client.open(server.getUrl("/")); 1901 assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1902 } 1903 1904 @Test public void notRedirectedFromHttpToHttps() throws IOException, InterruptedException { 1905 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1906 .addHeader("Location: https://anyhost/foo") 1907 .setBody("This page has moved!")); 1908 1909 client.client().setFollowSslRedirects(false); 1910 connection = client.open(server.getUrl("/")); 1911 assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 1912 } 1913 1914 @Test public void redirectedFromHttpsToHttpFollowingProtocolRedirects() throws Exception { 1915 server2.enqueue(new MockResponse().setBody("This is insecure HTTP!")); 1916 1917 server.useHttps(sslContext.getSocketFactory(), false); 1918 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1919 .addHeader("Location: " + server2.getUrl("/")) 1920 .setBody("This page has moved!")); 1921 1922 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1923 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1924 client.client().setFollowSslRedirects(true); 1925 HttpsURLConnection connection = (HttpsURLConnection) client.open(server.getUrl("/")); 1926 assertContent("This is insecure HTTP!", connection); 1927 assertNull(connection.getCipherSuite()); 1928 assertNull(connection.getLocalCertificates()); 1929 assertNull(connection.getServerCertificates()); 1930 assertNull(connection.getPeerPrincipal()); 1931 assertNull(connection.getLocalPrincipal()); 1932 } 1933 1934 @Test public void redirectedFromHttpToHttpsFollowingProtocolRedirects() throws Exception { 1935 server2.useHttps(sslContext.getSocketFactory(), false); 1936 server2.enqueue(new MockResponse().setBody("This is secure HTTPS!")); 1937 1938 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1939 .addHeader("Location: " + server2.getUrl("/")) 1940 .setBody("This page has moved!")); 1941 1942 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1943 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1944 client.client().setFollowSslRedirects(true); 1945 connection = client.open(server.getUrl("/")); 1946 assertContent("This is secure HTTPS!", connection); 1947 assertFalse(connection instanceof HttpsURLConnection); 1948 } 1949 1950 @Test public void redirectToAnotherOriginServer() throws Exception { 1951 redirectToAnotherOriginServer(false); 1952 } 1953 1954 @Test public void redirectToAnotherOriginServerWithHttps() throws Exception { 1955 redirectToAnotherOriginServer(true); 1956 } 1957 1958 private void redirectToAnotherOriginServer(boolean https) throws Exception { 1959 if (https) { 1960 server.useHttps(sslContext.getSocketFactory(), false); 1961 server2.useHttps(sslContext.getSocketFactory(), false); 1962 server2.setProtocolNegotiationEnabled(false); 1963 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 1964 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 1965 } 1966 1967 server2.enqueue(new MockResponse().setBody("This is the 2nd server!")); 1968 server2.enqueue(new MockResponse().setBody("This is the 2nd server, again!")); 1969 1970 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 1971 .addHeader("Location: " + server2.getUrl("/").toString()) 1972 .setBody("This page has moved!")); 1973 server.enqueue(new MockResponse().setBody("This is the first server again!")); 1974 1975 connection = client.open(server.getUrl("/")); 1976 assertContent("This is the 2nd server!", connection); 1977 assertEquals(server2.getUrl("/"), connection.getURL()); 1978 1979 // make sure the first server was careful to recycle the connection 1980 assertContent("This is the first server again!", client.open(server.getUrl("/"))); 1981 assertContent("This is the 2nd server, again!", client.open(server2.getUrl("/"))); 1982 1983 String server1Host = server.getHostName() + ":" + server.getPort(); 1984 String server2Host = server2.getHostName() + ":" + server2.getPort(); 1985 assertEquals(server1Host, server.takeRequest().getHeader("Host")); 1986 assertEquals(server2Host, server2.takeRequest().getHeader("Host")); 1987 assertEquals("Expected connection reuse", 1, server.takeRequest().getSequenceNumber()); 1988 assertEquals("Expected connection reuse", 1, server2.takeRequest().getSequenceNumber()); 1989 } 1990 1991 @Test public void redirectWithProxySelector() throws Exception { 1992 final List<URI> proxySelectionRequests = new ArrayList<URI>(); 1993 client.client().setProxySelector(new ProxySelector() { 1994 @Override public List<Proxy> select(URI uri) { 1995 proxySelectionRequests.add(uri); 1996 MockWebServer proxyServer = (uri.getPort() == server.getPort()) 1997 ? server 1998 : server2; 1999 return Arrays.asList(proxyServer.toProxyAddress()); 2000 } 2001 2002 @Override public void connectFailed(URI uri, SocketAddress address, IOException failure) { 2003 throw new AssertionError(); 2004 } 2005 }); 2006 2007 server2.enqueue(new MockResponse().setBody("This is the 2nd server!")); 2008 2009 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 2010 .addHeader("Location: " + server2.getUrl("/b").toString()) 2011 .setBody("This page has moved!")); 2012 2013 assertContent("This is the 2nd server!", client.open(server.getUrl("/a"))); 2014 2015 assertEquals(Arrays.asList(server.getUrl("/").toURI(), server2.getUrl("/").toURI()), 2016 proxySelectionRequests); 2017 } 2018 2019 @Test public void redirectWithAuthentication() throws Exception { 2020 server2.enqueue(new MockResponse().setBody("Page 2")); 2021 2022 server.enqueue(new MockResponse().setResponseCode(401)); 2023 server.enqueue(new MockResponse().setResponseCode(302) 2024 .addHeader("Location: " + server2.getUrl("/b"))); 2025 2026 client.client().setAuthenticator( 2027 new RecordingOkAuthenticator(Credentials.basic("jesse", "secret"))); 2028 assertContent("Page 2", client.open(server.getUrl("/a"))); 2029 2030 RecordedRequest redirectRequest = server2.takeRequest(); 2031 assertNull(redirectRequest.getHeader("Authorization")); 2032 assertEquals("/b", redirectRequest.getPath()); 2033 } 2034 2035 @Test public void response300MultipleChoiceWithPost() throws Exception { 2036 // Chrome doesn't follow the redirect, but Firefox and the RI both do 2037 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE, TransferKind.END_OF_STREAM); 2038 } 2039 2040 @Test public void response301MovedPermanentlyWithPost() throws Exception { 2041 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM, TransferKind.END_OF_STREAM); 2042 } 2043 2044 @Test public void response302MovedTemporarilyWithPost() throws Exception { 2045 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.END_OF_STREAM); 2046 } 2047 2048 @Test public void response303SeeOtherWithPost() throws Exception { 2049 testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER, TransferKind.END_OF_STREAM); 2050 } 2051 2052 @Test public void postRedirectToGetWithChunkedRequest() throws Exception { 2053 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.CHUNKED); 2054 } 2055 2056 @Test public void postRedirectToGetWithStreamedRequest() throws Exception { 2057 testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.FIXED_LENGTH); 2058 } 2059 2060 private void testResponseRedirectedWithPost(int redirectCode, TransferKind transferKind) 2061 throws Exception { 2062 server.enqueue(new MockResponse().setResponseCode(redirectCode) 2063 .addHeader("Location: /page2") 2064 .setBody("This page has moved!")); 2065 server.enqueue(new MockResponse().setBody("Page 2")); 2066 2067 connection = client.open(server.getUrl("/page1")); 2068 connection.setDoOutput(true); 2069 transferKind.setForRequest(connection, 4); 2070 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 2071 OutputStream outputStream = connection.getOutputStream(); 2072 outputStream.write(requestBody); 2073 outputStream.close(); 2074 assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2075 assertTrue(connection.getDoOutput()); 2076 2077 RecordedRequest page1 = server.takeRequest(); 2078 assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine()); 2079 assertEquals("ABCD", page1.getBody().readUtf8()); 2080 2081 RecordedRequest page2 = server.takeRequest(); 2082 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 2083 } 2084 2085 @Test public void redirectedPostStripsRequestBodyHeaders() throws Exception { 2086 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 2087 .addHeader("Location: /page2")); 2088 server.enqueue(new MockResponse().setBody("Page 2")); 2089 2090 connection = client.open(server.getUrl("/page1")); 2091 connection.setDoOutput(true); 2092 connection.addRequestProperty("Content-Length", "4"); 2093 connection.addRequestProperty("Content-Type", "text/plain; charset=utf-8"); 2094 connection.addRequestProperty("Transfer-Encoding", "identity"); 2095 OutputStream outputStream = connection.getOutputStream(); 2096 outputStream.write("ABCD".getBytes("UTF-8")); 2097 outputStream.close(); 2098 assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2099 2100 assertEquals("POST /page1 HTTP/1.1", server.takeRequest().getRequestLine()); 2101 2102 RecordedRequest page2 = server.takeRequest(); 2103 assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine()); 2104 assertNull(page2.getHeader("Content-Length")); 2105 assertNull(page2.getHeader("Content-Type")); 2106 assertNull(page2.getHeader("Transfer-Encoding")); 2107 } 2108 2109 @Test public void response305UseProxy() throws Exception { 2110 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_USE_PROXY) 2111 .addHeader("Location: " + server.getUrl("/")) 2112 .setBody("This page has moved!")); 2113 server.enqueue(new MockResponse().setBody("Proxy Response")); 2114 2115 connection = client.open(server.getUrl("/foo")); 2116 // Fails on the RI, which gets "Proxy Response" 2117 assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2118 2119 RecordedRequest page1 = server.takeRequest(); 2120 assertEquals("GET /foo HTTP/1.1", page1.getRequestLine()); 2121 assertEquals(1, server.getRequestCount()); 2122 } 2123 2124 @Test public void response307WithGet() throws Exception { 2125 testRedirect(true, "GET"); 2126 } 2127 2128 @Test public void response307WithHead() throws Exception { 2129 testRedirect(true, "HEAD"); 2130 } 2131 2132 @Test public void response307WithOptions() throws Exception { 2133 testRedirect(true, "OPTIONS"); 2134 } 2135 2136 @Test public void response307WithPost() throws Exception { 2137 testRedirect(true, "POST"); 2138 } 2139 2140 @Test public void response308WithGet() throws Exception { 2141 testRedirect(false, "GET"); 2142 } 2143 2144 @Test public void response308WithHead() throws Exception { 2145 testRedirect(false, "HEAD"); 2146 } 2147 2148 @Test public void response308WithOptions() throws Exception { 2149 testRedirect(false, "OPTIONS"); 2150 } 2151 2152 @Test public void response308WithPost() throws Exception { 2153 testRedirect(false, "POST"); 2154 } 2155 2156 private void testRedirect(boolean temporary, String method) throws Exception { 2157 MockResponse response1 = new MockResponse() 2158 .setResponseCode(temporary ? HTTP_TEMP_REDIRECT : HTTP_PERM_REDIRECT) 2159 .addHeader("Location: /page2"); 2160 if (!method.equals("HEAD")) { 2161 response1.setBody("This page has moved!"); 2162 } 2163 server.enqueue(response1); 2164 server.enqueue(new MockResponse().setBody("Page 2")); 2165 2166 connection = client.open(server.getUrl("/page1")); 2167 connection.setRequestMethod(method); 2168 byte[] requestBody = { 'A', 'B', 'C', 'D' }; 2169 if (method.equals("POST")) { 2170 connection.setDoOutput(true); 2171 OutputStream outputStream = connection.getOutputStream(); 2172 outputStream.write(requestBody); 2173 outputStream.close(); 2174 } 2175 2176 String response = readAscii(connection.getInputStream(), Integer.MAX_VALUE); 2177 2178 RecordedRequest page1 = server.takeRequest(); 2179 assertEquals(method + " /page1 HTTP/1.1", page1.getRequestLine()); 2180 2181 if (method.equals("GET")) { 2182 assertEquals("Page 2", response); 2183 } else if (method.equals("HEAD")) { 2184 assertEquals("", response); 2185 } else { 2186 // Methods other than GET/HEAD shouldn't follow the redirect 2187 if (method.equals("POST")) { 2188 assertTrue(connection.getDoOutput()); 2189 assertEquals("ABCD", page1.getBody().readUtf8()); 2190 } 2191 assertEquals(1, server.getRequestCount()); 2192 assertEquals("This page has moved!", response); 2193 return; 2194 } 2195 2196 // GET/HEAD requests should have followed the redirect with the same method 2197 assertFalse(connection.getDoOutput()); 2198 assertEquals(2, server.getRequestCount()); 2199 RecordedRequest page2 = server.takeRequest(); 2200 assertEquals(method + " /page2 HTTP/1.1", page2.getRequestLine()); 2201 } 2202 2203 @Test public void follow20Redirects() throws Exception { 2204 for (int i = 0; i < 20; i++) { 2205 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 2206 .addHeader("Location: /" + (i + 1)) 2207 .setBody("Redirecting to /" + (i + 1))); 2208 } 2209 server.enqueue(new MockResponse().setBody("Success!")); 2210 2211 connection = client.open(server.getUrl("/0")); 2212 assertContent("Success!", connection); 2213 assertEquals(server.getUrl("/20"), connection.getURL()); 2214 } 2215 2216 @Test public void doesNotFollow21Redirects() throws Exception { 2217 for (int i = 0; i < 21; i++) { 2218 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 2219 .addHeader("Location: /" + (i + 1)) 2220 .setBody("Redirecting to /" + (i + 1))); 2221 } 2222 2223 connection = client.open(server.getUrl("/0")); 2224 try { 2225 connection.getInputStream(); 2226 fail(); 2227 } catch (ProtocolException expected) { 2228 assertEquals(HttpURLConnection.HTTP_MOVED_TEMP, connection.getResponseCode()); 2229 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 2230 assertContent("Redirecting to /21", connection); 2231 assertEquals(server.getUrl("/20"), connection.getURL()); 2232 } 2233 } 2234 2235 @Test public void httpsWithCustomTrustManager() throws Exception { 2236 RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 2237 RecordingTrustManager trustManager = new RecordingTrustManager(sslContext); 2238 SSLContext sc = SSLContext.getInstance("TLS"); 2239 sc.init(null, new TrustManager[] {trustManager}, new SecureRandom()); 2240 2241 client.client().setHostnameVerifier(hostnameVerifier); 2242 client.client().setSslSocketFactory(sc.getSocketFactory()); 2243 server.useHttps(sslContext.getSocketFactory(), false); 2244 server.enqueue(new MockResponse().setBody("ABC")); 2245 server.enqueue(new MockResponse().setBody("DEF")); 2246 server.enqueue(new MockResponse().setBody("GHI")); 2247 2248 URL url = server.getUrl("/"); 2249 assertContent("ABC", client.open(url)); 2250 assertContent("DEF", client.open(url)); 2251 assertContent("GHI", client.open(url)); 2252 2253 assertEquals(Arrays.asList("verify " + server.getHostName()), hostnameVerifier.calls); 2254 assertEquals(Arrays.asList("checkServerTrusted [CN=" + server.getHostName() + " 1]"), 2255 trustManager.calls); 2256 } 2257 2258 @Test public void readTimeouts() throws IOException { 2259 // This relies on the fact that MockWebServer doesn't close the 2260 // connection after a response has been sent. This causes the client to 2261 // try to read more bytes than are sent, which results in a timeout. 2262 MockResponse timeout = 2263 new MockResponse().setBody("ABC").clearHeaders().addHeader("Content-Length: 4"); 2264 server.enqueue(timeout); 2265 server.enqueue(new MockResponse().setBody("unused")); // to keep the server alive 2266 2267 URLConnection connection = client.open(server.getUrl("/")); 2268 connection.setReadTimeout(1000); 2269 InputStream in = connection.getInputStream(); 2270 assertEquals('A', in.read()); 2271 assertEquals('B', in.read()); 2272 assertEquals('C', in.read()); 2273 try { 2274 in.read(); // if Content-Length was accurate, this would return -1 immediately 2275 fail(); 2276 } catch (SocketTimeoutException expected) { 2277 } 2278 in.close(); 2279 } 2280 2281 /** Confirm that an unacknowledged write times out. */ 2282 @Test public void writeTimeouts() throws IOException { 2283 MockWebServer server = new MockWebServer(); 2284 // Sockets on some platforms can have large buffers that mean writes do not block when 2285 // required. These socket factories explicitly set the buffer sizes on sockets created. 2286 final int SOCKET_BUFFER_SIZE = 4 * 1024; 2287 server.setServerSocketFactory( 2288 new DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) { 2289 @Override 2290 protected ServerSocket configureServerSocket(ServerSocket serverSocket) 2291 throws IOException { 2292 serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); 2293 return serverSocket; 2294 } 2295 }); 2296 client.client().setSocketFactory(new DelegatingSocketFactory(SocketFactory.getDefault()) { 2297 @Override 2298 protected Socket configureSocket(Socket socket) throws IOException { 2299 socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); 2300 socket.setSendBufferSize(SOCKET_BUFFER_SIZE); 2301 return socket; 2302 } 2303 }); 2304 2305 server.start(); 2306 server.enqueue(new MockResponse() 2307 .throttleBody(1, 1, TimeUnit.SECONDS)); // Prevent the server from reading! 2308 2309 client.client().setWriteTimeout(500, TimeUnit.MILLISECONDS); 2310 connection = client.open(server.getUrl("/")); 2311 connection.setDoOutput(true); 2312 connection.setChunkedStreamingMode(0); 2313 OutputStream out = connection.getOutputStream(); 2314 try { 2315 byte[] data = new byte[2 * 1024 * 1024]; // 2 MiB. 2316 out.write(data); 2317 fail(); 2318 } catch (SocketTimeoutException expected) { 2319 } 2320 } 2321 2322 @Test public void setChunkedEncodingAsRequestProperty() throws IOException, InterruptedException { 2323 server.enqueue(new MockResponse()); 2324 2325 connection = client.open(server.getUrl("/")); 2326 connection.setRequestProperty("Transfer-encoding", "chunked"); 2327 connection.setDoOutput(true); 2328 connection.getOutputStream().write("ABC".getBytes("UTF-8")); 2329 assertEquals(200, connection.getResponseCode()); 2330 2331 RecordedRequest request = server.takeRequest(); 2332 assertEquals("ABC", request.getBody().readUtf8()); 2333 } 2334 2335 @Test public void connectionCloseInRequest() throws IOException, InterruptedException { 2336 server.enqueue(new MockResponse()); // server doesn't honor the connection: close header! 2337 server.enqueue(new MockResponse()); 2338 2339 HttpURLConnection a = client.open(server.getUrl("/")); 2340 a.setRequestProperty("Connection", "close"); 2341 assertEquals(200, a.getResponseCode()); 2342 2343 HttpURLConnection b = client.open(server.getUrl("/")); 2344 assertEquals(200, b.getResponseCode()); 2345 2346 assertEquals(0, server.takeRequest().getSequenceNumber()); 2347 assertEquals("When connection: close is used, each request should get its own connection", 0, 2348 server.takeRequest().getSequenceNumber()); 2349 } 2350 2351 @Test public void connectionCloseInResponse() throws IOException, InterruptedException { 2352 server.enqueue(new MockResponse().addHeader("Connection: close")); 2353 server.enqueue(new MockResponse()); 2354 2355 HttpURLConnection a = client.open(server.getUrl("/")); 2356 assertEquals(200, a.getResponseCode()); 2357 2358 HttpURLConnection b = client.open(server.getUrl("/")); 2359 assertEquals(200, b.getResponseCode()); 2360 2361 assertEquals(0, server.takeRequest().getSequenceNumber()); 2362 assertEquals("When connection: close is used, each request should get its own connection", 0, 2363 server.takeRequest().getSequenceNumber()); 2364 } 2365 2366 @Test public void connectionCloseWithRedirect() throws IOException, InterruptedException { 2367 MockResponse response = new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 2368 .addHeader("Location: /foo") 2369 .addHeader("Connection: close"); 2370 server.enqueue(response); 2371 server.enqueue(new MockResponse().setBody("This is the new location!")); 2372 2373 URLConnection connection = client.open(server.getUrl("/")); 2374 assertEquals("This is the new location!", 2375 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2376 2377 assertEquals(0, server.takeRequest().getSequenceNumber()); 2378 assertEquals("When connection: close is used, each request should get its own connection", 0, 2379 server.takeRequest().getSequenceNumber()); 2380 } 2381 2382 /** 2383 * Retry redirects if the socket is closed. 2384 * https://code.google.com/p/android/issues/detail?id=41576 2385 */ 2386 @Test public void sameConnectionRedirectAndReuse() throws Exception { 2387 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 2388 .setSocketPolicy(SHUTDOWN_INPUT_AT_END) 2389 .addHeader("Location: /foo")); 2390 server.enqueue(new MockResponse().setBody("This is the new page!")); 2391 2392 assertContent("This is the new page!", client.open(server.getUrl("/"))); 2393 2394 assertEquals(0, server.takeRequest().getSequenceNumber()); 2395 assertEquals(0, server.takeRequest().getSequenceNumber()); 2396 } 2397 2398 @Test public void responseCodeDisagreesWithHeaders() throws IOException, InterruptedException { 2399 server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NO_CONTENT) 2400 .setBody("This body is not allowed!")); 2401 2402 URLConnection connection = client.open(server.getUrl("/")); 2403 assertEquals("This body is not allowed!", 2404 readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2405 } 2406 2407 @Test public void singleByteReadIsSigned() throws IOException { 2408 server.enqueue(new MockResponse().setBody(new Buffer().writeByte(-2).writeByte(-1))); 2409 2410 connection = client.open(server.getUrl("/")); 2411 InputStream in = connection.getInputStream(); 2412 assertEquals(254, in.read()); 2413 assertEquals(255, in.read()); 2414 assertEquals(-1, in.read()); 2415 } 2416 2417 @Test public void flushAfterStreamTransmittedWithChunkedEncoding() throws IOException { 2418 testFlushAfterStreamTransmitted(TransferKind.CHUNKED); 2419 } 2420 2421 @Test public void flushAfterStreamTransmittedWithFixedLength() throws IOException { 2422 testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH); 2423 } 2424 2425 @Test public void flushAfterStreamTransmittedWithNoLengthHeaders() throws IOException { 2426 testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM); 2427 } 2428 2429 /** 2430 * We explicitly permit apps to close the upload stream even after it has 2431 * been transmitted. We also permit flush so that buffered streams can 2432 * do a no-op flush when they are closed. http://b/3038470 2433 */ 2434 private void testFlushAfterStreamTransmitted(TransferKind transferKind) throws IOException { 2435 server.enqueue(new MockResponse().setBody("abc")); 2436 2437 connection = client.open(server.getUrl("/")); 2438 connection.setDoOutput(true); 2439 byte[] upload = "def".getBytes("UTF-8"); 2440 2441 if (transferKind == TransferKind.CHUNKED) { 2442 connection.setChunkedStreamingMode(0); 2443 } else if (transferKind == TransferKind.FIXED_LENGTH) { 2444 connection.setFixedLengthStreamingMode(upload.length); 2445 } 2446 2447 OutputStream out = connection.getOutputStream(); 2448 out.write(upload); 2449 assertEquals("abc", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2450 2451 out.flush(); // Dubious but permitted. 2452 try { 2453 out.write("ghi".getBytes("UTF-8")); 2454 fail(); 2455 } catch (IOException expected) { 2456 } 2457 } 2458 2459 @Test public void getHeadersThrows() throws IOException { 2460 server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START)); 2461 2462 connection = client.open(server.getUrl("/")); 2463 try { 2464 connection.getInputStream(); 2465 fail(); 2466 } catch (IOException expected) { 2467 } 2468 2469 try { 2470 connection.getInputStream(); 2471 fail(); 2472 } catch (IOException expected) { 2473 } 2474 } 2475 2476 @Test public void dnsFailureThrowsIOException() throws IOException { 2477 connection = client.open(new URL("http://host.unlikelytld")); 2478 try { 2479 connection.connect(); 2480 fail(); 2481 } catch (IOException expected) { 2482 } 2483 } 2484 2485 @Test public void malformedUrlThrowsUnknownHostException() throws IOException { 2486 connection = client.open(new URL("http://./foo.html")); 2487 try { 2488 connection.connect(); 2489 fail(); 2490 } catch (UnknownHostException expected) { 2491 } 2492 } 2493 2494 @Test public void getKeepAlive() throws Exception { 2495 server.enqueue(new MockResponse().setBody("ABC")); 2496 2497 // The request should work once and then fail 2498 HttpURLConnection connection1 = client.open(server.getUrl("/")); 2499 connection1.setReadTimeout(100); 2500 InputStream input = connection1.getInputStream(); 2501 assertEquals("ABC", readAscii(input, Integer.MAX_VALUE)); 2502 server.shutdown(); 2503 try { 2504 HttpURLConnection connection2 = client.open(server.getUrl("")); 2505 connection2.setReadTimeout(100); 2506 connection2.getInputStream(); 2507 fail(); 2508 } catch (ConnectException expected) { 2509 } 2510 } 2511 2512 /** http://code.google.com/p/android/issues/detail?id=14562 */ 2513 @Test public void readAfterLastByte() throws Exception { 2514 server.enqueue(new MockResponse().setBody("ABC") 2515 .clearHeaders() 2516 .addHeader("Connection: close") 2517 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)); 2518 2519 connection = client.open(server.getUrl("/")); 2520 InputStream in = connection.getInputStream(); 2521 assertEquals("ABC", readAscii(in, 3)); 2522 assertEquals(-1, in.read()); 2523 assertEquals(-1, in.read()); // throws IOException in Gingerbread 2524 } 2525 2526 @Test public void getContent() throws Exception { 2527 server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("A")); 2528 connection = client.open(server.getUrl("/")); 2529 InputStream in = (InputStream) connection.getContent(); 2530 assertEquals("A", readAscii(in, Integer.MAX_VALUE)); 2531 } 2532 2533 @Test public void getContentOfType() throws Exception { 2534 server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("A")); 2535 connection = client.open(server.getUrl("/")); 2536 try { 2537 connection.getContent(null); 2538 fail(); 2539 } catch (NullPointerException expected) { 2540 } 2541 try { 2542 connection.getContent(new Class[] { null }); 2543 fail(); 2544 } catch (NullPointerException expected) { 2545 } 2546 assertNull(connection.getContent(new Class[] { getClass() })); 2547 connection.getInputStream().close(); 2548 } 2549 2550 @Test public void getOutputStreamOnGetFails() throws Exception { 2551 server.enqueue(new MockResponse()); 2552 connection = client.open(server.getUrl("/")); 2553 try { 2554 connection.getOutputStream(); 2555 fail(); 2556 } catch (ProtocolException expected) { 2557 } 2558 connection.getInputStream().close(); 2559 } 2560 2561 @Test public void getOutputAfterGetInputStreamFails() throws Exception { 2562 server.enqueue(new MockResponse()); 2563 connection = client.open(server.getUrl("/")); 2564 connection.setDoOutput(true); 2565 try { 2566 connection.getInputStream(); 2567 connection.getOutputStream(); 2568 fail(); 2569 } catch (ProtocolException expected) { 2570 } 2571 } 2572 2573 @Test public void setDoOutputOrDoInputAfterConnectFails() throws Exception { 2574 server.enqueue(new MockResponse()); 2575 connection = client.open(server.getUrl("/")); 2576 connection.connect(); 2577 try { 2578 connection.setDoOutput(true); 2579 fail(); 2580 } catch (IllegalStateException expected) { 2581 } 2582 try { 2583 connection.setDoInput(true); 2584 fail(); 2585 } catch (IllegalStateException expected) { 2586 } 2587 connection.getInputStream().close(); 2588 } 2589 2590 @Test public void clientSendsContentLength() throws Exception { 2591 server.enqueue(new MockResponse().setBody("A")); 2592 connection = client.open(server.getUrl("/")); 2593 connection.setDoOutput(true); 2594 OutputStream out = connection.getOutputStream(); 2595 out.write(new byte[] { 'A', 'B', 'C' }); 2596 out.close(); 2597 assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); 2598 RecordedRequest request = server.takeRequest(); 2599 assertEquals("3", request.getHeader("Content-Length")); 2600 connection.getInputStream().close(); 2601 } 2602 2603 @Test public void getContentLengthConnects() throws Exception { 2604 server.enqueue(new MockResponse().setBody("ABC")); 2605 connection = client.open(server.getUrl("/")); 2606 assertEquals(3, connection.getContentLength()); 2607 connection.getInputStream().close(); 2608 } 2609 2610 @Test public void getContentTypeConnects() throws Exception { 2611 server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("ABC")); 2612 connection = client.open(server.getUrl("/")); 2613 assertEquals("text/plain", connection.getContentType()); 2614 connection.getInputStream().close(); 2615 } 2616 2617 @Test public void getContentEncodingConnects() throws Exception { 2618 server.enqueue(new MockResponse().addHeader("Content-Encoding: identity").setBody("ABC")); 2619 connection = client.open(server.getUrl("/")); 2620 assertEquals("identity", connection.getContentEncoding()); 2621 connection.getInputStream().close(); 2622 } 2623 2624 // http://b/4361656 2625 @Test public void urlContainsQueryButNoPath() throws Exception { 2626 server.enqueue(new MockResponse().setBody("A")); 2627 2628 URL url = new URL("http", server.getHostName(), server.getPort(), "?query"); 2629 assertEquals("A", readAscii(client.open(url).getInputStream(), Integer.MAX_VALUE)); 2630 RecordedRequest request = server.takeRequest(); 2631 assertEquals("GET /?query HTTP/1.1", request.getRequestLine()); 2632 } 2633 2634 @Test public void doOutputForMethodThatDoesntSupportOutput() throws Exception { 2635 connection = client.open(server.getUrl("/")); 2636 connection.setRequestMethod("HEAD"); 2637 connection.setDoOutput(true); 2638 try { 2639 connection.connect(); 2640 fail(); 2641 } catch (IOException expected) { 2642 } 2643 } 2644 2645 // http://code.google.com/p/android/issues/detail?id=20442 2646 @Test public void inputStreamAvailableWithChunkedEncoding() throws Exception { 2647 testInputStreamAvailable(TransferKind.CHUNKED); 2648 } 2649 2650 @Test public void inputStreamAvailableWithContentLengthHeader() throws Exception { 2651 testInputStreamAvailable(TransferKind.FIXED_LENGTH); 2652 } 2653 2654 @Test public void inputStreamAvailableWithNoLengthHeaders() throws Exception { 2655 testInputStreamAvailable(TransferKind.END_OF_STREAM); 2656 } 2657 2658 private void testInputStreamAvailable(TransferKind transferKind) throws IOException { 2659 String body = "ABCDEFGH"; 2660 MockResponse response = new MockResponse(); 2661 transferKind.setBody(response, body, 4); 2662 server.enqueue(response); 2663 connection = client.open(server.getUrl("/")); 2664 InputStream in = connection.getInputStream(); 2665 for (int i = 0; i < body.length(); i++) { 2666 assertTrue(in.available() >= 0); 2667 assertEquals(body.charAt(i), in.read()); 2668 } 2669 assertEquals(0, in.available()); 2670 assertEquals(-1, in.read()); 2671 } 2672 2673 @Test public void postFailsWithBufferedRequestForSmallRequest() throws Exception { 2674 reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 1024); 2675 } 2676 2677 @Test public void postFailsWithBufferedRequestForLargeRequest() throws Exception { 2678 reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 16384); 2679 } 2680 2681 @Test public void postFailsWithChunkedRequestForSmallRequest() throws Exception { 2682 reusedConnectionFailsWithPost(TransferKind.CHUNKED, 1024); 2683 } 2684 2685 @Test public void postFailsWithChunkedRequestForLargeRequest() throws Exception { 2686 reusedConnectionFailsWithPost(TransferKind.CHUNKED, 16384); 2687 } 2688 2689 @Test public void postFailsWithFixedLengthRequestForSmallRequest() throws Exception { 2690 reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 1024); 2691 } 2692 2693 @Test public void postFailsWithFixedLengthRequestForLargeRequest() throws Exception { 2694 reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 16384); 2695 } 2696 2697 private void reusedConnectionFailsWithPost(TransferKind transferKind, int requestSize) 2698 throws Exception { 2699 SocketShutdownListener shutdownListener = new SocketShutdownListener(); 2700 server.enqueue(new MockResponse().setBody("A") 2701 .setSocketPolicy(DISCONNECT_AT_END) 2702 .setSocketShutdownListener(shutdownListener)); 2703 server.enqueue(new MockResponse().setBody("B")); 2704 server.enqueue(new MockResponse().setBody("C")); 2705 2706 assertContent("A", client.open(server.getUrl("/a"))); 2707 2708 shutdownListener.waitForSocketShutdown(); 2709 2710 // If the request body is larger than OkHttp's replay buffer, the failure may still occur. 2711 byte[] requestBody = new byte[requestSize]; 2712 new Random(0).nextBytes(requestBody); 2713 2714 for (int j = 0; j < 2; j++) { 2715 try { 2716 connection = client.open(server.getUrl("/b")); 2717 connection.setRequestMethod("POST"); 2718 transferKind.setForRequest(connection, requestBody.length); 2719 for (int i = 0; i < requestBody.length; i += 1024) { 2720 connection.getOutputStream().write(requestBody, i, 1024); 2721 } 2722 connection.getOutputStream().close(); 2723 assertContent("B", connection); 2724 break; 2725 } catch (IOException socketException) { 2726 // If there's a socket exception, this must have a streamed request body. 2727 assertEquals(0, j); 2728 assertTrue(transferKind == TransferKind.CHUNKED 2729 || transferKind == TransferKind.FIXED_LENGTH); 2730 } 2731 } 2732 2733 RecordedRequest requestA = server.takeRequest(); 2734 assertEquals("/a", requestA.getPath()); 2735 RecordedRequest requestB = server.takeRequest(); 2736 assertEquals("/b", requestB.getPath()); 2737 assertEquals(Arrays.toString(requestBody), Arrays.toString(requestB.getBody().readByteArray())); 2738 } 2739 2740 @Test public void postBodyRetransmittedOnFailureRecovery() throws Exception { 2741 server.enqueue(new MockResponse().setBody("abc")); 2742 server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); 2743 server.enqueue(new MockResponse().setBody("def")); 2744 2745 // Seed the connection pool so we have something that can fail. 2746 assertContent("abc", client.open(server.getUrl("/"))); 2747 2748 HttpURLConnection post = client.open(server.getUrl("/")); 2749 post.setDoOutput(true); 2750 post.getOutputStream().write("body!".getBytes(Util.UTF_8)); 2751 assertContent("def", post); 2752 2753 RecordedRequest get = server.takeRequest(); 2754 assertEquals(0, get.getSequenceNumber()); 2755 2756 RecordedRequest post1 = server.takeRequest(); 2757 assertEquals("body!", post1.getBody().readUtf8()); 2758 assertEquals(1, post1.getSequenceNumber()); 2759 2760 RecordedRequest post2 = server.takeRequest(); 2761 assertEquals("body!", post2.getBody().readUtf8()); 2762 assertEquals(0, post2.getSequenceNumber()); 2763 } 2764 2765 @Test public void fullyBufferedPostIsTooShort() throws Exception { 2766 server.enqueue(new MockResponse().setBody("A")); 2767 2768 connection = client.open(server.getUrl("/b")); 2769 connection.setRequestProperty("Content-Length", "4"); 2770 connection.setRequestMethod("POST"); 2771 OutputStream out = connection.getOutputStream(); 2772 out.write('a'); 2773 out.write('b'); 2774 out.write('c'); 2775 try { 2776 out.close(); 2777 fail(); 2778 } catch (IOException expected) { 2779 } 2780 } 2781 2782 @Test public void fullyBufferedPostIsTooLong() throws Exception { 2783 server.enqueue(new MockResponse().setBody("A")); 2784 2785 connection = client.open(server.getUrl("/b")); 2786 connection.setRequestProperty("Content-Length", "3"); 2787 connection.setRequestMethod("POST"); 2788 OutputStream out = connection.getOutputStream(); 2789 out.write('a'); 2790 out.write('b'); 2791 out.write('c'); 2792 try { 2793 out.write('d'); 2794 out.flush(); 2795 fail(); 2796 } catch (IOException expected) { 2797 } 2798 } 2799 2800 @Test @Ignore public void testPooledConnectionsDetectHttp10() { 2801 // TODO: write a test that shows pooled connections detect HTTP/1.0 (vs. HTTP/1.1) 2802 fail("TODO"); 2803 } 2804 2805 @Test @Ignore public void postBodiesRetransmittedOnAuthProblems() { 2806 fail("TODO"); 2807 } 2808 2809 @Test @Ignore public void cookiesAndTrailers() { 2810 // Do cookie headers get processed too many times? 2811 fail("TODO"); 2812 } 2813 2814 @Test @Ignore public void headerNamesContainingNullCharacter() { 2815 // This is relevant for SPDY 2816 fail("TODO"); 2817 } 2818 2819 @Test @Ignore public void headerValuesContainingNullCharacter() { 2820 // This is relevant for SPDY 2821 fail("TODO"); 2822 } 2823 2824 @Test public void emptyRequestHeaderValueIsAllowed() throws Exception { 2825 server.enqueue(new MockResponse().setBody("body")); 2826 connection = client.open(server.getUrl("/")); 2827 connection.addRequestProperty("B", ""); 2828 assertContent("body", connection); 2829 assertEquals("", connection.getRequestProperty("B")); 2830 } 2831 2832 @Test public void emptyResponseHeaderValueIsAllowed() throws Exception { 2833 server.enqueue(new MockResponse().addHeader("A:").setBody("body")); 2834 connection = client.open(server.getUrl("/")); 2835 assertContent("body", connection); 2836 assertEquals("", connection.getHeaderField("A")); 2837 } 2838 2839 @Test public void emptyRequestHeaderNameIsStrict() throws Exception { 2840 server.enqueue(new MockResponse().setBody("body")); 2841 connection = client.open(server.getUrl("/")); 2842 try { 2843 connection.setRequestProperty("", "A"); 2844 fail(); 2845 } catch (IllegalArgumentException expected) { 2846 } 2847 } 2848 2849 @Test public void emptyResponseHeaderNameIsLenient() throws Exception { 2850 Headers.Builder headers = new Headers.Builder(); 2851 Internal.instance.addLenient(headers, ":A"); 2852 server.enqueue(new MockResponse().setHeaders(headers.build()).setBody("body")); 2853 connection = client.open(server.getUrl("/")); 2854 connection.getResponseCode(); 2855 assertEquals("A", connection.getHeaderField("")); 2856 connection.getInputStream().close(); 2857 } 2858 2859 @Test public void requestHeaderValidationIsStrict() throws Exception { 2860 connection = client.open(server.getUrl("/")); 2861 try { 2862 connection.addRequestProperty("a\tb", "Value"); 2863 fail(); 2864 } catch (IllegalArgumentException expected) { 2865 } 2866 try { 2867 connection.addRequestProperty("Name", "c\u007fd"); 2868 fail(); 2869 } catch (IllegalArgumentException expected) { 2870 } 2871 try { 2872 connection.addRequestProperty("", "Value"); 2873 fail(); 2874 } catch (IllegalArgumentException expected) { 2875 } 2876 try { 2877 connection.addRequestProperty("\ud83c\udf69", "Value"); 2878 fail(); 2879 } catch (IllegalArgumentException expected) { 2880 } 2881 2882 // ANDROID-BEGIN Disabled for http://b/28867041 2883 // try { 2884 // connection.addRequestProperty("Name", "\u2615\ufe0f"); 2885 // fail(); 2886 // } catch (IllegalArgumentException expected) { 2887 // } 2888 // ANDROID-END 2889 } 2890 2891 @Test public void responseHeaderParsingIsLenient() throws Exception { 2892 Headers headers = new Headers.Builder() 2893 .add("Content-Length", "0") 2894 .addLenient("a\tb: c\u007fd") 2895 .addLenient(": ef") 2896 .addLenient("\ud83c\udf69: \u2615\ufe0f") 2897 .build(); 2898 server.enqueue(new MockResponse().setHeaders(headers)); 2899 2900 connection = client.open(server.getUrl("/")); 2901 connection.getResponseCode(); 2902 assertEquals("c\u007fd", connection.getHeaderField("a\tb")); 2903 assertEquals("\u2615\ufe0f", connection.getHeaderField("\ud83c\udf69")); 2904 assertEquals("ef", connection.getHeaderField("")); 2905 } 2906 2907 @Test @Ignore public void deflateCompression() { 2908 fail("TODO"); 2909 } 2910 2911 @Test @Ignore public void postBodiesRetransmittedOnIpAddressProblems() { 2912 fail("TODO"); 2913 } 2914 2915 @Test @Ignore public void pooledConnectionProblemsNotReportedToProxySelector() { 2916 fail("TODO"); 2917 } 2918 2919 @Test public void customBasicAuthenticator() throws Exception { 2920 MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401) 2921 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"") 2922 .setBody("Please authenticate."); 2923 server.enqueue(pleaseAuthenticate); 2924 server.enqueue(new MockResponse().setBody("A")); 2925 2926 String credential = Credentials.basic("jesse", "peanutbutter"); 2927 RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator(credential); 2928 client.client().setAuthenticator(authenticator); 2929 assertContent("A", client.open(server.getUrl("/private"))); 2930 2931 assertNull(server.takeRequest().getHeader("Authorization")); 2932 assertEquals(credential, server.takeRequest().getHeader("Authorization")); 2933 2934 assertEquals(Proxy.NO_PROXY, authenticator.onlyProxy()); 2935 Response response = authenticator.onlyResponse(); 2936 assertEquals("/private", response.request().url().getPath()); 2937 assertEquals(Arrays.asList(new Challenge("Basic", "protected area")), response.challenges()); 2938 } 2939 2940 @Test public void customTokenAuthenticator() throws Exception { 2941 MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401) 2942 .addHeader("WWW-Authenticate: Bearer realm=\"oauthed\"") 2943 .setBody("Please authenticate."); 2944 server.enqueue(pleaseAuthenticate); 2945 server.enqueue(new MockResponse().setBody("A")); 2946 2947 RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator("oauthed abc123"); 2948 client.client().setAuthenticator(authenticator); 2949 assertContent("A", client.open(server.getUrl("/private"))); 2950 2951 assertNull(server.takeRequest().getHeader("Authorization")); 2952 assertEquals("oauthed abc123", server.takeRequest().getHeader("Authorization")); 2953 2954 Response response = authenticator.onlyResponse(); 2955 assertEquals("/private", response.request().url().getPath()); 2956 assertEquals(Arrays.asList(new Challenge("Bearer", "oauthed")), response.challenges()); 2957 } 2958 2959 @Test public void authenticateCallsTrackedAsRedirects() throws Exception { 2960 server.enqueue(new MockResponse() 2961 .setResponseCode(302) 2962 .addHeader("Location: /b")); 2963 server.enqueue(new MockResponse() 2964 .setResponseCode(401) 2965 .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")); 2966 server.enqueue(new MockResponse().setBody("c")); 2967 2968 RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator( 2969 Credentials.basic("jesse", "peanutbutter")); 2970 client.client().setAuthenticator(authenticator); 2971 assertContent("c", client.open(server.getUrl("/a"))); 2972 2973 Response challengeResponse = authenticator.responses.get(0); 2974 assertEquals("/b", challengeResponse.request().url().getPath()); 2975 2976 Response redirectedBy = challengeResponse.priorResponse(); 2977 assertEquals("/a", redirectedBy.request().url().getPath()); 2978 } 2979 2980 @Test public void attemptAuthorization20Times() throws Exception { 2981 for (int i = 0; i < 20; i++) { 2982 server.enqueue(new MockResponse().setResponseCode(401)); 2983 } 2984 server.enqueue(new MockResponse().setBody("Success!")); 2985 2986 String credential = Credentials.basic("jesse", "peanutbutter"); 2987 client.client().setAuthenticator(new RecordingOkAuthenticator(credential)); 2988 2989 connection = client.open(server.getUrl("/0")); 2990 assertContent("Success!", connection); 2991 } 2992 2993 @Test public void doesNotAttemptAuthorization21Times() throws Exception { 2994 for (int i = 0; i < 21; i++) { 2995 server.enqueue(new MockResponse().setResponseCode(401)); 2996 } 2997 2998 String credential = Credentials.basic("jesse", "peanutbutter"); 2999 client.client().setAuthenticator(new RecordingOkAuthenticator(credential)); 3000 3001 connection = client.open(server.getUrl("/")); 3002 try { 3003 connection.getInputStream(); 3004 fail(); 3005 } catch (ProtocolException expected) { 3006 assertEquals(401, connection.getResponseCode()); 3007 assertEquals("Too many follow-up requests: 21", expected.getMessage()); 3008 } 3009 } 3010 3011 @Test public void setsNegotiatedProtocolHeader_SPDY_3() throws Exception { 3012 setsNegotiatedProtocolHeader(Protocol.SPDY_3); 3013 } 3014 3015 @Test public void setsNegotiatedProtocolHeader_HTTP_2() throws Exception { 3016 setsNegotiatedProtocolHeader(Protocol.HTTP_2); 3017 } 3018 3019 private void setsNegotiatedProtocolHeader(Protocol protocol) throws IOException { 3020 enableProtocol(protocol); 3021 server.enqueue(new MockResponse().setBody("A")); 3022 client.client().setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1)); 3023 connection = client.open(server.getUrl("/")); 3024 List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL); 3025 assertEquals(Arrays.asList(protocol.toString()), protocolValues); 3026 assertContent("A", connection); 3027 } 3028 3029 @Test public void http10SelectedProtocol() throws Exception { 3030 server.enqueue(new MockResponse().setStatus("HTTP/1.0 200 OK")); 3031 connection = client.open(server.getUrl("/")); 3032 List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL); 3033 assertEquals(Arrays.asList("http/1.0"), protocolValues); 3034 } 3035 3036 @Test public void http11SelectedProtocol() throws Exception { 3037 server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK")); 3038 connection = client.open(server.getUrl("/")); 3039 List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL); 3040 assertEquals(Arrays.asList("http/1.1"), protocolValues); 3041 } 3042 3043 /** For example, empty Protobuf RPC messages end up as a zero-length POST. */ 3044 @Test public void zeroLengthPost() throws IOException, InterruptedException { 3045 zeroLengthPayload("POST"); 3046 } 3047 3048 @Test public void zeroLengthPost_SPDY_3() throws Exception { 3049 enableProtocol(Protocol.SPDY_3); 3050 zeroLengthPost(); 3051 } 3052 3053 @Test public void zeroLengthPost_HTTP_2() throws Exception { 3054 enableProtocol(Protocol.HTTP_2); 3055 zeroLengthPost(); 3056 } 3057 3058 /** For example, creating an Amazon S3 bucket ends up as a zero-length POST. */ 3059 @Test public void zeroLengthPut() throws IOException, InterruptedException { 3060 zeroLengthPayload("PUT"); 3061 } 3062 3063 @Test public void zeroLengthPut_SPDY_3() throws Exception { 3064 enableProtocol(Protocol.SPDY_3); 3065 zeroLengthPut(); 3066 } 3067 3068 @Test public void zeroLengthPut_HTTP_2() throws Exception { 3069 enableProtocol(Protocol.HTTP_2); 3070 zeroLengthPut(); 3071 } 3072 3073 private void zeroLengthPayload(String method) 3074 throws IOException, InterruptedException { 3075 server.enqueue(new MockResponse()); 3076 connection = client.open(server.getUrl("/")); 3077 connection.setRequestProperty("Content-Length", "0"); 3078 connection.setRequestMethod(method); 3079 connection.setFixedLengthStreamingMode(0); 3080 connection.setDoOutput(true); 3081 assertContent("", connection); 3082 RecordedRequest zeroLengthPayload = server.takeRequest(); 3083 assertEquals(method, zeroLengthPayload.getMethod()); 3084 assertEquals("0", zeroLengthPayload.getHeader("content-length")); 3085 assertEquals(0L, zeroLengthPayload.getBodySize()); 3086 } 3087 3088 @Test public void unspecifiedRequestBodyContentTypeGetsDefault() throws Exception { 3089 server.enqueue(new MockResponse()); 3090 3091 connection = client.open(server.getUrl("/")); 3092 connection.setDoOutput(true); 3093 connection.getOutputStream().write("abc".getBytes(UTF_8)); 3094 assertEquals(200, connection.getResponseCode()); 3095 3096 RecordedRequest request = server.takeRequest(); 3097 assertEquals("application/x-www-form-urlencoded", request.getHeader("Content-Type")); 3098 assertEquals("3", request.getHeader("Content-Length")); 3099 assertEquals("abc", request.getBody().readUtf8()); 3100 } 3101 3102 @Test public void setProtocols() throws Exception { 3103 server.enqueue(new MockResponse().setBody("A")); 3104 client.client().setProtocols(Arrays.asList(Protocol.HTTP_1_1)); 3105 assertContent("A", client.open(server.getUrl("/"))); 3106 } 3107 3108 @Test public void setProtocolsWithoutHttp11() throws Exception { 3109 try { 3110 client.client().setProtocols(Arrays.asList(Protocol.SPDY_3)); 3111 fail(); 3112 } catch (IllegalArgumentException expected) { 3113 } 3114 } 3115 3116 @Test public void setProtocolsWithNull() throws Exception { 3117 try { 3118 client.client().setProtocols(Arrays.asList(Protocol.HTTP_1_1, null)); 3119 fail(); 3120 } catch (IllegalArgumentException expected) { 3121 } 3122 } 3123 3124 @Test public void veryLargeFixedLengthRequest() throws Exception { 3125 server.setBodyLimit(0); 3126 server.enqueue(new MockResponse()); 3127 3128 connection = client.open(server.getUrl("/")); 3129 connection.setDoOutput(true); 3130 long contentLength = Integer.MAX_VALUE + 1L; 3131 connection.setFixedLengthStreamingMode(contentLength); 3132 OutputStream out = connection.getOutputStream(); 3133 byte[] buffer = new byte[1024 * 1024]; 3134 for (long bytesWritten = 0; bytesWritten < contentLength; ) { 3135 int byteCount = (int) Math.min(buffer.length, contentLength - bytesWritten); 3136 out.write(buffer, 0, byteCount); 3137 bytesWritten += byteCount; 3138 } 3139 assertContent("", connection); 3140 3141 RecordedRequest request = server.takeRequest(); 3142 assertEquals(Long.toString(contentLength), request.getHeader("Content-Length")); 3143 } 3144 3145 /** 3146 * We had a bug where we attempted to gunzip responses that didn't have a 3147 * body. This only came up with 304s since that response code can include 3148 * headers (like "Content-Encoding") without any content to go along with it. 3149 * https://github.com/square/okhttp/issues/358 3150 */ 3151 @Test public void noTransparentGzipFor304NotModified() throws Exception { 3152 server.enqueue(new MockResponse() 3153 .clearHeaders() 3154 .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED) 3155 .addHeader("Content-Encoding: gzip")); 3156 server.enqueue(new MockResponse().setBody("b")); 3157 3158 HttpURLConnection connection1 = client.open(server.getUrl("/")); 3159 assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection1.getResponseCode()); 3160 assertContent("", connection1); 3161 3162 HttpURLConnection connection2 = client.open(server.getUrl("/")); 3163 assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode()); 3164 assertContent("b", connection2); 3165 3166 RecordedRequest requestA = server.takeRequest(); 3167 assertEquals(0, requestA.getSequenceNumber()); 3168 3169 RecordedRequest requestB = server.takeRequest(); 3170 assertEquals(1, requestB.getSequenceNumber()); 3171 } 3172 3173 /** 3174 * We had a bug where we weren't closing Gzip streams on redirects. 3175 * https://github.com/square/okhttp/issues/441 3176 */ 3177 @Test public void gzipWithRedirectAndConnectionReuse() throws Exception { 3178 server.enqueue(new MockResponse() 3179 .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP) 3180 .addHeader("Location: /foo") 3181 .addHeader("Content-Encoding: gzip") 3182 .setBody(gzip("Moved! Moved! Moved!"))); 3183 server.enqueue(new MockResponse().setBody("This is the new page!")); 3184 3185 HttpURLConnection connection = client.open(server.getUrl("/")); 3186 assertContent("This is the new page!", connection); 3187 3188 RecordedRequest requestA = server.takeRequest(); 3189 assertEquals(0, requestA.getSequenceNumber()); 3190 3191 RecordedRequest requestB = server.takeRequest(); 3192 assertEquals(1, requestB.getSequenceNumber()); 3193 } 3194 3195 /** 3196 * The RFC is unclear in this regard as it only specifies that this should 3197 * invalidate the cache entry (if any). 3198 */ 3199 @Test public void bodyPermittedOnDelete() throws Exception { 3200 server.enqueue(new MockResponse()); 3201 3202 HttpURLConnection connection = client.open(server.getUrl("/")); 3203 connection.setRequestMethod("DELETE"); 3204 connection.setDoOutput(true); 3205 connection.getOutputStream().write("BODY".getBytes(UTF_8)); 3206 assertEquals(200, connection.getResponseCode()); 3207 3208 RecordedRequest request = server.takeRequest(); 3209 assertEquals("DELETE", request.getMethod()); 3210 assertEquals("BODY", request.getBody().readUtf8()); 3211 } 3212 3213 @Test public void userAgentPicksUpHttpAgentSystemProperty() throws Exception { 3214 server.enqueue(new MockResponse().setBody("abc")); 3215 3216 System.setProperty("http.agent", "foo"); 3217 assertContent("abc", client.open(server.getUrl("/"))); 3218 3219 RecordedRequest request = server.takeRequest(); 3220 assertEquals("foo", request.getHeader("User-Agent")); 3221 } 3222 3223 /** https://github.com/square/okhttp/issues/891 */ 3224 @Test public void userAgentSystemPropertyIsNotAscii() throws Exception { 3225 server.enqueue(new MockResponse().setBody("abc")); 3226 3227 System.setProperty("http.agent", "a\nb\ud83c\udf69c\ud83c\udf68d\u007fe"); 3228 assertContent("abc", client.open(server.getUrl("/"))); 3229 3230 RecordedRequest request = server.takeRequest(); 3231 assertEquals("a?b?c?d?e", request.getHeader("User-Agent")); 3232 } 3233 3234 @Test public void userAgentDefaultsToOkHttpVersion() throws Exception { 3235 server.enqueue(new MockResponse().setBody("abc")); 3236 3237 assertContent("abc", client.open(server.getUrl("/"))); 3238 3239 RecordedRequest request = server.takeRequest(); 3240 assertEquals(Version.userAgent(), request.getHeader("User-Agent")); 3241 } 3242 3243 @Test public void interceptorsNotInvoked() throws Exception { 3244 Interceptor interceptor = new Interceptor() { 3245 @Override public Response intercept(Chain chain) throws IOException { 3246 throw new AssertionError(); 3247 } 3248 }; 3249 client.client().interceptors().add(interceptor); 3250 client.client().networkInterceptors().add(interceptor); 3251 3252 server.enqueue(new MockResponse().setBody("abc")); 3253 assertContent("abc", client.open(server.getUrl("/"))); 3254 } 3255 3256 @Test public void urlWithSpaceInHost() throws Exception { 3257 URLConnection urlConnection = client.open(new URL("http://and roid.com/")); 3258 try { 3259 urlConnection.getInputStream(); 3260 fail(); 3261 } catch (UnknownHostException expected) { 3262 } 3263 } 3264 3265 @Test public void urlWithSpaceInHostViaHttpProxy() throws Exception { 3266 server.enqueue(new MockResponse()); 3267 URLConnection urlConnection = 3268 client.open(new URL("http://and roid.com/"), server.toProxyAddress()); 3269 3270 try { 3271 // This test is to check that a NullPointerException is not thrown. 3272 urlConnection.getInputStream(); 3273 fail(); // the RI makes a bogus proxy request for "GET http://and roid.com/ HTTP/1.1" 3274 } catch (UnknownHostException expected) { 3275 } 3276 } 3277 3278 @Test public void urlHostWithNul() throws Exception { 3279 URLConnection urlConnection = client.open(new URL("http://host\u0000/")); 3280 try { 3281 urlConnection.getInputStream(); 3282 fail(); 3283 } catch (UnknownHostException expected) { 3284 } 3285 } 3286 3287 @Test public void urlRedirectToHostWithNul() throws Exception { 3288 String redirectUrl = "http://host\u0000/"; 3289 server.enqueue(new MockResponse().setResponseCode(302) 3290 .addHeaderLenient("Location", redirectUrl)); 3291 3292 HttpURLConnection urlConnection = client.open(server.getUrl("/")); 3293 assertEquals(302, urlConnection.getResponseCode()); 3294 assertEquals(redirectUrl, urlConnection.getHeaderField("Location")); 3295 } 3296 3297 @Test public void urlWithBadAsciiHost() throws Exception { 3298 URLConnection urlConnection = client.open(new URL("http://host\u0001/")); 3299 try { 3300 urlConnection.getInputStream(); 3301 fail(); 3302 } catch (UnknownHostException expected) { 3303 } 3304 } 3305 3306 @Test public void instanceFollowsRedirects() throws Exception { 3307 testInstanceFollowsRedirects("http://www.google.com/"); 3308 testInstanceFollowsRedirects("https://www.google.com/"); 3309 } 3310 3311 private void testInstanceFollowsRedirects(String spec) throws Exception { 3312 URL url = new URL(spec); 3313 HttpURLConnection urlConnection = client.open(url); 3314 urlConnection.setInstanceFollowRedirects(true); 3315 assertTrue(urlConnection.getInstanceFollowRedirects()); 3316 urlConnection.setInstanceFollowRedirects(false); 3317 assertFalse(urlConnection.getInstanceFollowRedirects()); 3318 } 3319 3320 /** Returns a gzipped copy of {@code bytes}. */ 3321 public Buffer gzip(String data) throws IOException { 3322 Buffer result = new Buffer(); 3323 BufferedSink gzipSink = Okio.buffer(new GzipSink(result)); 3324 gzipSink.writeUtf8(data); 3325 gzipSink.close(); 3326 return result; 3327 } 3328 3329 /** 3330 * Reads at most {@code limit} characters from {@code in} and asserts that 3331 * content equals {@code expected}. 3332 */ 3333 private void assertContent(String expected, HttpURLConnection connection, int limit) 3334 throws IOException { 3335 connection.connect(); 3336 assertEquals(expected, readAscii(connection.getInputStream(), limit)); 3337 } 3338 3339 private void assertContent(String expected, HttpURLConnection connection) throws IOException { 3340 assertContent(expected, connection, Integer.MAX_VALUE); 3341 } 3342 3343 private Set<String> newSet(String... elements) { 3344 return new LinkedHashSet<>(Arrays.asList(elements)); 3345 } 3346 3347 enum TransferKind { 3348 CHUNKED() { 3349 @Override void setBody(MockResponse response, Buffer content, int chunkSize) 3350 throws IOException { 3351 response.setChunkedBody(content, chunkSize); 3352 } 3353 @Override void setForRequest(HttpURLConnection connection, int contentLength) { 3354 connection.setChunkedStreamingMode(5); 3355 } 3356 }, 3357 FIXED_LENGTH() { 3358 @Override void setBody(MockResponse response, Buffer content, int chunkSize) { 3359 response.setBody(content); 3360 } 3361 @Override void setForRequest(HttpURLConnection connection, int contentLength) { 3362 connection.setFixedLengthStreamingMode(contentLength); 3363 } 3364 }, 3365 END_OF_STREAM() { 3366 @Override void setBody(MockResponse response, Buffer content, int chunkSize) { 3367 response.setBody(content); 3368 response.setSocketPolicy(DISCONNECT_AT_END); 3369 response.removeHeader("Content-Length"); 3370 } 3371 @Override void setForRequest(HttpURLConnection connection, int contentLength) { 3372 } 3373 }; 3374 3375 abstract void setBody(MockResponse response, Buffer content, int chunkSize) throws IOException; 3376 3377 abstract void setForRequest(HttpURLConnection connection, int contentLength); 3378 3379 void setBody(MockResponse response, String content, int chunkSize) throws IOException { 3380 setBody(response, new Buffer().writeUtf8(content), chunkSize); 3381 } 3382 } 3383 3384 enum ProxyConfig { 3385 NO_PROXY() { 3386 @Override public HttpURLConnection connect( 3387 MockWebServer server, OkUrlFactory streamHandlerFactory, URL url) 3388 throws IOException { 3389 streamHandlerFactory.client().setProxy(Proxy.NO_PROXY); 3390 return streamHandlerFactory.open(url); 3391 } 3392 }, 3393 3394 CREATE_ARG() { 3395 @Override public HttpURLConnection connect( 3396 MockWebServer server, OkUrlFactory streamHandlerFactory, URL url) 3397 throws IOException { 3398 streamHandlerFactory.client().setProxy(server.toProxyAddress()); 3399 return streamHandlerFactory.open(url); 3400 } 3401 }, 3402 3403 PROXY_SYSTEM_PROPERTY() { 3404 @Override public HttpURLConnection connect( 3405 MockWebServer server, OkUrlFactory streamHandlerFactory, URL url) 3406 throws IOException { 3407 System.setProperty("proxyHost", server.getHostName()); 3408 System.setProperty("proxyPort", Integer.toString(server.getPort())); 3409 return streamHandlerFactory.open(url); 3410 } 3411 }, 3412 3413 HTTP_PROXY_SYSTEM_PROPERTY() { 3414 @Override public HttpURLConnection connect( 3415 MockWebServer server, OkUrlFactory streamHandlerFactory, URL url) 3416 throws IOException { 3417 System.setProperty("http.proxyHost", server.getHostName()); 3418 System.setProperty("http.proxyPort", Integer.toString(server.getPort())); 3419 return streamHandlerFactory.open(url); 3420 } 3421 }, 3422 3423 HTTPS_PROXY_SYSTEM_PROPERTY() { 3424 @Override public HttpURLConnection connect( 3425 MockWebServer server, OkUrlFactory streamHandlerFactory, URL url) 3426 throws IOException { 3427 System.setProperty("https.proxyHost", server.getHostName()); 3428 System.setProperty("https.proxyPort", Integer.toString(server.getPort())); 3429 return streamHandlerFactory.open(url); 3430 } 3431 }; 3432 3433 public abstract HttpURLConnection connect( 3434 MockWebServer server, OkUrlFactory streamHandlerFactory, URL url) 3435 throws IOException; 3436 } 3437 3438 private static class RecordingTrustManager implements X509TrustManager { 3439 private final List<String> calls = new ArrayList<String>(); 3440 private final X509TrustManager delegate; 3441 3442 public RecordingTrustManager(SSLContext sslContext) { 3443 this.delegate = Platform.get().trustManager(sslContext.getSocketFactory()); 3444 } 3445 3446 public X509Certificate[] getAcceptedIssuers() { 3447 return delegate.getAcceptedIssuers(); 3448 } 3449 3450 public void checkClientTrusted(X509Certificate[] chain, String authType) 3451 throws CertificateException { 3452 calls.add("checkClientTrusted " + certificatesToString(chain)); 3453 } 3454 3455 public void checkServerTrusted(X509Certificate[] chain, String authType) 3456 throws CertificateException { 3457 calls.add("checkServerTrusted " + certificatesToString(chain)); 3458 } 3459 3460 private String certificatesToString(X509Certificate[] certificates) { 3461 List<String> result = new ArrayList<String>(); 3462 for (X509Certificate certificate : certificates) { 3463 result.add(certificate.getSubjectDN() + " " + certificate.getSerialNumber()); 3464 } 3465 return result.toString(); 3466 } 3467 } 3468 3469 private static class FakeProxySelector extends ProxySelector { 3470 List<Proxy> proxies = new ArrayList<>(); 3471 3472 @Override public List<Proxy> select(URI uri) { 3473 // Don't handle 'socket' schemes, which the RI's Socket class may request (for SOCKS). 3474 return uri.getScheme().equals("http") || uri.getScheme().equals("https") ? proxies 3475 : Collections.singletonList(Proxy.NO_PROXY); 3476 } 3477 3478 @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { 3479 } 3480 } 3481 3482 /** 3483 * Tests that use this will fail unless boot classpath is set. Ex. {@code 3484 * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317} 3485 */ 3486 private void enableProtocol(Protocol protocol) { 3487 client.client().setSslSocketFactory(sslContext.getSocketFactory()); 3488 client.client().setHostnameVerifier(new RecordingHostnameVerifier()); 3489 client.client().setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1)); 3490 server.useHttps(sslContext.getSocketFactory(), false); 3491 server.setProtocolNegotiationEnabled(true); 3492 server.setProtocols(client.client().getProtocols()); 3493 } 3494 3495 /** 3496 * Used during tests that involve TLS connection fallback attempts. OkHttp includes the 3497 * TLS_FALLBACK_SCSV cipher on fallback connections. See 3498 * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details. 3499 */ 3500 private void suppressTlsFallbackScsv(OkHttpClient client) { 3501 FallbackTestClientSocketFactory clientSocketFactory = 3502 new FallbackTestClientSocketFactory(sslContext.getSocketFactory()); 3503 client.setSslSocketFactory(clientSocketFactory); 3504 } 3505 } 3506