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&lt;Matcher&gt; 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&lt;Matcher&gt; 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&lt;Matcher&gt; 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 -&gt; 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}