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