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