1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.squareup.okhttp.internal.http; 19 20 import com.squareup.okhttp.Address; 21 import com.squareup.okhttp.CertificatePinner; 22 import com.squareup.okhttp.Connection; 23 import com.squareup.okhttp.ConnectionPool; 24 import com.squareup.okhttp.Headers; 25 import com.squareup.okhttp.Interceptor; 26 import com.squareup.okhttp.MediaType; 27 import com.squareup.okhttp.OkHttpClient; 28 import com.squareup.okhttp.Protocol; 29 import com.squareup.okhttp.Request; 30 import com.squareup.okhttp.Response; 31 import com.squareup.okhttp.ResponseBody; 32 import com.squareup.okhttp.Route; 33 import com.squareup.okhttp.internal.Internal; 34 import com.squareup.okhttp.internal.InternalCache; 35 import com.squareup.okhttp.internal.Util; 36 import com.squareup.okhttp.internal.Version; 37 import java.io.IOException; 38 import java.io.InterruptedIOException; 39 import java.net.CookieHandler; 40 import java.net.ProtocolException; 41 import java.net.Proxy; 42 import java.net.URL; 43 import java.net.UnknownHostException; 44 import java.security.cert.CertificateException; 45 import java.util.Date; 46 import java.util.List; 47 import java.util.Map; 48 import javax.net.ssl.HostnameVerifier; 49 import javax.net.ssl.SSLHandshakeException; 50 import javax.net.ssl.SSLPeerUnverifiedException; 51 import javax.net.ssl.SSLSocketFactory; 52 import okio.Buffer; 53 import okio.BufferedSink; 54 import okio.BufferedSource; 55 import okio.GzipSource; 56 import okio.Okio; 57 import okio.Sink; 58 import okio.Source; 59 import okio.Timeout; 60 61 import static com.squareup.okhttp.internal.Util.closeQuietly; 62 import static com.squareup.okhttp.internal.Util.getDefaultPort; 63 import static com.squareup.okhttp.internal.Util.getEffectivePort; 64 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE; 65 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_PERM_REDIRECT; 66 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT; 67 import static java.net.HttpURLConnection.HTTP_MOVED_PERM; 68 import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; 69 import static java.net.HttpURLConnection.HTTP_MULT_CHOICE; 70 import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED; 71 import static java.net.HttpURLConnection.HTTP_NO_CONTENT; 72 import static java.net.HttpURLConnection.HTTP_PROXY_AUTH; 73 import static java.net.HttpURLConnection.HTTP_SEE_OTHER; 74 import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; 75 import static java.util.concurrent.TimeUnit.MILLISECONDS; 76 77 /** 78 * Handles a single HTTP request/response pair. Each HTTP engine follows this 79 * lifecycle: 80 * <ol> 81 * <li>It is created. 82 * <li>The HTTP request message is sent with sendRequest(). Once the request 83 * is sent it is an error to modify the request headers. After 84 * sendRequest() has been called the request body can be written to if 85 * it exists. 86 * <li>The HTTP response message is read with readResponse(). After the 87 * response has been read the response headers and body can be read. 88 * All responses have a response body input stream, though in some 89 * instances this stream is empty. 90 * </ol> 91 * 92 * <p>The request and response may be served by the HTTP response cache, by the 93 * network, or by both in the event of a conditional GET. 94 */ 95 public final class HttpEngine { 96 /** 97 * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox, 98 * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5. 99 */ 100 public static final int MAX_FOLLOW_UPS = 20; 101 102 private static final ResponseBody EMPTY_BODY = new ResponseBody() { 103 @Override public MediaType contentType() { 104 return null; 105 } 106 @Override public long contentLength() { 107 return 0; 108 } 109 @Override public BufferedSource source() { 110 return new Buffer(); 111 } 112 }; 113 114 final OkHttpClient client; 115 116 private Connection connection; 117 private Address address; 118 private RouteSelector routeSelector; 119 private Route route; 120 private final Response priorResponse; 121 122 private Transport transport; 123 124 /** The time when the request headers were written, or -1 if they haven't been written yet. */ 125 long sentRequestMillis = -1; 126 127 /** 128 * True if this client added an "Accept-Encoding: gzip" header field and is 129 * therefore responsible for also decompressing the transfer stream. 130 */ 131 private boolean transparentGzip; 132 133 /** 134 * True if the request body must be completely buffered before transmission; 135 * false if it can be streamed. Buffering has two advantages: we don't need 136 * the content-length in advance and we can retransmit if necessary. The 137 * upside of streaming is that we can save memory. 138 */ 139 public final boolean bufferRequestBody; 140 141 /** 142 * The original application-provided request. Never modified by OkHttp. When 143 * follow-up requests are necessary, they are derived from this request. 144 */ 145 private final Request userRequest; 146 147 /** 148 * The request to send on the network, or null for no network request. This is 149 * derived from the user request, and customized to support OkHttp features 150 * like compression and caching. 151 */ 152 private Request networkRequest; 153 154 /** 155 * The cached response, or null if the cache doesn't exist or cannot be used 156 * for this request. Conditional caching means this may be non-null even when 157 * the network request is non-null. Never modified by OkHttp. 158 */ 159 private Response cacheResponse; 160 161 /** 162 * The user-visible response. This is derived from either the network 163 * response, cache response, or both. It is customized to support OkHttp 164 * features like compression and caching. 165 */ 166 private Response userResponse; 167 168 private Sink requestBodyOut; 169 private BufferedSink bufferedRequestBody; 170 private final boolean callerWritesRequestBody; 171 private final boolean forWebSocket; 172 173 /** The cache request currently being populated from a network response. */ 174 private CacheRequest storeRequest; 175 private CacheStrategy cacheStrategy; 176 177 /** 178 * @param request the HTTP request without a body. The body must be written via the engine's 179 * request body stream. 180 * @param callerWritesRequestBody true for the {@code HttpURLConnection}-style interaction 181 * model where control flow is returned to the calling application to write the request body 182 * before the response body is readable. 183 * @param connection the connection used for an intermediate response immediately prior to this 184 * request/response pair, such as a same-host redirect. This engine assumes ownership of the 185 * connection and must release it when it is unneeded. 186 * @param routeSelector the route selector used for a failed attempt immediately preceding this 187 */ 188 public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody, 189 boolean callerWritesRequestBody, boolean forWebSocket, Connection connection, 190 RouteSelector routeSelector, RetryableSink requestBodyOut, Response priorResponse) { 191 this.client = client; 192 this.userRequest = request; 193 this.bufferRequestBody = bufferRequestBody; 194 this.callerWritesRequestBody = callerWritesRequestBody; 195 this.forWebSocket = forWebSocket; 196 this.connection = connection; 197 this.routeSelector = routeSelector; 198 this.requestBodyOut = requestBodyOut; 199 this.priorResponse = priorResponse; 200 201 if (connection != null) { 202 Internal.instance.setOwner(connection, this); 203 this.route = connection.getRoute(); 204 } else { 205 this.route = null; 206 } 207 } 208 209 /** 210 * Figures out what the response source will be, and opens a socket to that 211 * source if necessary. Prepares the request headers and gets ready to start 212 * writing the request body if it exists. 213 * 214 * @throws RequestException if there was a problem with request setup. Unrecoverable. 215 * @throws RouteException if the was a problem during connection via a specific route. Sometimes 216 * recoverable. See {@link #recover(RouteException)}. 217 * @throws IOException if there was a problem while making a request. Sometimes recoverable. See 218 * {@link #recover(IOException)}. 219 * 220 */ 221 public void sendRequest() throws RequestException, RouteException, IOException { 222 if (cacheStrategy != null) return; // Already sent. 223 if (transport != null) throw new IllegalStateException(); 224 225 Request request = networkRequest(userRequest); 226 227 InternalCache responseCache = Internal.instance.internalCache(client); 228 Response cacheCandidate = responseCache != null 229 ? responseCache.get(request) 230 : null; 231 232 long now = System.currentTimeMillis(); 233 cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get(); 234 networkRequest = cacheStrategy.networkRequest; 235 cacheResponse = cacheStrategy.cacheResponse; 236 237 if (responseCache != null) { 238 responseCache.trackResponse(cacheStrategy); 239 } 240 241 if (cacheCandidate != null && cacheResponse == null) { 242 closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. 243 } 244 245 if (networkRequest != null) { 246 // Open a connection unless we inherited one from a redirect. 247 if (connection == null) { 248 connect(); 249 } 250 251 transport = Internal.instance.newTransport(connection, this); 252 253 // If the caller's control flow writes the request body, we need to create that stream 254 // immediately. And that means we need to immediately write the request headers, so we can 255 // start streaming the request body. (We may already have a request body if we're retrying a 256 // failed POST.) 257 if (callerWritesRequestBody && permitsRequestBody() && requestBodyOut == null) { 258 long contentLength = OkHeaders.contentLength(request); 259 if (bufferRequestBody) { 260 if (contentLength > Integer.MAX_VALUE) { 261 throw new IllegalStateException("Use setFixedLengthStreamingMode() or " 262 + "setChunkedStreamingMode() for requests larger than 2 GiB."); 263 } 264 265 if (contentLength != -1) { 266 // Buffer a request body of a known length. 267 transport.writeRequestHeaders(networkRequest); 268 requestBodyOut = new RetryableSink((int) contentLength); 269 } else { 270 // Buffer a request body of an unknown length. Don't write request 271 // headers until the entire body is ready; otherwise we can't set the 272 // Content-Length header correctly. 273 requestBodyOut = new RetryableSink(); 274 } 275 } else { 276 transport.writeRequestHeaders(networkRequest); 277 requestBodyOut = transport.createRequestBody(networkRequest, contentLength); 278 } 279 } 280 281 } else { 282 // We aren't using the network. Recycle a connection we may have inherited from a redirect. 283 if (connection != null) { 284 Internal.instance.recycle(client.getConnectionPool(), connection); 285 connection = null; 286 } 287 288 if (cacheResponse != null) { 289 // We have a valid cached response. Promote it to the user response immediately. 290 this.userResponse = cacheResponse.newBuilder() 291 .request(userRequest) 292 .priorResponse(stripBody(priorResponse)) 293 .cacheResponse(stripBody(cacheResponse)) 294 .build(); 295 } else { 296 // We're forbidden from using the network, and the cache is insufficient. 297 this.userResponse = new Response.Builder() 298 .request(userRequest) 299 .priorResponse(stripBody(priorResponse)) 300 .protocol(Protocol.HTTP_1_1) 301 .code(504) 302 .message("Unsatisfiable Request (only-if-cached)") 303 .body(EMPTY_BODY) 304 .build(); 305 } 306 307 userResponse = unzip(userResponse); 308 } 309 } 310 311 private static Response stripBody(Response response) { 312 return response != null && response.body() != null 313 ? response.newBuilder().body(null).build() 314 : response; 315 } 316 317 /** Connect to the origin server either directly or via a proxy. */ 318 private void connect() throws RequestException, RouteException { 319 if (connection != null) throw new IllegalStateException(); 320 321 if (routeSelector == null) { 322 address = createAddress(client, networkRequest); 323 try { 324 routeSelector = RouteSelector.get(address, networkRequest, client); 325 } catch (IOException e) { 326 throw new RequestException(e); 327 } 328 } 329 330 connection = nextConnection(); 331 route = connection.getRoute(); 332 } 333 334 /** 335 * Returns the next connection to attempt. 336 * 337 * @throws java.util.NoSuchElementException if there are no more routes to attempt. 338 */ 339 private Connection nextConnection() throws RouteException { 340 Connection connection = createNextConnection(); 341 Internal.instance.connectAndSetOwner(client, connection, this, networkRequest); 342 return connection; 343 } 344 345 private Connection createNextConnection() throws RouteException { 346 ConnectionPool pool = client.getConnectionPool(); 347 348 // Always prefer pooled connections over new connections. 349 for (Connection pooled; (pooled = pool.get(address)) != null; ) { 350 if (networkRequest.method().equals("GET") || Internal.instance.isReadable(pooled)) { 351 return pooled; 352 } 353 closeQuietly(pooled.getSocket()); 354 } 355 356 try { 357 Route route = routeSelector.next(); 358 return new Connection(pool, route); 359 } catch (IOException e) { 360 throw new RouteException(e); 361 } 362 } 363 364 /** 365 * Called immediately before the transport transmits HTTP request headers. 366 * This is used to observe the sent time should the request be cached. 367 */ 368 public void writingRequestHeaders() { 369 if (sentRequestMillis != -1) throw new IllegalStateException(); 370 sentRequestMillis = System.currentTimeMillis(); 371 } 372 373 boolean permitsRequestBody() { 374 return HttpMethod.permitsRequestBody(userRequest.method()); 375 } 376 377 /** Returns the request body or null if this request doesn't have a body. */ 378 public Sink getRequestBody() { 379 if (cacheStrategy == null) throw new IllegalStateException(); 380 return requestBodyOut; 381 } 382 383 public BufferedSink getBufferedRequestBody() { 384 BufferedSink result = bufferedRequestBody; 385 if (result != null) return result; 386 Sink requestBody = getRequestBody(); 387 return requestBody != null 388 ? (bufferedRequestBody = Okio.buffer(requestBody)) 389 : null; 390 } 391 392 public boolean hasResponse() { 393 return userResponse != null; 394 } 395 396 public Request getRequest() { 397 return userRequest; 398 } 399 400 /** Returns the engine's response. */ 401 // TODO: the returned body will always be null. 402 public Response getResponse() { 403 if (userResponse == null) throw new IllegalStateException(); 404 return userResponse; 405 } 406 407 public Connection getConnection() { 408 return connection; 409 } 410 411 /** 412 * Attempt to recover from failure to connect via a route. Returns a new HTTP engine 413 * that should be used for the retry if there are other routes to try, or null if 414 * there are no more routes to try. 415 */ 416 public HttpEngine recover(RouteException e) { 417 if (routeSelector != null && connection != null) { 418 connectFailed(routeSelector, e.getLastConnectException()); 419 } 420 421 if (routeSelector == null && connection == null // No connection. 422 || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt. 423 || !isRecoverable(e)) { 424 return null; 425 } 426 427 Connection connection = close(); 428 429 // For failure recovery, use the same route selector with a new connection. 430 return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody, 431 forWebSocket, connection, routeSelector, (RetryableSink) requestBodyOut, priorResponse); 432 } 433 434 private boolean isRecoverable(RouteException e) { 435 // If the application has opted-out of recovery, don't recover. 436 if (!client.getRetryOnConnectionFailure()) { 437 return false; 438 } 439 440 // Problems with a route may mean the connection can be retried with a new route, or may 441 // indicate a client-side or server-side issue that should not be retried. To tell, we must look 442 // at the cause. 443 444 IOException ioe = e.getLastConnectException(); 445 446 // TODO(nfuller): This is the same logic as in ConnectionSpecSelector 447 // If there was a protocol problem, don't recover. 448 if (ioe instanceof ProtocolException) { 449 return false; 450 } 451 452 // Look for known client-side or negotiation errors that are unlikely to be fixed by trying 453 // again with a different route. 454 if (ioe instanceof SSLHandshakeException) { 455 // If the problem was a CertificateException from the X509TrustManager, 456 // do not retry. 457 if (ioe.getCause() instanceof CertificateException) { 458 return false; 459 } 460 } 461 if (ioe instanceof SSLPeerUnverifiedException) { 462 // e.g. a certificate pinning error. 463 return false; 464 } 465 // TODO(nfuller): End of common code. 466 467 // An example of one we might want to retry with a different route is a problem connecting to a 468 // proxy and would manifest as a standard IOException. Unless it is one we know we should not 469 // retry, we return true and try a new route. 470 return true; 471 } 472 473 /** 474 * Report and attempt to recover from a failure to communicate with a server. Returns a new 475 * HTTP engine that should be used for the retry if {@code e} is recoverable, or null if 476 * the failure is permanent. Requests with a body can only be recovered if the 477 * body is buffered. 478 */ 479 public HttpEngine recover(IOException e, Sink requestBodyOut) { 480 if (routeSelector != null && connection != null) { 481 connectFailed(routeSelector, e); 482 } 483 484 boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink; 485 if (routeSelector == null && connection == null // No connection. 486 || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt. 487 || !isRecoverable(e) 488 || !canRetryRequestBody) { 489 return null; 490 } 491 492 Connection connection = close(); 493 494 // For failure recovery, use the same route selector with a new connection. 495 return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody, 496 forWebSocket, connection, routeSelector, (RetryableSink) requestBodyOut, priorResponse); 497 } 498 499 private void connectFailed(RouteSelector routeSelector, IOException e) { 500 // If this is a recycled connection, don't count its failure against the route. 501 if (Internal.instance.recycleCount(connection) > 0) return; 502 Route failedRoute = connection.getRoute(); 503 routeSelector.connectFailed(failedRoute, e); 504 } 505 506 public HttpEngine recover(IOException e) { 507 return recover(e, requestBodyOut); 508 } 509 510 private boolean isRecoverable(IOException e) { 511 // If the application has opted-out of recovery, don't recover. 512 if (!client.getRetryOnConnectionFailure()) { 513 return false; 514 } 515 516 // If there was a protocol problem, don't recover. 517 if (e instanceof ProtocolException) { 518 return false; 519 } 520 521 // If there was an interruption or timeout, don't recover. 522 if (e instanceof InterruptedIOException) { 523 return false; 524 } 525 526 return true; 527 } 528 529 /** 530 * Returns the route used to retrieve the response. Null if we haven't 531 * connected yet, or if no connection was necessary. 532 */ 533 public Route getRoute() { 534 return route; 535 } 536 537 private void maybeCache() throws IOException { 538 InternalCache responseCache = Internal.instance.internalCache(client); 539 if (responseCache == null) return; 540 541 // Should we cache this response for this request? 542 if (!CacheStrategy.isCacheable(userResponse, networkRequest)) { 543 if (HttpMethod.invalidatesCache(networkRequest.method())) { 544 try { 545 responseCache.remove(networkRequest); 546 } catch (IOException ignored) { 547 // The cache cannot be written. 548 } 549 } 550 return; 551 } 552 553 // Offer this request to the cache. 554 storeRequest = responseCache.put(stripBody(userResponse)); 555 } 556 557 /** 558 * Configure the socket connection to be either pooled or closed when it is 559 * either exhausted or closed. If it is unneeded when this is called, it will 560 * be released immediately. 561 */ 562 public void releaseConnection() throws IOException { 563 if (transport != null && connection != null) { 564 transport.releaseConnectionOnIdle(); 565 } 566 connection = null; 567 } 568 569 /** 570 * Immediately closes the socket connection if it's currently held by this 571 * engine. Use this to interrupt an in-flight request from any thread. It's 572 * the caller's responsibility to close the request body and response body 573 * streams; otherwise resources may be leaked. 574 */ 575 public void disconnect() { 576 if (transport != null) { 577 try { 578 transport.disconnect(this); 579 } catch (IOException ignored) { 580 } 581 } 582 } 583 584 /** 585 * Release any resources held by this engine. If a connection is still held by 586 * this engine, it is returned. 587 */ 588 public Connection close() { 589 if (bufferedRequestBody != null) { 590 // This also closes the wrapped requestBodyOut. 591 closeQuietly(bufferedRequestBody); 592 } else if (requestBodyOut != null) { 593 closeQuietly(requestBodyOut); 594 } 595 596 // If this engine never achieved a response body, its connection cannot be reused. 597 if (userResponse == null) { 598 if (connection != null) closeQuietly(connection.getSocket()); // TODO: does this break SPDY? 599 connection = null; 600 return null; 601 } 602 603 // Close the response body. This will recycle the connection if it is eligible. 604 closeQuietly(userResponse.body()); 605 606 // Close the connection if it cannot be reused. 607 if (transport != null && connection != null && !transport.canReuseConnection()) { 608 closeQuietly(connection.getSocket()); 609 connection = null; 610 return null; 611 } 612 613 // Prevent this engine from disconnecting a connection it no longer owns. 614 if (connection != null && !Internal.instance.clearOwner(connection)) { 615 connection = null; 616 } 617 618 Connection result = connection; 619 connection = null; 620 return result; 621 } 622 623 /** 624 * Returns a new response that does gzip decompression on {@code response}, if transparent gzip 625 * was both offered by OkHttp and used by the origin server. 626 * 627 * <p>In addition to decompression, this will also strip the corresponding headers. We strip the 628 * Content-Encoding header to prevent the application from attempting to double decompress. We 629 * strip the Content-Length header because it is the length of the compressed content, but the 630 * application is only interested in the length of the uncompressed content. 631 * 632 * <p>This method should only be used for non-empty response bodies. Response codes like "304 Not 633 * Modified" can include "Content-Encoding: gzip" without a response body and we will crash if we 634 * attempt to decompress the zero-byte source. 635 */ 636 private Response unzip(final Response response) throws IOException { 637 if (!transparentGzip || !"gzip".equalsIgnoreCase(userResponse.header("Content-Encoding"))) { 638 return response; 639 } 640 641 if (response.body() == null) { 642 return response; 643 } 644 645 GzipSource responseBody = new GzipSource(response.body().source()); 646 Headers strippedHeaders = response.headers().newBuilder() 647 .removeAll("Content-Encoding") 648 .removeAll("Content-Length") 649 .build(); 650 return response.newBuilder() 651 .headers(strippedHeaders) 652 .body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody))) 653 .build(); 654 } 655 656 /** 657 * Returns true if the response must have a (possibly 0-length) body. 658 * See RFC 2616 section 4.3. 659 */ 660 public static boolean hasBody(Response response) { 661 // HEAD requests never yield a body regardless of the response headers. 662 if (response.request().method().equals("HEAD")) { 663 return false; 664 } 665 666 int responseCode = response.code(); 667 if ((responseCode < HTTP_CONTINUE || responseCode >= 200) 668 && responseCode != HTTP_NO_CONTENT 669 && responseCode != HTTP_NOT_MODIFIED) { 670 return true; 671 } 672 673 // If the Content-Length or Transfer-Encoding headers disagree with the 674 // response code, the response is malformed. For best compatibility, we 675 // honor the headers. 676 if (OkHeaders.contentLength(response) != -1 677 || "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) { 678 return true; 679 } 680 681 return false; 682 } 683 684 /** 685 * Populates request with defaults and cookies. 686 * 687 * <p>This client doesn't specify a default {@code Accept} header because it 688 * doesn't know what content types the application is interested in. 689 */ 690 private Request networkRequest(Request request) throws IOException { 691 Request.Builder result = request.newBuilder(); 692 693 if (request.header("Host") == null) { 694 result.header("Host", hostHeader(request.url())); 695 } 696 697 if ((connection == null || connection.getProtocol() != Protocol.HTTP_1_0) 698 && request.header("Connection") == null) { 699 result.header("Connection", "Keep-Alive"); 700 } 701 702 if (request.header("Accept-Encoding") == null) { 703 transparentGzip = true; 704 result.header("Accept-Encoding", "gzip"); 705 } 706 707 CookieHandler cookieHandler = client.getCookieHandler(); 708 if (cookieHandler != null) { 709 // Capture the request headers added so far so that they can be offered to the CookieHandler. 710 // This is mostly to stay close to the RI; it is unlikely any of the headers above would 711 // affect cookie choice besides "Host". 712 Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null); 713 714 Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers); 715 716 // Add any new cookies to the request. 717 OkHeaders.addCookies(result, cookies); 718 } 719 720 if (request.header("User-Agent") == null) { 721 result.header("User-Agent", Version.userAgent()); 722 } 723 724 return result.build(); 725 } 726 727 public static String hostHeader(URL url) { 728 return getEffectivePort(url) != getDefaultPort(url.getProtocol()) 729 ? url.getHost() + ":" + url.getPort() 730 : url.getHost(); 731 } 732 733 /** 734 * Flushes the remaining request header and body, parses the HTTP response 735 * headers and starts reading the HTTP response body if it exists. 736 */ 737 public void readResponse() throws IOException { 738 if (userResponse != null) { 739 return; // Already ready. 740 } 741 if (networkRequest == null && cacheResponse == null) { 742 throw new IllegalStateException("call sendRequest() first!"); 743 } 744 if (networkRequest == null) { 745 return; // No network response to read. 746 } 747 748 Response networkResponse; 749 750 if (forWebSocket) { 751 transport.writeRequestHeaders(networkRequest); 752 networkResponse = readNetworkResponse(); 753 754 } else if (!callerWritesRequestBody) { 755 networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest); 756 757 } else { 758 // Emit the request body's buffer so that everything is in requestBodyOut. 759 if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) { 760 bufferedRequestBody.emit(); 761 } 762 763 // Emit the request headers if we haven't yet. We might have just learned the Content-Length. 764 if (sentRequestMillis == -1) { 765 if (OkHeaders.contentLength(networkRequest) == -1 766 && requestBodyOut instanceof RetryableSink) { 767 long contentLength = ((RetryableSink) requestBodyOut).contentLength(); 768 networkRequest = networkRequest.newBuilder() 769 .header("Content-Length", Long.toString(contentLength)) 770 .build(); 771 } 772 transport.writeRequestHeaders(networkRequest); 773 } 774 775 // Write the request body to the socket. 776 if (requestBodyOut != null) { 777 if (bufferedRequestBody != null) { 778 // This also closes the wrapped requestBodyOut. 779 bufferedRequestBody.close(); 780 } else { 781 requestBodyOut.close(); 782 } 783 if (requestBodyOut instanceof RetryableSink) { 784 transport.writeRequestBody((RetryableSink) requestBodyOut); 785 } 786 } 787 788 networkResponse = readNetworkResponse(); 789 } 790 791 receiveHeaders(networkResponse.headers()); 792 793 // If we have a cache response too, then we're doing a conditional get. 794 if (cacheResponse != null) { 795 if (validate(cacheResponse, networkResponse)) { 796 userResponse = cacheResponse.newBuilder() 797 .request(userRequest) 798 .priorResponse(stripBody(priorResponse)) 799 .headers(combine(cacheResponse.headers(), networkResponse.headers())) 800 .cacheResponse(stripBody(cacheResponse)) 801 .networkResponse(stripBody(networkResponse)) 802 .build(); 803 networkResponse.body().close(); 804 releaseConnection(); 805 806 // Update the cache after combining headers but before stripping the 807 // Content-Encoding header (as performed by initContentStream()). 808 InternalCache responseCache = Internal.instance.internalCache(client); 809 responseCache.trackConditionalCacheHit(); 810 responseCache.update(cacheResponse, stripBody(userResponse)); 811 userResponse = unzip(userResponse); 812 return; 813 } else { 814 closeQuietly(cacheResponse.body()); 815 } 816 } 817 818 userResponse = networkResponse.newBuilder() 819 .request(userRequest) 820 .priorResponse(stripBody(priorResponse)) 821 .cacheResponse(stripBody(cacheResponse)) 822 .networkResponse(stripBody(networkResponse)) 823 .build(); 824 825 if (hasBody(userResponse)) { 826 maybeCache(); 827 userResponse = unzip(cacheWritingResponse(storeRequest, userResponse)); 828 } 829 } 830 831 class NetworkInterceptorChain implements Interceptor.Chain { 832 private final int index; 833 private final Request request; 834 private int calls; 835 836 NetworkInterceptorChain(int index, Request request) { 837 this.index = index; 838 this.request = request; 839 } 840 841 @Override public Connection connection() { 842 return connection; 843 } 844 845 @Override public Request request() { 846 return request; 847 } 848 849 @Override public Response proceed(Request request) throws IOException { 850 calls++; 851 852 if (index > 0) { 853 Interceptor caller = client.networkInterceptors().get(index - 1); 854 Address address = connection().getRoute().getAddress(); 855 856 // Confirm that the interceptor uses the connection we've already prepared. 857 if (!request.url().getHost().equals(address.getUriHost()) 858 || getEffectivePort(request.url()) != address.getUriPort()) { 859 throw new IllegalStateException("network interceptor " + caller 860 + " must retain the same host and port"); 861 } 862 863 // Confirm that this is the interceptor's first call to chain.proceed(). 864 if (calls > 1) { 865 throw new IllegalStateException("network interceptor " + caller 866 + " must call proceed() exactly once"); 867 } 868 } 869 870 if (index < client.networkInterceptors().size()) { 871 // There's another interceptor in the chain. Call that. 872 NetworkInterceptorChain chain = new NetworkInterceptorChain(index + 1, request); 873 Interceptor interceptor = client.networkInterceptors().get(index); 874 Response interceptedResponse = interceptor.intercept(chain); 875 876 // Confirm that the interceptor made the required call to chain.proceed(). 877 if (chain.calls != 1) { 878 throw new IllegalStateException("network interceptor " + interceptor 879 + " must call proceed() exactly once"); 880 } 881 882 return interceptedResponse; 883 } 884 885 transport.writeRequestHeaders(request); 886 887 //Update the networkRequest with the possibly updated interceptor request. 888 networkRequest = request; 889 890 if (permitsRequestBody() && request.body() != null) { 891 Sink requestBodyOut = transport.createRequestBody(request, request.body().contentLength()); 892 BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); 893 request.body().writeTo(bufferedRequestBody); 894 bufferedRequestBody.close(); 895 } 896 897 return readNetworkResponse(); 898 } 899 } 900 901 private Response readNetworkResponse() throws IOException { 902 transport.finishRequest(); 903 904 Response networkResponse = transport.readResponseHeaders() 905 .request(networkRequest) 906 .handshake(connection.getHandshake()) 907 .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis)) 908 .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())) 909 .build(); 910 911 if (!forWebSocket) { 912 networkResponse = networkResponse.newBuilder() 913 .body(transport.openResponseBody(networkResponse)) 914 .build(); 915 } 916 917 Internal.instance.setProtocol(connection, networkResponse.protocol()); 918 return networkResponse; 919 } 920 921 /** 922 * Returns a new source that writes bytes to {@code cacheRequest} as they are read by the source 923 * consumer. This is careful to discard bytes left over when the stream is closed; otherwise we 924 * may never exhaust the source stream and therefore not complete the cached response. 925 */ 926 private Response cacheWritingResponse(final CacheRequest cacheRequest, Response response) 927 throws IOException { 928 // Some apps return a null body; for compatibility we treat that like a null cache request. 929 if (cacheRequest == null) return response; 930 Sink cacheBodyUnbuffered = cacheRequest.body(); 931 if (cacheBodyUnbuffered == null) return response; 932 933 final BufferedSource source = response.body().source(); 934 final BufferedSink cacheBody = Okio.buffer(cacheBodyUnbuffered); 935 936 Source cacheWritingSource = new Source() { 937 boolean cacheRequestClosed; 938 939 @Override public long read(Buffer sink, long byteCount) throws IOException { 940 long bytesRead; 941 try { 942 bytesRead = source.read(sink, byteCount); 943 } catch (IOException e) { 944 if (!cacheRequestClosed) { 945 cacheRequestClosed = true; 946 cacheRequest.abort(); // Failed to write a complete cache response. 947 } 948 throw e; 949 } 950 951 if (bytesRead == -1) { 952 if (!cacheRequestClosed) { 953 cacheRequestClosed = true; 954 cacheBody.close(); // The cache response is complete! 955 } 956 return -1; 957 } 958 959 sink.copyTo(cacheBody.buffer(), sink.size() - bytesRead, bytesRead); 960 cacheBody.emitCompleteSegments(); 961 return bytesRead; 962 } 963 964 @Override public Timeout timeout() { 965 return source.timeout(); 966 } 967 968 @Override public void close() throws IOException { 969 if (!cacheRequestClosed 970 && !Util.discard(this, Transport.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) { 971 cacheRequestClosed = true; 972 cacheRequest.abort(); 973 } 974 source.close(); 975 } 976 }; 977 978 return response.newBuilder() 979 .body(new RealResponseBody(response.headers(), Okio.buffer(cacheWritingSource))) 980 .build(); 981 } 982 983 /** 984 * Returns true if {@code cached} should be used; false if {@code network} 985 * response should be used. 986 */ 987 private static boolean validate(Response cached, Response network) { 988 if (network.code() == HTTP_NOT_MODIFIED) { 989 return true; 990 } 991 992 // The HTTP spec says that if the network's response is older than our 993 // cached response, we may return the cache's response. Like Chrome (but 994 // unlike Firefox), this client prefers to return the newer response. 995 Date lastModified = cached.headers().getDate("Last-Modified"); 996 if (lastModified != null) { 997 Date networkLastModified = network.headers().getDate("Last-Modified"); 998 if (networkLastModified != null 999 && networkLastModified.getTime() < lastModified.getTime()) { 1000 return true; 1001 } 1002 } 1003 1004 return false; 1005 } 1006 1007 /** 1008 * Combines cached headers with a network headers as defined by RFC 2616, 1009 * 13.5.3. 1010 */ 1011 private static Headers combine(Headers cachedHeaders, Headers networkHeaders) throws IOException { 1012 Headers.Builder result = new Headers.Builder(); 1013 1014 for (int i = 0, size = cachedHeaders.size(); i < size; i++) { 1015 String fieldName = cachedHeaders.name(i); 1016 String value = cachedHeaders.value(i); 1017 if ("Warning".equalsIgnoreCase(fieldName) && value.startsWith("1")) { 1018 continue; // Drop 100-level freshness warnings. 1019 } 1020 if (!OkHeaders.isEndToEnd(fieldName) || networkHeaders.get(fieldName) == null) { 1021 result.add(fieldName, value); 1022 } 1023 } 1024 1025 for (int i = 0, size = networkHeaders.size(); i < size; i++) { 1026 String fieldName = networkHeaders.name(i); 1027 if ("Content-Length".equalsIgnoreCase(fieldName)) { 1028 continue; // Ignore content-length headers of validating responses. 1029 } 1030 if (OkHeaders.isEndToEnd(fieldName)) { 1031 result.add(fieldName, networkHeaders.value(i)); 1032 } 1033 } 1034 1035 return result.build(); 1036 } 1037 1038 public void receiveHeaders(Headers headers) throws IOException { 1039 CookieHandler cookieHandler = client.getCookieHandler(); 1040 if (cookieHandler != null) { 1041 cookieHandler.put(userRequest.uri(), OkHeaders.toMultimap(headers, null)); 1042 } 1043 } 1044 1045 /** 1046 * Figures out the HTTP request to make in response to receiving this engine's 1047 * response. This will either add authentication headers or follow redirects. 1048 * If a follow-up is either unnecessary or not applicable, this returns null. 1049 */ 1050 public Request followUpRequest() throws IOException { 1051 if (userResponse == null) throw new IllegalStateException(); 1052 Proxy selectedProxy = getRoute() != null 1053 ? getRoute().getProxy() 1054 : client.getProxy(); 1055 int responseCode = userResponse.code(); 1056 1057 switch (responseCode) { 1058 case HTTP_PROXY_AUTH: 1059 if (selectedProxy.type() != Proxy.Type.HTTP) { 1060 throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy"); 1061 } 1062 // fall-through 1063 case HTTP_UNAUTHORIZED: 1064 return OkHeaders.processAuthHeader(client.getAuthenticator(), userResponse, selectedProxy); 1065 1066 case HTTP_PERM_REDIRECT: 1067 case HTTP_TEMP_REDIRECT: 1068 // "If the 307 or 308 status code is received in response to a request other than GET 1069 // or HEAD, the user agent MUST NOT automatically redirect the request" 1070 if (!userRequest.method().equals("GET") && !userRequest.method().equals("HEAD")) { 1071 return null; 1072 } 1073 // fall-through 1074 case HTTP_MULT_CHOICE: 1075 case HTTP_MOVED_PERM: 1076 case HTTP_MOVED_TEMP: 1077 case HTTP_SEE_OTHER: 1078 // Does the client allow redirects? 1079 if (!client.getFollowRedirects()) return null; 1080 1081 String location = userResponse.header("Location"); 1082 if (location == null) return null; 1083 URL url = new URL(userRequest.url(), location); 1084 1085 // Don't follow redirects to unsupported protocols. 1086 if (!url.getProtocol().equals("https") && !url.getProtocol().equals("http")) return null; 1087 1088 // If configured, don't follow redirects between SSL and non-SSL. 1089 boolean sameProtocol = url.getProtocol().equals(userRequest.url().getProtocol()); 1090 if (!sameProtocol && !client.getFollowSslRedirects()) return null; 1091 1092 // Redirects don't include a request body. 1093 Request.Builder requestBuilder = userRequest.newBuilder(); 1094 if (HttpMethod.permitsRequestBody(userRequest.method())) { 1095 requestBuilder.method("GET", null); 1096 requestBuilder.removeHeader("Transfer-Encoding"); 1097 requestBuilder.removeHeader("Content-Length"); 1098 requestBuilder.removeHeader("Content-Type"); 1099 } 1100 1101 // When redirecting across hosts, drop all authentication headers. This 1102 // is potentially annoying to the application layer since they have no 1103 // way to retain them. 1104 if (!sameConnection(url)) { 1105 requestBuilder.removeHeader("Authorization"); 1106 } 1107 1108 return requestBuilder.url(url).build(); 1109 1110 default: 1111 return null; 1112 } 1113 } 1114 1115 /** 1116 * Returns true if an HTTP request for {@code followUp} can reuse the 1117 * connection used by this engine. 1118 */ 1119 public boolean sameConnection(URL followUp) { 1120 URL url = userRequest.url(); 1121 return url.getHost().equals(followUp.getHost()) 1122 && getEffectivePort(url) == getEffectivePort(followUp) 1123 && url.getProtocol().equals(followUp.getProtocol()); 1124 } 1125 1126 private static Address createAddress(OkHttpClient client, Request request) 1127 throws RequestException { 1128 String uriHost = request.url().getHost(); 1129 if (uriHost == null || uriHost.length() == 0) { 1130 throw new RequestException(new UnknownHostException(request.url().toString())); 1131 } 1132 1133 SSLSocketFactory sslSocketFactory = null; 1134 HostnameVerifier hostnameVerifier = null; 1135 CertificatePinner certificatePinner = null; 1136 if (request.isHttps()) { 1137 sslSocketFactory = client.getSslSocketFactory(); 1138 hostnameVerifier = client.getHostnameVerifier(); 1139 certificatePinner = client.getCertificatePinner(); 1140 } 1141 1142 return new Address(uriHost, getEffectivePort(request.url()), 1143 client.getSocketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner, 1144 client.getAuthenticator(), client.getProxy(), client.getProtocols(), 1145 client.getConnectionSpecs(), client.getProxySelector()); 1146 } 1147 } 1148