001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.client.classic;
018
019import static org.apache.juneau.commons.utils.IoUtils.*;
020import static org.apache.juneau.commons.utils.ObjectUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.commons.utils.StringUtils.*;
023import static org.apache.juneau.commons.utils.ThrowableUtils.*;
024
025import java.io.*;
026import java.lang.reflect.*;
027import java.nio.charset.*;
028import java.util.concurrent.*;
029import java.util.regex.*;
030
031import org.apache.http.*;
032import org.apache.http.conn.*;
033import org.apache.juneau.commons.conversion.*;
034import org.apache.juneau.commons.http.*;
035import org.apache.juneau.commons.reflect.ConstructorInfo;
036import org.apache.juneau.commons.reflect.ExecutableException;
037import org.apache.juneau.commons.utils.*;
038import org.apache.juneau.http.classic.entity.*;
039import org.apache.juneau.http.classic.resource.*;
040import org.apache.juneau.marshall.*;
041import org.apache.juneau.marshall.collections.*;
042import org.apache.juneau.marshall.httppart.*;
043import org.apache.juneau.marshall.oapi.*;
044import org.apache.juneau.marshall.parser.*;
045import org.apache.juneau.marshall.parser.ParseException;
046import org.apache.juneau.marshall.stream.*;
047import org.apache.juneau.rest.client.classic.assertion.*;
048import org.apache.juneau.bct.assertions.*;
049
050/**
051 * Represents the body of an HTTP response.
052 *
053 * <p>
054 * An extension of an HttpClient {@link HttpEntity} that provides various support for converting the body to POJOs and
055 * other convenience methods.
056 *
057 * <p>
058 * To quickly retrieve a single value from inside a larger JSON document, parse the body into a {@link JsonMap} and use
059 * its path-addressing methods &mdash; e.g. <c>getContent().as(JsonMap.<jk>class</jk>).getAt(<jv>path</jv>, <jv>type</jv>)</c>
060 * or the RFC 6901 form <c>getContent().as(JsonMap.<jk>class</jk>).at(<jv>pointer</jv>)</c>.
061 *
062 * <h5 class='section'>See Also:</h5><ul>
063 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClient">juneau-rest-client-classic</a>
064 * </ul>
065 */
066@SuppressWarnings({
067   "java:S115", // Constants use UPPER_snakeCase convention (e.g., HEADER_ContentType)
068   "java:S4144", // Identical methods intentional for different test scenarios
069   "resource" // Resource management handled externally
070})
071public class ResponseContent implements HttpEntity {
072
073   private static final String HEADER_ContentType = "Content-Type";
074   private static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain";
075
076   private static final HttpEntity NULL_ENTITY = new HttpEntity() {
077
078      @Override
079      public void consumeContent() throws IOException {
080         // No-op: Mock implementation - full functionality not required
081      }
082
083      @Override
084      public InputStream getContent() throws IOException, UnsupportedOperationException { return new ByteArrayInputStream(new byte[0]); }
085
086      @Override
087      public Header getContentEncoding() { return ResponseHeader.NULL_HEADER; }
088
089      @Override
090      public long getContentLength() { return -1; }
091
092      @Override
093      public Header getContentType() { return ResponseHeader.NULL_HEADER; }
094
095      @Override
096      public boolean isChunked() { return false; }
097
098      @Override
099      public boolean isRepeatable() { return false; }
100
101      @Override
102      public boolean isStreaming() { return false; }
103
104      @Override
105      public void writeTo(OutputStream outstream) throws IOException {
106         // Fail-fast: writeTo() is never legitimately invoked on this sentinel -- pipeTo() and asBytes()/
107         // asInputStream() route through getContent()/getContentLength() instead, so a silent no-op here would
108         // mask a caller bypassing those paths.
109         throw new UnsupportedOperationException("NULL_ENTITY.writeTo() should never be called.");
110      }
111   };
112
113   private final RestClient client;
114   final RestRequest request;
115   final RestResponse response;
116   private final HttpEntity entity;
117   private HttpPartSchema schema;
118   private Parser parser;
119   private byte[] body;
120   private boolean cached;
121   boolean isConsumed;
122
123   /**
124    * Constructor.
125    *
126    * @param client The client used to build this request.
127    * @param request The request object.
128    * @param response The response object.
129    * @param parser The parser to use to consume the body.  Can be <jk>null</jk> to use the response's negotiated/default parser.
130    */
131   public ResponseContent(RestClient client, RestRequest request, RestResponse response, Parser parser) {
132      this.client = client;
133      this.request = request;
134      this.response = response;
135      this.parser = parser;
136      this.entity = coalesce(response.asHttpResponse().getEntity(), NULL_ENTITY);
137   }
138
139   /**
140    * Same as {@link #as(Type,Type...)} except optimized for a non-parameterized class.
141    *
142    * <p>
143    * This is the preferred parse method for simple types since you don't need to cast the results.
144    *
145    * <h5 class='section'>Examples:</h5>
146    * <p class='bjava'>
147    *    <jc>// Parse into a string.</jc>
148    *    String <jv>string</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(String.<jk>class</jk>);
149    *
150    *    <jc>// Parse into a bean.</jc>
151    *    MyBean <jv>bean</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(MyBean.<jk>class</jk>);
152    *
153    *    <jc>// Parse into a bean array.</jc>
154    *    MyBean[] <jv>beanArray</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(MyBean[].<jk>class</jk>);
155    *
156    *    <jc>// Parse into a linked-list of objects.</jc>
157    *    List <jv>list</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(LinkedList.<jk>class</jk>);
158    *
159    *    <jc>// Parse into a map of object keys/values.</jc>
160    *    Map <jv>map</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(TreeMap.<jk>class</jk>);
161    * </p>
162    *
163    * <h5 class='section'>Notes:</h5><ul>
164    *    <li class='note'>
165    *       You can also specify any of the following types:
166    *       <ul class='compact'>
167    *          <li>{@link ResponseContent}/{@link HttpEntity} - Returns access to this object.
168    *          <li>{@link Reader} - Returns access to the raw reader of the response.
169    *          <li>{@link InputStream} - Returns access to the raw input stream of the response.
170    *          <li>{@link HttpResource} - Response will be converted to a {@link StreamResource}.
171    *          <li>Any type that takes in an {@link HttpResponse} object.
172    *       </ul>
173    *    <li class='note'>
174    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
175    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
176    *    with an inner {@link IllegalStateException} will be thrown.
177    *    <li class='note'>
178    *       The input stream is automatically closed after this call.
179    * </ul>
180    *
181    * @param <T>
182    *    The class type of the object being created.
183    *    See {@link #as(Type,Type...)} for details.
184    * @param type The object type to create.
185    * @return The parsed object.
186    * @throws RestCallException
187    *    If the input contains a syntax error or is malformed, or is not valid for the specified type, or if a connection
188    *    error occurred.
189    */
190   public <T> T as(Class<T> type) throws RestCallException {
191      return as(getClassMeta(type));
192   }
193
194   /**
195    * Same as {@link #as(Class)} except allows you to predefine complex data types using the {@link ClassMeta} API.
196    *
197    * <h5 class='section'>Examples:</h5>
198    * <p class='bjava'>
199    *    MarshallingContext <jv>marshallingContext</jv> = MarshallingContext.<jsf>DEFAULT</jsf>;
200    *
201    *    <jc>// Parse into a linked-list of strings.</jc>
202    * ClassMeta&lt;List&lt;String&gt;&gt; <jv>cm1</jv> = <jv>marshallingContext</jv>.getClassMeta(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
203    *    List&lt;String&gt; <jv>list1</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm1</jv>);
204    *
205    *    <jc>// Parse into a linked-list of beans.</jc>
206    * ClassMeta&lt;List&lt;String&gt;&gt; <jv>cm2</jv> = <jv>marshallingContext</jv>.getClassMeta(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
207    *    List&lt;MyBean&gt; <jv>list2</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm2</jv>);
208    *
209    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
210    * ClassMeta&lt;List&lt;String&gt;&gt; <jv>cm3</jv> = <jv>marshallingContext</jv>.getClassMeta(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
211    *    List&lt;List&lt;String&gt;&gt; <jv>list3</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm3</jv>);
212    *
213    *    <jc>// Parse into a map of string keys/values.</jc>
214    * ClassMeta&lt;List&lt;String&gt;&gt; <jv>cm4</jv> = <jv>marshallingContext</jv>.getClassMeta(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
215    *    Map&lt;String,String&gt; <jv>map4</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm4</jv>);
216    *
217    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
218    * ClassMeta&lt;List&lt;String&gt;&gt; <jv>cm5</jv> = <jv>marshallingContext</jv>.getClassMeta(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
219    *    Map&lt;String,List&lt;MyBean&gt;&gt; <jv>map5</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm5</jv>);
220    * </p>
221    *
222    * <h5 class='section'>Notes:</h5><ul>
223    *    <li class='note'>
224    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
225    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
226    *    with an inner {@link IllegalStateException} will be thrown.
227    *    <li class='note'>
228    *       The input stream is automatically closed after this call.
229    * </ul>
230    *
231    * @param <T> The class type of the object to create.
232    * @param type The object type to create.
233    * @return The parsed object.
234    * @throws RestCallException
235    *    <ul>
236    *       <li>If the input contains a syntax error or is malformed, or is not valid for the specified type.
237    *       <li>If a connection error occurred.
238    *    </ul>
239    * @see MarshallingSession#getClassMeta(Class) for argument syntax for maps and collections.
240    */
241   @SuppressWarnings({
242      "java:S3776", // Cognitive complexity acceptable for this specific logic
243      "unchecked" // Type erasure requires unchecked casts in content parsing
244   })
245   public <T> T as(ClassMeta<T> type) throws RestCallException {
246      try {
247         if (type.is(ResponseContent.class) || type.is(HttpEntity.class))
248            return (T)this;
249
250         if (type.is(Reader.class))
251            return (T)asReader();
252
253         if (type.is(InputStream.class))
254            return (T)asInputStream();
255
256         if (type.isChildOf(RecordReader.class) || type.is(RecordReader.class))
257            return (T) asCursor(type);
258
259         if (type.is(HttpResponse.class))
260            return (T)response;
261
262         if (type.is(HttpResource.class) || type.is(BasicResource.class))
263            type = (ClassMeta<T>)getClassMeta(StreamResource.class);
264
265         var responseCtor = type.getPublicConstructor(x -> x.hasParameterTypes(HttpResponse.class));
266         if (responseCtor.isPresent())
267            return construct(responseCtor.get(), type);
268
269         var ct = firstNonEmpty(response.getHeader(HEADER_ContentType).orElse(CONTENT_TYPE_TEXT_PLAIN));
270
271         if (parser == null)
272            parser = client.getMatchingParser(ct);
273
274         var mt = MediaType.of(ct);
275
276         if ((parser == null || (mt.toString().contains(CONTENT_TYPE_TEXT_PLAIN) && ! parser.canHandle(ct))) && type.hasStringMutater())
277            return BasicConverter.INSTANCE.to(asString(), type.inner());
278
279         if (nn(parser))
280            return parse(type, mt);
281
282         if (type.hasReaderMutater())
283            return BasicConverter.INSTANCE.to(asReader(), type.inner());
284
285         if (type.hasInputStreamMutater())
286            return BasicConverter.INSTANCE.to(asInputStream(), type.inner());
287
288         ct = response.getStringHeader(HEADER_ContentType).orElse(null);
289
290         if (ct == null && client.hasParsers())
291            throw new ParseException("Content-Type not specified in response header.  Cannot find appropriate parser.");
292
293         throw new ParseException("Unsupported media-type in request header 'Content-Type': '%s'", ct);
294
295      } catch (ParseException | IOException e) {
296         response.close();
297         throw new RestCallException(response, e, "Could not parse response body.");
298      }
299   }
300
301   /**
302    * Constructs the target type from the response through its {@code (HttpResponse)} constructor.
303    */
304   private <T> T construct(ConstructorInfo ctor, ClassMeta<T> type) throws RestCallException {
305      try {
306         return ctor.newInstance(response);
307      } catch (ExecutableException e) {
308         // The (HttpResponse) constructor rejected the response (e.g. the status code does not match the class).
309         response.close();
310         throw new RestCallException(response, e.getCause() == null ? e : e.getCause(), "Could not construct %s from response.", type.inner().getName());
311      }
312   }
313
314   /**
315    * Parses the response body with the matched parser.
316    */
317   @SuppressWarnings({
318      "unchecked" // Type erasure requires unchecked casts in content parsing
319   })
320   private <T> T parse(ClassMeta<T> type, MediaType mt) throws ParseException, IOException {
321      try (Closeable in = parser.isReaderParser() ? asReader() : asInputStream()) {
322
323         // @formatter:off
324         T t = parser
325            .createSession()
326            .properties(JsonMap.create().inner(request.getSessionProperties()))
327            .locale(response.getLocale())
328            .mediaType(mt)
329            .schema(schema)
330            .build()
331            .read(in, type);
332         // @formatter:on
333
334         // Some HTTP responses have no body, so try to create these beans if they've got no-arg constructors.
335         if (t == null && ! type.is(String.class)) {
336            var result2 = type.getPublicConstructor(cons -> cons.getParameterCount() == 0).map(c -> safe(() -> (T)c.newInstance()));
337            if (result2.isPresent())
338               return result2.get();
339         }
340
341         return t;
342      }
343   }
344
345   /**
346    * Resolves a cursor return type ({@link RecordReader} / {@link TokenReader} or any subtype)
347    * by negotiating the response Content-Type to a parser and calling its
348    * {@code readRecords(...)} / {@code readTokens(...)} factory.
349    *
350    * <p>
351    * Implements two-layer resolution: the declared type filters eligible parsers (a concrete
352    * subtype like {@code JsonlTokenReader} is only assignable from a parser that produces it);
353    * the response's Content-Type negotiates within that filter.  Capability is detected via
354    * {@code instanceof} against the {@link RecordReadable} / {@link TokenReadable} role interfaces.
355    *
356    * <p>
357    * The caller owns the returned cursor's lifecycle — wrap it in try-with-resources.
358    *
359    * @param type The declared parameter / return type (must be {@link RecordReader} or a subtype).
360    * @return A new cursor.
361    * @throws RestCallException If no parser produces a cursor assignable to the declared type
362    *    for the response's Content-Type, or if the matched parser does not implement the requested
363    *    cursor role interface.
364    */
365   private Object asCursor(ClassMeta<?> type) throws RestCallException {
366      try {
367         var ct = firstNonEmpty(response.getHeader(HEADER_ContentType).orElse(CONTENT_TYPE_TEXT_PLAIN));
368         var matchedParser = parser != null ? parser : client.getMatchingParser(ct);
369         if (matchedParser == null)
370            throw new RestCallException(response, null,
371               "No registered parser matches Content-Type '%s' for cursor return type '%s'.",
372               ct, type.inner().getName());
373
374         var isToken = type.isChildOf(TokenReader.class) || type.is(TokenReader.class);
375         var supported = isToken ? matchedParser instanceof TokenReadable : matchedParser instanceof RecordReadable;
376         if (!supported)
377            throw new RestCallException(response, null,
378               "Parser '%s' does not support the %s surface.",
379               matchedParser.getClass().getName(),
380               isToken ? "token-reader" : "record-reader");
381
382         Object input = matchedParser.isReaderParser() ? asReader() : asInputStream();
383         Object cursor = isToken
384            ? ((TokenReadable) matchedParser).readTokens(input)
385            : ((RecordReadable) matchedParser).readRecords(input);
386
387         if (!type.inner().isInstance(cursor))
388            throw new RestCallException(response, null,
389               "Parser '%s' produced cursor type '%s' which is not assignable to the declared type '%s'.",
390               matchedParser.getClass().getName(),
391               cursor == null ? "null" : cursor.getClass().getName(),
392               type.inner().getName());
393
394         return cursor;
395      } catch (RestCallException e) {
396         throw e;
397      } catch (IOException e) {
398         throw new RestCallException(response, e, "Could not open cursor over response body.");
399      }
400   }
401
402   /**
403    * Parses HTTP body into the specified object type.
404    *
405    * <p>
406    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
407    *
408    * <h5 class='section'>Examples:</h5>
409    * <p class='bjava'>
410    *    <jc>// Parse into a linked-list of strings.</jc>
411    *    List&lt;String&gt; <jv>list1</jv> = <jv>client</jv>
412    *       .get(<jsf>URI</jsf>)
413    *       .run()
414    *       .getContent().as(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
415    *
416    *    <jc>// Parse into a linked-list of beans.</jc>
417    *    List&lt;MyBean&gt; <jv>list2</jv> = <jv>client</jv>
418    *       .get(<jsf>URI</jsf>)
419    *       .run()
420    *       .getContent().as(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
421    *
422    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
423    *    List&lt;List&lt;String&gt;&gt; <jv>list3</jv> = <jv>client</jv>
424    *       .get(<jsf>URI</jsf>)
425    *       .run()
426    *       .getContent().as(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
427    *
428    *    <jc>// Parse into a map of string keys/values.</jc>
429    *    Map&lt;String,String&gt; <jv>map1</jv> = <jv>client</jv>
430    *       .get(<jsf>URI</jsf>)
431    *       .run()
432    *       .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
433    *
434    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
435    *    Map&lt;String,List&lt;MyBean&gt;&gt; <jv>map2</jv> = <jv>client</jv>
436    *       .get(<jsf>URI</jsf>)
437    *       .run()
438    *       .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
439    * </p>
440    *
441    * <p>
442    * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type.
443    *
444    * <p>
445    * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
446    *
447    * <p>
448    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
449    *
450    * <h5 class='section'>Notes:</h5><ul>
451    *    <li class='note'>
452    *       Use the {@link #as(Class)} method instead if you don't need a parameterized map/collection.
453    *    <li class='note'>
454    *       You can also specify any of the following types:
455    *       <ul class='compact'>
456    *          <li>{@link ResponseContent}/{@link HttpEntity} - Returns access to this object.
457    *          <li>{@link Reader} - Returns access to the raw reader of the response.
458    *          <li>{@link InputStream} - Returns access to the raw input stream of the response.
459    *          <li>{@link HttpResource} - Response will be converted to a {@link StreamResource}.
460    *          <li>Any type that takes in an {@link HttpResponse} object.
461    *       </ul>
462    *    <li class='note'>
463    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
464    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
465    *    with an inner {@link IllegalStateException} will be thrown.
466    *    <li class='note'>
467    *       The input stream is automatically closed after this call.
468    * </ul>
469    *
470    * @param <T> The class type of the object to create.
471    * @param type
472    *    The object type to create.
473    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
474    * @param args
475    *    The type arguments of the class if it's a collection or map.
476    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
477    *    <br>Ignored if the main type is not a map or collection.
478    * @return The parsed object.
479    * @throws RestCallException
480    *    <ul>
481    *       <li>If the input contains a syntax error or is malformed, or is not valid for the specified type.
482    *       <li>If a connection error occurred.
483    *    </ul>
484    * @see MarshallingSession#getClassMeta(Class) for argument syntax for maps and collections.
485    */
486   public <T> T as(Type type, Type...args) throws RestCallException {
487      return as(getClassMeta(type, args));
488   }
489
490   /**
491    * Same as {@link #asString()} but truncates the string to the specified length.
492    *
493    * <p>
494    * If truncation occurs, the string will be suffixed with <js>"..."</js>.
495    *
496    * @param length The max length of the returned string.
497    * @return The truncated string.
498    * @throws RestCallException If a problem occurred trying to read from the reader.
499    */
500   public String asAbbreviatedString(int length) throws RestCallException {
501      return StringUtils.abbreviate(asString(), length);
502   }
503
504   /**
505    * Returns the HTTP response message body as a byte array.
506    *
507    *    The HTTP response message body as a byte array, never <jk>null</jk>.
508    *    <br>For responses without a body(e.g. HTTP 204), returns an empty array.
509    *
510    * @return The HTTP response body as a byte array.
511    * @throws RestCallException If an exception occurred.
512    */
513   public byte[] asBytes() throws RestCallException {
514      if (body == null) {
515         try {
516            if (entity instanceof BasicHttpEntity<?> entity2) {
517               body = entity2.asBytes();
518            } else {
519               body = readBytes(entity.getContent());
520            }
521         } catch (IOException e) {
522            throw new RestCallException(response, e, "Could not read response body.");
523         } finally {
524            response.close();
525         }
526      }
527      return body;
528   }
529
530   /**
531    * Same as {@link #as(Class)} but allows you to run the call asynchronously.
532    *
533    * <h5 class='section'>Notes:</h5><ul>
534    *    <li class='note'>
535    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
536    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
537    *    with an inner {@link IllegalStateException} will be thrown.
538    *    <li class='note'>
539    *       The input stream is automatically closed after the execution of the future.
540    * </ul>
541    *
542    * @param <T> The class type of the object being created.
543    * @param type The object type to create.
544    * @return The future object.
545    * @throws RestCallException If the executor service was not defined.
546    * @see
547    *    RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating
548    *    Future instances.
549    */
550   public <T> Future<T> asFuture(Class<T> type) throws RestCallException {
551      return client.getExecutorService().submit(() -> as(type));
552   }
553
554   /**
555    * Same as {@link #as(ClassMeta)} but allows you to run the call asynchronously.
556    *
557    * <h5 class='section'>Notes:</h5><ul>
558    *    <li class='note'>
559    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
560    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
561    *    with an inner {@link IllegalStateException} will be thrown.
562    *    <li class='note'>
563    *       The input stream is automatically closed after the execution of the future.
564    * </ul>
565    *
566    * @param <T>
567    *    The class type of the object being created.
568    *    See {@link #as(Type, Type...)} for details.
569    * @param type The object type to create.
570    * @return The future object.
571    * @throws RestCallException If the executor service was not defined.
572    * @see
573    *    RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating
574    *    Future instances.
575    */
576   public <T> Future<T> asFuture(ClassMeta<T> type) throws RestCallException {
577      return client.getExecutorService().submit(() -> as(type));
578   }
579
580   /**
581    * Same as {@link #as(Type,Type...)} but allows you to run the call asynchronously.
582    *
583    * <h5 class='section'>Notes:</h5><ul>
584    *    <li class='note'>
585    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
586    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
587    *    with an inner {@link IllegalStateException} will be thrown.
588    *    <li class='note'>
589    *       The input stream is automatically closed after the execution of the future.
590    * </ul>
591    *
592    * @param <T>
593    *    The class type of the object being created.
594    *    See {@link #as(Type, Type...)} for details.
595    * @param type
596    *    The object type to create.
597    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
598    * @param args
599    *    The type arguments of the class if it's a collection or map.
600    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
601    *    <br>Ignored if the main type is not a map or collection.
602    * @return The future object.
603    * @throws RestCallException If the executor service was not defined.
604    * @see
605    *    RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating
606    *    Future instances.
607    */
608   public <T> Future<T> asFuture(Type type, Type...args) throws RestCallException {
609      return client.getExecutorService().submit(() -> as(type, args));
610   }
611
612   /**
613    * Returns the HTTP body content as a simple hexadecimal character string.
614    *
615    * <h5 class='section'>Example:</h5>
616    * <p class='bcode'>
617    *    0123456789ABCDEF
618    * </p>
619    *
620    * @return The incoming input from the connection as a plain string.
621    * @throws RestCallException If a problem occurred trying to read from the reader.
622    */
623   public String asHex() throws RestCallException {
624      return toHex(asBytes());
625   }
626
627   /**
628    * Returns the HTTP response message body as an input stream.
629    *
630    * <h5 class='section'>Notes:</h5><ul>
631    *    <li class='note'>
632    *       Once this input stream is exhausted, it will automatically be closed.
633    *  <li class='note'>
634    *    This method can be called multiple times if {@link #cache()} has been called.
635    *  <li class='note'>
636    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
637    *    with an inner {@link IllegalStateException} to be thrown.
638    * </ul>
639    *
640    * @return
641    *    The HTTP response message body input stream, never <jk>null</jk>.
642    *    <br>For responses without a body(e.g. HTTP 204), returns an empty stream.
643    * @throws IOException If a stream or illegal state exception was thrown.
644    */
645   public InputStream asInputStream() throws IOException {
646      try {
647         if (nn(body))
648            return new ByteArrayInputStream(body);
649
650         if (cached) {
651            body = readBytes(entity.getContent());
652            response.close();
653            return new ByteArrayInputStream(body);
654         }
655
656         if (isConsumed && ! entity.isRepeatable())
657            throw new IllegalStateException("Method cannot be called.  Response has already been consumed.  Consider using the RestResponse.cacheBody() method.");
658
659         HttpEntity e = response.asHttpResponse().getEntity();
660         InputStream is = e == null ? new ByteArrayInputStream(new byte[0]) : e.getContent();
661
662         is = new EofSensorInputStream(is, new EofSensorWatcher() {
663            @Override
664            public boolean eofDetected(InputStream wrapped) throws IOException {
665               response.close();
666               return true;
667            }
668
669            @Override
670            public boolean streamAbort(InputStream wrapped) throws IOException {
671               response.close();
672               return true;
673            }
674
675            @Override
676            public boolean streamClosed(InputStream wrapped) throws IOException {
677               response.close();
678               return true;
679            }
680         });
681
682         isConsumed = true;
683
684         return is;
685      } catch (UnsupportedOperationException e) {
686         throw ioex(e);
687      }
688   }
689
690   /**
691    * Converts the contents of the response body to a string and then matches the specified pattern against it.
692    *
693    * <h5 class='section'>Example:</h5>
694    * <p class='bjava'>
695    *    <jc>// Parse response using a regular expression.</jc>
696    *    Matcher <jv>matcher</jv> = <jv>client</jv>
697    *       .get(<jsf>URI</jsf>)
698    *       .run()
699    *       .getContent().asMatcher(Pattern.<jsm>compile</jsm>(<js>"foo=(.*)"</js>));
700    *
701    *    <jk>if</jk> (<jv>matcher</jv>.matches()) {
702    *       String <jv>foo</jv> = <jv>matcher</jv>.group(1);
703    *    }
704    * </p>
705    *
706    * <h5 class='section'>Notes:</h5><ul>
707    *    <li class='note'>
708    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
709    *  <li class='note'>
710    *    This method automatically calls {@link #cache()} so that the body can be retrieved multiple times.
711    *    <li class='note'>
712    *       The input stream is automatically closed after this call.
713    * </ul>
714    *
715    * @param pattern The regular expression pattern to match.
716    * @return The matcher.
717    * @throws RestCallException If a connection error occurred.
718    */
719   public Matcher asMatcher(Pattern pattern) throws RestCallException {
720      return pattern.matcher(asString());
721   }
722
723   /**
724    * Converts the contents of the response body to a string and then matches the specified pattern against it.
725    *
726    * <h5 class='section'>Example:</h5>
727    * <p class='bjava'>
728    *    <jc>// Parse response using a regular expression.</jc>
729    *    Matcher <jv>matcher</jv> = <jv>client</jv>
730    *       .get(<jsf>URI</jsf>)
731    *       .run()
732    *       .getContent().asMatcher(<js>"foo=(.*)"</js>);
733    *
734    *    <jk>if</jk> (<jv>matcher</jv>.matches()) {
735    *       String <jv>foo</jv> = <jv>matcher</jv>.group(1);
736    *    }
737    * </p>
738    *
739    *
740    * <h5 class='section'>Notes:</h5><ul>
741    *    <li class='note'>
742    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
743    *  <li class='note'>
744    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
745    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
746    *    with an inner {@link IllegalStateException} will be thrown.
747    *    <li class='note'>
748    *       The input stream is automatically closed after this call.
749    * </ul>
750    *
751    * @param regex The regular expression pattern to match.
752    * @return The matcher.
753    * @throws RestCallException If a connection error occurred.
754    */
755   public Matcher asMatcher(String regex) throws RestCallException {
756      return asMatcher(regex, 0);
757   }
758
759   /**
760    * Converts the contents of the response body to a string and then matches the specified pattern against it.
761    *
762    * <h5 class='section'>Example:</h5>
763    * <p class='bjava'>
764    *    <jc>// Parse response using a regular expression.</jc>
765    *    Matcher <jv>matcher</jv> = <jv>client</jv>
766    *       .get(<jsf>URI</jsf>)
767    *       .run()
768    *       .getContent().asMatcher(<js>"foo=(.*)"</js>, <jsf>MULTILINE</jsf> &amp; <jsf>CASE_INSENSITIVE</jsf>);
769    *
770    *    <jk>if</jk> (<jv>matcher</jv>.matches()) {
771    *       String <jv>foo</jv> = <jv>matcher</jv>.group(1);
772    *    }
773    * </p>
774    *
775    *
776    * <h5 class='section'>Notes:</h5><ul>
777    *    <li class='note'>
778    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
779    *  <li class='note'>
780    *    If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with
781    *    other methods that retrieve the content of the response.  Otherwise a {@link RestCallException}
782    *    with an inner {@link IllegalStateException} will be thrown.
783    *    <li class='note'>
784    *       The input stream is automatically closed after this call.
785    * </ul>
786    *
787    * @param regex The regular expression pattern to match.
788    * @param flags Pattern match flags.  See {@link Pattern#compile(String, int)}.
789    * @return The matcher.
790    * @throws RestCallException If a connection error occurred.
791    */
792   public Matcher asMatcher(String regex, int flags) throws RestCallException {
793      return asMatcher(Pattern.compile(regex, flags));
794   }
795
796   /**
797    * Returns the HTTP response message body as a reader based on the charset on the <code>Content-Type</code> response header.
798    *
799    * <h5 class='section'>Notes:</h5><ul>
800    *    <li class='note'>
801    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
802    *    <li class='note'>
803    *       Once this input stream is exhausted, it will automatically be closed.
804    *  <li class='note'>
805    *    This method can be called multiple times if {@link #cache()} has been called.
806    *  <li class='note'>
807    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
808    *    with an inner {@link IllegalStateException} to be thrown.
809    * </ul>
810    *
811    * @return
812    *    The HTTP response message body reader, never <jk>null</jk>.
813    *    <br>For responses without a body(e.g. HTTP 204), returns an empty reader.
814    * @throws IOException If an exception occurred.
815    */
816   public Reader asReader() throws IOException {
817
818      // Figure out what the charset of the response is.
819      String cs = null;
820      var ct = getContentType().orElse(null);
821
822      // First look for "charset=" in Content-Type header of response.
823      if (nn(ct) && ct.contains("charset="))
824         cs = ct.substring(ct.indexOf("charset=") + 8).trim();
825
826      return asReader(cs == null ? UTF8 : Charset.forName(cs));
827   }
828
829   /**
830    * Returns the HTTP response message body as a reader using the specified charset.
831    *
832    * <h5 class='section'>Notes:</h5><ul>
833    *    <li class='note'>
834    *       Once this input stream is exhausted, it will automatically be closed.
835    *  <li class='note'>
836    *    This method can be called multiple times if {@link #cache()} has been called.
837    *  <li class='note'>
838    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
839    *    with an inner {@link IllegalStateException} to be thrown.
840    * </ul>
841    *
842    * @param charset
843    *    The charset to use for the reader.
844    *    <br>If <jk>null</jk>, <js>"UTF-8"</js> is used.
845    * @return
846    *    The HTTP response message body reader, never <jk>null</jk>.
847    *    <br>For responses without a body(e.g. HTTP 204), returns an empty reader.
848    * @throws IOException If an exception occurred.
849    */
850   public Reader asReader(Charset charset) throws IOException {
851      return new InputStreamReader(asInputStream(), charset == null ? UTF8 : charset);
852   }
853
854   /**
855    * Shortcut for calling <c>assertValue().asBytes()</c>.
856    *
857    * @return A new fluent assertion.
858    */
859   public FluentByteArrayAssertion<ResponseContent> assertBytes() {
860      return new FluentResponseBodyAssertion<>(this, this).asBytes();
861   }
862
863   /**
864    * Shortcut for calling <c>assertValue().as(<jv>type</jv>)</c>.
865    *
866    * @param <T> The object type to create.
867    * @param type The object type to create.
868    * @return A new fluent assertion.
869    */
870   public <T> FluentAnyAssertion<T,ResponseContent> assertObject(Class<T> type) {
871      return new FluentResponseBodyAssertion<>(this, this).as(type);
872   }
873
874   /**
875    * Shortcut for calling <c>assertValue().as(<jv>type</jv>, <jv>args</jv>)</c>.
876    *
877    * @param type The object type to create.
878    * @param args Optional type arguments.
879    * @return A new fluent assertion.
880    */
881   public FluentAnyAssertion<Object,ResponseContent> assertObject(Type type, Type...args) {
882      return new FluentResponseBodyAssertion<>(this, this).as(type, args);
883   }
884
885   /**
886    * Shortcut for calling <c>assertValue().asString()</c>.
887    *
888    * @return A new fluent assertion.
889    */
890   public FluentStringAssertion<ResponseContent> assertString() {
891      return new FluentResponseBodyAssertion<>(this, this).asString();
892   }
893
894   /**
895    * Provides the ability to perform fluent-style assertions on this response body.
896    *
897    * <p>
898    * This method is called directly from the {@link RestResponse#assertContent()} method to instantiate a fluent assertions object.
899    *
900    * <h5 class='section'>Examples:</h5>
901    * <p class='bjava'>
902    *    <jc>// Validates the response body equals the text "OK".</jc>
903    *    <jv>client</jv>
904    *       .get(<jsf>URI</jsf>)
905    *       .run()
906    *       .getContent().assertValue().equals(<js>"OK"</js>);
907    *
908    *    <jc>// Validates the response body contains the text "OK".</jc>
909    *    <jv>client</jv>
910    *       .get(<jsf>URI</jsf>)
911    *       .run()
912    *       .getContent().assertValue().contains(<js>"OK"</js>);
913    *
914    *    <jc>// Validates the response body passes a predicate test.</jc>
915    *    <jv>client</jv>
916    *       .get(<jsf>URI</jsf>)
917    *       .run()
918    *       .getContent().assertValue().is(<jv>x</jv> -&gt; <jv>x</jv>.contains(<js>"OK"</js>));
919    *
920    *    <jc>// Validates the response body matches a regular expression.</jc>
921    *    <jv>client</jv>
922    *       .get(<jsf>URI</jsf>)
923    *       .run()
924    *       .getContent().assertValue().isPattern(<js>".*OK.*"</js>);
925    *
926    *    <jc>// Validates the response body matches a regular expression using regex flags.</jc>
927    *    <jv>client</jv>
928    *       .get(<jsf>URI</jsf>)
929    *       .run()
930    *       .getContent().assertValue().isPattern(<js>".*OK.*"</js>,  <jsf>MULTILINE</jsf> &amp; <jsf>CASE_INSENSITIVE</jsf>);
931    *
932    *    <jc>// Validates the response body matches a regular expression in the form of an existing Pattern.</jc>
933    *    Pattern <jv>pattern</jv> = Pattern.<jsm>compile</jsm>(<js>".*OK.*"</js>);
934    *    <jv>client</jv>
935    *       .get(<jsf>URI</jsf>)
936    *       .run()
937    *       .getContent().assertValue().isPattern(<jv>pattern</jv>);
938    * </p>
939    *
940    * <p>
941    * The assertion test returns the original response object allowing you to chain multiple requests like so:
942    * <p class='bjava'>
943    *    <jc>// Validates the response body matches a regular expression.</jc>
944    *    MyBean <jv>bean</jv> = <jv>client</jv>
945    *       .get(<jsf>URI</jsf>)
946    *       .run()
947    *       .getContent().assertValue().isPattern(<js>".*OK.*"</js>);
948    *       .getContent().assertValue().isNotPattern(<js>".*ERROR.*"</js>)
949    *       .getContent().as(MyBean.<jk>class</jk>);
950    * </p>
951    *
952    * <h5 class='section'>Notes:</h5><ul>
953    *    <li class='note'>
954    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
955    *  <li class='note'>
956    *    This method automatically calls {@link #cache()} so that the body can be retrieved multiple times.
957    *    <li class='note'>
958    *       The input stream is automatically closed after this call.
959    * </ul>
960    *
961    * @return A new fluent assertion object.
962    */
963   public FluentResponseBodyAssertion<ResponseContent> assertValue() {
964      return new FluentResponseBodyAssertion<>(this, this);
965   }
966
967   /**
968    * Returns the HTTP body content as a simple space-delimited hexadecimal character string.
969    *
970    * <h5 class='section'>Example:</h5>
971    * <p class='bcode'>
972    *    01 23 45 67 89 AB CD EF
973    * </p>
974    *
975    * @return The incoming input from the connection as a plain string.
976    * @throws RestCallException If a problem occurred trying to read from the reader.
977    */
978   public String asSpacedHex() throws RestCallException {
979      return toSpacedHex(asBytes());
980   }
981
982   /**
983    * Returns the contents of this body as a string.
984    *
985    * <h5 class='section'>Notes:</h5><ul>
986    *    <li class='note'>
987    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
988    *  <li class='note'>
989    *    This method automatically calls {@link #cache()} so that the body can be retrieved multiple times.
990    *    <li class='note'>
991    *       The input stream is automatically closed after this call.
992    * </ul>
993    *
994    * @return The response as a string.
995    * @throws RestCallException
996    *    <ul>
997    *       <li>If the input contains a syntax error or is malformed, or is not valid for the specified type.
998    *       <li>If a connection error occurred.
999    *    </ul>
1000    */
1001   public String asString() throws RestCallException {
1002      cache();
1003      try (Reader r = asReader()) {
1004         return read(r);
1005      } catch (IOException e) {
1006         response.close();
1007         throw new RestCallException(response, e, "Could not read response body.");
1008      }
1009   }
1010
1011   /**
1012    * Same as {@link #asString()} but allows you to run the call asynchronously.
1013    *
1014    * <h5 class='section'>Notes:</h5><ul>
1015    *    <li class='note'>
1016    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
1017    *  <li class='note'>
1018    *    This method automatically calls {@link #cache()} so that the body can be retrieved multiple times.
1019    *    <li class='note'>
1020    *       The input stream is automatically closed after this call.
1021    * </ul>
1022    *
1023    * @return The future object.
1024    * @throws RestCallException If the executor service was not defined.
1025    * @see
1026    *    RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating
1027    *    Future instances.
1028    */
1029   public Future<String> asStringFuture() throws RestCallException {
1030      return client.getExecutorService().submit(this::asString);
1031   }
1032
1033   /**
1034    * Causes the contents of the response body to be stored so that it can be repeatedly read.
1035    *
1036    * <p>
1037    * Calling this method allows methods that read the response body to be called multiple times.
1038    *
1039    * <h5 class='section'>Notes:</h5><ul>
1040    *    <li class='note'>
1041    *       Multiple calls to this method are ignored.
1042    * </ul>
1043    *
1044    * @return This object.
1045    */
1046   public ResponseContent cache() {
1047      this.cached = true;
1048      return this;
1049   }
1050
1051   /**
1052    * This method is called to indicate that the content of this entity is no longer required.
1053    *
1054    * <p>
1055    * This method is of particular importance for entities being received from a connection.
1056    * <br>The entity needs to be consumed completely in order to re-use the connection with keep-alive.
1057    *
1058    * @throws IOException If an I/O error occurs.
1059    * @deprecated Use standard java convention to ensure resource deallocation by calling {@link InputStream#close()} on
1060    * the input stream returned by {@link #getContent()}
1061    */
1062   @Override /* Overridden from HttpEntity */
1063   @Deprecated(since = "10.0", forRemoval = true)
1064   @SuppressWarnings({
1065      "java:S1133" // Intentional deprecation retained for backward compatibility until the documented removal; the reminder is not actionable now.
1066   })
1067   public void consumeContent() throws IOException {
1068      entity.consumeContent();
1069   }
1070
1071   /**
1072    * Returns a content stream of the entity.
1073    *
1074    * <h5 class='section'>Notes:</h5><ul>
1075    *    <li class='note'>This method is equivalent to {@link #asInputStream()} which is the preferred method for fluent-style coding.
1076    *    <li class='note'>This input stream will auto-close once the end of stream has been reached.
1077    *    <li class='note'>It is up to the caller to properly close this stream if not fully consumed.
1078    *    <li class='note'>This method can be called multiple times if the entity is repeatable or the cache flag is set on this object.
1079    *    <li class='note'>Calling this method multiple times on a non-repeatable or cached body will throw a {@link IllegalStateException}.
1080    *       Note that this is different from the HttpClient specs for this method.
1081    * </ul>
1082    *
1083    * @return Content stream of the entity.
1084    */
1085   @Override /* Overridden from HttpEntity */
1086   public InputStream getContent() throws IOException, UnsupportedOperationException { return asInputStream(); }
1087
1088   /**
1089    * Obtains the Content-Encoding header, if known.
1090    *
1091    * <p>
1092    * This is the header that should be used when sending the entity, or the one that was received with the entity.
1093    * <br>Wrapping entities that modify the content encoding should adjust this header accordingly.
1094    *
1095    * @return The <c>Content-Encoding</c> header for this entity.  Never <jk>null</jk>, but wraps a <jk>null</jk> value if the content encoding is unknown.
1096    */
1097   @Override /* Overridden from HttpEntity */
1098   public ResponseHeader getContentEncoding() { return new ResponseHeader("Content-Encoding", request, response, entity.getContentEncoding()); }
1099
1100   /**
1101    * Tells the length of the content, if known.
1102    *
1103    * @return
1104    *    The number of bytes of the content, or a negative number if unknown.
1105    *    <br>If the content length is known but exceeds {@link Long#MAX_VALUE}, a negative number is returned.
1106    */
1107   @Override /* Overridden from HttpEntity */
1108   public long getContentLength() { return nn(body) ? body.length : entity.getContentLength(); }
1109
1110   /**
1111    * Obtains the <c>Content-Type</c> header, if known.
1112    *
1113    * <p>
1114    * This is the header that should be used when sending the entity, or the one that was received with the entity.
1115    * It can include a charset attribute.
1116    *
1117    * @return The <c>Content-Type</c> header for this entity.  Never <jk>null</jk>, but wraps a <jk>null</jk> value if the content type is unknown.
1118    */
1119   @Override /* Overridden from HttpEntity */
1120   public ResponseHeader getContentType() { return new ResponseHeader(HEADER_ContentType, request, response, entity.getContentType()); }
1121
1122   /**
1123    * Tells about chunked encoding for this entity.
1124    *
1125    * <p>
1126    * The primary purpose of this method is to indicate whether chunked encoding should be used when the entity is sent.
1127    * <br>For entities that are received, it can also indicate whether the entity was received with chunked encoding.
1128    *
1129    * <p>
1130    * The behavior of wrapping entities is implementation dependent, but should respect the primary purpose.
1131    *
1132    * @return <jk>true</jk> if chunked encoding is preferred for this entity, or <jk>false</jk> if it is not.
1133    */
1134   @Override /* Overridden from HttpEntity */
1135   public boolean isChunked() { return entity.isChunked(); }
1136
1137   /**
1138    * Tells if the entity is capable of producing its data more than once.
1139    *
1140    * <p>
1141    * A repeatable entity's {@link #getContent()} and {@link #writeTo(OutputStream)} methods can be called more than
1142    * once whereas a non-repeatable entity's can not.
1143    *
1144    * <h5 class='section'>Notes:</h5><ul>
1145    * <li class='note'>This method always returns <jk>true</jk> if the response body is cached (see {@link #cache()}).
1146    * </ul>
1147    *
1148    * @return <jk>true</jk> if the entity is repeatable, <jk>false</jk> otherwise.
1149    */
1150   @Override /* Overridden from HttpEntity */
1151   public boolean isRepeatable() { return cached || entity.isRepeatable(); }
1152
1153   /**
1154    * Tells whether this entity depends on an underlying stream.
1155    *
1156    * <h5 class='section'>Notes:</h5><ul>
1157    * <li class='note'>This method always returns <jk>false</jk> if the response body is cached (see {@link #cache()}.
1158    * </ul>
1159    *
1160    * @return <jk>true</jk> if the entity content is streamed, <jk>false</jk> otherwise.
1161    */
1162   @Override /* Overridden from HttpEntity */
1163   public boolean isStreaming() { return !cached && entity.isStreaming(); }
1164
1165   /**
1166    * Specifies the parser to use for this body.
1167    *
1168    * <p>
1169    * If not specified, uses the parser defined on the client set via {@link RestClient.Builder#parser(Class)}.
1170    *
1171    * @param value
1172    *    The new parser to use for this body.
1173    * @return This object.
1174    */
1175   public ResponseContent parser(Parser value) {
1176      parser = value;
1177      return this;
1178   }
1179
1180   /**
1181    * Pipes the contents of the response to the specified output stream.
1182    *
1183    * <h5 class='section'>Notes:</h5><ul>
1184    * <li class='note'>
1185    *    The output stream is not automatically closed.
1186    *    <li class='note'>
1187    *       Once the input stream is exhausted, it will automatically be closed.
1188    *  <li class='note'>
1189    *    This method can be called multiple times if {@link #cache()} has been called.
1190    *  <li class='note'>
1191    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
1192    *    with an inner {@link IllegalStateException} to be thrown.
1193    * </ul>
1194    *
1195    * @param os The output stream to pipe the output to.
1196    * @return This object.
1197    * @throws IOException If an IO exception occurred.
1198    */
1199   public RestResponse pipeTo(OutputStream os) throws IOException {
1200      pipe(asInputStream(), os);
1201      return response;
1202   }
1203
1204   /**
1205    * Pipes the contents of the response to the specified writer.
1206    *
1207    * <h5 class='section'>Notes:</h5><ul>
1208    * <li class='note'>
1209    *    The writer is not automatically closed.
1210    *    <li class='note'>
1211    *       Once the reader is exhausted, it will automatically be closed.
1212    *    <li class='note'>
1213    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
1214    *  <li class='note'>
1215    *    This method can be called multiple times if {@link #cache()} has been called.
1216    *  <li class='note'>
1217    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
1218    *    with an inner {@link IllegalStateException} to be thrown.
1219    * </ul>
1220    *
1221    * @param w The writer to pipe the output to.
1222    * @return This object.
1223    * @throws IOException If an IO exception occurred.
1224    */
1225   public RestResponse pipeTo(Writer w) throws IOException {
1226      return pipeTo(w, false);
1227   }
1228
1229   /**
1230    * Pipes the contents of the response to the specified writer.
1231    *
1232    * <h5 class='section'>Notes:</h5><ul>
1233    * <li class='note'>
1234    *    The writer is not automatically closed.
1235    *    <li class='note'>
1236    *       Once the reader is exhausted, it will automatically be closed.
1237    *    <li class='note'>
1238    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
1239    *  <li class='note'>
1240    *    This method can be called multiple times if {@link #cache()} has been called.
1241    *  <li class='note'>
1242    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
1243    *    with an inner {@link IllegalStateException} to be thrown.
1244    * </ul>
1245    *
1246    * @param w The writer to write the output to.
1247    * @param byLines Flush the writers after every line of output.
1248    * @return This object.
1249    * @throws IOException If an IO exception occurred.
1250    */
1251   public RestResponse pipeTo(Writer w, boolean byLines) throws IOException {
1252      return pipeTo(w, null, byLines);
1253   }
1254
1255   /**
1256    * Pipes the contents of the response to the specified writer.
1257    *
1258    * <h5 class='section'>Notes:</h5><ul>
1259    * <li class='note'>
1260    *    The writer is not automatically closed.
1261    *    <li class='note'>
1262    *       Once the reader is exhausted, it will automatically be closed.
1263    *  <li class='note'>
1264    *    This method can be called multiple times if {@link #cache()} has been called.
1265    *  <li class='note'>
1266    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
1267    *    with an inner {@link IllegalStateException} to be thrown.
1268    * </ul>
1269    *
1270    * @param w The writer to pipe the output to.
1271    * @param charset
1272    *    The charset to use for the reader.
1273    *    <br>If <jk>null</jk>, <js>"UTF-8"</js> is used.
1274    * @return This object.
1275    * @throws IOException If an IO exception occurred.
1276    */
1277   public RestResponse pipeTo(Writer w, Charset charset) throws IOException {
1278      return pipeTo(w, charset, false);
1279   }
1280
1281   /**
1282    * Pipes the contents of the response to the specified writer.
1283    *
1284    * <h5 class='section'>Notes:</h5><ul>
1285    * <li class='note'>
1286    *    The writer is not automatically closed.
1287    *    <li class='note'>
1288    *       Once the reader is exhausted, it will automatically be closed.
1289    *  <li class='note'>
1290    *    This method can be called multiple times if {@link #cache()} has been called.
1291    *  <li class='note'>
1292    *    Calling this method multiple times without caching enabled will cause a {@link RestCallException}
1293    *    with an inner {@link IllegalStateException} to be thrown.
1294    * </ul>
1295    *
1296    * @param w The writer to pipe the output to.
1297    * @param byLines Flush the writers after every line of output.
1298    * @param charset
1299    *    The charset to use for the reader.
1300    *    <br>If <jk>null</jk>, <js>"UTF-8"</js> is used.
1301    * @return This object.
1302    * @throws IOException If an IO exception occurred.
1303    */
1304   public RestResponse pipeTo(Writer w, Charset charset, boolean byLines) throws IOException {
1305      if (byLines)
1306         pipeLines(asReader(charset), w);
1307      else
1308         pipe(asReader(charset), w);
1309      return response;
1310   }
1311
1312   /**
1313    * Returns the response that created this object.
1314    *
1315    * @return The response that created this object.
1316    */
1317   public RestResponse response() {
1318      return response;
1319   }
1320
1321   /**
1322    * Specifies the schema for this body.
1323    *
1324    * <p>
1325    * Used by schema-based parsers such as {@link OpenApiParser}.
1326    *
1327    * @param value The schema.
1328    * @return This object.
1329    */
1330   public ResponseContent schema(HttpPartSchema value) {
1331      schema = value;
1332      return this;
1333   }
1334
1335   @Override
1336   public String toString() {
1337      try {
1338         return asString();
1339      } catch (RestCallException e) {
1340         return localizedMessage(e);
1341      }
1342   }
1343
1344   /**
1345    * Writes the entity content out to the output stream.
1346    *
1347    * <h5 class='section'>Notes:</h5><ul>
1348    *    <li class='note'>This method is equivalent to {@link #pipeTo(OutputStream)} which is the preferred method for fluent-style coding.
1349    * </ul>
1350    *
1351    * @param outstream The output stream to write entity content to.
1352    */
1353   @Override /* Overridden from HttpEntity */
1354   public void writeTo(OutputStream outstream) throws IOException {
1355      pipeTo(outstream);
1356   }
1357
1358   private MarshallingContext getMarshallingContext() { return parser == null ? MarshallingContext.DEFAULT : parser.getMarshallingContext(); }
1359
1360   private <T> ClassMeta<T> getClassMeta(Class<T> c) {
1361      return getMarshallingContext().getClassMeta(c);
1362   }
1363
1364   private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
1365      return getMarshallingContext().getClassMeta(type, args);
1366   }
1367}