Home | History | Annotate | Download | only in okhttp
      1 /*
      2  * Copyright (C) 2013 Square, Inc.
      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 package com.squareup.okhttp;
     17 
     18 import com.squareup.okhttp.internal.http.OkHeaders;
     19 import java.util.Collections;
     20 import java.util.List;
     21 
     22 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_PERM_REDIRECT;
     23 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
     24 import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
     25 import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
     26 import static java.net.HttpURLConnection.HTTP_MULT_CHOICE;
     27 import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
     28 import static java.net.HttpURLConnection.HTTP_SEE_OTHER;
     29 import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
     30 
     31 /**
     32  * An HTTP response. Instances of this class are not immutable: the response
     33  * body is a one-shot value that may be consumed only once. All other properties
     34  * are immutable.
     35  */
     36 public final class Response {
     37   private final Request request;
     38   private final Protocol protocol;
     39   private final int code;
     40   private final String message;
     41   private final Handshake handshake;
     42   private final Headers headers;
     43   private final ResponseBody body;
     44   private Response networkResponse;
     45   private Response cacheResponse;
     46   private final Response priorResponse;
     47 
     48   private volatile CacheControl cacheControl; // Lazily initialized.
     49 
     50   private Response(Builder builder) {
     51     this.request = builder.request;
     52     this.protocol = builder.protocol;
     53     this.code = builder.code;
     54     this.message = builder.message;
     55     this.handshake = builder.handshake;
     56     this.headers = builder.headers.build();
     57     this.body = builder.body;
     58     this.networkResponse = builder.networkResponse;
     59     this.cacheResponse = builder.cacheResponse;
     60     this.priorResponse = builder.priorResponse;
     61   }
     62 
     63   /**
     64    * The wire-level request that initiated this HTTP response. This is not
     65    * necessarily the same request issued by the application:
     66    * <ul>
     67    *     <li>It may be transformed by the HTTP client. For example, the client
     68    *         may copy headers like {@code Content-Length} from the request body.
     69    *     <li>It may be the request generated in response to an HTTP redirect or
     70    *         authentication challenge. In this case the request URL may be
     71    *         different than the initial request URL.
     72    * </ul>
     73    */
     74   public Request request() {
     75     return request;
     76   }
     77 
     78   /**
     79    * Returns the HTTP protocol, such as {@link Protocol#HTTP_1_1} or {@link
     80    * Protocol#HTTP_1_0}.
     81    */
     82   public Protocol protocol() {
     83     return protocol;
     84   }
     85 
     86   /** Returns the HTTP status code. */
     87   public int code() {
     88     return code;
     89   }
     90 
     91   /**
     92    * Returns true if the code is in [200..300), which means the request was
     93    * successfully received, understood, and accepted.
     94    */
     95   public boolean isSuccessful() {
     96     return code >= 200 && code < 300;
     97   }
     98 
     99   /** Returns the HTTP status message or null if it is unknown. */
    100   public String message() {
    101     return message;
    102   }
    103 
    104   /**
    105    * Returns the TLS handshake of the connection that carried this response, or
    106    * null if the response was received without TLS.
    107    */
    108   public Handshake handshake() {
    109     return handshake;
    110   }
    111 
    112   public List<String> headers(String name) {
    113     return headers.values(name);
    114   }
    115 
    116   public String header(String name) {
    117     return header(name, null);
    118   }
    119 
    120   public String header(String name, String defaultValue) {
    121     String result = headers.get(name);
    122     return result != null ? result : defaultValue;
    123   }
    124 
    125   public Headers headers() {
    126     return headers;
    127   }
    128 
    129   public ResponseBody body() {
    130     return body;
    131   }
    132 
    133   public Builder newBuilder() {
    134     return new Builder(this);
    135   }
    136 
    137   /** Returns true if this response redirects to another resource. */
    138   public boolean isRedirect() {
    139     switch (code) {
    140       case HTTP_PERM_REDIRECT:
    141       case HTTP_TEMP_REDIRECT:
    142       case HTTP_MULT_CHOICE:
    143       case HTTP_MOVED_PERM:
    144       case HTTP_MOVED_TEMP:
    145       case HTTP_SEE_OTHER:
    146         return true;
    147       default:
    148         return false;
    149     }
    150   }
    151 
    152   /**
    153    * Returns the raw response received from the network. Will be null if this
    154    * response didn't use the network, such as when the response is fully cached.
    155    * The body of the returned response should not be read.
    156    */
    157   public Response networkResponse() {
    158     return networkResponse;
    159   }
    160 
    161   /**
    162    * Returns the raw response received from the cache. Will be null if this
    163    * response didn't use the cache. For conditional get requests the cache
    164    * response and network response may both be non-null. The body of the
    165    * returned response should not be read.
    166    */
    167   public Response cacheResponse() {
    168     return cacheResponse;
    169   }
    170 
    171   /**
    172    * Returns the response for the HTTP redirect or authorization challenge that
    173    * triggered this response, or null if this response wasn't triggered by an
    174    * automatic retry. The body of the returned response should not be read
    175    * because it has already been consumed by the redirecting client.
    176    */
    177   public Response priorResponse() {
    178     return priorResponse;
    179   }
    180 
    181   /**
    182    * Returns the authorization challenges appropriate for this response's code.
    183    * If the response code is 401 unauthorized, this returns the
    184    * "WWW-Authenticate" challenges. If the response code is 407 proxy
    185    * unauthorized, this returns the "Proxy-Authenticate" challenges. Otherwise
    186    * this returns an empty list of challenges.
    187    */
    188   public List<Challenge> challenges() {
    189     String responseField;
    190     if (code == HTTP_UNAUTHORIZED) {
    191       responseField = "WWW-Authenticate";
    192     } else if (code == HTTP_PROXY_AUTH) {
    193       responseField = "Proxy-Authenticate";
    194     } else {
    195       return Collections.emptyList();
    196     }
    197     return OkHeaders.parseChallenges(headers(), responseField);
    198   }
    199 
    200   /**
    201    * Returns the cache control directives for this response. This is never null,
    202    * even if this response contains no {@code Cache-Control} header.
    203    */
    204   public CacheControl cacheControl() {
    205     CacheControl result = cacheControl;
    206     return result != null ? result : (cacheControl = CacheControl.parse(headers));
    207   }
    208 
    209   @Override public String toString() {
    210     return "Response{protocol="
    211         + protocol
    212         + ", code="
    213         + code
    214         + ", message="
    215         + message
    216         + ", url="
    217         + request.urlString()
    218         + '}';
    219   }
    220 
    221   public static class Builder {
    222     private Request request;
    223     private Protocol protocol;
    224     private int code = -1;
    225     private String message;
    226     private Handshake handshake;
    227     private Headers.Builder headers;
    228     private ResponseBody body;
    229     private Response networkResponse;
    230     private Response cacheResponse;
    231     private Response priorResponse;
    232 
    233     public Builder() {
    234       headers = new Headers.Builder();
    235     }
    236 
    237     private Builder(Response response) {
    238       this.request = response.request;
    239       this.protocol = response.protocol;
    240       this.code = response.code;
    241       this.message = response.message;
    242       this.handshake = response.handshake;
    243       this.headers = response.headers.newBuilder();
    244       this.body = response.body;
    245       this.networkResponse = response.networkResponse;
    246       this.cacheResponse = response.cacheResponse;
    247       this.priorResponse = response.priorResponse;
    248     }
    249 
    250     public Builder request(Request request) {
    251       this.request = request;
    252       return this;
    253     }
    254 
    255     public Builder protocol(Protocol protocol) {
    256       this.protocol = protocol;
    257       return this;
    258     }
    259 
    260     public Builder code(int code) {
    261       this.code = code;
    262       return this;
    263     }
    264 
    265     public Builder message(String message) {
    266       this.message = message;
    267       return this;
    268     }
    269 
    270     public Builder handshake(Handshake handshake) {
    271       this.handshake = handshake;
    272       return this;
    273     }
    274 
    275     /**
    276      * Sets the header named {@code name} to {@code value}. If this request
    277      * already has any headers with that name, they are all replaced.
    278      */
    279     public Builder header(String name, String value) {
    280       headers.set(name, value);
    281       return this;
    282     }
    283 
    284     /**
    285      * Adds a header with {@code name} and {@code value}. Prefer this method for
    286      * multiply-valued headers like "Set-Cookie".
    287      */
    288     public Builder addHeader(String name, String value) {
    289       headers.add(name, value);
    290       return this;
    291     }
    292 
    293     public Builder removeHeader(String name) {
    294       headers.removeAll(name);
    295       return this;
    296     }
    297 
    298     /** Removes all headers on this builder and adds {@code headers}. */
    299     public Builder headers(Headers headers) {
    300       this.headers = headers.newBuilder();
    301       return this;
    302     }
    303 
    304     public Builder body(ResponseBody body) {
    305       this.body = body;
    306       return this;
    307     }
    308 
    309     public Builder networkResponse(Response networkResponse) {
    310       if (networkResponse != null) checkSupportResponse("networkResponse", networkResponse);
    311       this.networkResponse = networkResponse;
    312       return this;
    313     }
    314 
    315     public Builder cacheResponse(Response cacheResponse) {
    316       if (cacheResponse != null) checkSupportResponse("cacheResponse", cacheResponse);
    317       this.cacheResponse = cacheResponse;
    318       return this;
    319     }
    320 
    321     private void checkSupportResponse(String name, Response response) {
    322       if (response.body != null) {
    323         throw new IllegalArgumentException(name + ".body != null");
    324       } else if (response.networkResponse != null) {
    325         throw new IllegalArgumentException(name + ".networkResponse != null");
    326       } else if (response.cacheResponse != null) {
    327         throw new IllegalArgumentException(name + ".cacheResponse != null");
    328       } else if (response.priorResponse != null) {
    329         throw new IllegalArgumentException(name + ".priorResponse != null");
    330       }
    331     }
    332 
    333     public Builder priorResponse(Response priorResponse) {
    334       if (priorResponse != null) checkPriorResponse(priorResponse);
    335       this.priorResponse = priorResponse;
    336       return this;
    337     }
    338 
    339     private void checkPriorResponse(Response response) {
    340       if (response.body != null) {
    341         throw new IllegalArgumentException("priorResponse.body != null");
    342       }
    343     }
    344 
    345     public Response build() {
    346       if (request == null) throw new IllegalStateException("request == null");
    347       if (protocol == null) throw new IllegalStateException("protocol == null");
    348       if (code < 0) throw new IllegalStateException("code < 0: " + code);
    349       return new Response(this);
    350     }
    351   }
    352 }
    353