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 == nullthrow 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