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