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.internal.ArrayUtils.*;
016import static org.apache.juneau.internal.StringUtils.*;
017
018import java.lang.reflect.*;
019import java.util.*;
020
021import org.apache.juneau.*;
022import org.apache.juneau.http.*;
023import org.apache.juneau.http.Date;
024import org.apache.juneau.httppart.*;
025import org.apache.juneau.internal.*;
026import org.apache.juneau.json.*;
027import org.apache.juneau.oapi.*;
028import org.apache.juneau.parser.*;
029import org.apache.juneau.http.exception.*;
030
031/**
032 * Represents the headers in an HTTP request.
033 *
034 * <p>
035 * Entries are stored in a case-insensitive map.
036 *
037 * <ul class='seealso'>
038 *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestHeaders}
039 * </ul>
040 */
041public class RequestHeaders extends TreeMap<String,String[]> {
042   private static final long serialVersionUID = 1L;
043
044   private final RestRequest req;
045   private HttpPartParser parser;
046   private RequestQuery queryParams;
047   private Set<String> allowedQueryParams;
048
049   RequestHeaders(RestRequest req) {
050      super(String.CASE_INSENSITIVE_ORDER);
051      this.req = req;
052   }
053
054   RequestHeaders parser(HttpPartParser parser) {
055      this.parser = parser;
056      return this;
057   }
058
059   RequestHeaders queryParams(RequestQuery queryParams, Set<String> allowedQueryParams) {
060      this.queryParams = queryParams;
061      this.allowedQueryParams = allowedQueryParams;
062      return this;
063   }
064
065   /**
066    * Adds default entries to these headers.
067    *
068    * <p>
069    * Similar to {@link #put(String, Object)} but doesn't override existing values.
070    *
071    * @param defaultEntries
072    *    The default entries.
073    *    <br>Can be <jk>null</jk>.
074    * @return This object (for method chaining).
075    */
076   public RequestHeaders addDefault(Map<String,Object> defaultEntries) {
077      if (defaultEntries != null) {
078         for (Map.Entry<String,Object> e : defaultEntries.entrySet()) {
079            String key = e.getKey();
080            Object value = e.getValue();
081            String[] v = get(key);
082            if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
083               put(key, stringifyAll(value));
084         }
085      }
086      return this;
087   }
088
089   /**
090    * Adds a default header value on this request.
091    *
092    * <p>
093    * Similar to {@link #put(String, Object)} but doesn't override existing values.
094    *
095    * @param name
096    *    The header name.
097    * @param value
098    *    The header value.
099    *    <br>Converted to a String using <c>toString()</c>.
100    *    <br>Ignored if value is <jk>null</jk> or blank.
101    * @return This object (for method chaining).
102    */
103   public RequestHeaders addDefault(String name, Object value) {
104      return addDefault(Collections.singletonMap(name, value));
105   }
106
107   /**
108    * Adds a set of header values to this object.
109    *
110    * @param name The header name.
111    * @param values The header values.
112    * @return This object (for method chaining).
113    */
114   public RequestHeaders put(String name, Enumeration<String> values) {
115      // Optimized for enumerations of one entry, the most-common case.
116      if (values.hasMoreElements()) {
117         String v = values.nextElement();
118         String[] s = new String[]{v};
119         while (values.hasMoreElements())
120            s = append(s, values.nextElement());
121         put(name, s);
122      }
123      return this;
124   }
125
126   /**
127    * Returns the specified header value as a string.
128    *
129    * <ul class='notes'>
130    *    <li>
131    *       If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
132    * </ul>
133    *
134    * @param name The header name.
135    * @return The header value, or <jk>null</jk> if it doesn't exist.
136    */
137   public String getString(String name) {
138      String[] v = null;
139      if (queryParams != null)
140         if (allowedQueryParams.contains("*") || allowedQueryParams.contains(name))
141            v = queryParams.get(name, true);
142      if (v == null || v.length == 0)
143         v = get(name);
144      if (v == null || v.length == 0)
145         return null;
146      return v[0];
147   }
148
149   /**
150    * Returns the specified header value as a string.
151    *
152    * <ul class='notes'>
153    *    <li>
154    *       If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
155    * </ul>
156    *
157    * @param name The HTTP header name.
158    * @param def The default value to return if the header value isn't found.
159    * @return The header value, or the default value if the header isn't present.
160    */
161   public String getString(String name, String def) {
162      String s = getString(name);
163      return StringUtils.isEmpty(s) ? def : s;
164   }
165
166   /**
167    * Same as {@link #getString(String)} but converts the value to an integer.
168    *
169    * @param name The HTTP header name.
170    * @return The header value, or the default value if the header isn't present.
171    */
172   public int getInt(String name) {
173      return getInt(name, 0);
174   }
175
176   /**
177    * Same as {@link #getString(String,String)} but converts the value to an integer.
178    *
179    * @param name The HTTP header name.
180    * @param def The default value to return if the header value isn't found.
181    * @return The header value, or the default value if the header isn't present.
182    */
183   public int getInt(String name, int def) {
184      String s = getString(name);
185      return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
186   }
187
188   /**
189    * Same as {@link #getString(String)} but converts the value to a boolean.
190    *
191    * @param name The HTTP header name.
192    * @return The header value, or the default value if the header isn't present.
193    */
194   public boolean getBoolean(String name) {
195      return getBoolean(name, false);
196   }
197
198   /**
199    * Same as {@link #getString(String,String)} but converts the value to a boolean.
200    *
201    * @param name The HTTP header name.
202    * @param def The default value to return if the header value isn't found.
203    * @return The header value, or the default value if the header isn't present.
204    */
205   public boolean getBoolean(String name, boolean def) {
206      String s = getString(name);
207      return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
208   }
209
210   /**
211    * Sets a request header value.
212    *
213    * <p>
214    * This overwrites any previous value.
215    *
216    * @param name The header name.
217    * @param value The header value.
218    */
219   public void put(String name, Object value) {
220      super.put(name, stringifyAll(value));
221   }
222
223   /**
224    * Returns the specified header value converted to a POJO using the {@link HttpPartParser} registered with the resource.
225    *
226    * <h5 class='section'>Examples:</h5>
227    * <p class='bcode w800'>
228    *    <jc>// Parse into an integer.</jc>
229    *    <jk>int</jk> myheader = req.getHeader(<js>"My-Header"</js>, <jk>int</jk>.<jk>class</jk>);
230    *
231    *    <jc>// Parse a UUID.</jc>
232    *    UUID myheader = req.getHeader(<js>"My-Header"</js>, UUID.<jk>class</jk>);
233    * </p>
234    *
235    * <ul class='notes'>
236    *    <li>
237    *       If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
238    * </ul>
239    *
240    * <ul class='seealso'>
241    *    <li class='jf'>{@link RestContext#REST_partParser}
242    * </ul>
243    *
244    * @param name The HTTP header name.
245    * @param type The class type to convert the header value to.
246    * @param <T> The class type to convert the header value to.
247    * @return The parameter value converted to the specified class type.
248    * @throws BadRequest Thrown if input could not be parsed.
249    * @throws InternalServerError Thrown if any other exception occurs.
250    */
251   public <T> T get(String name, Class<T> type) throws BadRequest, InternalServerError {
252      return getInner(null, null, name, null, getClassMeta(type));
253   }
254
255   /**
256    * Same as {@link #get(String, Class)} but allows you to override the part parser used.
257    *
258    * @param parser
259    *    The parser to use for parsing the string header.
260    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
261    * @param schema
262    *    The schema object that defines the format of the input.
263    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
264    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
265    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
266    * @param name The HTTP header name.
267    * @param type The class type to convert the header value to.
268    * @param <T> The class type to convert the header value to.
269    * @return The parameter value converted to the specified class type.
270    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
271    * @throws InternalServerError Thrown if any other exception occurs.
272    */
273   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Class<T> type) throws BadRequest, InternalServerError {
274      return getInner(parser, schema, name, null, getClassMeta(type));
275   }
276
277   /**
278    * Same as {@link #get(String, Class)} but returns a default value if not found.
279    *
280    * @param name The HTTP header name.
281    * @param def The default value if the header was not specified or is <jk>null</jk>.
282    * @param type The class type to convert the header value to.
283    * @param <T> The class type to convert the header value to.
284    * @return The parameter value converted to the specified class type.
285    * @throws BadRequest Thrown if input could not be parsed.
286    * @throws InternalServerError Thrown if any other exception occurs.
287    */
288   public <T> T get(String name, T def, Class<T> type) throws BadRequest, InternalServerError {
289      return getInner(null, null, name, def, getClassMeta(type));
290   }
291
292   /**
293    * Same as {@link #get(String, Object, Class)} but allows you to override the part parser used.
294    *
295    * @param parser
296    *    The parser to use for parsing the string header.
297    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
298    * @param schema
299    *    The schema object that defines the format of the input.
300    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
301    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
302    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
303    * @param name The HTTP header name.
304    * @param def The default value if the header was not specified or is <jk>null</jk>.
305    * @param type The class type to convert the header value to.
306    * @param <T> The class type to convert the header value to.
307    * @return The parameter value converted to the specified class type.
308    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
309    * @throws InternalServerError Thrown if any other exception occurs.
310    */
311   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class<T> type) throws BadRequest, InternalServerError {
312      return getInner(parser, schema, name, def, getClassMeta(type));
313   }
314
315   /**
316    * Returns the specified header value converted to a POJO using the {@link HttpPartParser} registered with the resource.
317    *
318    * <p>
319    * Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created.
320    *
321    * <h5 class='section'>Examples:</h5>
322    * <p class='bcode w800'>
323    *    <jc>// Parse into a linked-list of strings.</jc>
324    *    List&lt;String&gt; myheader = req.getHeader(<js>"My-Header"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
325    * </p>
326    *
327    * <ul class='notes'>
328    *    <li>
329    *       <c>Collections</c> must be followed by zero or one parameter representing the value type.
330    *    <li>
331    *       <c>Maps</c> must be followed by zero or two parameters representing the key and value types.
332    *    <li>
333    *       If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
334    * </ul>
335    *
336    * <ul class='seealso'>
337    *    <li class='jf'>{@link RestContext#REST_partParser}
338    * </ul>
339    *
340    * @param name The HTTP header name.
341    * @param type
342    *    The type of object to create.
343    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
344    * @param args
345    *    The type arguments of the class if it's a collection or map.
346    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
347    *    <br>Ignored if the main type is not a map or collection.
348    * @param <T> The class type to convert the header value to.
349    * @return The parameter value converted to the specified class type.
350    * @throws BadRequest Thrown if input could not be parsed.
351    * @throws InternalServerError Thrown if any other exception occurs.
352    */
353   public <T> T get(String name, Type type, Type...args) throws BadRequest, InternalServerError {
354      return getInner(null, null, name, null, this.<T>getClassMeta(type, args));
355   }
356
357   /**
358    * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser used.
359    *
360    * @param parser
361    *    The parser to use for parsing the string header.
362    *    <br>If <jk>null</jk>, uses the part parser defined on the resource/method.
363    * @param schema
364    *    The schema object that defines the format of the input.
365    *    <br>If <jk>null</jk>, defaults to the schema defined on the parser.
366    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
367    *    <br>Only used if parser is schema-aware (e.g. {@link OpenApiParser}).
368    * @param name
369    *    The HTTP header name.
370    * @param type
371    *    The type of object to create.
372    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
373    * @param args
374    *    The type arguments of the class if it's a collection or map.
375    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
376    *    <br>Ignored if the main type is not a map or collection.
377    * @param <T> The class type to convert the header value to.
378    * @return The parameter value converted to the specified class type.
379    * @throws BadRequest Thrown if input could not be parsed or fails schema validation.
380    * @throws InternalServerError Thrown if any other exception occurs.
381    */
382   public <T> T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError {
383      return getInner(parser, schema, name, null, this.<T>getClassMeta(type, args));
384   }
385
386   /* Workhorse method */
387   private <T> T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta<T> cm) throws BadRequest, InternalServerError {
388      try {
389         if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
390            ObjectMap m = new ObjectMap();
391            for (Map.Entry<String,String[]> e : this.entrySet()) {
392               String k = e.getKey();
393               HttpPartSchema pschema = schema == null ? null : schema.getProperty(k);
394               ClassMeta<?> cm2 = cm.getValueType();
395               m.put(k, getInner(parser, pschema, k, null, cm2));
396            }
397            return req.getBeanSession().convertToType(m, cm);
398         }
399         T t = parse(parser, schema, getString(name), cm);
400         return (t == null ? def : t);
401      } catch (SchemaValidationException e) {
402         throw new BadRequest(e, "Validation failed on header ''{0}''. ", name);
403      } catch (ParseException e) {
404         throw new BadRequest(e, "Could not parse header ''{0}''.", name) ;
405      } catch (Exception e) {
406         throw new InternalServerError(e, "Could not parse header ''{0}''.", name);
407      }
408   }
409
410   /* Workhorse method */
411   private <T> T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta<T> cm) throws SchemaValidationException, ParseException {
412      if (parser == null)
413         parser = this.parser;
414      return parser.createPartSession(req.getParserSessionArgs()).parse(HttpPartType.HEADER, schema, val, cm);
415   }
416
417   /**
418    * Returns a copy of this object but only with the specified header names copied.
419    *
420    * @param headers The headers to include in the copy.
421    * @return A new headers object.
422    */
423   public RequestHeaders subset(String...headers) {
424      RequestHeaders rh2 = new RequestHeaders(req).parser(parser).queryParams(queryParams, allowedQueryParams);
425      for (String h : headers)
426         if (containsKey(h))
427            rh2.put(h, get(h));
428      return rh2;
429   }
430
431   /**
432    * Same as {@link #subset(String...)} but allows you to specify header names as a comma-delimited list.
433    *
434    * @param headers The headers to include in the copy.
435    * @return A new headers object.
436    */
437   public RequestHeaders subset(String headers) {
438      return subset(split(headers));
439   }
440
441   /**
442    * Returns the <c>Accept</c> header on the request.
443    *
444    * <p>
445    * Content-Types that are acceptable for the response.
446    *
447    * <h5 class='figure'>Example:</h5>
448    * <p class='bcode w800'>
449    *    Accept: text/plain
450    * </p>
451    *
452    * @return The parsed <c>Accept</c> header on the request, or <jk>null</jk> if not found.
453    */
454   public Accept getAccept() {
455      return Accept.forString(getString("Accept"));
456   }
457
458   /**
459    * Returns the <c>Accept-Charset</c> header on the request.
460    *
461    * <p>
462    * Character sets that are acceptable.
463    *
464    * <h5 class='figure'>Example:</h5>
465    * <p class='bcode w800'>
466    *    Accept-Charset: utf-8
467    * </p>
468    *
469    * @return The parsed <c>Accept-Charset</c> header on the request, or <jk>null</jk> if not found.
470    */
471   public AcceptCharset getAcceptCharset() {
472      return AcceptCharset.forString(getString("Accept-Charset"));
473   }
474
475   /**
476    * Returns the <c>Accept-Encoding</c> header on the request.
477    *
478    * <p>
479    * List of acceptable encodings.
480    *
481    * <h5 class='figure'>Example:</h5>
482    * <p class='bcode w800'>
483    *    Accept-Encoding: gzip, deflate
484    * </p>
485    *
486    * @return The parsed <c>Accept-Encoding</c> header on the request, or <jk>null</jk> if not found.
487    */
488   public AcceptEncoding getAcceptEncoding() {
489      return AcceptEncoding.forString(getString("Accept-Encoding"));
490   }
491
492   /**
493    * Returns the <c>Accept-Language</c> header on the request.
494    *
495    * <p>
496    * List of acceptable human languages for response.
497    *
498    * <h5 class='figure'>Example:</h5>
499    * <p class='bcode w800'>
500    *    Accept-Language: en-US
501    * </p>
502    *
503    * @return The parsed <c>Accept-Language</c> header on the request, or <jk>null</jk> if not found.
504    */
505   public AcceptLanguage getAcceptLanguage() {
506      return AcceptLanguage.forString(getString("Accept-Language"));
507   }
508
509   /**
510    * Returns the <c>Authorization</c> header on the request.
511    *
512    * <p>
513    * Authentication credentials for HTTP authentication.
514    *
515    * <h5 class='figure'>Example:</h5>
516    * <p class='bcode w800'>
517    *    Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
518    * </p>
519    *
520    * @return The parsed <c>Authorization</c> header on the request, or <jk>null</jk> if not found.
521    */
522   public Authorization getAuthorization() {
523      return Authorization.forString(getString("Authorization"));
524   }
525
526   /**
527    * Returns the <c>Cache-Control</c> header on the request.
528    *
529    * <p>
530    * Used to specify directives that must be obeyed by all caching mechanisms along the request-response chain.
531    *
532    * <h5 class='figure'>Example:</h5>
533    * <p class='bcode w800'>
534    *    Cache-Control: no-cache
535    * </p>
536    *
537    * @return The parsed <c>Cache-Control</c> header on the request, or <jk>null</jk> if not found.
538    */
539   public CacheControl getCacheControl() {
540      return CacheControl.forString(getString("Cache-Control"));
541   }
542
543   /**
544    * Returns the <c>Connection</c> header on the request.
545    *
546    * <p>
547    * Control options for the current connection and list of hop-by-hop request fields.
548    *
549    * <h5 class='figure'>Example:</h5>
550    * <p class='bcode w800'>
551    *    Connection: keep-alive
552    *    Connection: Upgrade
553    * </p>
554    *
555    * @return The parsed <code></code> header on the request, or <jk>null</jk> if not found.
556    */
557   public Connection getConnection() {
558      return Connection.forString(getString("Connection"));
559   }
560
561   /**
562    * Returns the <c>Content-Length</c> header on the request.
563    *
564    * <p>
565    * The length of the request body in octets (8-bit bytes).
566    *
567    * <h5 class='figure'>Example:</h5>
568    * <p class='bcode w800'>
569    *    Content-Length: 348
570    * </p>
571    *
572    * @return The parsed <c>Content-Length</c> header on the request, or <jk>null</jk> if not found.
573    */
574   public ContentLength getContentLength() {
575      return ContentLength.forString(getString("Content-Length"));
576   }
577
578   /**
579    * Returns the <c>Content-Type</c> header on the request.
580    *
581    * <p>
582    * The MIME type of the body of the request (used with POST and PUT requests).
583    *
584    * <h5 class='figure'>Example:</h5>
585    * <p class='bcode w800'>
586    *    Content-Type: application/x-www-form-urlencoded
587    * </p>
588    *
589    * @return The parsed <c>Content-Type</c> header on the request, or <jk>null</jk> if not found.
590    */
591   public ContentType getContentType() {
592      return ContentType.forString(getString("Content-Type"));
593   }
594
595   /**
596    * Returns the <c>Date</c> header on the request.
597    *
598    * <p>
599    * The date and time that the message was originated (in "HTTP-date" format as defined by RFC 7231 Date/Time Formats).
600    *
601    * <h5 class='figure'>Example:</h5>
602    * <p class='bcode w800'>
603    *    Date: Tue, 15 Nov 1994 08:12:31 GMT
604    * </p>
605    *
606    * @return The parsed <c>Date</c> header on the request, or <jk>null</jk> if not found.
607    */
608   public Date getDate() {
609      return Date.forString(getString("Date"));
610   }
611
612   /**
613    * Returns the <c>Expect</c> header on the request.
614    *
615    * <p>
616    * Indicates that particular server behaviors are required by the client.
617    *
618    * <h5 class='figure'>Example:</h5>
619    * <p class='bcode w800'>
620    *    Expect: 100-continue
621    * </p>
622    *
623    * @return The parsed <c>Expect</c> header on the request, or <jk>null</jk> if not found.
624    */
625   public Expect getExpect() {
626      return Expect.forString(getString("Expect"));
627   }
628
629   /**
630    * Returns the <c>From</c> header on the request.
631    *
632    * <p>
633    * The email address of the user making the request.
634    *
635    * <h5 class='figure'>Example:</h5>
636    * <p class='bcode w800'>
637    *    From: user@example.com
638    * </p>
639    *
640    * @return The parsed <c>From</c> header on the request, or <jk>null</jk> if not found.
641    */
642   public From getFrom() {
643      return From.forString(getString("From"));
644   }
645
646   /**
647    * Returns the <c>Host</c> header on the request.
648    *
649    * <p>
650    * The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening.
651    * The port number may be omitted if the port is the standard port for the service requested.
652    *
653    * <h5 class='figure'>Example:</h5>
654    * <p class='bcode w800'>
655    *    Host: en.wikipedia.org:8080
656    *    Host: en.wikipedia.org
657    * </p>
658    *
659    * @return The parsed <c>Host</c> header on the request, or <jk>null</jk> if not found.
660    */
661   public Host getHost() {
662      return Host.forString(getString("Host"));
663   }
664
665   /**
666    * Returns the <c>If-Match</c> header on the request.
667    *
668    * <p>
669    * Only perform the action if the client supplied entity matches the same entity on the server.
670    * This is mainly for methods like PUT to only update a resource if it has not been modified since the user last
671    * updated it.
672    *
673    * <h5 class='figure'>Example:</h5>
674    * <p class='bcode w800'>
675    *    If-Match: "737060cd8c284d8af7ad3082f209582d"
676    * </p>
677    *
678    * @return The parsed <c>If-Match</c> header on the request, or <jk>null</jk> if not found.
679    */
680   public IfMatch getIfMatch() {
681      return IfMatch.forString(getString("If-Match"));
682   }
683
684   /**
685    * Returns the <c>If-Modified-Since</c> header on the request.
686    *
687    * <p>
688    * Allows a 304 Not Modified to be returned if content is unchanged.
689    *
690    * <h5 class='figure'>Example:</h5>
691    * <p class='bcode w800'>
692    *    If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
693    * </p>
694    *
695    * @return The parsed <c>If-Modified-Since</c> header on the request, or <jk>null</jk> if not found.
696    */
697   public IfModifiedSince getIfModifiedSince() {
698      return IfModifiedSince.forString(getString("If-Modified-Since"));
699   }
700
701   /**
702    * Returns the <c>If-None-Match</c> header on the request.
703    *
704    * <p>
705    * Allows a 304 Not Modified to be returned if content is unchanged, see HTTP ETag.
706    *
707    * <h5 class='figure'>Example:</h5>
708    * <p class='bcode w800'>
709    *    If-None-Match: "737060cd8c284d8af7ad3082f209582d"
710    * </p>
711    *
712    * @return The parsed <c>If-None-Match</c> header on the request, or <jk>null</jk> if not found.
713    */
714   public IfNoneMatch getIfNoneMatch() {
715      return IfNoneMatch.forString(getString("If-None-Match"));
716   }
717
718   /**
719    * Returns the <c>If-Range</c> header on the request.
720    *
721    * <p>
722    * If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire new entity.
723    *
724    * <h5 class='figure'>Example:</h5>
725    * <p class='bcode w800'>
726    *    If-Range: "737060cd8c284d8af7ad3082f209582d"
727    * </p>
728    *
729    * @return The parsed <c>If-Range</c> header on the request, or <jk>null</jk> if not found.
730    */
731   public IfRange getIfRange() {
732      return IfRange.forString(getString("If-Range"));
733   }
734
735   /**
736    * Returns the <c>If-Unmodified-Since</c> header on the request.
737    *
738    * <p>
739    * Only send the response if the entity has not been modified since a specific time.
740    *
741    * <h5 class='figure'>Example:</h5>
742    * <p class='bcode w800'>
743    *    If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
744    * </p>
745    *
746    * @return The parsed <c>If-Unmodified-Since</c> header on the request, or <jk>null</jk> if not found.
747    */
748   public IfUnmodifiedSince getIfUnmodifiedSince() {
749      return IfUnmodifiedSince.forString(getString("If-Unmodified-Since"));
750   }
751
752   /**
753    * Returns the <c>Max-Forwards</c> header on the request.
754    *
755    * <p>
756    * Limit the number of times the message can be forwarded through proxies or gateways.
757    *
758    * <h5 class='figure'>Example:</h5>
759    * <p class='bcode w800'>
760    *    Max-Forwards: 10
761    * </p>
762    *
763    * @return The parsed <c>Max-Forwards</c> header on the request, or <jk>null</jk> if not found.
764    */
765   public MaxForwards getMaxForwards() {
766      return MaxForwards.forString(getString("Max-Forwards"));
767   }
768
769   /**
770    * Returns the <c>Pragma</c> header on the request.
771    *
772    * <p>
773    * Implementation-specific fields that may have various effects anywhere along the request-response chain.
774    *
775    * <h5 class='figure'>Example:</h5>
776    * <p class='bcode w800'>
777    *    Pragma: no-cache
778    * </p>
779    *
780    * @return The parsed <c>Pragma</c> header on the request, or <jk>null</jk> if not found.
781    */
782   public Pragma getPragma() {
783      return Pragma.forString(getString("Pragma"));
784   }
785
786   /**
787    * Returns the <c>Proxy-Authorization</c> header on the request.
788    *
789    * <p>
790    * Authorization credentials for connecting to a proxy.
791    *
792    * <h5 class='figure'>Example:</h5>
793    * <p class='bcode w800'>
794    *    Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
795    * </p>
796    *
797    * @return The parsed <c>Proxy-Authorization</c> header on the request, or <jk>null</jk> if not found.
798    */
799   public ProxyAuthorization getProxyAuthorization() {
800      return ProxyAuthorization.forString(getString("Proxy-Authorization"));
801   }
802
803   /**
804    * Returns the <c>Range</c> header on the request.
805    *
806    * <p>
807    * Request only part of an entity. Bytes are numbered from 0.
808    *
809    * <h5 class='figure'>Example:</h5>
810    * <p class='bcode w800'>
811    *    Range: bytes=500-999
812    * </p>
813    *
814    * @return The parsed <c>Range</c> header on the request, or <jk>null</jk> if not found.
815    */
816   public Range getRange() {
817      return Range.forString(getString("Range"));
818   }
819
820   /**
821    * Returns the <c>Referer</c> header on the request.
822    *
823    * <p>
824    * This is the address of the previous web page from which a link to the currently requested page was followed.
825    *
826    * <h5 class='figure'>Example:</h5>
827    * <p class='bcode w800'>
828    *    Referer: http://en.wikipedia.org/wiki/Main_Page
829    * </p>
830    *
831    * @return The parsed <c>Referer</c> header on the request, or <jk>null</jk> if not found.
832    */
833   public Referer getReferer() {
834      return Referer.forString(getString("Referer"));
835   }
836
837   /**
838    * Returns the <c>TE</c> header on the request.
839    *
840    * <p>
841    * The transfer encodings the user agent is willing to accept: the same values as for the response header field
842    * Transfer-Encoding can be used, plus the "trailers" value (related to the "chunked" transfer method) to notify the
843    * server it expects to receive additional fields in the trailer after the last, zero-sized, chunk.
844    *
845    * <h5 class='figure'>Example:</h5>
846    * <p class='bcode w800'>
847    *    TE: trailers, deflate
848    * </p>
849    *
850    * @return The parsed <c>TE</c> header on the request, or <jk>null</jk> if not found.
851    */
852   public TE getTE() {
853      return TE.forString(getString("TE"));
854   }
855
856   /**
857    * Returns the <c>Time-Zone</c> header value on the request if there is one.
858    *
859    * <p>
860    * Example: <js>"GMT"</js>.
861    *
862    * @return The <c>Time-Zone</c> header value on the request, or <jk>null</jk> if not present.
863    */
864   public TimeZone getTimeZone() {
865      String tz = getString("Time-Zone");
866      if (tz != null)
867         return TimeZone.getTimeZone(tz);
868      return null;
869   }
870
871   /**
872    * Returns the <c>User-Agent</c> header on the request.
873    *
874    * <p>
875    * The user agent string of the user agent.
876    *
877    * <h5 class='figure'>Example:</h5>
878    * <p class='bcode w800'>
879    *    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0
880    * </p>
881    *
882    * @return The parsed <c>User-Agent</c> header on the request, or <jk>null</jk> if not found.
883    */
884   public UserAgent getUserAgent() {
885      return UserAgent.forString(getString("User-Agent"));
886   }
887
888   /**
889    * Returns the <c>Upgrade</c> header on the request.
890    *
891    * <p>
892    * Ask the server to upgrade to another protocol.
893    *
894    * <h5 class='figure'>Example:</h5>
895    * <p class='bcode w800'>
896    *    Upgrade: HTTP/2.0, HTTPS/1.3, IRC/6.9, RTA/x11, websocket
897    * </p>
898    *
899    * @return The parsed <c>Upgrade</c> header on the request, or <jk>null</jk> if not found.
900    */
901   public Upgrade getUpgrade() {
902      return Upgrade.forString(getString("Upgrade"));
903   }
904
905   /**
906    * Returns the <c>Via</c> header on the request.
907    *
908    * <p>
909    * Informs the server of proxies through which the request was sent.
910    *
911    * <h5 class='figure'>Example:</h5>
912    * <p class='bcode w800'>
913    *    Via: 1.0 fred, 1.1 example.com (Apache/1.1)
914    * </p>
915    *
916    * @return The parsed <c>Via</c> header on the request, or <jk>null</jk> if not found.
917    */
918   public Via getVia() {
919      return Via.forString(getString("Via"));
920   }
921
922   /**
923    * Returns the <c>Warning</c> header on the request.
924    *
925    * <p>
926    * A general warning about possible problems with the entity body.
927    *
928    * <h5 class='figure'>Example:</h5>
929    * <p class='bcode w800'>
930    *    Warning: 199 Miscellaneous warning
931    * </p>
932    *
933    * @return The parsed <c>Warning</c> header on the request, or <jk>null</jk> if not found.
934    */
935   public Warning getWarning() {
936      return Warning.forString(getString("Warning"));
937   }
938
939   /**
940    * Converts the headers to a readable string.
941    *
942    * @param sorted Sort the headers by name.
943    * @return A JSON string containing the contents of the headers.
944    */
945   public String toString(boolean sorted) {
946      Map<String,Object> m = null;
947      if (sorted)
948         m = new TreeMap<>();
949      else
950         m = new LinkedHashMap<>();
951      for (Map.Entry<String,String[]> e : this.entrySet()) {
952         String[] v = e.getValue();
953         m.put(e.getKey(), v.length == 1 ? v[0] : v);
954      }
955      return SimpleJsonSerializer.DEFAULT.toString(m);
956   }
957
958   @Override /* Object */
959   public String toString() {
960      return toString(false);
961   }
962
963   //-----------------------------------------------------------------------------------------------------------------
964   // Helper methods
965   //-----------------------------------------------------------------------------------------------------------------
966
967   private <T> ClassMeta<T> getClassMeta(Type type, Type...args) {
968      return req.getBeanSession().getClassMeta(type, args);
969   }
970
971   private <T> ClassMeta<T> getClassMeta(Class<T> type) {
972      return req.getBeanSession().getClassMeta(type);
973   }
974}