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; 014 015import static org.apache.juneau.httppart.HttpPartType.*; 016import java.lang.reflect.*; 017import java.util.*; 018import java.util.function.*; 019import java.util.regex.*; 020 021import org.apache.http.*; 022import org.apache.juneau.*; 023import org.apache.juneau.httppart.*; 024import org.apache.juneau.internal.*; 025import org.apache.juneau.oapi.*; 026import org.apache.juneau.parser.ParseException; 027import org.apache.juneau.utils.*; 028 029/** 030 * Represents a single header on an HTTP response. 031 */ 032public class RequestHeader implements Header { 033 034 static final Header NULL_HEADER = new Header() { 035 036 @Override /* Header */ 037 public String getName() { 038 return null; 039 } 040 041 @Override /* Header */ 042 public String getValue() { 043 return null; 044 } 045 046 @Override /* Header */ 047 public HeaderElement[] getElements() throws org.apache.http.ParseException { 048 return new HeaderElement[0]; 049 } 050 }; 051 052 private final Header header; 053 private final RestRequest request; 054 private final RestResponse response; 055 private HttpPartParserSession parser; 056 private HttpPartSchema schema; 057 058 /** 059 * Constructor. 060 * 061 * @param request The request object. 062 * @param response The response object. 063 * @param header The wrapped header. Can be <jk>null</jk>. 064 */ 065 public RequestHeader(RestRequest request, RestResponse response, Header header) { 066 this.request = request; 067 this.response = response; 068 this.header = header == null ? NULL_HEADER : header; 069 parser(null); 070 } 071 072 //------------------------------------------------------------------------------------------------------------------ 073 // Setters 074 //------------------------------------------------------------------------------------------------------------------ 075 076 /** 077 * Specifies the part schema for this header. 078 * 079 * <p> 080 * Used by schema-based part parsers such as {@link OpenApiParser}. 081 * 082 * @param value 083 * The part schema. 084 * @return This object (for method chaining). 085 */ 086 public RequestHeader schema(HttpPartSchema value) { 087 this.schema = value; 088 return this; 089 } 090 091 /** 092 * Specifies the part parser to use for this header. 093 * 094 * <p> 095 * If not specified, uses the part parser defined on the client by calling {@link RestContextBuilder#partParser(Class)}. 096 * 097 * @param value 098 * The new part parser to use for this header. 099 * <br>If <jk>null</jk>, {@link SimplePartParser#DEFAULT} will be used. 100 * @return This object (for method chaining). 101 */ 102 public RequestHeader parser(HttpPartParserSession value) { 103 this.parser = value == null ? SimplePartParser.DEFAULT_SESSION : value; 104 return this; 105 } 106 107 //------------------------------------------------------------------------------------------------------------------ 108 // Retrievers 109 //------------------------------------------------------------------------------------------------------------------ 110 111 /** 112 * Returns <jk>true</jk> if this header exists on the response. 113 * 114 * @return <jk>true</jk> if this header exists on the response. 115 */ 116 public boolean exists() { 117 return header != NULL_HEADER; 118 } 119 120 /** 121 * Returns the value of this header as a string. 122 * 123 * @return The value of this header as a string, or <jk>null</jk> if header was not present. 124 */ 125 public String asString() { 126 return getValue(); 127 } 128 129 /** 130 * Same as {@link #asString()} but sets the value in a mutable for fluent calls. 131 * 132 * @param m The mutable to set the header value in. 133 * @return The response object (for method chaining). 134 */ 135 public RestResponse asString(Mutable<String> m) { 136 m.set(asString()); 137 return response; 138 } 139 140 /** 141 * Returns the value of this header as an {@link Optional}. 142 * 143 * @return The value of this header as an {@link Optional}, or an empty optional if header was not present. 144 */ 145 public Optional<String> asOptionalString() { 146 return Optional.ofNullable(getValue()); 147 } 148 149 /** 150 * Returns the value of this header as a string with a default value. 151 * 152 * @param def The default value. 153 * @return The value of this header as a string, or the default value if header was not present. 154 */ 155 public String asStringOrElse(String def) { 156 return getValue() == null ? def : getValue(); 157 } 158 159 /** 160 * Same as {@link #asStringOrElse(String)} but sets the value in a mutable for fluent calls. 161 * 162 * @param m The mutable to set the header value in. 163 * @param def The default value. 164 * @return The response object (for method chaining). 165 */ 166 public RestResponse asStringOrElse(Mutable<String> m, String def) { 167 m.set(asStringOrElse(def)); 168 return response; 169 } 170 171 /** 172 * Converts this header to the specified type. 173 * 174 * @param <T> The type to convert to. 175 * @param type The type to convert to. 176 * @param args The type parameters. 177 * @return The converted type, or <jk>null</jk> if header is not present. 178 * @throws ParseException If value could not be parsed. 179 */ 180 public <T> T as(Type type, Type...args) throws ParseException { 181 return as(request.getClassMeta(type, args)); 182 } 183 184 /** 185 * Same as {@link #as(Type,Type...)} but sets the value in a mutable for fluent calls. 186 * 187 * @param m The mutable to set the parsed header value in. 188 * @param <T> The type to convert to. 189 * @param type The type to convert to. 190 * @param args The type parameters. 191 * @return The response object (for method chaining). 192 * @throws ParseException If value could not be parsed. 193 */ 194 public <T> RestResponse as(Mutable<T> m, Type type, Type...args) throws ParseException { 195 m.set(as(type, args)); 196 return response; 197 } 198 199 /** 200 * Converts this header to the specified type. 201 * 202 * @param <T> The type to convert to. 203 * @param type The type to convert to. 204 * @return The converted type, or <jk>null</jk> if header is not present. 205 * @throws ParseException If value could not be parsed. 206 */ 207 public <T> T as(Class<T> type) throws ParseException { 208 return as(request.getClassMeta(type)); 209 } 210 211 /** 212 * Same as {@link #as(Class)} but sets the value in a mutable for fluent calls. 213 * 214 * @param m The mutable to set the parsed header value in. 215 * @param <T> The type to convert to. 216 * @param type The type to convert to. 217 * @return The response object (for method chaining). 218 * @throws ParseException If value could not be parsed. 219 */ 220 public <T> RestResponse as(Mutable<T> m, Class<T> type) throws ParseException { 221 m.set(as(type)); 222 return response; 223 } 224 225 /** 226 * Converts this header to the specified type. 227 * 228 * @param <T> The type to convert to. 229 * @param type The type to convert to. 230 * @return The converted type, or <jk>null</jk> if header is not present. 231 * @throws ParseException If value could not be parsed. 232 */ 233 public <T> T as(ClassMeta<T> type) throws ParseException { 234 return parser.parse(HEADER, schema, asString(), type); 235 } 236 237 /** 238 * Same as {@link #as(ClassMeta)} but sets the value in a mutable for fluent calls. 239 * 240 * @param m The mutable to set the parsed header value in. 241 * @param <T> The type to convert to. 242 * @param type The type to convert to. 243 * @return The response object (for method chaining). 244 * @throws ParseException If value could not be parsed. 245 */ 246 public <T> RestResponse as(Mutable<T> m, ClassMeta<T> type) throws ParseException { 247 m.set(as(type)); 248 return response; 249 } 250 251 /** 252 * Same as {@link #as(Type,Type...)} but returns the value as an {@link Optional}. 253 * 254 * @param <T> The type to convert to. 255 * @param type The type to convert to. 256 * @param args The type parameters. 257 * @return The parsed value as an {@link Optional}, or an empty optional if header was not present. 258 * @throws ParseException If value could not be parsed. 259 */ 260 public <T> Optional<T> asOptional(Type type, Type...args) throws ParseException { 261 return Optional.ofNullable(as(type, args)); 262 } 263 264 /** 265 * Same as {@link #asOptional(Type,Type...)} but sets the value in a mutable for fluent calls. 266 * 267 * @param m The mutable to set the parsed header value in. 268 * @param <T> The type to convert to. 269 * @param type The type to convert to. 270 * @param args The type parameters. 271 * @return The response object (for method chaining). 272 * @throws ParseException If value could not be parsed. 273 */ 274 public <T> RestResponse asOptional(Mutable<Optional<T>> m, Type type, Type...args) throws ParseException { 275 m.set(asOptional(type, args)); 276 return response; 277 } 278 279 /** 280 * Same as {@link #as(Class)} but returns the value as an {@link Optional}. 281 * 282 * @param <T> The type to convert to. 283 * @param type The type to convert to. 284 * @return The parsed value as an {@link Optional}, or an empty optional if header was not present. 285 * @throws ParseException If value could not be parsed. 286 */ 287 public <T> Optional<T> asOptional(Class<T> type) throws ParseException { 288 return Optional.ofNullable(as(type)); 289 } 290 291 /** 292 * Same as {@link #asOptional(Class)} but sets the value in a mutable for fluent calls. 293 * 294 * @param m The mutable to set the parsed header value in. 295 * @param <T> The type to convert to. 296 * @param type The type to convert to. 297 * @return The response object (for method chaining). 298 * @throws ParseException If value could not be parsed. 299 */ 300 public <T> RestResponse asOptional(Mutable<Optional<T>> m, Class<T> type) throws ParseException { 301 m.set(asOptional(type)); 302 return response; 303 } 304 305 /** 306 * Same as {@link #as(ClassMeta)} but returns the value as an {@link Optional}. 307 * 308 * @param <T> The type to convert to. 309 * @param type The type to convert to. 310 * @return The parsed value as an {@link Optional}, or an empty optional if header was not present. 311 * @throws ParseException If value could not be parsed. 312 */ 313 public <T> Optional<T> asOptional(ClassMeta<T> type) throws ParseException { 314 return Optional.ofNullable(as(type)); 315 } 316 317 /** 318 * Same as {@link #asOptional(ClassMeta)} but sets the value in a mutable for fluent calls. 319 * 320 * @param m The mutable to set the parsed header value in. 321 * @param <T> The type to convert to. 322 * @param type The type to convert to. 323 * @return The response object (for method chaining). 324 * @throws ParseException If value could not be parsed. 325 */ 326 public <T> RestResponse asOptional(Mutable<Optional<T>> m, ClassMeta<T> type) throws ParseException { 327 m.set(asOptional(type)); 328 return response; 329 } 330 331 /** 332 * Matches the specified pattern against this header value. 333 * 334 * <h5 class='section'>Example:</h5> 335 * <p class='bcode w800'> 336 * <jc>// Parse header using a regular expression.</jc> 337 * Matcher m = client 338 * .get(<jsf>URL</jsf>) 339 * .run() 340 * .getHeader(<js>"Content-Type"</js>).asMatcher(Pattern.<jsm>compile</jsm>(<js>"application/(.*)"</js>)); 341 * 342 * <jk>if</jk> (m.matches()) 343 * String mediaType = m.group(1); 344 * </p> 345 * 346 * @param pattern The regular expression pattern to match. 347 * @return The matcher. 348 */ 349 public Matcher asMatcher(Pattern pattern) { 350 return pattern.matcher(asString()); 351 } 352 353 /** 354 * Same as {@link #asMatcher(Pattern)} but sets the value in a mutable for fluent calls. 355 * 356 * <h5 class='section'>Example:</h5> 357 * <p class='bcode w800'> 358 * <jc>// Parse header using a regular expression.</jc> 359 * Mutable<Matcher> m = Mutable.create(); 360 * Matcher m = client 361 * .get(<jsf>URL</jsf>) 362 * .run() 363 * .getHeader(<js>"Content-Type"</js>).asMatcher(m, Pattern.<jsm>compile</jsm>(<js>"application/(.*)"</js>)); 364 * 365 * <jk>if</jk> (m.get().matches()) 366 * String mediaType = m.get().group(1); 367 * </p> 368 * 369 * @param m The mutable to set the value in. 370 * @param pattern The regular expression pattern to match. 371 * @return The response object (for method chaining). 372 */ 373 public RestResponse asMatcher(Mutable<Matcher> m, Pattern pattern) { 374 m.set(pattern.matcher(asString())); 375 return response; 376 } 377 378 /** 379 * Matches the specified pattern against this header value. 380 * 381 * <h5 class='section'>Example:</h5> 382 * <p class='bcode w800'> 383 * <jc>// Parse header using a regular expression.</jc> 384 * Matcher m = client 385 * .get(<jsf>URL</jsf>) 386 * .run() 387 * .getHeader(<js>"Content-Type"</js>).asMatcher(<js>"application/(.*)"</js>); 388 * 389 * <jk>if</jk> (m.matches()) 390 * String mediaType = m.group(1); 391 * </p> 392 * 393 * @param regex The regular expression pattern to match. 394 * @return The matcher. 395 */ 396 public Matcher asMatcher(String regex) { 397 return asMatcher(regex, 0); 398 } 399 400 /** 401 * Same as {@link #asMatcher(String)} but sets the value in a mutable for fluent calls. 402 * 403 * <h5 class='section'>Example:</h5> 404 * <p class='bcode w800'> 405 * <jc>// Parse header using a regular expression.</jc> 406 * Mutable<Matcher> m = Mutable.create(); 407 * Matcher m = client 408 * .get(<jsf>URL</jsf>) 409 * .run() 410 * .getHeader(<js>"Content-Type"</js>).asMatcher(m, <js>"application/(.*)"</js>); 411 * 412 * <jk>if</jk> (m.get().matches()) 413 * String mediaType = m.get().group(1); 414 * </p> 415 * 416 * @param m The mutable to set the value in. 417 * @param regex The regular expression pattern to match. 418 * @return The response object (for method chaining). 419 */ 420 public RestResponse asMatcher(Mutable<Matcher> m, String regex) { 421 asMatcher(regex, 0); 422 return response; 423 } 424 425 /** 426 * Matches the specified pattern against this header value. 427 * 428 * <h5 class='section'>Example:</h5> 429 * <p class='bcode w800'> 430 * <jc>// Parse header using a regular expression.</jc> 431 * Matcher m = client 432 * .get(<jsf>URL</jsf>) 433 * .run() 434 * .getHeader(<js>"Content-Type"</js>).asMatcher(<js>"application/(.*)"</js>, <jsf>CASE_INSENSITIVE</jsf>); 435 * 436 * <jk>if</jk> (m.matches()) 437 * String mediaType = m.group(1); 438 * </p> 439 * 440 * @param regex The regular expression pattern to match. 441 * @param flags Pattern match flags. See {@link Pattern#compile(String, int)}. 442 * @return The matcher. 443 */ 444 public Matcher asMatcher(String regex, int flags) { 445 return asMatcher(Pattern.compile(regex, flags)); 446 } 447 448 /** 449 * Same as {@link #asMatcher(String,int)} but sets the value in a mutable for fluent calls. 450 * 451 * <h5 class='section'>Example:</h5> 452 * <p class='bcode w800'> 453 * <jc>// Parse header using a regular expression.</jc> 454 * Mutable<Matcher> m = Mutable.create(); 455 * Matcher m = client 456 * .get(<jsf>URL</jsf>) 457 * .run() 458 * .getHeader(<js>"Content-Type"</js>).asMatcher(m, <js>"application/(.*)"</js>, <jsf>CASE_INSENSITIVE</jsf>); 459 * 460 * <jk>if</jk> (m.get().matches()) 461 * String mediaType = m.get().group(1); 462 * </p> 463 * 464 * @param m The mutable to set the value in. 465 * @param regex The regular expression pattern to match. 466 * @param flags Pattern match flags. See {@link Pattern#compile(String, int)}. 467 * @return The response object (for method chaining). 468 */ 469 public RestResponse asMatcher(Mutable<Matcher> m, String regex, int flags) { 470 asMatcher(Pattern.compile(regex, flags)); 471 return response; 472 } 473 474 /** 475 * Returns the response that created this object. 476 * 477 * @return The response that created this object. 478 */ 479 public RestResponse toResponse() { 480 return response; 481 } 482 483 //------------------------------------------------------------------------------------------------------------------ 484 // Assertions. 485 //------------------------------------------------------------------------------------------------------------------ 486 487 /** 488 * Asserts that the header equals the specified value. 489 * 490 * <h5 class='section'>Example:</h5> 491 * <p class='bcode w800'> 492 * <jc>// Validates the content type header is provided.</jc> 493 * client 494 * .get(<jsf>URL</jsf>) 495 * .run() 496 * .getHeader(<js>"Content-Type"</js>).assertExists(); 497 * </p> 498 * 499 * @return The response object (for method chaining). 500 * @throws AssertionError If assertion failed. 501 */ 502 public RestResponse assertExists() throws AssertionError { 503 if (! exists()) 504 throw new BasicAssertionError("Response did not have the expected header {0}.", getName()); 505 return response; 506 } 507 508 /** 509 * Asserts that the header equals the specified value. 510 * 511 * <h5 class='section'>Example:</h5> 512 * <p class='bcode w800'> 513 * <jc>// Validates the content type is JSON.</jc> 514 * client 515 * .get(<jsf>URL</jsf>) 516 * .run() 517 * .getHeader(<js>"Content-Type"</js>).assertValue(<js>"application/json"</js>); 518 * </p> 519 * 520 * @param value The value to test for. 521 * @return The response object (for method chaining). 522 * @throws AssertionError If assertion failed. 523 */ 524 public RestResponse assertValue(String value) throws AssertionError { 525 if (! StringUtils.isEquals(value, asString())) 526 throw new BasicAssertionError("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", getName(), value, asString()); 527 return response; 528 } 529 530 /** 531 * Asserts that the header passes the specified predicate test. 532 * 533 * <h5 class='section'>Example:</h5> 534 * <p class='bcode w800'> 535 * <jc>// Validates the content type is JSON.</jc> 536 * client 537 * .get(<jsf>URL</jsf>) 538 * .run() 539 * .getHeader(<js>"Content-Type"</js>).assertValue(x -> x.equals(<js>"application/json"</js>)); 540 * </p> 541 * 542 * @param test The predicate to test for. 543 * @return The response object (for method chaining). 544 * @throws AssertionError If assertion failed. 545 */ 546 public RestResponse assertValue(Predicate<String> test) throws AssertionError { 547 String text = asString(); 548 if (! test.test(text)) 549 throw new BasicAssertionError("Response did not have the expected value for header {0}.\n\tActual=[{1}]", getName(), text); 550 return response; 551 } 552 553 /** 554 * Asserts that the header contains all the specified substrings. 555 * 556 * <h5 class='section'>Example:</h5> 557 * <p class='bcode w800'> 558 * <jc>// Validates the content type is JSON.</jc> 559 * client 560 * .get(<jsf>URL</jsf>) 561 * .run() 562 * .getHeader(<js>"Content-Type"</js>).assertValueContains(<js>"json"</js>); 563 * </p> 564 * 565 * @param values The substrings to test for. 566 * @return The response object (for method chaining). 567 * @throws AssertionError If assertion failed. 568 */ 569 public RestResponse assertContains(String...values) throws AssertionError { 570 String text = asString(); 571 for (String substring : values) 572 if (! StringUtils.contains(text, substring)) 573 throw new BasicAssertionError("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", getName(), substring, text); 574 return response; 575 } 576 577 /** 578 * Asserts that the header matches the specified regular expression. 579 * 580 * <h5 class='section'>Example:</h5> 581 * <p class='bcode w800'> 582 * <jc>// Validates the content type is JSON.</jc> 583 * client 584 * .get(<jsf>URL</jsf>) 585 * .run() 586 * .getHeader(<js>"Content-Type"</js>).assertValueMatches(<js>".*json.*"</js>); 587 * </p> 588 * 589 * @param regex The pattern to test for. 590 * @return The response object (for method chaining). 591 * @throws AssertionError If assertion failed. 592 */ 593 public RestResponse assertMatches(String regex) throws AssertionError { 594 return assertMatches(regex, 0); 595 } 596 597 /** 598 * Asserts that the header matches the specified regular expression. 599 * 600 * <h5 class='section'>Example:</h5> 601 * <p class='bcode w800'> 602 * <jc>// Validates the content type is JSON.</jc> 603 * client 604 * .get(<jsf>URL</jsf>) 605 * .run() 606 * .getHeader(<js>"Content-Type"</js>).assertValueMatches(<js>".*json.*"</js>, <jsf>CASE_INSENSITIVE</jsf>); 607 * </p> 608 * 609 * @param regex The pattern to test for. 610 * @param flags Pattern match flags. See {@link Pattern#compile(String, int)}. 611 * @return The response object (for method chaining). 612 * @throws AssertionError If assertion failed. 613 */ 614 public RestResponse assertMatches(String regex, int flags) throws AssertionError { 615 String text = asString(); 616 if (! Pattern.compile(regex, flags).matcher(text).matches()) 617 throw new BasicAssertionError("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), regex, text); 618 return response; 619 } 620 621 /** 622 * Asserts that the header matches the specified pattern. 623 * 624 * <p> 625 * The pattern can contain <js>"*"</js> to represent zero or more arbitrary characters. 626 * 627 * <h5 class='section'>Example:</h5> 628 * <p class='bcode w800'> 629 * <jc>// Validates the content type is JSON.</jc> 630 * Pattern p = Pattern.<jsm>compile</jsm>(<js>".*application\\/json.*"</js>); 631 * client 632 * .get(<jsf>URL</jsf>) 633 * .run() 634 * .getHeader(<js>"Content-Type"</js>).assertValueMatches(p); 635 * </p> 636 * 637 * @param pattern The pattern to test for. 638 * @return The response object (for method chaining). 639 * @throws AssertionError If assertion failed. 640 */ 641 public RestResponse assertMatches(Pattern pattern) throws AssertionError { 642 String text = asString(); 643 if (! pattern.matcher(text).matches()) 644 throw new BasicAssertionError("Response did not match expected pattern in header {0}.\n\tpattern=[{1}]\n\tHeader=[{2}]", getName(), pattern.pattern(), text); 645 return response; 646 } 647 648 //------------------------------------------------------------------------------------------------------------------ 649 // Header passthrough methods. 650 //------------------------------------------------------------------------------------------------------------------ 651 652 /** 653 * Gets the name of this pair. 654 * 655 * @return The name of this pair, never <jk>null</jk>. 656 */ 657 @Override /* Header */ 658 public String getName() { 659 return header.getName(); 660 } 661 662 /** 663 * Gets the value of this pair. 664 * 665 * <ul class='notes'> 666 * <li>{@link #asString()} is an equivalent method and the preferred method for fluent-style coding. 667 * </ul> 668 * 669 * @return The value of this pair, may be <jk>null</jk>. 670 */ 671 @Override /* Header */ 672 public String getValue() { 673 return header.getValue(); 674 } 675 676 /** 677 * Parses the value. 678 * 679 * @return An array of {@link HeaderElement} entries, may be empty, but is never <jk>null</jk>. 680 * @throws org.apache.http.ParseException In case of a parsing error. 681 */ 682 @Override /* Header */ 683 public HeaderElement[] getElements() throws org.apache.http.ParseException { 684 return header.getElements(); 685 } 686 687 @Override /* Object */ 688 public String toString() { 689 return getName() + ": " + getValue(); 690 } 691}