001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest;
018
019import static java.util.Optional.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.IoUtils.*;
022import static org.apache.juneau.commons.utils.StringUtils.*;
023import static org.apache.juneau.commons.utils.ThrowableUtils.*;
024import static org.apache.juneau.commons.utils.Utils.*;
025import static org.apache.juneau.httppart.HttpPartType.*;
026
027import java.io.*;
028import java.lang.reflect.*;
029import java.lang.reflect.Proxy;
030import java.net.*;
031import java.nio.charset.*;
032import java.text.*;
033import java.util.*;
034
035import org.apache.http.*;
036import org.apache.http.message.*;
037import org.apache.juneau.*;
038import org.apache.juneau.assertions.*;
039import org.apache.juneau.bean.swagger.*;
040import org.apache.juneau.bean.swagger.Swagger;
041import org.apache.juneau.commons.utils.*;
042import org.apache.juneau.config.*;
043import org.apache.juneau.cp.*;
044import org.apache.juneau.http.annotation.*;
045import org.apache.juneau.http.annotation.Header;
046import org.apache.juneau.http.header.*;
047import org.apache.juneau.http.header.Date;
048import org.apache.juneau.http.response.*;
049import org.apache.juneau.httppart.*;
050import org.apache.juneau.httppart.bean.*;
051import org.apache.juneau.rest.annotation.*;
052import org.apache.juneau.rest.assertions.*;
053import org.apache.juneau.rest.guard.*;
054import org.apache.juneau.rest.httppart.*;
055import org.apache.juneau.rest.logger.*;
056import org.apache.juneau.rest.staticfile.*;
057import org.apache.juneau.rest.swagger.*;
058import org.apache.juneau.rest.util.*;
059import org.apache.juneau.svl.*;
060import org.apache.juneau.uon.*;
061
062import jakarta.servlet.*;
063import jakarta.servlet.http.*;
064
065/**
066 * Represents an HTTP request for a REST resource.
067 *
068 * <p>
069 *    The {@link RestRequest} object is an extension of the <l>HttpServletRequest</l> class
070 *    with various built-in convenience methods for use in building REST interfaces.
071 *    It can be accessed by passing it as a parameter on your REST Java method:
072 * </p>
073 *
074 * <p class='bjava'>
075 *    <ja>@RestPost</ja>(...)
076 *    <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) {...}
077 * </p>
078 *
079 * <p>
080 *    The primary methods on this class are:
081 * </p>
082 * <ul class='javatree'>
083 *    <li class='jc'>{@link RestRequest}
084 *    <ul class='spaced-list'>
085 *       <li>Methods for accessing the request content:
086 *       <ul class='javatreec'>
087 *          <li class='jm'>{@link RestRequest#getContent() getContent()}
088 *          <li class='jm'>{@link RestRequest#getInputStream() getInputStream()}
089 *          <li class='jm'>{@link RestRequest#getReader() getReader()}
090 *       </ul>
091 *       <li>Methods for accessing HTTP parts:
092 *       <ul class='javatreec'>
093 *          <li class='jm'>{@link RestRequest#containsFormParam(String) containsFormParam(String)}
094 *          <li class='jm'>{@link RestRequest#containsHeader(String) containsHeader(String)}
095 *          <li class='jm'>{@link RestRequest#containsQueryParam(String) containsQueryParam(String)}
096 *          <li class='jm'>{@link RestRequest#getHeader(Class) getHeader(Class)}
097 *          <li class='jm'>{@link RestRequest#getHeader(String) getHeader(String)}
098 *          <li class='jm'>{@link RestRequest#getHeaders() getHeaders()}
099 *          <li class='jm'>{@link RestRequest#getFormParam(Class) getFormParam(Class)}
100 *          <li class='jm'>{@link RestRequest#getFormParam(String) getFormParam(String)}
101 *          <li class='jm'>{@link RestRequest#getFormParams() getFormParams()}
102 *          <li class='jm'>{@link RestRequest#getPathParam(Class) getPathParam(Class)}
103 *          <li class='jm'>{@link RestRequest#getPathParam(String) getPathParam(String)}
104 *          <li class='jm'>{@link RestRequest#getPathParams() getPathParams()}
105 *          <li class='jm'>{@link RestRequest#getPathRemainder() getPathRemainder()}
106 *          <li class='jm'>{@link RestRequest#getQueryParam(Class) getQueryParam(Class)}
107 *          <li class='jm'>{@link RestRequest#getQueryParam(String) getQueryParam(String)}
108 *          <li class='jm'>{@link RestRequest#getQueryParams() getQueryParams()}
109 *          <li class='jm'>{@link RestRequest#getQueryString() getQueryString()}
110 *       </ul>
111 *       <li>Methods for localization:
112 *       <ul class='javatreec'>
113 *          <li class='jm'>{@link RestRequest#getLocale() getLocale()}
114 *          <li class='jm'>{@link RestRequest#getMessage(String,Object...) getMessage(String,Object...)}
115 *          <li class='jm'>{@link RestRequest#getMessages() getMessages()}
116 *          <li class='jm'>{@link RestRequest#getTimeZone() getTimeZone()}
117 *       </ul>
118 *       <li>Methods for accessing static files:
119 *       <ul class='javatreec'>
120 *          <li class='jm'>{@link RestRequest#getStaticFiles() getStaticFiles()}
121 *          <li class='jm'>{@link RestRequest#getVarResolverSession() getVarResolverSession()}
122 *       </ul>
123 *       <li>Methods for assertions:
124 *       <ul class='javatreec'>
125 *          <li class='jm'>{@link RestRequest#assertContent() assertContent()}
126 *          <li class='jm'>{@link RestRequest#assertCharset() assertCharset()}
127 *          <li class='jm'>{@link RestRequest#assertFormParam(String) assertFormParam(String)}
128 *          <li class='jm'>{@link RestRequest#assertHeader(String) assertHeader(String)}
129 *          <li class='jm'>{@link RestRequest#assertQueryParam(String) assertQueryParam(String)}
130 *          <li class='jm'>{@link RestRequest#assertRequestLine() assertRequestLine()}
131 *       </ul>
132 *       <li>Other:
133 *       <ul class='javatreec'>
134 *          <li class='jm'>{@link RestRequest#getAttribute(String) getAttribute(String)}
135 *          <li class='jm'>{@link RestRequest#getAttributes() getAttributes()}
136 *          <li class='jm'>{@link RestRequest#getAuthorityPath() getAuthorityPath()}
137 *          <li class='jm'>{@link RestRequest#getBeanSession() getBeanSession()}
138 *          <li class='jm'>{@link RestRequest#getCharset() getCharset()}
139 *          <li class='jm'>{@link RestRequest#getConfig() getConfig()}
140 *          <li class='jm'>{@link RestRequest#getContext() getContext()}
141 *          <li class='jm'>{@link RestRequest#getContextPath() getContextPath()}
142 *          <li class='jm'>{@link RestRequest#getHttpServletRequest() getHttpServletRequest()}
143 *          <li class='jm'>{@link RestRequest#getMethod() getMethod()}
144 *          <li class='jm'>{@link RestRequest#getOpContext() getOpContext()}
145 *          <li class='jm'>{@link RestRequest#getOperationSwagger() getOperationSwagger()}
146 *          <li class='jm'>{@link RestRequest#getPartParserSession() getPartParserSession()}
147 *          <li class='jm'>{@link RestRequest#getPartSerializerSession() getPartSerializerSession()}
148 *          <li class='jm'>{@link RestRequest#getPathInfo() getPathInfo()}
149 *          <li class='jm'>{@link RestRequest#getProtocolVersion() getProtocolVersion()}
150 *          <li class='jm'>{@link RestRequest#getRequest(Class) getRequest(Class)}
151 *          <li class='jm'>{@link RestRequest#getRequestLine() getRequestLine()}
152 *          <li class='jm'>{@link RestRequest#getRequestURI() getRequestURI()}
153 *          <li class='jm'>{@link RestRequest#getRequestURL() getRequestURL()}
154 *          <li class='jm'>{@link RestRequest#getServletPath() getServletPath()}
155 *          <li class='jm'>{@link RestRequest#getSession() getSession()}
156 *          <li class='jm'>{@link RestRequest#getSwagger() getSwagger()}
157 *          <li class='jm'>{@link RestRequest#getUriContext() getUriContext()}
158 *          <li class='jm'>{@link RestRequest#getUriResolver() getUriResolver()}
159 *          <li class='jm'>{@link RestRequest#isDebug() isDebug()}
160 *          <li class='jm'>{@link RestRequest#isPlainText() isPlainText()}
161 *          <li class='jm'>{@link RestRequest#isUserInRole(String) isUserInRole(String)}
162 *          <li class='jm'>{@link RestRequest#setAttribute(String,Object) setAttribute(String,Object)}
163 *          <li class='jm'>{@link RestRequest#setCharset(Charset) setCharset(Charset)}
164 *          <li class='jm'>{@link RestRequest#setDebug() setDebug()}
165 *          <li class='jm'>{@link RestRequest#setException(Throwable) setException(Throwable)}
166 *          <li class='jm'>{@link RestRequest#setNoTrace() setNoTrace()}
167 *       </ul>
168 *    </ul>
169 * </ul>
170 *
171 */
172@SuppressWarnings({ "unchecked", "unused" })
173public class RestRequest extends HttpServletRequestWrapper {
174
175   /*
176    * Converts an Accept-Language value entry to a Locale.
177    */
178   private static Locale toLocale(String lang) {
179      var country = "";
180      var i = lang.indexOf('-');
181      if (i > -1) {
182         country = lang.substring(i + 1).trim();
183         lang = lang.substring(0, i).trim();
184      }
185      return new Locale(lang, country);
186   }
187
188   // Constructor initialized.
189   private HttpServletRequest inner;
190   private final RestContext context;
191   private final RestOpContext opContext;
192   private final RequestContent content;
193   private final BeanSession beanSession;
194   private final RequestQueryParams queryParams;
195   private final RequestPathParams pathParams;
196   private final RequestHeaders headers;
197   private final RequestAttributes attrs;
198   private final HttpPartParserSession partParserSession;
199
200   private final RestSession session;
201   // Lazy initialized.
202   private VarResolverSession varSession;
203   private RequestFormParams formParams;
204   private UriContext uriContext;
205   private String authorityPath;
206   private Config config;
207   private Swagger swagger;
208
209   private Charset charset;
210
211   /**
212    * Constructor.
213    */
214   RestRequest(RestOpContext opContext, RestSession session) throws Exception {
215      super(session.getRequest());
216      this.session = session;
217      this.opContext = opContext;
218
219      inner = session.getRequest();
220      context = session.getContext();
221
222      attrs = new RequestAttributes(this);
223
224      queryParams = new RequestQueryParams(this, session.getQueryParams(), true);
225
226      headers = new RequestHeaders(this, queryParams, false);
227
228      content = new RequestContent(this);
229
230      if (context.isAllowContentParam()) {
231         var b = queryParams.get("content").asString().orElse(null);
232         if (nn(b)) {
233            headers.set("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
234            content.mediaType(MediaType.UON).parser(UonParser.DEFAULT).content(b.getBytes(UTF8));
235         }
236      }
237
238      pathParams = new RequestPathParams(session, this, true);
239
240      beanSession = opContext.getBeanContext().getSession();
241
242      partParserSession = opContext.getPartParser().getPartSession();
243
244      pathParams.parser(partParserSession);
245
246      // @formatter:off
247      queryParams
248         .addDefault(opContext.getDefaultRequestQueryData().getAll())
249         .parser(partParserSession);
250
251      headers
252         .addDefault(opContext.getDefaultRequestHeaders().getAll())
253         .addDefault(context.getDefaultRequestHeaders().getAll())
254         .parser(partParserSession);
255
256      content
257         .encoders(opContext.getEncoders())
258         .parsers(opContext.getParsers())
259         .maxInput(opContext.getMaxInput());
260
261      attrs
262         .addDefault(opContext.getDefaultRequestAttributes())
263         .addDefault(context.getDefaultRequestAttributes());
264      // @formatter:on
265
266      if (isDebug())
267         inner = CachingHttpServletRequest.wrap(inner);
268   }
269
270   /**
271    * Provides the ability to perform fluent-style assertions on the response character encoding.
272    *
273    * <h5 class='section'>Examples:</h5>
274    * <p class='bjava'>
275    *    <jc>// Validates that the response content charset is UTF-8.</jc>
276    *    <jv>request</jv>
277    *       .assertCharset().is(<js>"utf-8"</js>);
278    * </p>
279    *
280    * @return A new fluent assertion object.
281    * @throws BasicHttpException If REST call failed.
282    */
283   public FluentStringAssertion<RestRequest> assertCharset() {
284      return new FluentStringAssertion<>(getCharset().name(), this);
285   }
286
287   /**
288    * Returns a fluent assertion for the request content.
289    *
290    * <h5 class='section'>Example:</h5>
291    * <p class='bjava'>
292    *    <jc>// Validates the request content contains "foo".</jc>
293    *    <jv>request</jv>
294    *       .assertContent().asString().is(<js>"foo"</js>);
295    * </p>
296    *
297    * @return A new fluent assertion on the content, never <jk>null</jk>.
298    */
299   public FluentRequestContentAssertion<RestRequest> assertContent() {
300      return new FluentRequestContentAssertion<>(getContent(), this);
301   }
302
303   /**
304    * Returns a fluent assertion for the specified form parameter.
305    *
306    * <h5 class='section'>Example:</h5>
307    * <p class='bjava'>
308    *    <jc>// Validates the content type is JSON.</jc>
309    *    <jv>request</jv>
310    *       .assertFormParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>);
311    * </p>
312    *
313    * @param name The query parameter name.
314    * @return A new fluent assertion on the parameter, never <jk>null</jk>.
315    */
316   public FluentRequestFormParamAssertion<RestRequest> assertFormParam(String name) {
317      return new FluentRequestFormParamAssertion<>(getFormParam(name), this);
318   }
319
320   /**
321    * Returns a fluent assertion for the specified header.
322    *
323    * <h5 class='section'>Example:</h5>
324    * <p class='bjava'>
325    *    <jc>// Validates the content type is JSON.</jc>
326    *    <jv>request</jv>
327    *       .assertHeader(<js>"Content-Type"</js>).asString().is(<js>"application/json"</js>);
328    * </p>
329    *
330    * @param name The header name.
331    * @return A new fluent assertion on the parameter, never <jk>null</jk>.
332    */
333   public FluentRequestHeaderAssertion<RestRequest> assertHeader(String name) {
334      return new FluentRequestHeaderAssertion<>(getHeaderParam(name), this);
335   }
336
337   /**
338    * Returns a fluent assertion for the specified query parameter.
339    *
340    * <h5 class='section'>Example:</h5>
341    * <p class='bjava'>
342    *    <jc>// Validates the content type is JSON.</jc>
343    *    <jv>request</jv>
344    *       .assertQueryParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>);
345    * </p>
346    *
347    * @param name The query parameter name.
348    * @return A new fluent assertion on the parameter, never <jk>null</jk>.
349    */
350   public FluentRequestQueryParamAssertion<RestRequest> assertQueryParam(String name) {
351      return new FluentRequestQueryParamAssertion<>(getQueryParam(name), this);
352   }
353
354   /**
355    * Returns an assertion on the request line returned by {@link #getRequestLine()}.
356    *
357    * <h5 class='section'>Example:</h5>
358    * <p class='bjava'>
359    *    <jc>// Validates the request content contains "foo".</jc>
360    *    <jv>request</jv>
361    *       .assertRequestLine().protocol().minor().is(1);
362    * </p>
363    *
364    * @return A new assertion object.
365    */
366   public FluentRequestLineAssertion<RestRequest> assertRequestLine() {
367      return new FluentRequestLineAssertion<>(getRequestLine(), this);
368   }
369
370   /**
371    * Returns <jk>true</jk> if this request contains the specified header.
372    *
373    * @param name The header name.
374    * @return <jk>true</jk> if this request contains the specified header.
375    */
376   public boolean containsFormParam(String name) {
377      return getFormParams().contains(name);
378   }
379
380   /**
381    * Returns <jk>true</jk> if this request contains the specified header.
382    *
383    * @param name The header name.
384    * @return <jk>true</jk> if this request contains the specified header.
385    */
386   public boolean containsHeader(String name) {
387      return headers.contains(name);
388   }
389
390   /**
391    * Returns <jk>true</jk> if this request contains the specified header.
392    *
393    * @param name The header name.
394    * @return <jk>true</jk> if this request contains the specified header.
395    */
396   public boolean containsQueryParam(String name) {
397      return queryParams.contains(name);
398   }
399
400   /**
401    * Returns the request attribute with the specified name.
402    *
403    * @param name The attribute name.
404    * @return The attribute value, never <jk>null</jk>.
405    */
406   @Override
407   public RequestAttribute getAttribute(String name) {
408      return attrs.get(name);
409   }
410
411   /**
412    * Request attributes.
413    *
414    * <p>
415    * Returns a {@link RequestAttributes} object that encapsulates access to attributes on the request.
416    *
417    * <h5 class='section'>Example:</h5>
418    * <p class='bjava'>
419    *    <ja>@RestPost</ja>(...)
420    *    <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) {
421    *
422    *       <jc>// Get access to attributes.</jc>
423    *       RequestAttributes <jv>attributes</jv> = <jv>req</jv>.getAttributes();
424    *
425    *    <jc>// Get a header value as a POJO.</jc>
426    *       UUID <jv>etag</jv> = <jv>attributes</jv>.get(<js>"ETag"</js>, UUID.<jk>class</jk>);
427    *    }
428    * </p>
429    *
430    * <h5 class='section'>Notes:</h5><ul>
431    *    <li class='note'>
432    *       This object is modifiable.
433    *    <li class='note'>
434    *       Values are converted from strings using the registered part parser on the resource class.
435    *    <li class='note'>
436    *       The {@link RequestAttributes} object can also be passed as a parameter on the method.
437    *    <li class='note'>
438    *       The {@link Attr @Attr} annotation can be used to access individual attribute values.
439    * </ul>
440    *
441    * <h5 class='section'>See Also:</h5><ul>
442    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a>
443    * </ul>
444    *
445    * @return
446    *    The headers on this request.
447    *    <br>Never <jk>null</jk>.
448    */
449   public RequestAttributes getAttributes() { return attrs; }
450
451   /**
452    * Returns the URI authority portion of the request.
453    *
454    * @return The URI authority portion of the request.
455    */
456   public String getAuthorityPath() {
457      if (authorityPath == null)
458         authorityPath = context.getUriAuthority();
459      if (authorityPath == null) {
460         var scheme = inner.getScheme();
461         var port = inner.getServerPort();
462         var sb = new StringBuilder(inner.getScheme()).append("://").append(inner.getServerName());
463         if (! (port == 80 && "http".equals(scheme) || port == 443 && "https".equals(scheme)))
464            sb.append(':').append(port);
465         authorityPath = sb.toString();
466      }
467      return authorityPath;
468   }
469
470   /**
471    * Returns the {@link BeanSession} associated with this request.
472    *
473    * @return The request bean session.
474    */
475   public BeanSession getBeanSession() { return beanSession; }
476
477   /**
478    * Returns the charset specified on the <c>Content-Type</c> header, or <js>"UTF-8"</js> if not specified.
479    *
480    * @return The charset to use to decode the request content.
481    */
482   public Charset getCharset() {
483      if (charset == null) {
484         // Determine charset
485         // NOTE:  Don't use super.getCharacterEncoding() because the spec is implemented inconsistently.
486         // Jetty returns the default charset instead of null if the character is not specified on the request.
487         var h = getHeaderParam("Content-Type").orElse(null);
488         if (nn(h)) {
489            var i = h.indexOf(";charset=");
490            if (i > 0)
491               charset = Charset.forName(h.substring(i + 9).trim());
492         }
493         if (charset == null)
494            charset = opContext.getDefaultCharset();
495         if (charset == null)
496            charset = Charset.forName("UTF-8");
497      }
498      return charset;
499   }
500
501   /**
502    * Config file associated with the resource.
503    *
504    * <p>
505    * Returns a config file with session-level variable resolution.
506    *
507    * The config file is identified via one of the following:
508    * <ul class='javatree'>
509    *    <li class='ja'>{@link Rest#config()}
510    *    <li class='jm'>{@link RestContext.Builder#config(Config)}
511    * </ul>
512    *
513    * <h5 class='section'>Example:</h5>
514    * <p class='bjava'>
515    *    <ja>@RestGet</ja>(...)
516    *    <jk>public void</jk> doGet(RestRequest <jv>req</jv>) {
517    *
518    *       <jc>// Get config file.</jc>
519    *       Config <jv>config</jv> = <jv>req</jv>.getConfig();
520    *
521    *       <jc>// Get simple values from config file.</jc>
522    *       <jk>int</jk> <jv>timeout</jv> = <jv>config</jv>.get(<js>"MyResource/timeout"</js>).asInteger().orElse(=10000);
523    *
524    *       <jc>// Get complex values from config file.</jc>
525    *       MyBean <jv>bean</jv> = <jv>config</jv>.get(<js>"MyResource/myBean"</js>).as(MyBean.<jk>class</jk>).orElse(<jk>null</jk>);
526    *    }
527    * </p>
528    *
529    * <h5 class='section'>Notes:</h5><ul>
530    *    <li class='note'>
531    *       The {@link Config} object can also be passed as a parameter on the method.
532    * </ul>
533    *
534    * <h5 class='section'>See Also:</h5><ul>
535    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ConfigurationFiles">Configuration Files</a>
536    * </ul>
537    *
538    * @return
539    *    The config file associated with the resource, or <jk>null</jk> if resource does not have a config file
540    *    associated with it.
541    */
542   public Config getConfig() {
543      if (config == null)
544         config = context.getConfig().resolving(getVarResolverSession());
545      return config;
546   }
547
548   /**
549    * Request content.
550    *
551    * <p>
552    * Returns a {@link RequestContent} object that encapsulates access to the HTTP request content.
553    *
554    * <h5 class='section'>Example:</h5>
555    * <p class='bjava'>
556    *    <ja>@RestPost</ja>(...)
557    *    <jk>public void</jk> doPost(RestRequest <jv>req</jv>) {
558    *
559    *       <jc>// Convert content to a linked list of Person objects.</jc>
560    *       List&lt;Person&gt; <jv>list</jv> = <jv>req</jv>.getContent().as(LinkedList.<jk>class</jk>, Person.<jk>class</jk>);
561    *       ..
562    *    }
563    * </p>
564    *
565    * <h5 class='section'>Notes:</h5><ul>
566    *    <li class='note'>
567    *       The {@link RequestContent} object can also be passed as a parameter on the method.
568    *    <li class='note'>
569    *       The {@link Content @Content} annotation can be used to access the content as well.
570    * </ul>
571    *
572    * <h5 class='section'>See Also:</h5><ul>
573    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a>
574    * </ul>
575    *
576    * @return
577    *    The content of this HTTP request.
578    *    <br>Never <jk>null</jk>.
579    */
580   public RequestContent getContent() { return content; }
581
582   /**
583    * Returns the resource context handling the request.
584    *
585    * <p>
586    * Can be used to access servlet-init parameters or annotations during requests, such as in calls to
587    * {@link RestGuard#guard(RestRequest, RestResponse)}..
588    *
589    * @return The resource context handling the request.
590    */
591   public RestContext getContext() { return context; }
592
593   /**
594    * Returns the portion of the request URI that indicates the context of the request.
595    *
596    * <p>The context path always comes first in a request URI.
597    * The path starts with a <js>"/"</js> character but does not end with a <js>"/"</js> character.
598    * For servlets in the default (root) context, this method returns <js>""</js>.
599    * The container does not decode this string.
600    *
601    * @return The context path, never <jk>null</jk>.
602    * @see HttpServletRequest#getContextPath()
603    */
604   @Override
605   public String getContextPath() {
606      var cp = context.getUriContext();
607      return cp == null ? inner.getContextPath() : cp;
608   }
609
610   /**
611    * Returns the request form-data parameter of the specified type.
612    *
613    * <p>
614    * Type must have a name specified via the {@link org.apache.juneau.http.annotation.FormData} annotation
615    * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings.
616    *
617    * @param <T> The bean type to create.
618    * @param type The bean type to create.
619    * @return The parsed form-data parameter on the request, never <jk>null</jk>.
620    */
621   public <T> Optional<T> getFormParam(Class<T> type) {
622      return getFormParams().get(type);
623   }
624
625   /**
626    * Shortcut for calling <c>getFormData().getString(name)</c>.
627    *
628    * @param name The form data parameter name.
629    * @return The form data parameter value, or <jk>null</jk> if not found.
630    */
631   public RequestFormParam getFormParam(String name) {
632      return getFormParams().get(name);
633   }
634
635   /**
636    * Form-data.
637    *
638    * <p>
639    * Returns a {@link RequestFormParams} object that encapsulates access to form post parameters.
640    *
641    * <p>
642    * Similar to {@link HttpServletRequest#getParameterMap()}, but only looks for form data in the HTTP content.
643    *
644    * <h5 class='section'>Example:</h5>
645    * <p class='bjava'>
646    *    <ja>@RestPost</ja>(...)
647    *    <jk>public void</jk> doPost(RestRequest <jv>req</jv>) {
648    *
649    *       <jc>// Get access to parsed form data parameters.</jc>
650    *       RequestFormParams <jv>formParams</jv> = <jv>req</jv>.getFormParams();
651    *
652    *       <jc>// Get form data parameters converted to various types.</jc>
653    *       <jk>int</jk> <jv>p1</jv> = <jv>formParams</jv>.get(<js>"p1"</js>).asInteger().orElse(0);
654    *       String <jv>p2</jv> = <jv>formParams</jv>.get(<js>"p2"</js>).asString().orElse(<jk>null</jk>);
655    *       UUID <jv>p3</jv> = <jv>formParams</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>);
656    *    }
657    * </p>
658    *
659    * <h5 class='section'>Notes:</h5><ul>
660    *    <li class='note'>
661    *       This object is modifiable.
662    *    <li class='note'>
663    *       Values are converted from strings using the registered part parser on the resource class.
664    *    <li class='note'>
665    *       The {@link RequestFormParams} object can also be passed as a parameter on the method.
666    *    <li class='note'>
667    *       The {@link FormData @FormDAta} annotation can be used to access individual form data parameter values.
668    * </ul>
669    *
670    * <h5 class='section'>See Also:</h5><ul>
671    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a>
672    * </ul>
673    *
674    * @return
675    *    The URL-encoded form data from the request.
676    *    <br>Never <jk>null</jk>.
677    * @throws InternalServerError If query parameters could not be parsed.
678    * @see org.apache.juneau.http.annotation.FormData
679    */
680   public RequestFormParams getFormParams() throws InternalServerError {
681      try {
682         if (formParams == null)
683            formParams = new RequestFormParams(this, true).parser(partParserSession);
684         formParams.addDefault(opContext.getDefaultRequestFormData().getAll());
685         return formParams;
686      } catch (Exception e) {
687         throw new InternalServerError(e);
688      }
689   }
690
691   /**
692    * Returns the request header of the specified type.
693    *
694    * <p>
695    * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Header} annotation
696    * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings.
697    *
698    * <p>
699    * Typically any of the following:
700    * <ul class='javatreec'>
701    *    <li class='jc'>{@link Accept}
702    *    <li class='jc'>{@link AcceptCharset}
703    *    <li class='jc'>{@link AcceptEncoding}
704    *    <li class='jc'>{@link AcceptLanguage}
705    *    <li class='jc'>{@link AcceptRanges}
706    *    <li class='jc'>{@link Authorization}
707    *    <li class='jc'>{@link CacheControl}
708    *    <li class='jc'>{@link ClientVersion}
709    *    <li class='jc'>{@link Connection}
710    *    <li class='jc'>{@link ContentDisposition}
711    *    <li class='jc'>{@link ContentEncoding}
712    *    <li class='jc'>{@link ContentLength}
713    *    <li class='jc'>{@link ContentType}
714    *    <li class='jc'>{@link Date}
715    *    <li class='jc'>{@link Debug}
716    *    <li class='jc'>{@link Expect}
717    *    <li class='jc'>{@link Forwarded}
718    *    <li class='jc'>{@link From}
719    *    <li class='jc'>{@link Host}
720    *    <li class='jc'>{@link IfMatch}
721    *    <li class='jc'>{@link IfModifiedSince}
722    *    <li class='jc'>{@link IfNoneMatch}
723    *    <li class='jc'>{@link IfRange}
724    *    <li class='jc'>{@link IfUnmodifiedSince}
725    *    <li class='jc'>{@link MaxForwards}
726    *    <li class='jc'>{@link NoTrace}
727    *    <li class='jc'>{@link Origin}
728    *    <li class='jc'>{@link Pragma}
729    *    <li class='jc'>{@link ProxyAuthorization}
730    *    <li class='jc'>{@link Range}
731    *    <li class='jc'>{@link Referer}
732    *    <li class='jc'>{@link TE}
733    *    <li class='jc'>{@link Thrown}
734    *    <li class='jc'>{@link Upgrade}
735    *    <li class='jc'>{@link UserAgent}
736    *    <li class='jc'>{@link Warning}
737    * </ul>
738    *
739    * @param <T> The bean type to create.
740    * @param type The bean type to create.
741    * @return The parsed header on the request, never <jk>null</jk>.
742    */
743   public <T> Optional<T> getHeader(Class<T> type) {
744      return headers.get(type);
745   }
746
747   /**
748    * Returns the last header with a specified name of this message.
749    *
750    * <p>
751    * If there is more than one matching header in the message the last element of <c>getHeaders(String)</c> is returned.
752    * <br>If there is no matching header in the message, an empty request header object is returned.
753    *
754    * <h5 class='section'>Example:</h5>
755    * <p class='bjava'>
756    *    <jc>// Gets a header and throws a BadRequest if it doesn't exist.</jc>
757    *    <jv>request</jv>
758    *       .getHeader(<js>"Foo"</js>)
759    *       .assertValue().exists()
760    *       .get();
761    * </p>
762    *
763    * @param name The header name.
764    * @return The request header object, never <jk>null</jk>.
765    */
766   public RequestHeader getHeaderParam(String name) {
767      return headers.getLast(name);
768   }
769
770   /**
771    * Request headers.
772    *
773    * <p>
774    * Returns a {@link RequestHeaders} object that encapsulates access to HTTP headers on the request.
775    *
776    * <h5 class='section'>Example:</h5>
777    * <p class='bjava'>
778    *    <ja>@RestPost</ja>(...)
779    *    <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) {
780    *
781    *       <jc>// Get access to headers.</jc>
782    *       RequestHeaders <jv>headers</jv> = <jv>req</jv>.getRequestHeaders();
783    *
784    *       <jc>// Add a default value.</jc>
785    *       <jv>headers</jv>.addDefault(<js>"ETag"</js>, <jsf>DEFAULT_UUID</jsf>);
786    *
787    *    <jc>// Get a header value as a POJO.</jc>
788    *       UUID etag = <jv>headers</jv>.get(<js>"ETag"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>);
789    *
790    *       <jc>// Get a standard header.</jc>
791    *       Optional&lt;CacheControl&gt; = <jv>headers</jv>.getCacheControl();
792    *    }
793    * </p>
794    *
795    * <h5 class='section'>Notes:</h5><ul>
796    *    <li class='note'>
797    *       This object is modifiable.
798    *    <li class='note'>
799    *       Values are converted from strings using the registered part parser on the resource class.
800    *    <li class='note'>
801    *       The {@link RequestHeaders} object can also be passed as a parameter on the method.
802    *    <li class='note'>
803    *       The {@link Header @Header} annotation can be used to access individual header values.
804    * </ul>
805    *
806    * <h5 class='section'>See Also:</h5><ul>
807    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a>
808    * </ul>
809    *
810    * @return
811    *    The headers on this request.
812    *    <br>Never <jk>null</jk>.
813    */
814   public RequestHeaders getHeaders() { return headers; }
815
816   /**
817    * Returns the wrapped servlet request.
818    *
819    * @return The wrapped servlet request.
820    */
821   public HttpServletRequest getHttpServletRequest() { return inner; }
822
823   /**
824    * Returns the HTTP content content as an {@link InputStream}.
825    *
826    * <p>
827    * Automatically handles GZipped input streams.
828    *
829    * <p>
830    * This method is equivalent to calling <c>getContent().getInputStream()</c>.
831    *
832    * @return The negotiated input stream.
833    * @throws IOException If any error occurred while trying to get the input stream or wrap it in the GZIP wrapper.
834    */
835   @Override
836   public ServletInputStream getInputStream() throws IOException { return getContent().getInputStream(); }
837
838   /**
839    * Returns the preferred Locale that the client will accept content in, based on the Accept-Language header.
840    *
841    * <p>
842    * If the client request doesn't provide an <c>Accept-Language</c> header, this method returns the default locale for the server.
843    *
844    * @return The preferred Locale that the client will accept content in.  Never <jk>null</jk>.
845    */
846   @Override
847   public Locale getLocale() {
848      var best = inner.getLocale();
849      var h = headers.get("Accept-Language").asString().orElse(null);
850      if (nn(h)) {
851         var sr = StringRanges.of(h);
852         float qValue = 0;
853         for (var r : sr.toList()) {
854            if (r.getQValue() > qValue) {
855               best = toLocale(r.getName());
856               qValue = r.getQValue();
857            }
858         }
859      }
860      return best;
861   }
862
863   /**
864    * Shortcut method for calling {@link RestRequest#getMessages()} and {@link Messages#getString(String,Object...)}.
865    *
866    * @param key The message key.
867    * @param args Optional {@link MessageFormat}-style arguments.
868    * @return The localized message.
869    */
870   public String getMessage(String key, Object...args) {
871      return getMessages().getString(key, args);
872   }
873
874   /**
875    * Returns the resource bundle for the request locale.
876    *
877    * <h5 class='section'>Example:</h5>
878    * <p class='bjava'>
879    *    <ja>@RestGet</ja>
880    *    <jk>public</jk> String hello(RestRequest <jv>req</jv>, <ja>@Query</ja>(<js>"user"</js>) String <jv>user</jv>) {
881    *
882    *       <jc>// Return a localized message.</jc>
883    *       <jk>return</jk> <jv>req</jv>.getMessages().getString(<js>"hello.message"</js>, <jv>user</jv>);
884    *    }
885    * </p>
886    *
887    * <h5 class='section'>Notes:</h5><ul>
888    *    <li class='note'>
889    *       The {@link Messages} object can also be passed as a parameter on the method.
890    * </ul>
891    *
892    * <h5 class='section'>See Also:</h5><ul>
893    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)}
894    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/LocalizedMessages">Localized Messages</a>
895    * </ul>
896    *
897    * @return
898    *    The resource bundle.
899    *    <br>Never <jk>null</jk>.
900    */
901   public Messages getMessages() { return context.getMessages().forLocale(getLocale()); }
902
903   /**
904    * Returns the HTTP method of this request.
905    *
906    * <p>
907    * If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then first looks for
908    * <c>&amp;method=xxx</c> in the URL query string.
909    *
910    * @return The HTTP method of this request.
911    */
912   @Override
913   public String getMethod() { return session.getMethod(); }
914
915   /**
916    * Returns access to the inner {@link RestOpContext} of this method.
917    *
918    * @return The {@link RestOpContext} of this method.  May be <jk>null</jk> if method has not yet been found.
919    */
920   public RestOpContext getOpContext() { return opContext; }
921
922   /**
923    * Returns the swagger for the Java method invoked.
924    *
925    * @return The swagger for the Java method as an {@link Optional}.  Never <jk>null</jk>.
926    */
927   public Optional<Operation> getOperationSwagger() {
928
929      var swagger = getSwagger();
930      if (! swagger.isPresent())
931         return opte();
932
933      return opt(swagger.get().getOperation(opContext.getPathPattern(), getMethod().toLowerCase()));
934   }
935
936   /**
937    * Returns the part serializer associated with this request.
938    *
939    * @return The part serializer associated with this request.
940    */
941   public HttpPartParserSession getPartParserSession() { return partParserSession; }
942
943   /**
944    * Returns the part serializer session for this request.
945    *
946    * @return The part serializer session for this request.
947    */
948   public HttpPartSerializerSession getPartSerializerSession() { return opContext.getPartSerializer().getPartSession(); }
949
950   /**
951    * Returns the request path parameter of the specified type.
952    *
953    * <p>
954    * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Path} annotation
955    * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings.
956    *
957    * @param <T> The bean type to create.
958    * @param type The bean type to create.
959    * @return The parsed form-data parameter on the request, never <jk>null</jk>.
960    */
961   public <T> Optional<T> getPathParam(Class<T> type) {
962      return pathParams.get(type);
963   }
964
965   /**
966    * Shortcut for calling <c>getPathParams().get(<jv>name</jv>)</c>.
967    *
968    * @param name The path parameter name.
969    * @return The path parameter, never <jk>null</jk>.
970    */
971   public RequestPathParam getPathParam(String name) {
972      return pathParams.get(name);
973   }
974
975   /**
976    * Path parameters.
977    *
978    * <p>
979    * Returns a {@link RequestPathParams} object that encapsulates access to URL path parameters.
980    *
981    * <h5 class='section'>Example:</h5>
982    * <p class='bjava'>
983    *    <ja>@RestGet</ja>(<js>"/{foo}/{bar}/{baz}/*"</js>)
984    *    <jk>public void</jk> doGet(RestRequest <jv>req</jv>) {
985    *
986    *       <jc>// Get access to path data.</jc>
987    *       RequestPathParams <jv>pathParams</jv> = <jv>req</jv>.getPathParams();
988    *
989    *       <jc>// Example URL:  /123/qux/true/quux</jc>
990    *
991    *       <jk>int</jk> <jv>foo</jv> = <jv>pathParams</jv>.get(<js>"foo"</js>).asInteger().orElse(-1);  <jc>// =123</jc>
992    *       String <jv>bar</jv> = <jv>pathParams</jv>.get(<js>"bar"</js>).orElse(<jk>null</jk>);  <jc>// =qux</jc>
993    *       <jk>boolean</jk> <jv>baz</jv> = <jv>pathParams</jv>.get(<js>"baz"</js>).asBoolean().orElse(<jk>false</jk>);  <jc>// =true</jc>
994    *       String <jv>remainder</jv> = <jv>pathParams</jv>.getRemainder().orElse(<jk>null</jk>);  <jc>// =quux</jc>
995    *    }
996    * </p>
997    *
998    * <h5 class='section'>Notes:</h5><ul>
999    *    <li class='note'>
1000    *       This object is modifiable.
1001    * </ul>
1002    *
1003    * @return
1004    *    The path parameters.
1005    *    <br>Never <jk>null</jk>.
1006    */
1007   public RequestPathParams getPathParams() { return pathParams; }
1008
1009   /**
1010    * Shortcut for calling <c>getPathParams().getRemainder()</c>.
1011    *
1012    * @return The path remainder value, never <jk>null</jk>.
1013    */
1014   public RequestPathParam getPathRemainder() { return pathParams.getRemainder(); }
1015
1016   /**
1017    * Returns the protocol version from the request line of this request.
1018    *
1019    * @return The protocol version from the request line of this request.
1020    */
1021   public ProtocolVersion getProtocolVersion() { return getRequestLine().getProtocolVersion(); }
1022
1023   /**
1024    * Returns the request query parameter of the specified type.
1025    *
1026    * <p>
1027    * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Query} annotation
1028    * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings.
1029    *
1030    * @param <T> The bean type to create.
1031    * @param type The bean type to create.
1032    * @return The parsed query parameter on the request, never <jk>null</jk>.
1033    */
1034   public <T> Optional<T> getQueryParam(Class<T> type) {
1035      return queryParams.get(type);
1036   }
1037
1038   /**
1039    * Shortcut for calling <c>getRequestQuery().getLast(<jv>name</jv>)</c>.
1040    *
1041    * @param name The query parameter name.
1042    * @return The query parameter, never <jk>null</jk>.
1043    */
1044   public RequestQueryParam getQueryParam(String name) {
1045      return queryParams.get(name);
1046   }
1047
1048   /**
1049    * Query parameters.
1050    *
1051    * <p>
1052    * Returns a {@link RequestQueryParams} object that encapsulates access to URL GET parameters.
1053    *
1054    * <p>
1055    * Similar to {@link HttpServletRequest#getParameterMap()} but only looks for query parameters in the URL and not form posts.
1056    *
1057    * <h5 class='section'>Example:</h5>
1058    * <p class='bjava'>
1059    *    <ja>@RestGet</ja>(...)
1060    *    <jk>public void</jk> doGet(RestRequest <jv>req</jv>) {
1061    *
1062    *       <jc>// Get access to query parameters on the URL.</jc>
1063    *       RequestQueryParams <jv>query</jv> = <jv>req</jv>.getQuery();
1064    *
1065    *       <jc>// Get query parameters converted to various types.</jc>
1066    *       <jk>int</jk> <jv>p1/</jv> = <jv>query</jv>.getInteger(<js>"p1"</js>).orElse(<jk>null</jk>);
1067    *       String <jv>p2</jv> = <jv>query</jv>.getString(<js>"p2"</js>).orElse(<jk>null</jk>);
1068    *       UUID <jv>p3</jv> = <jv>query</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>);
1069    *    }
1070    * </p>
1071    *
1072    * <h5 class='section'>Notes:</h5><ul>
1073    *    <li class='note'>
1074    *       This object is modifiable.
1075    * </ul>
1076    *
1077    * <h5 class='section'>See Also:</h5><ul>
1078    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a>
1079    * </ul>
1080    *
1081    * @return
1082    *    The query parameters as a modifiable map.
1083    *    <br>Never <jk>null</jk>.
1084    */
1085   public RequestQueryParams getQueryParams() { return queryParams; }
1086
1087   /**
1088    * Returns the HTTP content content as a {@link Reader}.
1089    *
1090    * <p>
1091    * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &content=xxx} in the URL query
1092    * string.
1093    *
1094    * <p>
1095    * Automatically handles GZipped input streams.
1096    *
1097    * <p>
1098    * This method is equivalent to calling <c>getContent().getReader()</c>.
1099    *
1100    * @return The HTTP content content as a {@link Reader}.
1101    * @throws IOException If content could not be read.
1102    */
1103   @Override
1104   public BufferedReader getReader() throws IOException { return getContent().getReader(); }
1105
1106   /**
1107    * Creates a proxy interface to retrieve HTTP parts of this request as a proxy bean.
1108    *
1109    * <h5 class='section'>Examples:</h5>
1110    * <p class='bjava'>
1111    *    <ja>@RestPost</ja>(<js>"/mypath/{p1}/{p2}/*"</js>)
1112    *    <jk>public void</jk> myMethod(<ja>@Request</ja> MyRequest <jv>requestBean</jv>) {...}
1113    *
1114    *    <jk>public interface</jk> MyRequest {
1115    *
1116    *       <ja>@Path</ja> <jc>// Path variable name inferred from getter.</jc>
1117    *       String getP1();
1118    *
1119    *       <ja>@Path</ja>(<js>"p2"</js>)
1120    *       String getX();
1121    *
1122    *       <ja>@Path</ja>(<js>"/*"</js>)
1123    *       String getRemainder();
1124    *
1125    *       <ja>@Query</ja>
1126    *       String getQ1();
1127    *
1128    *    <jc>// Schema-based query parameter:  Pipe-delimited lists of comma-delimited lists of integers.</jc>
1129    *       <ja>@Query</ja>(
1130    *          collectionFormat=<js>"pipes"</js>
1131    *          items=<ja>@Items</ja>(
1132    *             items=<ja>@SubItems</ja>(
1133    *                collectionFormat=<js>"csv"</js>
1134    *                type=<js>"integer"</js>
1135    *             )
1136    *          )
1137    *       )
1138    *       <jk>int</jk>[][] getQ3();
1139    *
1140    *       <ja>@Header</ja>(<js>"*"</js>)
1141    *       Map&lt;String,Object&gt; getHeaders();
1142    * </p>
1143    *
1144    * @param <T> The request bean interface to instantiate.
1145    * @param c The request bean interface to instantiate.
1146    * @return A new request bean proxy for this REST request.
1147    */
1148   public <T> T getRequest(Class<T> c) {
1149      return getRequest(RequestBeanMeta.create(c, getContext().getAnnotations()));
1150   }
1151
1152   /**
1153    * Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects.
1154    *
1155    * @param <T> The request bean interface to instantiate.
1156    * @param rbm The metadata about the request bean interface to create.
1157    * @return A new request bean proxy for this REST request.
1158    */
1159   public <T> T getRequest(RequestBeanMeta rbm) {
1160      try {
1161         var c = (Class<T>)rbm.getClassMeta().inner();
1162         final BeanSession bs = getBeanSession();
1163         final BeanMeta<T> bm = bs.getBeanMeta(c);
1164         return (T)Proxy.newProxyInstance(c.getClassLoader(), a(c), (InvocationHandler)(proxy, method, args) -> {
1165            RequestBeanPropertyMeta pm = rbm.getProperty(method.getName());
1166            if (nn(pm)) {
1167               HttpPartParserSession pp = pm.getParser(getPartParserSession());
1168               HttpPartSchema schema = pm.getSchema();
1169               String name = pm.getPartName();
1170               var type = bs.getClassMeta(method.getGenericReturnType());
1171               HttpPartType pt = pm.getPartType();
1172               if (pt == HttpPartType.BODY)
1173                  return getContent().setSchema(schema).as(type);
1174               if (pt == QUERY)
1175                  return getQueryParam(name).parser(pp).schema(schema).as(type).orElse(null);
1176               if (pt == FORMDATA)
1177                  return getFormParam(name).parser(pp).schema(schema).as(type).orElse(null);
1178               if (pt == HEADER)
1179                  return getHeaderParam(name).parser(pp).schema(schema).as(type).orElse(null);
1180               if (pt == PATH)
1181                  return getPathParam(name).parser(pp).schema(schema).as(type).orElse(null);
1182            }
1183            return null;
1184         });
1185      } catch (Exception e) {
1186         throw toRex(e);
1187      }
1188   }
1189
1190   /**
1191    * Returns the request line of this request.
1192    *
1193    * @return The request line of this request.
1194    */
1195   public RequestLine getRequestLine() {
1196      var x = inner.getProtocol();
1197      var i = x.indexOf('/');
1198      var j = x.indexOf('.', i);
1199      var pv = new ProtocolVersion(x.substring(0, i), StringUtils.parseInt(x.substring(i + 1, j)), StringUtils.parseInt(x.substring(j + 1)));
1200      return new BasicRequestLine(inner.getMethod(), inner.getRequestURI(), pv);
1201   }
1202
1203   /**
1204    * Returns the part of this request's URL that calls the servlet.
1205    *
1206    * <p>
1207    * This path starts with a <js>"/"</js> character and includes either the servlet name or a path to the servlet,
1208    * but does not include any extra path information or a query string.
1209    *
1210    * @return The servlet path, never <jk>null</jk>.
1211    * @see HttpServletRequest#getServletPath()
1212    */
1213   @Override
1214   public String getServletPath() {
1215      var cp = context.getUriContext();
1216      var sp = inner.getServletPath();
1217      return cp == null || ! sp.startsWith(cp) ? sp : sp.substring(cp.length());
1218   }
1219
1220   /**
1221    * Returns the static files registered on the REST resource context object.
1222    *
1223    * <p>
1224    * Used to retrieve localized files to be served up as static files through the REST API.
1225    *
1226    * @return This object.
1227    */
1228   public StaticFiles getStaticFiles() { return context.getStaticFiles(); }
1229
1230   /**
1231    * Returns the localized swagger associated with the resource.
1232    *
1233    * <p>
1234    * A shortcut for calling <c>getInfoProvider().getSwagger(request);</c>
1235    *
1236    * <h5 class='section'>Example:</h5>
1237    * <p class='bjava'>
1238    *    <ja>@RestGet</ja>
1239    *    <jk>public</jk> List&lt;Tag&gt; swaggerTags(RestRequest <jv>req</jv>) {
1240    *       <jk>return</jk> <jv>req</jv>.getSwagger().getTags();
1241    *    }
1242    * </p>
1243    *
1244    * <h5 class='section'>Notes:</h5><ul>
1245    *    <li class='note'>
1246    *       The {@link Swagger} object can also be passed as a parameter on the method.
1247    * </ul>
1248    *
1249    * <h5 class='section'>See Also:</h5><ul>
1250    *    <li class='jm'>{@link RestContext.Builder#swaggerProvider(Class)}
1251    *    <li class='jm'>{@link RestContext.Builder#swaggerProvider(SwaggerProvider)}
1252    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a>
1253    * </ul>
1254    *
1255    * @return
1256    *    The swagger associated with the resource.
1257    *    <br>Never <jk>null</jk>.
1258    */
1259   public Optional<Swagger> getSwagger() { return context.getSwagger(getLocale()); }
1260
1261   /**
1262    * Returns the <c>Time-Zone</c> header value on the request if there is one.
1263    *
1264    * <p>
1265    * Example: <js>"GMT"</js>.
1266    *
1267    * @return The parsed header on the request, never <jk>null</jk>.
1268    */
1269   public Optional<TimeZone> getTimeZone() {
1270      var tz = headers.get("Time-Zone").asString().orElse(null);
1271      if (nn(tz))
1272         return opt(TimeZone.getTimeZone(tz));
1273      return opte();
1274   }
1275
1276   /**
1277    * Returns the URI for this request.
1278    *
1279    * <p>
1280    * Similar to {@link #getRequestURI()} but returns the value as a {@link URI}.
1281    * It also gives you the capability to override the query parameters (e.g. add new query parameters to the existing
1282    * URI).
1283    *
1284    * @param includeQuery If <jk>true</jk> include the query parameters on the request.
1285    * @param addQueryParams Augment the request URI with the specified query parameters.
1286    * @return A new URI.
1287    */
1288   public URI getUri(boolean includeQuery, Map<String,Object> addQueryParams) {
1289      var uri = inner.getRequestURI();
1290      if (includeQuery || nn(addQueryParams)) {
1291         var sb = new StringBuilder(uri);
1292         RequestQueryParams rq = this.queryParams.copy();
1293         if (nn(addQueryParams))
1294            for (var e : addQueryParams.entrySet())
1295               rq.set(e.getKey(), e.getValue());
1296         if (! rq.isEmpty())
1297            sb.append('?').append(rq.asQueryString());
1298         uri = sb.toString();
1299      }
1300      try {
1301         return new URI(uri);
1302      } catch (URISyntaxException e) {
1303         // Shouldn't happen.
1304         throw toRex(e);
1305      }
1306   }
1307
1308   /**
1309    * Returns the URI context of the request.
1310    *
1311    * <p>
1312    * The URI context contains all the information about the URI of the request, such as the servlet URI, context
1313    * path, etc...
1314    *
1315    * @return The URI context of the request.
1316    */
1317   public UriContext getUriContext() {
1318      if (uriContext == null)
1319         uriContext = UriContext.of(getAuthorityPath(), getContextPath(), getServletPath(), urlEncodePath(inner.getPathInfo()));
1320      return uriContext;
1321   }
1322
1323   /**
1324    * Shortcut for calling {@link #getUriResolver()} using {@link UriResolution#ROOT_RELATIVE} and
1325    * {@link UriRelativity#RESOURCE}
1326    *
1327    * @return The URI resolver for this request.
1328    */
1329   public UriResolver getUriResolver() { return UriResolver.of(context.getUriResolution(), context.getUriRelativity(), getUriContext()); }
1330
1331   /**
1332    * Returns a URI resolver that can be used to convert URIs to absolute or root-relative form.
1333    *
1334    * @param resolution The URI resolution rule.
1335    * @param relativity The relative URI relativity rule.
1336    * @return The URI resolver for this request.
1337    */
1338   public UriResolver getUriResolver(UriResolution resolution, UriRelativity relativity) {
1339      return UriResolver.of(resolution, relativity, getUriContext());
1340   }
1341
1342   /**
1343    * Request-level variable resolver session.
1344    *
1345    * <p>
1346    * Used to resolve SVL variables in text.
1347    *
1348    * <h5 class='section'>Example:</h5>
1349    * <p class='bjava'>
1350    *    <ja>@RestGet</ja>
1351    *    <jk>public</jk> String hello(RestRequest <jv>req</jv>) {
1352    *
1353    *       <jc>// Get var resolver session.</jc>
1354    *       VarResolverSession <jv>session</jv> = getVarResolverSession();
1355    *
1356    *       <jc>// Use it to construct a customized message from a query parameter.</jc>
1357    *       <jk>return</jk> <jv>session</jv>.resolve(<js>"Hello $RQ{user}!"</js>);
1358    *    }
1359    * </p>
1360    *
1361    * <h5 class='section'>Notes:</h5><ul>
1362    *    <li class='note'>
1363    *       The {@link VarResolverSession} object can also be passed as a parameter on the method.
1364    * </ul>
1365    *
1366    * <h5 class='section'>See Also:</h5><ul>
1367    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
1368    *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a>
1369    * </ul>
1370    *
1371    * @return The variable resolver for this request.
1372    */
1373   public VarResolverSession getVarResolverSession() {
1374      // @formatter:off
1375      if (varSession == null)
1376         varSession = context
1377            .getVarResolver()
1378            .createSession(session.getBeanStore())
1379            .bean(RestRequest.class, this)
1380            .bean(RestSession.class, session);
1381      return varSession;
1382      // @formatter:on
1383   }
1384
1385   /**
1386    * Returns <jk>true</jk> if debug mode is enabled.
1387    *
1388    * Debug mode is enabled by simply adding <js>"?debug=true"</js> to the query string or adding a <c>Debug: true</c> header on the request.
1389    *
1390    * @return <jk>true</jk> if debug mode is enabled.
1391    */
1392   public boolean isDebug() { return getAttribute("Debug").as(Boolean.class).orElse(false); }
1393
1394   /**
1395    * Returns <jk>true</jk> if <c>&amp;plainText=true</c> was specified as a URL parameter.
1396    *
1397    * <p>
1398    * This indicates that the <c>Content-Type</c> of the output should always be set to <js>"text/plain"</js>
1399    * to make it easy to render in a browser.
1400    *
1401    * <p>
1402    * This feature is useful for debugging.
1403    *
1404    * @return <jk>true</jk> if {@code &amp;plainText=true} was specified as a URL parameter
1405    */
1406   public boolean isPlainText() { return "true".equals(queryParams.get("plainText").asString().orElse("false")); }
1407
1408   /**
1409    * Sets a request attribute.
1410    *
1411    * @param name The attribute name.
1412    * @param value The attribute value.
1413    */
1414   @Override
1415   public void setAttribute(String name, Object value) {
1416      attrs.set(name, value);
1417   }
1418
1419   /**
1420    * Sets the charset to expect on the request content.
1421    *
1422    * @param value The new value to use for the request content.
1423    */
1424   public void setCharset(Charset value) { this.charset = value; }
1425
1426   /**
1427    * Shortcut for calling <c>setDebug(<jk>true</jk>)</c>.
1428    *
1429    * @return This object.
1430    * @throws IOException If content could not be cached.
1431    */
1432   public RestRequest setDebug() throws IOException {
1433      return setDebug(true);
1434   }
1435
1436   /**
1437    * Sets the <js>"Debug"</js> attribute to the specified boolean.
1438    *
1439    * <p>
1440    * This flag is used by {@link CallLogger} to help determine how a request should be logged.
1441    *
1442    * @param b The attribute value.
1443    * @return This object.
1444    * @throws IOException If content could not be cached.
1445    */
1446   public RestRequest setDebug(Boolean b) throws IOException {
1447      setAttribute("Debug", b);
1448      if (b)
1449         inner = CachingHttpServletRequest.wrap(inner);
1450      return this;
1451   }
1452
1453   /**
1454    * Sets the <js>"Exception"</js> attribute to the specified throwable.
1455    *
1456    * <p>
1457    * This exception is used by {@link CallLogger} for logging purposes.
1458    *
1459    * @param t The attribute value.
1460    * @return This object.
1461    */
1462   public RestRequest setException(Throwable t) {
1463      setAttribute("Exception", t);
1464      return this;
1465   }
1466
1467   /**
1468    * Shortcut for calling <c>setNoTrace(<jk>true</jk>)</c>.
1469    *
1470    * @return This object.
1471    */
1472   public RestRequest setNoTrace() {
1473      return setNoTrace(true);
1474   }
1475
1476   /**
1477    * Sets the <js>"NoTrace"</js> attribute to the specified boolean.
1478    *
1479    * <p>
1480    * This flag is used by {@link CallLogger} and tells it not to log the current request.
1481    *
1482    * @param b The attribute value.
1483    * @return This object.
1484    */
1485   public RestRequest setNoTrace(Boolean b) {
1486      setAttribute("NoTrace", b);
1487      return this;
1488   }
1489
1490   @Override /* Overridden from Object */
1491   public String toString() {
1492      var sb = new StringBuilder("\n").append(getRequestLine()).append("\n");
1493      sb.append("---Headers---\n");
1494      getHeaders().forEach(x -> sb.append("\t").append(x).append("\n"));
1495      var m = getMethod();
1496      if (m.equals("PUT") || m.equals("POST")) {
1497         try {
1498            sb.append("---Content UTF-8---\n");
1499            sb.append(content.asString()).append("\n");
1500            sb.append("---Content Hex---\n");
1501            sb.append(content.asSpacedHex()).append("\n");
1502         } catch (Exception e1) {
1503            sb.append(lm(e1));
1504         }
1505      }
1506      return sb.toString();
1507   }
1508
1509   /* Called by RestSession.finish() */
1510   void close() {
1511      if (nn(config)) {
1512         config.close();
1513      }
1514   }
1515}