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