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