1 /*
2 * Copyright (C) 2014 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 okhttp3;
17
18 import java.io.Closeable;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.io.Reader;
23 import java.nio.charset.Charset;
24 import javax.annotation.Nullable;
25 import okhttp3.internal.Util;
26 import okio.Buffer;
27 import okio.BufferedSource;
28 import okio.ByteString;
29
30 import static java.nio.charset.StandardCharsets.UTF_8;
31
32 /**
33 * A one-shot stream from the origin server to the client application with the raw bytes of the
34 * response body. Each response body is supported by an active connection to the webserver. This
35 * imposes both obligations and limits on the client application.
36 *
37 * <h3>The response body must be closed.</h3>
38 *
39 * Each response body is backed by a limited resource like a socket (live network responses) or
40 * an open file (for cached responses). Failing to close the response body will leak resources and
41 * may ultimately cause the application to slow down or crash.
42 *
43 * <p>Both this class and {@link Response} implement {@link Closeable}. Closing a response simply
44 * closes its response body. If you invoke {@link Call#execute()} or implement {@link
45 * Callback#onResponse} you must close this body by calling any of the following methods:
46 *
47 * <ul>
48 * <li>Response.close()</li>
49 * <li>Response.body().close()</li>
50 * <li>Response.body().source().close()</li>
51 * <li>Response.body().charStream().close()</li>
52 * <li>Response.body().byteStream().close()</li>
53 * <li>Response.body().bytes()</li>
54 * <li>Response.body().string()</li>
55 * </ul>
56 *
57 * <p>There is no benefit to invoking multiple {@code close()} methods for the same response body.
58 *
59 * <p>For synchronous calls, the easiest way to make sure a response body is closed is with a {@code
60 * try} block. With this structure the compiler inserts an implicit {@code finally} clause that
61 * calls {@code close()} for you.
62 *
63 * <pre> {@code
64 *
65 * Call call = client.newCall(request);
66 * try (Response response = call.execute()) {
67 * ... // Use the response.
68 * }
69 * }</pre>
70 *
71 * You can use a similar block for asynchronous calls: <pre> {@code
72 *
73 * Call call = client.newCall(request);
74 * call.enqueue(new Callback() {
75 * public void onResponse(Call call, Response response) throws IOException {
76 * try (ResponseBody responseBody = response.body()) {
77 * ... // Use the response.
78 * }
79 * }
80 *
81 * public void onFailure(Call call, IOException e) {
82 * ... // Handle the failure.
83 * }
84 * });
85 * }</pre>
86 *
87 * These examples will not work if you're consuming the response body on another thread. In such
88 * cases the consuming thread must call {@link #close} when it has finished reading the response
89 * body.
90 *
91 * <h3>The response body can be consumed only once.</h3>
92 *
93 * <p>This class may be used to stream very large responses. For example, it is possible to use this
94 * class to read a response that is larger than the entire memory allocated to the current process.
95 * It can even stream a response larger than the total storage on the current device, which is a
96 * common requirement for video streaming applications.
97 *
98 * <p>Because this class does not buffer the full response in memory, the application may not
99 * re-read the bytes of the response. Use this one shot to read the entire response into memory with
100 * {@link #bytes()} or {@link #string()}. Or stream the response with either {@link #source()},
101 * {@link #byteStream()}, or {@link #charStream()}.
102 */
103 public abstract class ResponseBody implements Closeable {
104 /** Multiple calls to {@link #charStream()} must return the same instance. */
105 private @Nullable Reader reader;
106
107 public abstract @Nullable MediaType contentType();
108
109 /**
110 * Returns the number of bytes in that will returned by {@link #bytes}, or {@link #byteStream}, or
111 * -1 if unknown.
112 */
113 public abstract long contentLength();
114
115 public final InputStream byteStream() {
116 return source().inputStream();
117 }
118
119 public abstract BufferedSource source();
120
121 /**
122 * Returns the response as a byte array.
123 *
124 * <p>This method loads entire response body into memory. If the response body is very large this
125 * may trigger an {@link OutOfMemoryError}. Prefer to stream the response body if this is a
126 * possibility for your response.
127 */
128 public final byte[] bytes() throws IOException {
129 long contentLength = contentLength();
130 if (contentLength > Integer.MAX_VALUE) {
131 throw new IOException("Cannot buffer entire body for content length: " + contentLength);
132 }
133
134 byte[] bytes;
135 try (BufferedSource source = source()) {
136 bytes = source.readByteArray();
137 }
138 if (contentLength != -1 && contentLength != bytes.length) {
139 throw new IOException("Content-Length ("
140 + contentLength
141 + ") and stream length ("
142 + bytes.length
143 + ") disagree");
144 }
145 return bytes;
146 }
147
148 /**
149 * Returns the response as a character stream.
150 *
151 * <p>If the response starts with a <a href="https://en.wikipedia.org/wiki/Byte_order_mark">Byte
152 * Order Mark (BOM)</a>, it is consumed and used to determine the charset of the response bytes.
153 *
154 * <p>Otherwise if the response has a Content-Type header that specifies a charset, that is used
155 * to determine the charset of the response bytes.
156 *
157 * <p>Otherwise the response bytes are decoded as UTF-8.
158 */
159 public final Reader charStream() {
160 Reader r = reader;
161 return r != null ? r : (reader = new BomAwareReader(source(), charset()));
162 }
163
164 /**
165 * Returns the response as a string.
166 *
167 * <p>If the response starts with a <a href="https://en.wikipedia.org/wiki/Byte_order_mark">Byte
168 * Order Mark (BOM)</a>, it is consumed and used to determine the charset of the response bytes.
169 *
170 * <p>Otherwise if the response has a Content-Type header that specifies a charset, that is used
171 * to determine the charset of the response bytes.
172 *
173 * <p>Otherwise the response bytes are decoded as UTF-8.
174 *
175 * <p>This method loads entire response body into memory. If the response body is very large this
176 * may trigger an {@link OutOfMemoryError}. Prefer to stream the response body if this is a
177 * possibility for your response.
178 */
179 public final String string() throws IOException {
180 try (BufferedSource source = source()) {
181 Charset charset = Util.bomAwareCharset(source, charset());
182 return source.readString(charset);
183 }
184 }
185
186 private Charset charset() {
187 MediaType contentType = contentType();
188 return contentType != null ? contentType.charset(UTF_8) : UTF_8;
189 }
190
191 @Override public void close() {
192 Util.closeQuietly(source());
193 }
194
195 /**
196 * Returns a new response body that transmits {@code content}. If {@code contentType} is non-null
197 * and lacks a charset, this will use UTF-8.
198 */
199 public static ResponseBody create(@Nullable MediaType contentType, String content) {
200 Charset charset = UTF_8;
201 if (contentType != null) {
202 charset = contentType.charset();
203 if (charset == null) {
204 charset = UTF_8;
205 contentType = MediaType.parse(contentType + "; charset=utf-8");
206 }
207 }
208 Buffer buffer = new Buffer().writeString(content, charset);
209 return create(contentType, buffer.size(), buffer);
210 }
211
212 /** Returns a new response body that transmits {@code content}. */
213 public static ResponseBody create(final @Nullable MediaType contentType, byte[] content) {
214 Buffer buffer = new Buffer().write(content);
215 return create(contentType, content.length, buffer);
216 }
217
218 /** Returns a new response body that transmits {@code content}. */
219 public static ResponseBody create(@Nullable MediaType contentType, ByteString content) {
220 Buffer buffer = new Buffer().write(content);
221 return create(contentType, content.size(), buffer);
222 }
223
224 /** Returns a new response body that transmits {@code content}. */
225 public static ResponseBody create(final @Nullable MediaType contentType,
226 final long contentLength, final BufferedSource content) {
227 if (content == null) throw new NullPointerException("source == null");
228 return new ResponseBody() {
229 @Override public @Nullable MediaType contentType() {
230 return contentType;
231 }
232
233 @Override public long contentLength() {
234 return contentLength;
235 }
236
237 @Override public BufferedSource source() {
238 return content;
239 }
240 };
241 }
242
243 static final class BomAwareReader extends Reader {
244 private final BufferedSource source;
245 private final Charset charset;
246
247 private boolean closed;
248 private @Nullable Reader delegate;
249
250 BomAwareReader(BufferedSource source, Charset charset) {
251 this.source = source;
252 this.charset = charset;
253 }
254
255 @Override public int read(char[] cbuf, int off, int len) throws IOException {
256 if (closed) throw new IOException("Stream closed");
257
258 Reader delegate = this.delegate;
259 if (delegate == null) {
260 Charset charset = Util.bomAwareCharset(source, this.charset);
261 delegate = this.delegate = new InputStreamReader(source.inputStream(), charset);
262 }
263 return delegate.read(cbuf, off, len);
264 }
265
266 @Override public void close() throws IOException {
267 closed = true;
268 if (delegate != null) {
269 delegate.close();
270 } else {
271 source.close();
272 }
273 }
274 }
275 }
276