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.Util;
     19 import com.squareup.okhttp.internal.http.RawHeaders;
     20 import java.io.ByteArrayOutputStream;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.io.InputStreamReader;
     24 import java.io.Reader;
     25 import java.util.List;
     26 import java.util.Set;
     27 
     28 /**
     29  * An HTTP response. Instances of this class are not immutable: the response
     30  * body is a one-shot value that may be consumed only once. All other properties
     31  * are immutable.
     32  *
     33  * <h3>Warning: Experimental OkHttp 2.0 API</h3>
     34  * This class is in beta. APIs are subject to change!
     35  */
     36 public final class Response {
     37   private final Request request;
     38   private final int code;
     39   private final RawHeaders headers;
     40   private final Body body;
     41   private final Response redirectedBy;
     42 
     43   private Response(Builder builder) {
     44     this.request = builder.request;
     45     this.code = builder.code;
     46     this.headers = new RawHeaders(builder.headers);
     47     this.body = builder.body;
     48     this.redirectedBy = builder.redirectedBy;
     49   }
     50 
     51   /**
     52    * The wire-level request that initiated this HTTP response. This is usually
     53    * <strong>not</strong> the same request instance provided to the HTTP client:
     54    * <ul>
     55    *     <li>It may be transformed by the HTTP client. For example, the client
     56    *         may have added its own {@code Content-Encoding} header to enable
     57    *         response compression.
     58    *     <li>It may be the request generated in response to an HTTP redirect.
     59    *         In this case the request URL may be different than the initial
     60    *         request URL.
     61    * </ul>
     62    */
     63   public Request request() {
     64     return request;
     65   }
     66 
     67   public int code() {
     68     return code;
     69   }
     70 
     71   public String header(String name) {
     72     return header(name, null);
     73   }
     74 
     75   public String header(String name, String defaultValue) {
     76     String result = headers.get(name);
     77     return result != null ? result : defaultValue;
     78   }
     79 
     80   public List<String> headers(String name) {
     81     return headers.values(name);
     82   }
     83 
     84   public Set<String> headerNames() {
     85     return headers.names();
     86   }
     87 
     88   public int headerCount() {
     89     return headers.length();
     90   }
     91 
     92   public String headerName(int index) {
     93     return headers.getFieldName(index);
     94   }
     95 
     96   public String headerValue(int index) {
     97     return headers.getValue(index);
     98   }
     99 
    100   public Body body() {
    101     return body;
    102   }
    103 
    104   /**
    105    * Returns the response for the HTTP redirect that triggered this response, or
    106    * null if this response wasn't triggered by an automatic redirect. The body
    107    * of the returned response should not be read because it has already been
    108    * consumed by the redirecting client.
    109    */
    110   public Response redirectedBy() {
    111     return redirectedBy;
    112   }
    113 
    114   public abstract static class Body {
    115     public String contentType() {
    116       return null;
    117     }
    118 
    119     public long contentLength() {
    120       return -1;
    121     }
    122 
    123     public abstract InputStream byteStream() throws IOException;
    124 
    125     public byte[] bytes() throws IOException {
    126       long contentLength = contentLength();
    127       if (contentLength > Integer.MAX_VALUE) {
    128         throw new IOException("Cannot buffer entire body for content length: " + contentLength);
    129       }
    130 
    131       if (contentLength != -1) {
    132         byte[] content = new byte[(int) contentLength];
    133         InputStream in = byteStream();
    134         Util.readFully(in, content);
    135         if (in.read() != -1) throw new IOException("Content-Length and stream length disagree");
    136         return content;
    137 
    138       } else {
    139         ByteArrayOutputStream out = new ByteArrayOutputStream();
    140         Util.copy(byteStream(), out);
    141         return out.toByteArray();
    142       }
    143     }
    144 
    145     /**
    146      * Returns the response bytes as a UTF-8 character stream. Do not call this
    147      * method if the response content is not a UTF-8 character stream.
    148      */
    149     public Reader charStream() throws IOException {
    150       // TODO: parse content-type.
    151       return new InputStreamReader(byteStream(), "UTF-8");
    152     }
    153 
    154     /**
    155      * Returns the response bytes as a UTF-8 string. Do not call this method if
    156      * the response content is not a UTF-8 character stream.
    157      */
    158     public String string() throws IOException {
    159       // TODO: parse content-type.
    160       return new String(bytes(), "UTF-8");
    161     }
    162   }
    163 
    164   public interface Receiver {
    165     void onFailure(Failure failure);
    166     void onResponse(Response response) throws IOException;
    167   }
    168 
    169   public static class Builder {
    170     private final Request request;
    171     private final int code;
    172     private final RawHeaders headers = new RawHeaders();
    173     private Body body;
    174     private Response redirectedBy;
    175 
    176     public Builder(Request request, int code) {
    177       if (request == null) throw new IllegalArgumentException("request == null");
    178       if (code <= 0) throw new IllegalArgumentException("code <= 0");
    179       this.request = request;
    180       this.code = code;
    181     }
    182 
    183     /**
    184      * Sets the header named {@code name} to {@code value}. If this request
    185      * already has any headers with that name, they are all replaced.
    186      */
    187     public Builder header(String name, String value) {
    188       headers.set(name, value);
    189       return this;
    190     }
    191 
    192     /**
    193      * Adds a header with {@code name} and {@code value}. Prefer this method for
    194      * multiply-valued headers like "Set-Cookie".
    195      */
    196     public Builder addHeader(String name, String value) {
    197       headers.add(name, value);
    198       return this;
    199     }
    200 
    201     public Builder body(Body body) {
    202       this.body = body;
    203       return this;
    204     }
    205 
    206     public Builder redirectedBy(Response redirectedBy) {
    207       this.redirectedBy = redirectedBy;
    208       return this;
    209     }
    210 
    211     public Response build() {
    212       if (request == null) throw new IllegalStateException("Response has no request.");
    213       if (code == -1) throw new IllegalStateException("Response has no code.");
    214       return new Response(this);
    215     }
    216   }
    217 }
    218