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