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.httppart.HttpPartType.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.lang.reflect.*;
023import java.util.*;
024import java.util.logging.*;
025
026import org.apache.http.*;
027import org.apache.http.message.*;
028import org.apache.http.params.*;
029import org.apache.http.util.*;
030import org.apache.juneau.http.classic.header.*;
031import org.apache.juneau.httppart.bean.*;
032import org.apache.juneau.marshall.*;
033import org.apache.juneau.marshall.httppart.*;
034import org.apache.juneau.marshall.parser.*;
035import org.apache.juneau.rest.client.classic.assertion.*;
036import org.apache.juneau.bct.assertions.*;
037
038/**
039 * Represents a response from a remote REST resource.
040 *
041 * <p>
042 * Instances of this class are created by calling the {@link RestRequest#run()} method.
043 *
044 * <h5 class='section'>Example:</h5>
045 * <p class='bjava'>
046 *    <jc>// Create a request and response, automatically closing both.</jc>
047 *    <jk>try</jk> (
048 *       <jv>RestRequest</jv> <jv>req</jv> = <jv>client</jv>.get(<js>"/myResource"</js>);
049 *       <jv>RestResponse</jv> <jv>res</jv> = <jv>req</jv>.run()
050 *    ) {
051 *       String <jv>body</jv> = <jv>res</jv>.getContent().asString();
052 *    }
053 * </p>
054 *
055 * <p>
056 * Alternatively, you can rely on {@link RestRequest#close()} to automatically close the response:
057 *
058 * <p class='bjava'>
059 *    <jc>// Only specify RestRequest - it will close the response automatically.</jc>
060 *    <jk>try</jk> (<jv>RestRequest</jv> <jv>req</jv> = <jv>client</jv>.get(<js>"/myResource"</js>)) {
061 *       String <jv>body</jv> = <jv>req</jv>.run().getContent().asString();
062 *    }
063 * </p>
064 *
065 * <h5 class='section'>Notes:</h5><ul>
066 *    <li class='note'>This class implements {@link AutoCloseable} and can be used in try-with-resources blocks.
067 *       The {@link #close()} method allows unchecked exceptions to propagate for debuggability,
068 *       while catching and logging checked exceptions to follow AutoCloseable best practices.
069 * </ul>
070 *
071 * <h5 class='section'>See Also:</h5><ul>
072 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClient">juneau-rest-client-classic</a>
073 * </ul>
074 */
075@SuppressWarnings({
076   "java:S1133", // Intentional deprecation retained for backward compatibility until the documented removal; the reminder is not actionable now.
077   "java:S4144", // Identical methods intentional for different test scenarios
078   "resource" // Resource management handled externally
079})
080public class RestResponse implements HttpResponse, AutoCloseable {
081
082   private final RestClient client;
083   private final RestRequest request;
084   private final HttpResponse response;
085   private final Parser parser;
086   private ResponseContent responseContent;
087   private boolean isClosed;
088   private HeaderList headers;
089
090   private final Map<HttpPartParser,HttpPartParserSession> partParserSessions = new IdentityHashMap<>();
091   private HttpPartParserSession partParserSession;
092
093   /**
094    * Constructor.
095    *
096    * @param client The RestClient that created this response.
097    * @param request The REST request.
098    * @param response The HTTP response.  Can be <jk>null</jk> (a default empty response with a status code of <c>0</c> is substituted).
099    * @param parser The overridden parser passed into {@link RestRequest#parser(Parser)}.
100    */
101   protected RestResponse(RestClient client, RestRequest request, HttpResponse response, Parser parser) {
102      this.client = client;
103      this.request = request;
104      this.parser = parser;
105      this.response = response == null ? new BasicHttpResponse(null, 0, null) : response;
106      this.responseContent = new ResponseContent(client, request, this, parser);
107      this.headers = HeaderList.of(this.response.getAllHeaders());
108   }
109
110   /**
111    * Adds a header to this message.
112    *
113    * The header will be appended to the end of the list.
114    *
115    * @param header The header to append.
116    */
117   @Override /* Overridden from HttpMessage */
118   public void addHeader(Header header) {
119      headers.append(header);
120   }
121
122   /**
123    * Adds a header to this message.
124    *
125    * The header will be appended to the end of the list.
126    *
127    * @param name The name of the header.
128    * @param value The value of the header.
129    */
130   @Override /* Overridden from HttpMessage */
131   public void addHeader(String name, String value) {
132      headers.append(name, value);
133   }
134
135   /**
136    * Provides the ability to perform fluent-style assertions on the response character encoding.
137    *
138    * <h5 class='section'>Examples:</h5>
139    * <p class='bjava'>
140    *    <jc>// Validates that the response content charset is UTF-8.</jc>
141    *    <jv>client</jv>
142    *       .get(<jsf>URI</jsf>)
143    *       .run()
144    *       .assertCharset().is(<js>"utf-8"</js>);
145    * </p>
146    *
147    * @return A new fluent assertion object.
148    * @throws RestCallException If REST call failed.
149    */
150   public FluentStringAssertion<RestResponse> assertCharset() throws RestCallException {
151      return new FluentStringAssertion<>(getCharacterEncoding(), this);
152   }
153
154   /**
155    * Provides the ability to perform fluent-style assertions on this response body.
156    *
157    * <h5 class='section'>Examples:</h5>
158    * <p class='bjava'>
159    *    <jc>// Validates the response body equals the text "OK".</jc>
160    *    <jv>client</jv>
161    *       .get(<jsf>URI</jsf>)
162    *       .run()
163    *       .assertContent().is(<js>"OK"</js>);
164    *
165    *    <jc>// Validates the response body contains the text "OK".</jc>
166    *    <jv>client</jv>
167    *       .get(<jsf>URI</jsf>)
168    *       .run()
169    *       .assertContent().isContains(<js>"OK"</js>);
170    *
171    *    <jc>// Validates the response body passes a predicate test.</jc>
172    *    <jv>client</jv>
173    *       .get(<jsf>URI</jsf>)
174    *       .run()
175    *       .assertContent().is(<jv>x</jv> -&gt; <jv>x</jv>.contains(<js>"OK"</js>));
176    *
177    *    <jc>// Validates the response body matches a regular expression.</jc>
178    *    <jv>client</jv>
179    *       .get(<jsf>URI</jsf>)
180    *       .run()
181    *       .assertContent().isPattern(<js>".*OK.*"</js>);
182    *
183    *    <jc>// Validates the response body matches a regular expression using regex flags.</jc>
184    *    <jv>client</jv>
185    *       .get(<jsf>URI</jsf>)
186    *       .run()
187    *       .assertContent().isPattern(<js>".*OK.*"</js>, <jsf>MULTILINE</jsf> &amp; <jsf>CASE_INSENSITIVE</jsf>);
188    *
189    *    <jc>// Validates the response body matches a regular expression in the form of an existing Pattern.</jc>
190    *    Pattern <jv>pattern</jv> = Pattern.<jsm>compile</jsm>(<js>".*OK.*"</js>);
191    *    <jv>client</jv>
192    *       .get(<jsf>URI</jsf>)
193    *       .run()
194    *       .assertContent().isPattern(<jv>pattern</jv>);
195    * </p>
196    *
197    * <p>
198    * The assertion test returns the original response object allowing you to chain multiple requests like so:
199    * <p class='bjava'>
200    *    <jc>// Validates the response body matches a regular expression.</jc>
201    *    MyBean <jv>bean</jv> = <jv>client</jv>
202    *       .get(<jsf>URI</jsf>)
203    *       .run()
204    *       .assertContent().isPattern(<js>".*OK.*"</js>);
205    *       .assertContent().isNotPattern(<js>".*ERROR.*"</js>)
206    *       .getContent().as(MyBean.<jk>class</jk>);
207    * </p>
208    *
209    * <h5 class='section'>Notes:</h5><ul>
210    *    <li class='note'>
211    *       If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed.
212    *  <li class='note'>
213    *    When using this method, the body is automatically cached by calling the {@link ResponseContent#cache()}.
214    *    <li class='note'>
215    *       The input stream is automatically closed after this call.
216    * </ul>
217    *
218    * @return A new fluent assertion object.
219    */
220   public FluentResponseBodyAssertion<RestResponse> assertContent() {
221      return new FluentResponseBodyAssertion<>(responseContent, this);
222   }
223
224   /**
225    * Provides the ability to perform fluent-style assertions on this response body.
226    *
227    * <p>
228    * A shortcut for calling <c>assertContent().is(<jv>value</jv>)</c>.
229    *
230    * <h5 class='section'>Examples:</h5>
231    * <p class='bjava'>
232    *    <jc>// Validates the response body equals the text "OK".</jc>
233    *    <jv>client</jv>
234    *       .get(<jsf>URI</jsf>)
235    *       .run()
236    *       .assertContent(<js>"OK"</js>);
237    * </p>
238    *
239    * @param value The value to assert.
240    * @return This object.
241    */
242   public RestResponse assertContent(String value) {
243      assertContent().is(value);
244      return this;
245   }
246
247   /**
248    * Provides the ability to perform fluent-style assertions on this response body.
249    *
250    * <p>
251    * A shortcut for calling <c>assertContent().asString().isMatches(<jv>value</jv>)</c>.
252    *
253    * @see FluentStringAssertion#isMatches(String)
254    * @param value The value to assert.
255    * @return This object.
256    */
257   public RestResponse assertContentMatches(String value) {
258      assertContent().asString().isMatches(value);
259      return this;
260   }
261
262   /**
263    * Provides the ability to perform fluent-style assertions on a response header.
264    *
265    * <h5 class='section'>Examples:</h5>
266    * <p class='bjava'>
267    *    <jc>// Validates the content type header is provided.</jc>
268    *    <jv>client</jv>
269    *       .get(<jsf>URI</jsf>)
270    *       .run()
271    *       .assertHeader(<js>"Content-Type"</js>).exists();
272    *
273    *    <jc>// Validates the content type is JSON.</jc>
274    *    <jv>client</jv>
275    *       .get(<jsf>URI</jsf>)
276    *       .run()
277    *       .assertHeader(<js>"Content-Type"</js>).is(<js>"application/json"</js>);
278    *
279    *    <jc>// Validates the content type is JSON using test predicate.</jc>
280    *    <jv>client</jv>
281    *       .get(<jsf>URI</jsf>)
282    *       .run()
283    *       .assertHeader(<js>"Content-Type"</js>).is(<jv>x</jv> -&gt; <jv>x</jv>.equals(<js>"application/json"</js>));
284    *
285    *    <jc>// Validates the content type is JSON by just checking for substring.</jc>
286    *    <jv>client</jv>
287    *       .get(<jsf>URI</jsf>)
288    *       .run()
289    *       .assertHeader(<js>"Content-Type"</js>).contains(<js>"json"</js>);
290    *
291    *    <jc>// Validates the content type is JSON using regular expression.</jc>
292    *    <jv>client</jv>
293    *       .get(<jsf>URI</jsf>)
294    *       .run()
295    *       .assertHeader(<js>"Content-Type"</js>).isPattern(<js>".*json.*"</js>);
296    *
297    *    <jc>// Validates the content type is JSON using case-insensitive regular expression.</jc>
298    *    <jv>client</jv>
299    *       .get(<jsf>URI</jsf>)
300    *       .run()
301    *       .assertHeader(<js>"Content-Type"</js>).isPattern(<js>".*json.*"</js>, <jsf>CASE_INSENSITIVE</jsf>);
302    * </p>
303    *
304    * <p>
305    * The assertion test returns the original response object allowing you to chain multiple requests like so:
306    * <p class='bjava'>
307    *    <jc>// Validates the header and converts it to a bean.</jc>
308    *    MediaType <jv>mediaType</jv> = <jv>client</jv>
309    *       .get(<jsf>URI</jsf>)
310    *       .run()
311    *       .assertHeader(<js>"Content-Type"</js>).isNotEmpty()
312    *       .assertHeader(<js>"Content-Type"</js>).isPattern(<js>".*json.*"</js>)
313    *       .getHeader(<js>"Content-Type"</js>).as(MediaType.<jk>class</jk>);
314    * </p>
315    *
316    * @param name The header name.
317    * @return A new fluent assertion object.
318    */
319   public FluentResponseHeaderAssertion<RestResponse> assertHeader(String name) {
320      return new FluentResponseHeaderAssertion<>(getHeader(name), this);
321   }
322
323   /**
324    * Provides the ability to perform fluent-style assertions on the response {@link StatusLine} object.
325    *
326    * <h5 class='section'>Examples:</h5>
327    * <p class='bjava'>
328    *    MyBean <jv>bean</jv> = <jv>client</jv>
329    *       .get(<jsf>URI</jsf>)
330    *       .run()
331    *       .assertStatus().asCode().is(200)
332    *       .getContent().as(MyBean.<jk>class</jk>);
333    * </p>
334    *
335    * @return A new fluent assertion object.
336    */
337   public FluentResponseStatusLineAssertion<RestResponse> assertStatus() {
338      return new FluentResponseStatusLineAssertion<>(getStatusLine(), this);
339   }
340
341   /**
342    * Provides the ability to perform fluent-style assertions on the response status code.
343    *
344    * <h5 class='section'>Examples:</h5>
345    * <p class='bjava'>
346    *    MyBean <jv>bean</jv> = <jv>client</jv>
347    *       .get(<jsf>URI</jsf>)
348    *       .run()
349    *       .assertStatus(200)
350    *       .getContent().as(MyBean.<jk>class</jk>);
351    * </p>
352    *
353    * @param value The value to assert.
354    * @return A new fluent assertion object.
355    */
356   public RestResponse assertStatus(int value) {
357      assertStatus().asCode().is(value);
358      return this;
359   }
360
361   /**
362    * Caches the response body so that it can be read as a stream multiple times.
363    *
364    * This is equivalent to calling the following:
365    * <p class='bjava'>
366    *    getContent().cache();
367    * </p>
368    *
369    * @return The body of the response.
370    */
371   public RestResponse cacheContent() {
372      responseContent.cache();
373      return this;
374   }
375
376   /**
377    * Closes this response.
378    *
379    * <p>
380    * This method is idempotent and can be called multiple times without side effects.
381    *
382    * <h5 class='section'>Implementation Notes:</h5>
383    * <p>
384    * This implementation represents a compromise between strict AutoCloseable compliance and debuggability:
385    * <ul>
386    *    <li>Unchecked exceptions ({@link RuntimeException} and {@link Error}) from interceptors are allowed to propagate.
387    *       This ensures programming errors and serious issues are visible during development and testing.
388    *    <li>Checked exceptions (including {@link RestCallException}) are caught and logged but not thrown.
389    *       This follows AutoCloseable best practices and prevents close exceptions from interfering with
390    *       try-with-resources cleanup or masking the original exception.
391    * </ul>
392    */
393   @Override /* Overridden from AutoCloseable */
394   @SuppressWarnings({
395      "java:S3776" // Cognitive complexity acceptable for resource cleanup
396   })
397   public void close() {
398      if (isClosed)
399         return;
400      isClosed = true;
401
402      try {
403         // Render FULL/SIMPLE request logging BEFORE consuming the underlying entity.
404         //
405         // FULL logging reads the response body via getContent().asString(), which reads
406         // from the same HttpEntity returned by response.getEntity().  If consumeQuietly()
407         // runs first it closes the underlying network input stream, leaving the entity in
408         // a state where any subsequent read throws "Stream closed".  Running logging first
409         // lets it cache the body bytes into ResponseContent.body so both this log render
410         // and any later caller (e.g. user-level getContent().asString()) succeed.
411         //
412         // EntityUtils.consumeQuietly() is invoked in the finally block so the entity is
413         // still drained on the non-logged and exceptional paths.
414         try {
415            if (! request.isLoggingSuppressed() && (request.isDebug() || client.logRequestsPredicate.test(request, this))) {
416               if (client.logRequests == DetailLevel.SIMPLE) {
417                  client.log(client.logRequestsLevel, "HTTP %s %s, %s", request.getMethod(), request.getURI(), this.getStatusLine());
418               } else if (request.isDebug() || client.logRequests == DetailLevel.FULL) {
419                  var output = getContent().asString();
420                  var sb = new StringBuilder();
421                  sb.append("\n=== HTTP Call (outgoing) ======================================================");
422                  sb.append("\n=== REQUEST ===\n");
423                  sb.append(request.getMethod()).append(" ").append(request.getURI());
424                  sb.append("\n---request headers---");
425                  request.getHeaders().forEach(x -> sb.append("\n\t").append(x));
426                  if (request.hasHttpEntity()) {
427                     sb.append("\n---request entity---");
428                     var e = request.getHttpEntity();
429                     if (nn(e.getContentType()))
430                        sb.append("\n\t").append(e.getContentType());
431                     if (e.isRepeatable()) {
432                        appendRequestContent(sb, e);
433                     }
434                  }
435                  sb.append("\n=== RESPONSE ===\n").append(getStatusLine());
436                  sb.append("\n---response headers---");
437                  for (var h : getAllHeaders())
438                     sb.append("\n\t").append(h);
439                  sb.append("\n---response content---\n").append(output);
440                  sb.append("\n=== END =======================================================================");
441                  client.log(client.logRequestsLevel, sb.toString());
442               }
443            }
444         } finally {
445            EntityUtils.consumeQuietly(response.getEntity());
446         }
447
448         for (var r : request.interceptors) {
449            invokeInterceptorOnClose(r, request);
450         }
451      client.onCallClose(request, this);
452   } catch (RuntimeException e) {
453      // Let unchecked exceptions propagate for debuggability
454      throw e;
455   } catch (Exception e) {
456      // Log checked exceptions but don't throw - follows AutoCloseable best practices
457      client.log(Level.WARNING, e, "Error during RestResponse close");
458   }
459   }
460
461   /**
462    * Consumes the response body.
463    *
464    * <p>
465    * This is equivalent to closing the input stream.
466    *
467    * <p>
468    * Any exceptions thrown during close are logged but not propagated.
469    *
470    * @return This object.
471    */
472   public RestResponse consume() {
473      close();
474      return this;
475   }
476
477   private static void appendRequestContent(StringBuilder sb, HttpEntity e) {
478      try {
479         sb.append("\n---request content---\n").append(EntityUtils.toString(e));
480      } catch (Exception ex) {
481         sb.append("\n---request content exception---\n").append(ex.getMessage());
482      }
483   }
484
485   private void invokeInterceptorOnClose(RestCallInterceptor r, RestRequest request) throws RestCallException {
486      try {
487         r.onClose(request, this);
488      } catch (RuntimeException e) {
489         // Let unchecked exceptions propagate - these indicate programming errors that should be visible
490         throw e;
491      } catch (Exception e) {
492         // Wrap checked exceptions from interceptors (including RestCallException)
493         throw new RestCallException(this, e, "Interceptor throw exception on close");
494      }
495   }
496
497   /**
498    * Checks if a certain header is present in this message.
499    *
500    * <p>
501    * Header values are ignored.
502    *
503    * @param name The header name to check for.
504    * @return <jk>true</jk> if at least one header with this name is present.
505    */
506   @Override /* Overridden from HttpMessage */
507   public boolean containsHeader(String name) {
508      return response.containsHeader(name);
509   }
510
511   /**
512    * Returns all the headers of this message.
513    *
514    * Headers are ordered in the sequence they were sent over a connection.
515    *
516    * @return All the headers of this message.
517    */
518   @Override /* Overridden from HttpMessage */
519   public ResponseHeader[] getAllHeaders() {
520      return headers.stream().map(x -> new ResponseHeader(x.getName(), request, this, x).parser(getPartParserSession())).toArray(ResponseHeader[]::new);
521   }
522
523   /**
524    * Shortcut for retrieving the response charset from the <l>Content-Type</l> header.
525    *
526    * @return The response charset.
527    * @throws RestCallException If REST call failed.
528    */
529   public String getCharacterEncoding() throws RestCallException {
530      var ct = getContentType();
531      String s = null;
532      if (ct.isPresent())
533         s = ct.get().getParameter("charset");
534      return ie(s) ? "utf-8" : s;
535   }
536
537   /**
538    * Returns the body of the response.
539    *
540    * This method can be called multiple times returning the same response body each time.
541    *
542    * @return The body of the response.
543    */
544   public ResponseContent getContent() { return responseContent; }
545
546   /**
547    * Shortcut for retrieving the response content type from the <l>Content-Type</l> header.
548    *
549    * <p>
550    * This is equivalent to calling <c>getHeader(<js>"Content-Type"</js>).as(ContentType.<jk>class</jk>)</c>.
551    *
552    * @return The response content type, or an empty {@link Optional} if the <l>Content-Type</l> header is not present.
553    * @throws RestCallException If REST call failed.
554    */
555   public Optional<ContentType> getContentType() throws RestCallException { return getHeader("Content-Type").as(ContentType.class); }
556
557   /**
558    * Obtains the message entity of this response.
559    *
560    * <p>
561    * The entity is provided by calling setEntity.
562    *
563    * <h5 class='section'>Notes:</h5><ul>
564    *    <li class='note'>Unlike the {@link HttpResponse#getEntity()} method, this method never returns a <jk>null</jk> response.
565    *       Instead, <c>getContent().isPresent()</c> can be used to determine whether the response has a body.
566    * </ul>
567    *
568    * @return The response entity.  Never <jk>null</jk>.
569    */
570   @Override /* Overridden from HttpResponse */
571   public ResponseContent getEntity() { return responseContent; }
572
573   /**
574    * Returns the first header with a specified name of this message.
575    *
576    * <p>
577    * If there is more than one matching header in the message the first element of {@link #getHeaders(String)} is returned.
578    * <p>
579    * This method always returns a value so that you can perform assertions on the result.
580    *
581    * @param name The name of the header to return.
582    * @return The header, never <jk>null</jk>.
583    */
584   @Override /* Overridden from HttpMessage */
585   public ResponseHeader getFirstHeader(String name) {
586      return new ResponseHeader(name, request, this, headers.getFirst(name).orElse(null)).parser(getPartParserSession());
587   }
588
589   // -----------------------------------------------------------------------------------------------------------------
590   // HttpResponse pass-through methods.
591   // -----------------------------------------------------------------------------------------------------------------
592
593   /**
594    * Returns the response header with the specified name.
595    *
596    * <p>
597    * If more that one header with the given name exists the values will be combined with <js>", "</js> as per <a href='https://tools.ietf.org/html/rfc2616#section-4.2'>RFC 2616 Section 4.2</a>.
598    *
599    * @param name The name of the header to return.
600    * @return The header, never <jk>null</jk>.
601    */
602   public ResponseHeader getHeader(String name) {
603      return new ResponseHeader(name, request, this, headers.get(name).orElse(null)).parser(getPartParserSession());
604   }
605
606   /**
607    * Returns all the headers with a specified name of this message.
608    *
609    * Header values are ignored.
610    * <br>Headers are ordered in the sequence they were sent over a connection.
611    *
612    * @param name The name of the headers to return.
613    * @return All the headers with a specified name of this message.
614    */
615   @Override /* Overridden from HttpMessage */
616   public ResponseHeader[] getHeaders(String name) {
617      return headers.stream(name).map(x -> new ResponseHeader(name, request, this, x).parser(getPartParserSession())).toArray(ResponseHeader[]::new);
618   }
619
620   /**
621    * Returns the last header with a specified name of this message.
622    *
623    * <p>
624    * If there is more than one matching header in the message the last element of {@link #getHeaders(String)} is returned.
625    * <p>
626    * This method always returns a value so that you can perform assertions on the result.
627    *
628    * @param name The name of the header to return.
629    * @return The header, never <jk>null</jk>.
630    */
631   @Override /* Overridden from HttpMessage */
632   public ResponseHeader getLastHeader(String name) {
633      return new ResponseHeader(name, request, this, headers.getLast(name).orElse(null)).parser(getPartParserSession());
634   }
635
636   /**
637    * Obtains the locale of this response.
638    *
639    * The locale is used to determine the reason phrase for the status code.
640    * It can be changed using {@link #setLocale(Locale)}.
641    *
642    * @return The locale of this response, never <jk>null</jk>.
643    */
644   @Override /* Overridden from HttpResponse */
645   public Locale getLocale() { return response.getLocale(); }
646
647   /**
648    * Returns the parameters effective for this message as set by {@link #setParams(HttpParams)}.
649    *
650    * @return The parameters effective for this message as set by {@link #setParams(HttpParams)}.
651    * @deprecated Use configuration classes provided <jk>org.apache.http.config</jk> and <jk>org.apache.http.client.config</jk>.
652    */
653   @Override /* Overridden from HttpMessage */
654   @Deprecated(since = "10.0", forRemoval = true)
655   public HttpParams getParams() { return response.getParams(); }
656
657   /**
658    * Returns the protocol version this message is compatible with.
659    *
660    * @return The protocol version this message is compatible with.
661    */
662   @Override /* Overridden from HttpMessage */
663   public ProtocolVersion getProtocolVersion() { return response.getProtocolVersion(); }
664
665   /**
666    * Returns the status line reason phrase of the response.
667    *
668    * Shortcut for calling <code>getStatusLine().getReasonPhrase()</code>.
669    *
670    * @return The status line reason phrase of the response.
671    */
672   public String getReasonPhrase() { return getStatusLine().getReasonPhrase(); }
673
674   /**
675    * Returns the request object that created this response object.
676    *
677    * @return The request object that created this response object.
678    */
679   public RestRequest getRequest() { return request; }
680
681   /**
682    * Returns the status code of the response.
683    *
684    * Shortcut for calling <code>getStatusLine().getStatusCode()</code>.
685    *
686    * @return The status code of the response.
687    */
688   public int getStatusCode() { return getStatusLine().getStatusCode(); }
689
690   /**
691    * Obtains the status line of this response.
692    *
693    * The status line can be set using one of the setStatusLine methods, or it can be initialized in a constructor.
694    *
695    * @return The status line.  Never <jk>null</jk>.
696    */
697   @Override /* Overridden from HttpResponse */
698   public ResponseStatusLine getStatusLine() { return new ResponseStatusLine(this, response.getStatusLine()); }
699
700   /**
701    * Shortcut for calling <code>getHeader(name).asString()</code>.
702    *
703    * @param name The header name.
704    * @return The header value, never <jk>null</jk>
705    */
706   public Optional<String> getStringHeader(String name) {
707      return getHeader(name).asString();
708   }
709
710   /**
711    * Returns an iterator of all the headers.
712    *
713    * @return {@link Iterator} that returns {@link Header} objects in the sequence they are sent over a connection.
714    */
715   @Override /* Overridden from HttpMessage */
716   public HeaderIterator headerIterator() {
717      return headers.headerIterator();
718   }
719
720   /**
721    * Returns an iterator of the headers with a given name.
722    *
723    * @param name The name of the headers over which to iterate, or <jk>null</jk> for all headers.
724    * @return {@link Iterator} that returns {@link Header} objects with the argument name in the sequence they are sent over a connection.
725    */
726   @Override /* Overridden from HttpMessage */
727   public HeaderIterator headerIterator(String name) {
728      return headers.headerIterator(name);
729   }
730
731   /**
732    * Logs a message.
733    *
734    * @param level The log level.
735    * @param msg The message with {@link String#format(String, Object...) String.format}-style (<c>%s</c>) arguments.
736    * @param args The arguments.
737    * @return This object.
738    */
739   public RestResponse log(Level level, String msg, Object...args) {
740      client.log(level, msg, args);
741      return this;
742   }
743
744   /**
745    * Logs a message.
746    *
747    * @param level The log level.
748    * @param t The throwable cause.
749    * @param msg The message with {@link String#format(String, Object...) String.format}-style (<c>%s</c>) arguments.
750    * @param args The arguments.
751    * @return This object.
752    */
753   public RestResponse log(Level level, Throwable t, String msg, Object...args) {
754      client.log(level, t, msg, args);
755      return this;
756   }
757
758   /**
759    * Removes a header from this message.
760    *
761    * @param header The header to remove.
762    */
763   @Override /* Overridden from HttpMessage */
764   public void removeHeader(Header header) {
765      headers.remove(header);
766   }
767
768   /**
769    * Removes all headers with a certain name from this message.
770    *
771    * @param name The name of the headers to remove.
772    */
773   @Override /* Overridden from HttpMessage */
774   public void removeHeaders(String name) {
775      headers.remove(name);
776   }
777
778   /**
779    * Associates a response entity with this response.
780    *
781    * <h5 class='section'>Notes:</h5><ul>
782    *    <li class='note'>If an entity has already been set for this response and it depends on an input stream
783    *       ({@link HttpEntity#isStreaming()} returns <jk>true</jk>), it must be fully consumed in order to ensure
784    *       release of resources.
785    * </ul>
786    *
787    * @param entity The entity to associate with this response, or <jk>null</jk> to unset.
788    */
789   @Override /* Overridden from HttpResponse */
790   public void setEntity(HttpEntity entity) {
791      response.setEntity(entity);
792      this.responseContent = new ResponseContent(client, request, this, parser);
793   }
794
795   /**
796    * Overwrites the first header with the same name.
797    *
798    * The new header will be appended to the end of the list, if no header with the given name can be found.
799    *
800    * @param header The header to set.
801    */
802   @Override /* Overridden from HttpMessage */
803   public void setHeader(Header header) {
804      headers.set(header);
805   }
806
807   /**
808    * Overwrites the first header with the same name.
809    *
810    * The new header will be appended to the end of the list, if no header with the given name can be found.
811    *
812    * @param name The name of the header.
813    * @param value The value of the header.
814    */
815   @Override /* Overridden from HttpMessage */
816   public void setHeader(String name, String value) {
817      headers.set(name, value);
818   }
819
820   /**
821    * Overwrites all the headers in the message.
822    *
823    * @param headers The array of headers to set.
824    */
825   @Override /* Overridden from HttpMessage */
826   public void setHeaders(Header[] headers) { this.headers = HeaderList.of(headers); }
827
828   /**
829    * Changes the locale of this response.
830    *
831    * @param loc The new locale.
832    */
833   @Override /* Overridden from HttpResponse */
834   public void setLocale(Locale loc) {
835      response.setLocale(loc);
836   }
837
838   /**
839    * Provides parameters to be used for the processing of this message.
840    *
841    * @param params The parameters.
842    * @deprecated Use configuration classes provided <jk>org.apache.http.config</jk> and <jk>org.apache.http.client.config</jk>.
843    */
844   @Override /* Overridden from HttpMessage */
845   @Deprecated(since = "10.0", forRemoval = true)
846   public void setParams(HttpParams params) {
847      response.setParams(params);
848   }
849
850   /**
851    * Updates the status line of this response with a new reason phrase.
852    *
853    * @param reason The new reason phrase as a single-line string, or <jk>null</jk> to unset the reason phrase.
854    * @throws IllegalStateException If the status line has not be set.
855    */
856   @Override /* Overridden from HttpResponse */
857   public void setReasonPhrase(String reason) {
858      response.setReasonPhrase(reason);
859   }
860
861   /**
862    * Updates the status line of this response with a new status code.
863    *
864    * @param code The HTTP status code.
865    * @throws IllegalStateException If the status line has not be set.
866    */
867   @Override /* Overridden from HttpResponse */
868   public void setStatusCode(int code) {
869      response.setStatusCode(code);
870   }
871
872   /**
873    * Sets the status line of this response.
874    *
875    * <p>
876    * The reason phrase will be determined based on the current locale.
877    *
878    * @param ver The HTTP version.
879    * @param code The status code.
880    */
881   @Override /* Overridden from HttpResponse */
882   public void setStatusLine(ProtocolVersion ver, int code) {
883      response.setStatusLine(ver, code);
884   }
885
886   /**
887    * Sets the status line of this response with a reason phrase.
888    *
889    * @param ver The HTTP version.
890    * @param code The status code.
891    * @param reason The reason phrase, or <jk>null</jk> to omit.
892    */
893   @Override /* Overridden from HttpResponse */
894   public void setStatusLine(ProtocolVersion ver, int code, String reason) {
895      response.setStatusLine(ver, code, reason);
896   }
897
898   /**
899    * Sets the status line of this response.
900    *
901    * @param statusline The status line of this response
902    */
903   @Override /* Overridden from HttpResponse */
904   public void setStatusLine(StatusLine statusline) {
905      response.setStatusLine(statusline);
906   }
907
908   /**
909    * Creates a session of the client-default parat parser.
910    *
911    * @return A session of the specified parser.
912    */
913   protected HttpPartParserSession getPartParserSession() {
914      if (partParserSession == null)
915         partParserSession = client.getPartParser().getPartSession();
916      return partParserSession;
917   }
918
919   /**
920    * Creates a session of the specified part parser.
921    *
922    * @param parser The parser to create a session for.
923    * @return A session of the specified parser.
924    */
925   protected HttpPartParserSession getPartParserSession(HttpPartParser parser) {
926      return partParserSessions.computeIfAbsent(parser, HttpPartParser::getPartSession);
927   }
928
929   @SuppressWarnings({
930      "unchecked" // Type erasure requires unchecked casts
931   })
932   <T> T as(ResponseBeanMeta rbm) {
933      var c = (Class<T>)rbm.getBeanInfo().inner();
934      final RestClient rc = this.client;
935      return (T)Proxy.newProxyInstance(c.getClassLoader(), a(c), (InvocationHandler)(proxy, method, args) -> {
936         var pm = rbm.getProperty(method.getName());
937         var pp = getPartParserSession(pm.getParser().orElse(rc.getPartParser()));
938         var schema = pm.getSchema();
939         var pt = pm.getPartType();
940         var name = pm.getPartName().orElse(null);
941         var type = rc.getMarshallingContext().getClassMeta(method.getGenericReturnType());
942         if (pt == RESPONSE_HEADER)
943            return getHeader(name).parser(pp).schema(schema).as(type).orElse(null);
944         if (pt == RESPONSE_STATUS)
945            return getStatusCode();
946         return getContent().schema(schema).as(type);
947      });
948   }
949
950   HttpResponse asHttpResponse() {
951      return response;
952   }
953}