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 new InvocationHandler() { 442 @Override /* InvocationHandler */ 443 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 444 ResponseBeanPropertyMeta pm = rbm.getProperty(method.getName()); 445 HttpPartParserSession pp = getPartParserSession(pm.getParser().orElse(rc.getPartParser())); 446 HttpPartSchema schema = pm.getSchema(); 447 HttpPartType pt = pm.getPartType(); 448 String name = pm.getPartName().orElse(null); 449 ClassMeta<?> type = rc.getBeanContext().getClassMeta(method.getGenericReturnType()); 450 if (pt == RESPONSE_HEADER) 451 return getHeader(name).parser(pp).schema(schema).as(type).orElse(null); 452 if (pt == RESPONSE_STATUS) 453 return getStatusCode(); 454 return getContent().schema(schema).as(type); 455 } 456 }); 457 } 458 459 /** 460 * Logs a message. 461 * 462 * @param level The log level. 463 * @param t The throwable cause. 464 * @param msg The message with {@link MessageFormat}-style arguments. 465 * @param args The arguments. 466 * @return This object. 467 */ 468 public RestResponse log(Level level, Throwable t, String msg, Object...args) { 469 client.log(level, t, msg, args); 470 return this; 471 } 472 473 /** 474 * Logs a message. 475 * 476 * @param level The log level. 477 * @param msg The message with {@link MessageFormat}-style arguments. 478 * @param args The arguments. 479 * @return This object. 480 */ 481 public RestResponse log(Level level, String msg, Object...args) { 482 client.log(level, msg, args); 483 return this; 484 } 485 486 // ----------------------------------------------------------------------------------------------------------------- 487 // HttpResponse pass-through methods. 488 // ----------------------------------------------------------------------------------------------------------------- 489 490 /** 491 * Obtains the status line of this response. 492 * 493 * The status line can be set using one of the setStatusLine methods, or it can be initialized in a constructor. 494 * 495 * @return The status line, or <jk>null</jk> if not yet set. 496 */ 497 @Override /* HttpResponse */ 498 public ResponseStatusLine getStatusLine() { 499 return new ResponseStatusLine(this, response.getStatusLine()); 500 } 501 502 /** 503 * Sets the status line of this response. 504 * 505 * @param statusline The status line of this response 506 */ 507 @Override /* HttpResponse */ 508 public void setStatusLine(StatusLine statusline) { 509 response.setStatusLine(statusline); 510 } 511 512 /** 513 * Sets the status line of this response. 514 * 515 * <p> 516 * The reason phrase will be determined based on the current locale. 517 * 518 * @param ver The HTTP version. 519 * @param code The status code. 520 */ 521 @Override /* HttpResponse */ 522 public void setStatusLine(ProtocolVersion ver, int code) { 523 response.setStatusLine(ver, code); 524 } 525 526 /** 527 * Sets the status line of this response with a reason phrase. 528 * 529 * @param ver The HTTP version. 530 * @param code The status code. 531 * @param reason The reason phrase, or <jk>null</jk> to omit. 532 */ 533 @Override /* HttpResponse */ 534 public void setStatusLine(ProtocolVersion ver, int code, String reason) { 535 response.setStatusLine(ver, code, reason); 536 } 537 538 /** 539 * Updates the status line of this response with a new status code. 540 * 541 * @param code The HTTP status code. 542 * @throws IllegalStateException If the status line has not be set. 543 */ 544 @Override /* HttpResponse */ 545 public void setStatusCode(int code) { 546 response.setStatusCode(code); 547 } 548 549 /** 550 * Updates the status line of this response with a new reason phrase. 551 * 552 * @param reason The new reason phrase as a single-line string, or <jk>null</jk> to unset the reason phrase. 553 * @throws IllegalStateException If the status line has not be set. 554 */ 555 @Override /* HttpResponse */ 556 public void setReasonPhrase(String reason) { 557 response.setReasonPhrase(reason); 558 } 559 560 /** 561 * Obtains the message entity of this response. 562 * 563 * <p> 564 * The entity is provided by calling setEntity. 565 * 566 * <h5 class='section'>Notes:</h5><ul> 567 * <li class='note'>Unlike the {@link HttpResponse#getEntity()} method, this method never returns a <jk>null</jk> response. 568 * Instead, <c>getContent().isPresent()</c> can be used to determine whether the response has a body. 569 * </ul> 570 * 571 * @return The response entity. Never <jk>null</jk>. 572 */ 573 @Override /* HttpResponse */ 574 public ResponseContent getEntity() { 575 return responseContent; 576 } 577 578 /** 579 * Associates a response entity with this response. 580 * 581 * <h5 class='section'>Notes:</h5><ul> 582 * <li class='note'>If an entity has already been set for this response and it depends on an input stream 583 * ({@link HttpEntity#isStreaming()} returns <jk>true</jk>), it must be fully consumed in order to ensure 584 * release of resources. 585 * </ul> 586 * 587 * @param entity The entity to associate with this response, or <jk>null</jk> to unset. 588 */ 589 @Override /* HttpResponse */ 590 public void setEntity(HttpEntity entity) { 591 response.setEntity(entity); 592 this.responseContent = new ResponseContent(client, request, this, parser); 593 } 594 595 /** 596 * Obtains the locale of this response. 597 * 598 * The locale is used to determine the reason phrase for the status code. 599 * It can be changed using {@link #setLocale(Locale)}. 600 * 601 * @return The locale of this response, never <jk>null</jk>. 602 */ 603 @Override /* HttpResponse */ 604 public Locale getLocale() { 605 return response.getLocale(); 606 } 607 608 /** 609 * Changes the locale of this response. 610 * 611 * @param loc The new locale. 612 */ 613 @Override /* HttpResponse */ 614 public void setLocale(Locale loc) { 615 response.setLocale(loc); 616 } 617 618 /** 619 * Returns the protocol version this message is compatible with. 620 * 621 * @return The protocol version this message is compatible with. 622 */ 623 @Override /* HttpMessage */ 624 public ProtocolVersion getProtocolVersion() { 625 return response.getProtocolVersion(); 626 } 627 628 /** 629 * Checks if a certain header is present in this message. 630 * 631 * <p> 632 * Header values are ignored. 633 * 634 * @param name The header name to check for. 635 * @return <jk>true</jk> if at least one header with this name is present. 636 */ 637 @Override /* HttpMessage */ 638 public boolean containsHeader(String name) { 639 return response.containsHeader(name); 640 } 641 642 /** 643 * Returns all the headers with a specified name of this message. 644 * 645 * Header values are ignored. 646 * <br>Headers are ordered in the sequence they were sent over a connection. 647 * 648 * @param name The name of the headers to return. 649 * @return All the headers with a specified name of this message. 650 */ 651 @Override /* HttpMessage */ 652 public ResponseHeader[] getHeaders(String name) { 653 return headers.stream(name).map(x -> new ResponseHeader(name, request, this, x).parser(getPartParserSession())).toArray(ResponseHeader[]::new); 654 } 655 656 /** 657 * Returns the first header with a specified name of this message. 658 * 659 * <p> 660 * If there is more than one matching header in the message the first element of {@link #getHeaders(String)} is returned. 661 * <p> 662 * This method always returns a value so that you can perform assertions on the result. 663 * 664 * @param name The name of the header to return. 665 * @return The header, never <jk>null</jk>. 666 */ 667 @Override /* HttpMessage */ 668 public ResponseHeader getFirstHeader(String name) { 669 return new ResponseHeader(name, request, this, headers.getFirst(name).orElse(null)).parser(getPartParserSession()); 670 } 671 672 /** 673 * Returns the last header with a specified name of this message. 674 * 675 * <p> 676 * If there is more than one matching header in the message the last element of {@link #getHeaders(String)} is returned. 677 * <p> 678 * This method always returns a value so that you can perform assertions on the result. 679 * 680 * @param name The name of the header to return. 681 * @return The header, never <jk>null</jk>. 682 */ 683 @Override /* HttpMessage */ 684 public ResponseHeader getLastHeader(String name) { 685 return new ResponseHeader(name, request, this, headers.getLast(name).orElse(null)).parser(getPartParserSession()); 686 } 687 688 /** 689 * Returns the response header with the specified name. 690 * 691 * <p> 692 * 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>. 693 * 694 * @param name The name of the header to return. 695 * @return The header, never <jk>null</jk>. 696 */ 697 public ResponseHeader getHeader(String name) { 698 return new ResponseHeader(name, request, this, headers.get(name).orElse(null)).parser(getPartParserSession()); 699 } 700 701 /** 702 * Returns all the headers of this message. 703 * 704 * Headers are ordered in the sequence they were sent over a connection. 705 * 706 * @return All the headers of this message. 707 */ 708 @Override /* HttpMessage */ 709 public ResponseHeader[] getAllHeaders() { 710 return headers.stream().map(x -> new ResponseHeader(x.getName(), request, this, x).parser(getPartParserSession())).toArray(ResponseHeader[]::new); 711 } 712 713 /** 714 * Adds a header to this message. 715 * 716 * The header will be appended to the end of the list. 717 * 718 * @param header The header to append. 719 */ 720 @Override /* HttpMessage */ 721 public void addHeader(Header header) { 722 headers.append(header); 723 } 724 725 /** 726 * Adds a header to this message. 727 * 728 * The header will be appended to the end of the list. 729 * 730 * @param name The name of the header. 731 * @param value The value of the header. 732 */ 733 @Override /* HttpMessage */ 734 public void addHeader(String name, String value) { 735 headers.append(name, value); 736 } 737 738 /** 739 * Overwrites the first header with the same name. 740 * 741 * The new header will be appended to the end of the list, if no header with the given name can be found. 742 * 743 * @param header The header to set. 744 */ 745 @Override /* HttpMessage */ 746 public void setHeader(Header header) { 747 headers.set(header); 748 } 749 750 /** 751 * Overwrites the first header with the same name. 752 * 753 * The new header will be appended to the end of the list, if no header with the given name can be found. 754 * 755 * @param name The name of the header. 756 * @param value The value of the header. 757 */ 758 @Override /* HttpMessage */ 759 public void setHeader(String name, String value) { 760 headers.set(name, value); 761 } 762 763 /** 764 * Overwrites all the headers in the message. 765 * 766 * @param headers The array of headers to set. 767 */ 768 @Override /* HttpMessage */ 769 public void setHeaders(Header[] headers) { 770 this.headers = HeaderList.of(headers); 771 } 772 773 /** 774 * Removes a header from this message. 775 * 776 * @param header The header to remove. 777 */ 778 @Override /* HttpMessage */ 779 public void removeHeader(Header header) { 780 headers.remove(header); 781 } 782 783 /** 784 * Removes all headers with a certain name from this message. 785 * 786 * @param name The name of the headers to remove. 787 */ 788 @Override /* HttpMessage */ 789 public void removeHeaders(String name) { 790 headers.remove(name); 791 } 792 793 /** 794 * Returns an iterator of all the headers. 795 * 796 * @return {@link Iterator} that returns {@link Header} objects in the sequence they are sent over a connection. 797 */ 798 @Override /* HttpMessage */ 799 public HeaderIterator headerIterator() { 800 return headers.headerIterator(); 801 } 802 803 /** 804 * Returns an iterator of the headers with a given name. 805 * 806 * @param name The name of the headers over which to iterate, or <jk>null</jk> for all headers. 807 * @return {@link Iterator} that returns {@link Header} objects with the argument name in the sequence they are sent over a connection. 808 */ 809 @Override /* HttpMessage */ 810 public HeaderIterator headerIterator(String name) { 811 return headers.headerIterator(name); 812 } 813 814 /** 815 * Returns the parameters effective for this message as set by {@link #setParams(HttpParams)}. 816 * 817 * @return The parameters effective for this message as set by {@link #setParams(HttpParams)}. 818 * @deprecated Use configuration classes provided <jk>org.apache.http.config</jk> and <jk>org.apache.http.client.config</jk>. 819 */ 820 @Override /* HttpMessage */ 821 @Deprecated 822 public HttpParams getParams() { 823 return response.getParams(); 824 } 825 826 /** 827 * Provides parameters to be used for the processing of this message. 828 * 829 * @param params The parameters. 830 * @deprecated Use configuration classes provided <jk>org.apache.http.config</jk> and <jk>org.apache.http.client.config</jk>. 831 */ 832 @Override /* HttpMessage */ 833 @Deprecated 834 public void setParams(HttpParams params) { 835 response.setParams(params); 836 } 837 838 void close() throws RestCallException { 839 if (isClosed) 840 return; 841 isClosed = true; 842 EntityUtils.consumeQuietly(response.getEntity()); 843 844 if (!request.isLoggingSuppressed() && (request.isDebug() || client.logRequestsPredicate.test(request, this))) { 845 if (client.logRequests == DetailLevel.SIMPLE) { 846 client.log(client.logRequestsLevel, "HTTP {0} {1}, {2}", request.getMethod(), request.getURI(), this.getStatusLine()); 847 } else if (request.isDebug() || client.logRequests == DetailLevel.FULL) { 848 String output = getContent().asString(); 849 StringBuilder sb = new StringBuilder(); 850 sb.append("\n=== HTTP Call (outgoing) ======================================================"); 851 sb.append("\n=== REQUEST ===\n"); 852 sb.append(request.getMethod()).append(" ").append(request.getURI()); 853 sb.append("\n---request headers---"); 854 request.getHeaders().forEach(x -> sb.append("\n\t").append(x)); 855 if (request.hasHttpEntity()) { 856 sb.append("\n---request entity---"); 857 HttpEntity e = request.getHttpEntity(); 858 if (e.getContentType() != null) 859 sb.append("\n\t").append(e.getContentType()); 860 if (e.isRepeatable()) { 861 try { 862 sb.append("\n---request content---\n").append(EntityUtils.toString(e)); 863 } catch (Exception ex) { 864 sb.append("\n---request content exception---\n").append(ex.getMessage()); 865 } 866 } 867 } 868 sb.append("\n=== RESPONSE ===\n").append(getStatusLine()); 869 sb.append("\n---response headers---"); 870 for (Header h : getAllHeaders()) 871 sb.append("\n\t").append(h); 872 sb.append("\n---response content---\n").append(output); 873 sb.append("\n=== END ======================================================================="); 874 client.log(client.logRequestsLevel, sb.toString()); 875 } 876 } 877 878 for (RestCallInterceptor r : request.interceptors) { 879 try { 880 r.onClose(request, this); 881 } catch (RuntimeException | RestCallException e) { 882 throw e; 883 } catch (Exception e) { 884 throw new RestCallException(this, e, "Interceptor throw exception on close"); 885 } 886 } 887 client.onCallClose(request, this); 888 } 889 890 //------------------------------------------------------------------------------------------------------------------ 891 // Other methods 892 //------------------------------------------------------------------------------------------------------------------ 893 894 /** 895 * Creates a session of the specified part parser. 896 * 897 * @param parser The parser to create a session for. 898 * @return A session of the specified parser. 899 */ 900 protected HttpPartParserSession getPartParserSession(HttpPartParser parser) { 901 HttpPartParserSession s = partParserSessions.get(parser); 902 if (s == null) { 903 s = parser.getPartSession(); 904 partParserSessions.put(parser, s); 905 } 906 return s; 907 } 908 909 /** 910 * Creates a session of the client-default parat parser. 911 * 912 * @return A session of the specified parser. 913 */ 914 protected HttpPartParserSession getPartParserSession() { 915 if (partParserSession == null) 916 partParserSession = client.getPartParser().getPartSession(); 917 return partParserSession; 918 } 919 920 HttpResponse asHttpResponse() { 921 return response; 922 } 923 924 //----------------------------------------------------------------------------------------------------------------- 925 // Fluent setters 926 //----------------------------------------------------------------------------------------------------------------- 927 928 // <FluentSetters> 929 930 // </FluentSetters> 931}