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