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