Response.java revision 166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3
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 */
16package com.squareup.okhttp;
17
18import com.squareup.okhttp.internal.Util;
19import com.squareup.okhttp.internal.http.RawHeaders;
20import java.io.ByteArrayOutputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.InputStreamReader;
24import java.io.Reader;
25import java.util.List;
26import 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/* OkHttp 2.0: 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