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