This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new ef82a51  REST refactoring.
ef82a51 is described below

commit ef82a51b928963745c56e374e721fe946f4ce993
Author: JamesBognar <james.bog...@salesforce.com>
AuthorDate: Sat Feb 20 10:52:32 2021 -0500

    REST refactoring.
---
 .../juneau/http/header/BasicStringHeader.java      |  13 +
 .../apache/juneau/http/pair/BasicNamedString.java  |  13 +
 .../juneau/examples/rest/RequestEchoResource.java  |   2 +-
 .../apache/juneau/rest/client/ResponseBody.java    |   4 +-
 .../apache/juneau/rest/client/ResponseHeader.java  |  17 +-
 .../org/apache/juneau/rest/RequestAttributes.java  |   6 +-
 .../java/org/apache/juneau/rest/RequestBody.java   |   8 +-
 .../java/org/apache/juneau/rest/RequestHeader.java | 105 +----
 .../org/apache/juneau/rest/RequestHeaders.java     | 255 ++++++++----
 .../org/apache/juneau/rest/RequestQueryParam.java  |  23 +-
 .../main/java/org/apache/juneau/rest/RestCall.java |   2 +-
 .../apache/juneau/rest/RestOperationContext.java   |   7 +-
 .../java/org/apache/juneau/rest/RestRequest.java   | 452 ++++++++++++++++-----
 .../java/org/apache/juneau/rest/RestResponse.java  |  14 +-
 .../assertions/FluentProtocolVersionAssertion.java |  90 ++++
 .../assertions/FluentRequestLineAssertion.java     |  91 +++++
 .../juneau/rest/logging/BasicRestLogger.java       |   2 -
 .../apache/juneau/rest/vars/RequestHeaderVar.java  |   2 +-
 .../apache/juneau/rest/vars/RequestQueryVar.java   |   2 +-
 .../apache/juneau/rest/widget/MenuItemWidget.java  |   6 +-
 .../juneau/http/SerializedHttpEntity_Test.java     |   2 +-
 .../org/apache/juneau/http/remote/Remote_Test.java |   2 +-
 .../juneau/rest/Header_AcceptCharset_Test.java     |   6 +-
 .../org/apache/juneau/rest/RestOp_Params_Test.java |   2 +-
 .../juneau/rest/annotation/RestHook_Test.java      |  14 +-
 .../rest/client/RestClient_BasicCalls_Test.java    |   2 +-
 .../client/RestClient_Config_BeanContext_Test.java |   2 +-
 .../client/RestClient_Config_OpenApi_Test.java     |   2 +-
 .../client/RestClient_Config_RestClient_Test.java  |   2 +-
 .../rest/client/RestClient_Headers_Test.java       |   2 +-
 .../client/RestClient_Response_Headers_Test.java   |   4 +-
 .../rest/client/RestClient_Response_Test.java      |   2 +-
 32 files changed, 851 insertions(+), 305 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicStringHeader.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicStringHeader.java
index bf03dd0..900b249 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicStringHeader.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicStringHeader.java
@@ -131,6 +131,19 @@ public class BasicStringHeader extends BasicHeader {
                return Optional.ofNullable(getParsedValue());
        }
 
+       /**
+        * Return the value if present, otherwise return other.
+        *
+        * <p>
+        * This is a shortened form for calling 
<c>asString().orElse(<jv>other</jv>)</c>.
+        *
+        * @param other The value to be returned if there is no value present, 
may be <jk>null</jk>.
+        * @return The value, if present, otherwise other.
+        */
+       public String orElse(String other) {
+               return asString().orElse(other);
+       }
+
        private String getParsedValue() {
                if (parsed != null)
                        return parsed;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/pair/BasicNamedString.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/pair/BasicNamedString.java
index e9c0b48..bb4aacc 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/pair/BasicNamedString.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/pair/BasicNamedString.java
@@ -111,6 +111,19 @@ public class BasicNamedString extends BasicNameValuePair {
                return Optional.ofNullable(getParsedValue());
        }
 
+       /**
+        * Return the value if present, otherwise return other.
+        *
+        * <p>
+        * This is a shortened form for calling 
<c>asString().orElse(<jv>other</jv>)</c>.
+        *
+        * @param other The value to be returned if there is no value present, 
may be <jk>null</jk>.
+        * @return The value, if present, otherwise other.
+        */
+       public String orElse(String other) {
+               return asString().orElse(other);
+       }
+
        private String getParsedValue() {
                if (parsed != null)
                        return parsed;
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RequestEchoResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RequestEchoResource.java
index ca36454..2f8bbed 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RequestEchoResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/RequestEchoResource.java
@@ -88,6 +88,6 @@ public class RequestEchoResource extends BasicRestObject {
        @RestOp(method="*", path="/*", 
converters={Traversable.class,Queryable.class}, summary="Serializes the 
incoming HttpServletRequest object.")
        public HttpServletRequest doGet(RestRequest req) {
                // Just echo the request back as the response.
-               return req;
+               return req.getHttpServletRequest();
        }
 }
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
index aedc868..c47eed2 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
@@ -266,7 +266,7 @@ public class ResponseBody implements HttpEntity {
 
                // Figure out what the charset of the response is.
                String cs = null;
-               String ct = getContentType().asString().orElse(null);
+               String ct = getContentType().orElse(null);
 
                // First look for "charset=" in Content-Type header of response.
                if (ct != null)
@@ -675,7 +675,7 @@ public class ResponseBody implements HttpEntity {
                                return (T)r;
                        }
 
-                       String ct = 
firstNonEmpty(response.getResponseHeader("Content-Type").asString().orElse("text/plain"));
+                       String ct = 
firstNonEmpty(response.getResponseHeader("Content-Type").orElse("text/plain"));
 
                        if (parser == null)
                                parser = client.getMatchingParser(ct);
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseHeader.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseHeader.java
index 2afa920..679c55a 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseHeader.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseHeader.java
@@ -135,6 +135,19 @@ public class ResponseHeader implements Header {
        }
 
        /**
+        * Return the value if present, otherwise return other.
+        *
+        * <p>
+        * This is a shortened form for calling 
<c>asString().orElse(<jv>other</jv>)</c>.
+        *
+        * @param other The value to be returned if there is no value present, 
may be <jk>null</jk>.
+        * @return The value, if present, otherwise other.
+        */
+       public String orElse(String other) {
+               return asString().orElse(other);
+       }
+
+       /**
         * Returns the value of this header as a string.
         *
         * @return The value of this header as a string, or {@link 
Optional#empty()} if the header was not present.
@@ -307,7 +320,7 @@ public class ResponseHeader implements Header {
         * @return The response object (for method chaining).
         */
        public RestResponse asString(Mutable<String> m) {
-               m.set(asString().orElse(null));
+               m.set(orElse(null));
                return response;
        }
 
@@ -417,7 +430,7 @@ public class ResponseHeader implements Header {
         * @throws RestCallException If a connection error occurred.
         */
        public Matcher asMatcher(Pattern pattern) throws RestCallException {
-               return pattern.matcher(asString().orElse(""));
+               return pattern.matcher(orElse(""));
        }
 
        /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestAttributes.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestAttributes.java
index 2421b7e..59bd365 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestAttributes.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestAttributes.java
@@ -16,6 +16,8 @@ import static org.apache.juneau.internal.CollectionUtils.*;
 
 import java.util.*;
 
+import javax.servlet.http.*;
+
 import org.apache.juneau.collections.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.internal.*;
@@ -35,13 +37,13 @@ public class RequestAttributes extends OMap {
 
        private static final long serialVersionUID = 1L;
 
-       final RestRequest req;
+       final HttpServletRequest req;
        final OMap defaultEntries;
        final VarResolverSession vs;
 
        RequestAttributes(RestRequest req) {
                super();
-               this.req = req;
+               this.req = req.getHttpServletRequest();
                this.defaultEntries = new OMap();
                this.vs = req.getVarResolverSession();
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
index 5927855..1b8257f 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
@@ -314,7 +314,7 @@ public class RequestBody {
                Reader r = getUnbufferedReader();
                if (r instanceof BufferedReader)
                        return (BufferedReader)r;
-               int len = req.getContentLength();
+               int len = req.getHttpServletRequest().getContentLength();
                int buffSize = len <= 0 ? 8192 : Math.max(len, 8192);
                return new BufferedReader(r, buffSize);
        }
@@ -328,7 +328,7 @@ public class RequestBody {
        protected Reader getUnbufferedReader() throws IOException {
                if (body != null)
                        return new CharSequenceReader(new String(body, UTF8));
-               return new InputStreamReader(getInputStream(), 
req.getCharacterEncoding());
+               return new InputStreamReader(getInputStream(), 
req.getCharset());
        }
 
        /**
@@ -491,14 +491,14 @@ public class RequestBody {
 
        private Encoder getEncoder() throws UnsupportedMediaType {
                if (encoder == null) {
-                       String ce = req.getHeader("content-encoding");
+                       String ce = 
req.getStringHeader("content-encoding").orElse(null);
                        if (isNotEmpty(ce)) {
                                ce = ce.trim();
                                encoder = encoders.getEncoder(ce);
                                if (encoder == null)
                                        throw new UnsupportedMediaType(
                                                "Unsupported encoding in 
request header ''Content-Encoding'': ''{0}''\n\tSupported codings: {1}",
-                                               
req.getHeader("content-encoding"), encoders.getSupportedEncodings()
+                                               
req.getStringHeader("content-encoding").orElse(null), 
encoders.getSupportedEncodings()
                                        );
                        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
index ec1d72b..68309b4 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeader.java
@@ -20,7 +20,6 @@ import java.util.regex.*;
 
 import org.apache.http.*;
 import org.apache.juneau.*;
-import org.apache.juneau.assertions.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.http.exception.*;
 import org.apache.juneau.http.exception.HttpException;
@@ -29,6 +28,7 @@ import org.apache.juneau.httppart.*;
 import org.apache.juneau.oapi.*;
 import org.apache.juneau.parser.ParseException;
 import org.apache.juneau.reflect.*;
+import org.apache.juneau.rest.assertions.*;
 
 /**
  * Represents a single header on an HTTP request.
@@ -103,6 +103,19 @@ public class RequestHeader implements Header {
        }
 
        /**
+        * Return the value if present, otherwise return other.
+        *
+        * <p>
+        * This is a shortened form for calling 
<c>asString().orElse(<jv>other</jv>)</c>.
+        *
+        * @param other The value to be returned if there is no value present, 
may be <jk>null</jk>.
+        * @return The value, if present, otherwise other.
+        */
+       public String orElse(String other) {
+               return asString().orElse(other);
+       }
+
+       /**
         * Returns the value of this header as a string.
         *
         * @return The value of this header as a string, or {@link 
Optional#empty()} if the header was not present.
@@ -168,10 +181,10 @@ public class RequestHeader implements Header {
                        ClassInfo ci = ClassInfo.of(c);
                        ConstructorInfo cc = 
ci.getConstructor(Visibility.PUBLIC, String.class);
                        if (cc != null)
-                               return cc.invoke(asString());
+                               return cc.invoke(orElse(null));
                        cc = ci.getConstructor(Visibility.PUBLIC, String.class, 
String.class);
                        if (cc != null)
-                               return cc.invoke(getName(), asString());
+                               return cc.invoke(getName(), orElse(null));
                } catch (Exception e) {
                        throw new RuntimeException(e);
                }
@@ -303,7 +316,7 @@ public class RequestHeader implements Header {
         */
        public <T> Optional<T> asType(ClassMeta<T> type) throws HttpException {
                try {
-                       return Optional.ofNullable(parser.parse(HEADER, schema, 
asString().orElse(null), type));
+                       return Optional.ofNullable(parser.parse(HEADER, schema, 
orElse(null), type));
                } catch (ParseException e) {
                        throw new BadRequest(e, "Could not parse header 
''{0}''.", getName());
                }
@@ -328,7 +341,7 @@ public class RequestHeader implements Header {
         * @throws HttpException If a connection error occurred.
         */
        public Matcher asMatcher(Pattern pattern) throws HttpException {
-               return pattern.matcher(asString().orElse(""));
+               return pattern.matcher(orElse(""));
        }
 
        /**
@@ -386,88 +399,14 @@ public class RequestHeader implements Header {
         * <h5 class='section'>Examples:</h5>
         * <p class='bcode w800'>
         *      <jv>request</jv>
-        *              .getRequestHeader(<js>"Content-Type"</js>)
-        *              .assertString().is(<js>"application/json"</js>);
-        * </p>
-        *
-        * <p>
-        * The assertion test returns the header object allowing you to chain 
multiple requests like so:
-        * <p class='bcode w800'>
-        *      <jc>// Validates the header and then convert it to a bean.</jc>
-        *      MediaType <jv>mediaType</jv> =  <jv>request</jv>
-        *              .getRequestHeader(<js>"Content-Type"</js>)
-        *              .assertString().matches(<js>".*json.*"</js>)
-        *              .as(MediaType.<jk>class</jk>).get();
-        * </p>
-        *
-        * @return A new fluent assertion object.
-        */
-       public FluentStringAssertion<RequestHeader> assertString() {
-               return new FluentStringAssertion<>(asString().orElse(null), 
this);
-       }
-
-       /**
-        * Provides the ability to perform fluent-style assertions on an 
integer request header.
-        *
-        * <h5 class='section'>Examples:</h5>
-        * <p class='bcode w800'>
-        *      <jv>request</jv>
-        *              .getRequestHeader(<js>"Age"</js>)
-        *              .assertInteger().isGreaterThan(1);
-        * </p>
-        *
-        * @return A new fluent assertion object.
-        */
-       public FluentIntegerAssertion<RequestHeader> assertInteger() {
-               return new 
FluentIntegerAssertion<>(asIntegerHeader().asInteger().orElse(null), this);
-       }
-
-       /**
-        * Provides the ability to perform fluent-style assertions on a long 
request header.
-        *
-        * <h5 class='section'>Examples:</h5>
-        * <p class='bcode w800'>
-        *      <jv>request</jv>
-        *              .getRequestHeader(<js>"Length"</js>)
-        *              .assertLong().isLessThan(100000);
-        * </p>
-        *
-        * @return A new fluent assertion object.
-        */
-       public FluentLongAssertion<RequestHeader> assertLong() {
-               return new 
FluentLongAssertion<>(asLongHeader().asLong().orElse(null), this);
-       }
-
-       /**
-        * Provides the ability to perform fluent-style assertions on a date 
request header.
-        *
-        * <h5 class='section'>Examples:</h5>
-        * <p class='bcode w800'>
-        *      <jv>request</jv>
-        *              .getRequestHeader(<js>"Expires"</js>)
-        *              .assertDate().isAfterNow();
-        * </p>
-        *
-        * @return A new fluent assertion object.
-        */
-       public FluentZonedDateTimeAssertion<RequestHeader> assertDate() {
-               return new 
FluentZonedDateTimeAssertion<>(asDateHeader().asZonedDateTime().orElse(null), 
this);
-       }
-
-       /**
-        * Provides the ability to perform fluent-style assertions on 
comma-separated string headers.
-        *
-        * <h5 class='section'>Examples:</h5>
-        * <p class='bcode w800'>
-        *      <jv>request</jv>
-        *              .getRequestHeader(<js>"Allow"</js>)
-        *              .assertCsvArray().contains(<js>"GET"</js>);
+        *              .getHeader(<js>"Content-Type"</js>)
+        *              .assertValue().is(<js>"application/json"</js>);
         * </p>
         *
         * @return A new fluent assertion object.
         */
-       public FluentListAssertion<RequestHeader> assertCsvArray() {
-               return new 
FluentListAssertion<>(asCsvArrayHeader().asList().orElse(null), this);
+       public FluentRequestHeaderAssertion<RequestHeader> assertValue() {
+               return new FluentRequestHeaderAssertion<>(this, this);
        }
 
        
//------------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
index 2bdb107..d939a23 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
@@ -23,6 +23,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.http.*;
+import org.apache.http.message.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.collections.*;
@@ -53,11 +54,11 @@ public class RequestHeaders {
                this.req = req;
                this.caseSensitive = caseSensitive;
 
-               for (Enumeration<String> e = req.getHeaderNames(); 
e.hasMoreElements();) {
+               for (Enumeration<String> e = 
req.getHttpServletRequest().getHeaderNames(); e.hasMoreElements();) {
                        String name = e.nextElement();
                        String key = key(name);
                        List<RequestHeader> l = new ArrayList<>();
-                       for (Enumeration<String> ve = req.getHeaders(name); 
ve.hasMoreElements();) {
+                       for (Enumeration<String> ve = 
req.getHttpServletRequest().getHeaders(name); ve.hasMoreElements();) {
                                RequestHeader h = new RequestHeader(req, name, 
ve.nextElement());
                                list.add(h);
                                l.add(h);
@@ -99,18 +100,21 @@ public class RequestHeaders {
                return this;
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Basic operations.
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Adds default entries to these headers.
         *
         * <p>
-        * Similar to {@link #put(String, Object)} but doesn't override 
existing values.
+        * Similar to {@link #set(String, Object)} but doesn't override 
existing values.
         *
-        * @param pairs
-        *      The default entries.
-        *      <br>Can be <jk>null</jk>.
+        * @param pairs The default entries.  Must not be <jk>null</jk>.
         * @return This object (for method chaining).
         */
        public RequestHeaders addDefault(List<Header> pairs) {
+               assertArgNotNull("pairs", pairs);
                for (Header p : pairs) {
                        String name = p.getName();
                        String key = key(name);
@@ -134,7 +138,7 @@ public class RequestHeaders {
         * Note that this method never returns <jk>null</jk> and that {@link 
RequestHeader#exists()} can be used
         * to test for the existence of the header.
         *
-        * @param name The header name.
+        * @param name The header name.  Must not be <jk>null</jk>.
         * @return The header.  Never <jk>null</jk>.
         */
        public RequestHeader getFirst(String name) {
@@ -150,7 +154,7 @@ public class RequestHeaders {
         * Note that this method never returns <jk>null</jk> and that {@link 
RequestHeader#exists()} can be used
         * to test for the existence of the header.
         *
-        * @param name The header name.
+        * @param name The header name.  Must not be <jk>null</jk>.
         * @return The header.  Never <jk>null</jk>.
         */
        public RequestHeader getLast(String name) {
@@ -162,7 +166,7 @@ public class RequestHeaders {
        /**
         * Returns all the headers with the specified name.
         *
-        * @param name The header name.
+        * @param name The header name.  Must not be <jk>null</jk>.
         * @return The list of all headers with the specified name, or an empty 
list if none are found.
         */
        public List<RequestHeader> getAll(String name) {
@@ -181,6 +185,173 @@ public class RequestHeaders {
        }
 
        /**
+        * Returns <jk>true</jk> if the header with the specified name is 
present.
+        *
+        * @param name The header name.  Must not be <jk>null</jk>.
+        * @return <jk>true</jk> if the header with the specified name is 
present.
+        */
+       public boolean contains(String name) {
+               assertArgNotNull("name", name);
+               return map.containsKey(key(name));
+       }
+
+       /**
+        * Adds a request header value.
+        *
+        * <p>
+        * Header is added to the end of the headers.
+        * <br>Existing headers with the same name are not changed.
+        *
+        * @param name The header name.  Must not be <jk>null</jk>.
+        * @param value The header value.  Can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders add(String name, Object value) {
+               assertArgNotNull("name", name);
+               String key = key(name);
+               RequestHeader h = new RequestHeader(req, name, 
stringify(value)).parser(parser);
+               if (map.containsKey(key))
+                       map.get(key).add(h);
+               else
+                       map.put(key, AList.of(h));
+               list.add(h);
+               return this;
+       }
+
+       /**
+        * Adds request header values.
+        *
+        * <p>
+        * Headers are added to the end of the headers.
+        * <br>Existing headers with the same name are not changed.
+        *
+        * @param header The header objects.  Must not be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders add(Header...header) {
+               assertArgNotNull("header", header);
+               for (Header h : header) {
+                       assertArgNotNull("header entry", h);
+                       add(h.getName(), h.getValue());
+               }
+               return this;
+       }
+
+       /**
+        * Sets a request header value.
+        *
+        * <p>
+        * Header is added to the end of the headers.
+        * <br>Any previous headers with the same name are removed.
+        *
+        * @param name The header name.  Must not be <jk>null</jk>.
+        * @param value
+        *      The header value.
+        *      <br>Converted to a string using {@link Object#toString()}.
+        *      <br>Can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders set(String name, Object value) {
+               assertArgNotNull("name", name);
+               String key = key(name);
+               remove(key);
+               RequestHeader h = new RequestHeader(req, name, 
stringify(value)).parser(parser);
+               map.put(key, AList.of(h));
+               list.add(h);
+               return this;
+       }
+
+       /**
+        * Sets request header values.
+        *
+        * <p>
+        * Headers are added to the end of the headers.
+        * <br>Any previous headers with the same name are removed.
+        *
+        * @param headers The header beans.  Must not be <jk>null</jk> or 
contain <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders set(Header...headers) {
+               assertArgNotNull("headers", headers);
+               for (Header h : headers)
+                       remove(h);
+               for (Header h : headers)
+                       add(h);
+               return this;
+       }
+
+       /**
+        * Remove headers.
+        *
+        * @param name The header names.  Must not be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders remove(String...name) {
+               assertArgNotNull("name", name);
+               for (String n : name) {
+                       String key = key(n);
+                       if (map.containsKey(key))
+                               list.removeAll(map.get(key));
+                       map.remove(key);
+               }
+               return this;
+       }
+
+       /**
+        * Remove headers.
+        *
+        * @param headers The headers to remove.  Must not be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders remove(Header...headers) {
+               for (Header h : headers)
+                       remove(h.getName());
+               return this;
+       }
+
+       /**
+        * Returns an iterator of all the headers.
+        *
+        * @return An iterator of all the headers.  Never <jk>null</jk>.
+        */
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       public HeaderIterator iterator() {
+               return new BasicListHeaderIterator((List)list, null);
+       }
+
+       /**
+        * Returns an iterator of all the headers with the specified name.
+        *
+        * @param name The header name.
+        * @return An iterator of all the headers with the specified name.  
Never <jk>null</jk>.
+        */
+       @SuppressWarnings({ "unchecked", "rawtypes" })
+       public HeaderIterator iterator(String name) {
+               return new BasicListHeaderIterator((List)map.get(key(name)), 
null);
+       }
+
+       /**
+        * Returns a copy of this object but only with the specified header 
names copied.
+        *
+        * @param headers The list to include in the copy.
+        * @return A new list object.
+        */
+       public RequestHeaders subset(String...headers) {
+               Map<String,List<RequestHeader>> m = Arrays
+                       .asList(headers)
+                       .stream()
+                       .map(x -> key(x))
+                       .filter(map::containsKey)
+                       .collect(toMap(Function.identity(),map::get));
+
+               return new RequestHeaders(req, m, parser, caseSensitive);
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Convenience getters.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
         * Returns the last header with the specified name as a string.
         *
         * <ul class='notes'>
@@ -270,65 +441,9 @@ public class RequestHeaders {
                return getLast(name).asDate();
        }
 
-       /**
-        * Sets a request header value.
-        *
-        * <p>
-        * This overwrites any previous value.
-        *
-        * @param name The header name.
-        * @param value The header value.
-        * @return This object (for method chaining).
-        */
-       public RequestHeaders put(String name, Object value) {
-               assertArgNotNull("name", name);
-               String key = key(name);
-               RequestHeader h = new RequestHeader(req, name, 
stringify(value)).parser(parser);
-               if (map.containsKey(key))
-                       
list.removeIf(x->caseSensitive?x.getName().equals(name):x.getName().equalsIgnoreCase(name));
-               list.add(h);
-               map.put(key, AList.of(h));
-               return this;
-       }
-
-       /**
-        * Adds a request header value.
-        *
-        * <p>
-        * Header is added to the end of the headers.
-        *
-        * @param name The header name.
-        * @param value The header value.
-        * @return This object (for method chaining).
-        */
-       public RequestHeaders add(String name, Object value) {
-               assertArgNotNull("name", name);
-               String key = key(name);
-               RequestHeader h = new RequestHeader(req, name, 
stringify(value)).parser(parser);
-               if (map.containsKey(key))
-                       map.get(key).add(h);
-               else
-                       map.put(key, AList.of(h));
-               list.add(h);
-               return this;
-       }
-
-       /**
-        * Returns a copy of this object but only with the specified header 
names copied.
-        *
-        * @param headers The list to include in the copy.
-        * @return A new list object.
-        */
-       public RequestHeaders subset(String...headers) {
-               Map<String,List<RequestHeader>> m = Arrays
-                       .asList(headers)
-                       .stream()
-                       .map(x -> key(x))
-                       .filter(map::containsKey)
-                       .collect(toMap(Function.identity(),map::get));
-
-               return new RequestHeaders(req, m, parser, caseSensitive);
-       }
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Standard headers.
+       
//-----------------------------------------------------------------------------------------------------------------
 
        /**
         * Returns the <c>Accept</c> header on the request.
@@ -828,6 +943,10 @@ public class RequestHeaders {
                return 
ofNullable(Warning.of(getString("Warning").orElse(null)));
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Other methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
        /**
         * Converts the headers to a readable string.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQueryParam.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQueryParam.java
index 6af47f7..568082c 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQueryParam.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQueryParam.java
@@ -103,6 +103,19 @@ public class RequestQueryParam implements NameValuePair {
        }
 
        /**
+        * Return the value if present, otherwise return other.
+        *
+        * <p>
+        * This is a shortened form for calling 
<c>asString().orElse(<jv>other</jv>)</c>.
+        *
+        * @param other The value to be returned if there is no value present, 
may be <jk>null</jk>.
+        * @return The value, if present, otherwise other.
+        */
+       public String orElse(String other) {
+               return asString().orElse(other);
+       }
+
+       /**
         * Returns the value of this parameter as a string.
         *
         * @return The value of this parameter as a string, or {@link 
Optional#empty()} if the parameter was not present.
@@ -168,10 +181,10 @@ public class RequestQueryParam implements NameValuePair {
                        ClassInfo ci = ClassInfo.of(c);
                        ConstructorInfo cc = 
ci.getConstructor(Visibility.PUBLIC, String.class);
                        if (cc != null)
-                               return cc.invoke(asString());
+                               return cc.invoke(orElse(null));
                        cc = ci.getConstructor(Visibility.PUBLIC, String.class, 
String.class);
                        if (cc != null)
-                               return cc.invoke(getName(), asString());
+                               return cc.invoke(getName(), orElse(null));
                } catch (Exception e) {
                        throw new RuntimeException(e);
                }
@@ -276,7 +289,7 @@ public class RequestQueryParam implements NameValuePair {
         */
        public <T> Optional<T> asType(ClassMeta<T> type) throws HttpException {
                try {
-                       return Optional.ofNullable(parser.parse(HEADER, schema, 
asString().orElse(null), type));
+                       return Optional.ofNullable(parser.parse(HEADER, schema, 
orElse(null), type));
                } catch (ParseException e) {
                        throw new BadRequest(e, "Could not parse query 
parameter ''{0}''.", getName());
                }
@@ -302,7 +315,7 @@ public class RequestQueryParam implements NameValuePair {
         * @throws HttpException If a connection error occurred.
         */
        public Matcher asMatcher(Pattern pattern) throws HttpException {
-               return pattern.matcher(asString().orElse(""));
+               return pattern.matcher(orElse(""));
        }
 
        /**
@@ -378,7 +391,7 @@ public class RequestQueryParam implements NameValuePair {
         * @return A new fluent assertion object.
         */
        public FluentStringAssertion<RequestQueryParam> assertString() {
-               return new FluentStringAssertion<>(asString().orElse(null), 
this);
+               return new FluentStringAssertion<>(orElse(null), this);
        }
 
        /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
index 00d45d7..270bac7 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
@@ -469,7 +469,7 @@ public class RestCall {
 
        /**
         * Returns the output that was set by calling {@link 
RestResponse#setOutput(Object)}.
-        * 
+        *
         * <p>
         * If it's empty, then {@link RestResponse#setOutput(Object)} wasn't 
called.
         * <br>If it's not empty but contains an empty, then 
<c>response.setObject(<jk>null</jk>)</c> was called.
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
index e089fcf..3fb6370 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperationContext.java
@@ -24,6 +24,7 @@ import static java.util.Collections.*;
 
 import java.lang.annotation.*;
 import java.lang.reflect.Method;
+import java.nio.charset.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.function.*;
@@ -658,7 +659,7 @@ public class RestOperationContext extends BeanContext 
implements Comparable<Rest
        private final List<org.apache.http.Header> defaultRequestHeaders, 
defaultResponseHeaders;
        private final List<NameValuePair> defaultRequestQuery, 
defaultRequestFormData;
        private final List<NamedAttribute> defaultRequestAttributes;
-       private final String defaultCharset;
+       private final Charset defaultCharset;
        private final long maxInput;
        private final List<MediaType>
                supportedAcceptTypes,
@@ -763,7 +764,7 @@ public class RestOperationContext extends BeanContext 
implements Comparable<Rest
                                _httpMethod = "*";
                        httpMethod = _httpMethod.toUpperCase(Locale.ENGLISH);
 
-                       defaultCharset = 
cp.getString(REST_defaultCharset).orElse("utf-8");
+                       defaultCharset = 
Charset.forName(cp.getString(REST_defaultCharset).orElse("utf-8"));
 
                        maxInput = 
StringUtils.parseLongWithSuffix(cp.get(REST_maxInput, 
String.class).orElse("100M"));
 
@@ -1734,7 +1735,7 @@ public class RestOperationContext extends BeanContext 
implements Comparable<Rest
         *
         * @return The default charset.  Never <jk>null</jk>.
         */
-       public String getDefaultCharset() {
+       public Charset getDefaultCharset() {
                return defaultCharset;
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 45eb25c..c7674bc 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -39,6 +39,7 @@ import javax.servlet.http.*;
 
 import org.apache.http.*;
 import org.apache.http.message.*;
+import org.apache.http.params.*;
 import org.apache.juneau.*;
 import org.apache.juneau.assertions.*;
 import org.apache.juneau.config.*;
@@ -103,7 +104,7 @@ import org.apache.juneau.utils.*;
  * </ul>
  */
 @SuppressWarnings({ "unchecked", "unused" })
-public final class RestRequest extends HttpServletRequestWrapper {
+public final class RestRequest implements HttpRequest {
 
        // Constructor initialized.
        private HttpServletRequest inner;
@@ -125,22 +126,23 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        private VarResolverSession varSession;
        private RequestFormData formData;
        private UriContext uriContext;
-       private String charset, authorityPath;
+       private String authorityPath;
        private Config config;
        private Swagger swagger;
+       private Charset charset;
 
        /**
         * Constructor.
         */
        RestRequest(RestCall call) throws Exception {
-               super(call.getRequest());
-
                this.call = call;
                this.opContext = call.getRestOperationContext();
 
                inner = call.getRequest();
                context = call.getContext();
 
+               attrs = new RequestAttributes(this);
+
                queryParams = new RequestQueryParams(this, 
call.getQueryParams(), true);
 
                headers = new RequestHeaders(this, queryParams, false);
@@ -150,7 +152,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                if (context.isAllowBodyParam()) {
                        String b = queryParams.getString("body").orElse(null);
                        if (b != null) {
-                               headers.put("Content-Type", 
UonSerializer.DEFAULT.getResponseContentType());
+                               headers.set("Content-Type", 
UonSerializer.DEFAULT.getResponseContentType());
                                body.load(MediaType.UON, UonParser.DEFAULT, 
b.getBytes(UTF8));
                        }
                }
@@ -193,26 +195,25 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                        .addDefault(context.getDefaultRequestHeaders())
                        .parser(partParserSession);
 
-               attrs = new RequestAttributes(this);
-               attrs
-                       .addDefault(opContext.getDefaultRequestAttributes())
-                       .addDefault(context.getDefaultRequestAttributes());
-
                body
                        .encoders(opContext.getEncoders())
                        .parsers(opContext.getParsers())
                        .headers(headers)
                        .maxInput(opContext.getMaxInput());
 
+               attrs
+                       .addDefault(opContext.getDefaultRequestAttributes())
+                       .addDefault(context.getDefaultRequestAttributes());
+
                if (isDebug())
                        inner = CachingHttpServletRequest.wrap(inner);
        }
 
-       /**
-        * Returns a string of the form <js>"HTTP method-name full-url"</js>
-        *
-        * @return A description string of the request.
-        */
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Request line.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Override /* HttpRequest */
        public RequestLine getRequestLine() {
                String x = inner.getProtocol();
                int i = x.indexOf('/');
@@ -221,11 +222,32 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                return new BasicRequestLine(inner.getMethod(), 
inner.getRequestURI(), pv);
        }
 
+       @Override /* HttpRequest */
+       public ProtocolVersion getProtocolVersion() {
+               return getRequestLine().getProtocolVersion();
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Assertions
        
//-----------------------------------------------------------------------------------------------------------------
 
        /**
+        * Returns an assertion on the request line returned by {@link 
#getRequestLine()}.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bcode w800'>
+        *      <jc>// Validates the request body contains "foo".</jc>
+        *      <jv>request</jv>
+        *              .assertRequestLine().protocol().minor().is(1);
+        * </p>
+        *
+        * @return A new assertion object.
+        */
+       public FluentRequestLineAssertion<RestRequest> assertRequestLine() {
+               return new 
FluentRequestLineAssertion<RestRequest>(getRequestLine(), this);
+       }
+
+       /**
         * Returns a fluent assertion for the request body.
         *
         * <h5 class='section'>Example:</h5>
@@ -248,14 +270,14 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * <p class='bcode w800'>
         *      <jc>// Validates the content type is JSON.</jc>
         *      <jv>request</jv>
-        *              
.assertHeader(<js>"Content-Type"</js>).asString().is(<js>"application/json"</js>);
+        *              
.assertHeader(<js>"Content-Type"</js>).is(<js>"application/json"</js>);
         * </p>
         *
         * @param name The header name.
         * @return A new fluent assertion on the parameter, never <jk>null</jk>.
         */
        public FluentRequestHeaderAssertion<RestRequest> assertHeader(String 
name) {
-               return new 
FluentRequestHeaderAssertion<RestRequest>(getRequestHeader(name), this);
+               return new 
FluentRequestHeaderAssertion<RestRequest>(getHeader(name), this);
        }
 
        /**
@@ -297,7 +319,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         *              <jv>headers</jv>.addDefault(<js>"ETag"</js>, 
<jsf>DEFAULT_UUID</jsf>);
         *
         *      <jc>// Get a header value as a POJO.</jc>
-        *              UUID etag = 
<jv>headers</jv>.get(<js>"ETag"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>);
+        *              UUID etag = 
<jv>headers</jv>.get(<js>"ETag"</js>).asType(UUID.<jk>class</jk>).orElse(<jk>null</jk>);
         *
         *              <jc>// Get a standard header.</jc>
         *              Optional&lt;CacheControl&gt; = 
<jv>headers</jv>.getCacheControl();
@@ -328,21 +350,150 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        }
 
        /**
-        * Returns the last header with the specified name.
+        * Returns the last header with a specified name of this message.
+        *
+        * <p>
+        * If there is more than one matching header in the message the last 
element of <c>getHeaders(String)</c> is returned.
+        * <br>If there is no matching header in the message, an empty request 
header object is returned.
         *
-        * Unlike {@link #getHeader(String)}, this method returns an empty
-        * {@link ResponseHeader} object instead of returning <jk>null</jk>.  
This allows it to be used more easily
-        * in fluent calls.
+        * <p>
+        * This method is identical to {@link #getLastHeader(String)}.
         *
+        * <h5 class='section'>Example:</h5>
         * <p class='bcode w800'>
-        *              
<jv>req</jv>.getRequestHeader("Foo").asInteger().orElse(-1);
+        *      <jc>// Gets a header and throws a BadRequest if it doesn't 
exist.</jc>
+        *      <jv>request</jv>
+        *              .getHeader(<js>"Foo"</js>)
+        *              .assertValue().exists()
+        *              .asString().get();
         * </p>
         *
         * @param name The header name.
-        * @return The header.  Never <jk>null</jk>.
+        * @return The request header object, never <jk>null</jk>.
         */
-       public RequestHeader getRequestHeader(String name) {
-               return headers.getLast(name).parser(getPartParserSession());
+       public RequestHeader getHeader(String name) {
+               return getLastHeader(name);
+       }
+
+       /**
+        * Returns the last header with a specified name as a string value.
+        *
+        * <p>
+        * This method is equivalent to calling 
<c>getLastHeader(<jv>name</jv>).asString()</c>.
+        *
+        * @param name The header name.
+        * @return The request header value as a string, never <jk>null</jk>.
+        */
+       public Optional<String> getStringHeader(String name) {
+               return getLastHeader(name).asString();
+       }
+
+       /**
+        * Returns the first header with a specified name of this message.
+        *
+        * <p>
+        * If there is more than one matching header in the message the first 
element of <c>getHeaders(String)</c> is returned.
+        * <br>If there is no matching header in the message, an empty request 
header object is returned.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bcode w800'>
+        *      <jc>// Gets a header and throws a BadRequest if it doesn't 
exist.</jc>
+        *      <jv>request</jv>
+        *              .getFirstHeader(<js>"Foo"</js>)
+        *              .assertValue().exists()
+        *              .asString().get();
+        * </p>
+        *
+        * @param name The header name.
+        * @return The request header object, never <jk>null</jk>.
+        */
+       @Override /* HttpRequest */
+       public RequestHeader getFirstHeader(String name) {
+               return headers.getFirst(name);
+       }
+
+       /**
+        * Returns the last header with a specified name of this message.
+        *
+        * <p>
+        * If there is more than one matching header in the message the last 
element of <c>getHeaders(String)</c> is returned.
+        * <br>If there is no matching header in the message, an empty request 
header object is returned.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bcode w800'>
+        *      <jc>// Gets a header and throws a BadRequest if it doesn't 
exist.</jc>
+        *      <jv>request</jv>
+        *              .getLastHeader(<js>"Foo"</js>)
+        *              .assertValue().exists()
+        *              .asString().get();
+        * </p>
+        *
+        * @param name The header name.
+        * @return The request header object, never <jk>null</jk>.
+        */
+       @Override /* HttpRequest */
+       public RequestHeader getLastHeader(String name) {
+               return headers.getLast(name);
+       }
+
+       @Override /* HttpRequest */
+       public RequestHeader[] getAllHeaders() {
+               return headers.getAll().toArray(new RequestHeader[0]);
+       }
+
+       @Override /* HttpRequest */
+       public void addHeader(org.apache.http.Header header) {
+               headers.add(header);
+       }
+
+       @Override /* HttpRequest */
+       public void addHeader(String name, String value) {
+               headers.add(name, value);
+       }
+
+       @Override /* HttpRequest */
+       public void setHeader(org.apache.http.Header header) {
+               headers.set(header);
+       }
+
+       @Override /* HttpRequest */
+       public void setHeader(String name, String value) {
+               headers.set(name, value);
+       }
+
+       @Override /* HttpRequest */
+       public void setHeaders(org.apache.http.Header[] headers) {
+               this.headers.set(headers);
+       }
+
+       @Override /* HttpRequest */
+       public void removeHeader(org.apache.http.Header header) {
+               headers.remove(header);
+       }
+
+       @Override /* HttpRequest */
+       public void removeHeaders(String name) {
+               headers.remove(name);
+       }
+
+       @Override /* HttpRequest */
+       public HeaderIterator headerIterator() {
+               return headers.iterator();
+       }
+
+       @Override /* HttpRequest */
+       public HeaderIterator headerIterator(String name) {
+               return headers.iterator(name);
+       }
+
+       @Override /* HttpRequest */
+       public boolean containsHeader(String name) {
+               return headers.contains(name);
+       }
+
+       @Override /* HttpRequest */
+       public RequestHeader[] getHeaders(String name) {
+               return headers.getAll(name).toArray(new RequestHeader[0]);
        }
 
        /**
@@ -359,65 +510,52 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @throws HttpException If REST call failed.
         */
        public FluentStringAssertion<RestRequest> assertCharset() {
-               return new FluentStringAssertion<>(getCharacterEncoding(), 
this);
-       }
-
-       @Override /* ServletRequest */
-       public String getHeader(String name) {
-               return getRequestHeaders().getString(name).orElse(null);
-       }
-
-       @Override /* ServletRequest */
-       public Enumeration<String> getHeaders(String name) {
-               return inner.getHeaders(name);
+               return new FluentStringAssertion<>(getCharset().name(), this);
        }
 
        /**
         * Sets the charset to expect on the request body.
+        *
+        * @param value The new value to use for the request body.
         */
-       @Override /* ServletRequest */
-       public void setCharacterEncoding(String charset) {
-               this.charset = charset;
+       public void setCharset(Charset value) {
+               this.charset = value;
        }
 
        /**
         * Returns the charset specified on the <c>Content-Type</c> header, or 
<js>"UTF-8"</js> if not specified.
+        *
+        * @return The charset to use to decode the request body.
         */
-       @Override /* ServletRequest */
-       public String getCharacterEncoding() throws UnsupportedMediaType {
+       public Charset getCharset() {
                if (charset == null) {
                        // Determine charset
                        // NOTE:  Don't use super.getCharacterEncoding() 
because the spec is implemented inconsistently.
                        // Jetty returns the default charset instead of null if 
the character is not specified on the request.
-                       String h = getHeader("Content-Type");
+                       String h = getLastHeader("Content-Type").orElse(null);
                        if (h != null) {
                                int i = h.indexOf(";charset=");
                                if (i > 0)
-                                       charset = h.substring(i+9).trim();
+                                       charset = 
Charset.forName(h.substring(i+9).trim());
                        }
                        if (charset == null)
                                charset = opContext.getDefaultCharset();
                        if (charset == null)
-                               charset = "UTF-8";
-                       if (! Charset.isSupported(charset))
-                               throw new UnsupportedMediaType("Unsupported 
charset in header ''Content-Type'': ''{0}''", h);
+                               charset = Charset.forName("UTF-8");
                }
                return charset;
        }
 
        /**
-        * Wrapper around {@link #getCharacterEncoding()} that converts the 
value to a {@link Charset}.
+        * Returns the preferred Locale that the client will accept content in, 
based on the Accept-Language header.
+        *
+        * <p>
+        * If the client request doesn't provide an <c>Accept-Language</c> 
header, this method returns the default locale for the server.
         *
-        * @return The request character encoding converted to a {@link 
Charset}.
+        * @return The preferred Locale that the client will accept content in. 
 Never <jk>null</jk>.
         */
-       public Charset getCharset() {
-               String s = getCharacterEncoding();
-               return s == null ? null : Charset.forName(s);
-       }
-
-       @Override /* ServletRequest */
        public Locale getLocale() {
-               Locale best = super.getLocale();
+               Locale best = inner.getLocale();
                String h = headers.getString("Accept-Language").orElse(null);
                if (h != null) {
                        StringRanges sr = StringRanges.of(h);
@@ -432,21 +570,6 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                return best;
        }
 
-       @Override /* ServletRequest */
-       public Enumeration<Locale> getLocales() {
-               String h = headers.getString("Accept-Language").orElse(null);
-               if (h != null) {
-                       StringRanges mr = StringRanges.of(h);
-                       if (! mr.getRanges().isEmpty()) {
-                               List<Locale> l = new 
ArrayList<>(mr.getRanges().size());
-                               for (StringRange r : mr.getRanges())
-                                       if (r.getQValue() > 0)
-                                               l.add(toLocale(r.getName()));
-                               return enumeration(l);
-                       }
-               }
-               return super.getLocales();
-       }
 
        
//-----------------------------------------------------------------------------------------------------------------
        // Attributes
@@ -494,6 +617,26 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                return attrs;
        }
 
+       /**
+        * Returns the request attribute with the specified name.
+        *
+        * @param name The attribute name.
+        * @return The attribute value, never <jk>null</jk>.
+        */
+       public Optional<Object> getAttribute(String name) {
+               return Optional.ofNullable(attrs.get(name));
+       }
+
+       /**
+        * Sets a request attribute.
+        *
+        * @param name The attribute name.
+        * @param value The attribute value.
+        */
+       public void setAttribute(String name, Object value) {
+               attrs.put(name, value);
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Query parameters
        
//-----------------------------------------------------------------------------------------------------------------
@@ -505,7 +648,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * Returns a {@link RequestQueryParams} object that encapsulates access 
to URL GET parameters.
         *
         * <p>
-        * Similar to {@link #getParameterMap()} but only looks for query 
parameters in the URL and not form posts.
+        * Similar to {@link HttpServletRequest#getParameterMap()} but only 
looks for query parameters in the URL and not form posts.
         *
         * <h5 class='section'>Example:</h5>
         * <p class='bcode w800'>
@@ -561,7 +704,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * Returns a {@link RequestFormData} object that encapsulates access to 
form post parameters.
         *
         * <p>
-        * Similar to {@link #getParameterMap()}, but only looks for form data 
in the HTTP body.
+        * Similar to {@link HttpServletRequest#getParameterMap()}, but only 
looks for form data in the HTTP body.
         *
         * <h5 class='section'>Example:</h5>
         * <p class='bcode w800'>
@@ -604,7 +747,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                        if (formData == null) {
                                formData = new RequestFormData(this, 
partParserSession);
                                if (! body.isLoaded()) {
-                                       formData.putAll(getParameterMap());
+                                       
formData.putAll(inner.getParameterMap());
                                } else {
                                        Map<String,String[]> m = 
RestUtils.parseQuery(body.getReader());
                                        for (Map.Entry<String,String[]> e : 
m.entrySet()) {
@@ -749,8 +892,13 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         *
         * <p>
         * Automatically handles GZipped input streams.
+        *
+        * <p>
+        * This method is equivalent to calling <c>getBody().getReader()</c>.
+        *
+        * @return The HTTP body content as a {@link Reader}.
+        * @throws IOException If body could not be read.
         */
-       @Override /* ServletRequest */
        public BufferedReader getReader() throws IOException {
                return getBody().getReader();
        }
@@ -761,10 +909,12 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * <p>
         * Automatically handles GZipped input streams.
         *
+        * <p>
+        * This method is equivalent to calling 
<c>getBody().getInputStream()</c>.
+        *
         * @return The negotiated input stream.
         * @throws IOException If any error occurred while trying to get the 
input stream or wrap it in the GZIP wrapper.
         */
-       @Override /* ServletRequest */
        public ServletInputStream getInputStream() throws IOException {
                return getBody().getInputStream();
        }
@@ -773,10 +923,20 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        // URI-related methods
        
//-----------------------------------------------------------------------------------------------------------------
 
-       @Override /* HttpServletRequest */
+       /**
+        * Returns the portion of the request URI that indicates the context of 
the request.
+        *
+        * <p>The context path always comes first in a request URI.
+        * The path starts with a <js>"/"</js> character but does not end with 
a <js>"/"</js> character.
+        * For servlets in the default (root) context, this method returns 
<js>""</js>.
+        * The container does not decode this string.
+        *
+        * @return The context path, never <jk>null</jk>.
+        * @see HttpServletRequest#getContextPath()
+        */
        public String getContextPath() {
                String cp = context.getUriContext();
-               return cp == null ? super.getContextPath() : cp;
+               return cp == null ? inner.getContextPath() : cp;
        }
 
        /**
@@ -788,9 +948,9 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                if (authorityPath == null)
                        authorityPath = context.getUriAuthority();
                if (authorityPath == null) {
-                       String scheme = getScheme();
-                       int port = getServerPort();
-                       StringBuilder sb = new 
StringBuilder(getScheme()).append("://").append(getServerName());
+                       String scheme = inner.getScheme();
+                       int port = inner.getServerPort();
+                       StringBuilder sb = new 
StringBuilder(inner.getScheme()).append("://").append(inner.getServerName());
                        if (! (port == 80 && "http".equals(scheme) || port == 
443 && "https".equals(scheme)))
                                sb.append(':').append(port);
                        authorityPath = sb.toString();
@@ -798,10 +958,19 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                return authorityPath;
        }
 
-       @Override /* HttpServletRequest */
+       /**
+        * Returns the part of this request's URL that calls the servlet.
+        *
+        * <p>
+        * This path starts with a <js>"/"</js> character and includes either 
the servlet name or a path to the servlet,
+        * but does not include any extra path information or a query string.
+        *
+        * @return The servlet path, never <jk>null</jk>.
+        * @see HttpServletRequest#getServletPath()
+        */
        public String getServletPath() {
                String cp = context.getUriContext();
-               String sp = super.getServletPath();
+               String sp = inner.getServletPath();
                return cp == null || ! sp.startsWith(cp) ? sp : 
sp.substring(cp.length());
        }
 
@@ -816,7 +985,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         */
        public UriContext getUriContext() {
                if (uriContext == null)
-                       uriContext = UriContext.of(getAuthorityPath(), 
getContextPath(), getServletPath(), 
StringUtils.urlEncodePath(super.getPathInfo()));
+                       uriContext = UriContext.of(getAuthorityPath(), 
getContextPath(), getServletPath(), 
StringUtils.urlEncodePath(inner.getPathInfo()));
                return uriContext;
        }
 
@@ -854,7 +1023,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return A new URI.
         */
        public URI getUri(boolean includeQuery, Map<String,?> addQueryParams) {
-               String uri = getRequestURI();
+               String uri = inner.getRequestURI();
                if (includeQuery || addQueryParams != null) {
                        StringBuilder sb = new StringBuilder(uri);
                        RequestQueryParams rq = this.queryParams.copy();
@@ -873,6 +1042,58 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                }
        }
 
+       /**
+        * Returns any extra path information associated with the URL the 
client sent when it made this request.
+        *
+        * <p>
+        * The extra path information follows the servlet path but precedes the 
query string and will start with a <js>"/"</js> character.
+        * This method returns <jk>null</jk> if there was no extra path 
information.
+        *
+        * @return The extra path information.
+        * @see HttpServletRequest#getPathInfo()
+        */
+       public String getPathInfo() {
+               return inner.getPathInfo();
+       }
+
+       /**
+        * Returns the part of this request's URL from the protocol name up to 
the query string in the first line of the HTTP request.
+        *
+        * The web container does not decode this String
+        *
+        * @return The request URI.
+        * @see HttpServletRequest#getRequestURI()
+        */
+       public String getRequestURI() {
+               return inner.getRequestURI();
+       }
+
+
+       /**
+        * Returns the query string that is contained in the request URL after 
the path.
+        *
+        * <p>
+        * This method returns <jk>null</jk> if the URL does not have a query 
string.
+        *
+        * @return The query string.
+        * @see HttpServletRequest#getQueryString()
+        */
+       public String getQueryString() {
+               return inner.getQueryString();
+       }
+
+       /**
+        * Reconstructs the URL the client used to make the request.
+        *
+        * <p>
+        * The returned URL contains a protocol, server name, port number, and 
server path, but it does not include query string parameters.
+        *
+        * @return The request URL.
+        * @see HttpServletRequest#getRequestURL()
+        */
+       public StringBuffer getRequestURL() {
+               return inner.getRequestURL();
+       }
        
//-----------------------------------------------------------------------------------------------------------------
        // Labels
        
//-----------------------------------------------------------------------------------------------------------------
@@ -946,22 +1167,18 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        }
 
        /**
-        * Returns the method of this request.
+        * Returns the HTTP method of this request.
         *
         * <p>
         * If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then 
first looks for
         * <c>&amp;method=xxx</c> in the URL query string.
+        *
+        * @return The HTTP method of this request.
         */
-       @Override /* ServletRequest */
        public String getMethod() {
                return call.getMethod();
        }
 
-       @Override /* ServletRequest */
-       public int getContentLength() {
-               return getBody().getContentLength();
-       }
-
        /**
         * Returns <jk>true</jk> if <c>&amp;plainText=true</c> was specified as 
a URL parameter.
         *
@@ -1060,7 +1277,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return <jk>true</jk> if debug mode is enabled.
         */
        public boolean isDebug() {
-               Boolean b = ObjectUtils.castOrNull(getAttribute("Debug"), 
Boolean.class);
+               Boolean b = 
ObjectUtils.castOrNull(getAttribute("Debug").orElse(null), Boolean.class);
                return b == null ? false : b;
        }
 
@@ -1323,7 +1540,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                                                        if (pt == FORMDATA)
                                                                return 
getFormData().get(pp, schema, name, type);
                                                        if (pt == HEADER)
-                                                               return 
getRequestHeaders().getLast(name).parser(pp).schema(schema).asType(type);
+                                                               return 
getLastHeader(name).parser(pp).schema(schema).asType(type);
                                                        if (pt == PATH)
                                                                return 
getPathMatch().get(pp, schema, name, type);
                                                }
@@ -1366,6 +1583,48 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        }
 
        /**
+        * Not implemented.
+        */
+       @SuppressWarnings("deprecation")
+       @Override
+       public HttpParams getParams() {
+               return null;
+       }
+
+       /**
+        * Not implemented.
+        */
+       @SuppressWarnings("deprecation")
+       @Override
+       public void setParams(HttpParams params) {
+       }
+
+       /**
+        * Returns the current session associated with this request, or if the 
request does not have a session, creates one.
+        *
+        * @return The current request session.
+        * @see HttpServletRequest#getSession()
+        */
+       public HttpSession getSession() {
+               return inner.getSession();
+       }
+
+       /**
+        * Returns a boolean indicating whether the authenticated user is 
included in the specified logical "role".
+        *
+        * <p>
+        * Roles and role membership can be defined using deployment 
descriptors.
+        * If the user has not been authenticated, the method returns false.
+        *
+        * @param role The role name.
+        * @return <jk>true</jk> if the user holds the specified role.
+        * @see HttpServletRequest#isUserInRole(String)
+        */
+       public boolean isUserInRole(String role) {
+               return inner.isUserInRole(role);
+       }
+
+       /**
         * Returns the wrapped servlet request.
         *
         * @return The wrapped servlet request.
@@ -1378,9 +1637,8 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        public String toString() {
                StringBuilder sb = new 
StringBuilder("\n").append(getRequestLine()).append("\n");
                sb.append("---Headers---\n");
-               for (Enumeration<String> e = getHeaderNames(); 
e.hasMoreElements();) {
-                       String h = e.nextElement();
-                       sb.append("\t").append(h).append(": 
").append(getHeader(h)).append("\n");
+               for (RequestHeader h : getAllHeaders()) {
+                       sb.append("\t").append(h).append("\n");
                }
                String m = getMethod();
                if (m.equals("PUT") || m.equals("POST")) {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index aa2dad3..fc66df3 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -84,7 +84,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
                RestContext context = call.getContext();
 
                try {
-                       String passThroughHeaders = 
request.getHeader("x-response-headers");
+                       String passThroughHeaders = 
request.getHeader("x-response-headers").orElse(null);
                        if (passThroughHeaders != null) {
                                HttpPartParser p = context.getPartParser();
                                OMap m = 
p.createPartSession(request.getParserSessionArgs()).parse(HEADER, null, 
passThroughHeaders, context.getClassMeta(OMap.class));
@@ -96,8 +96,8 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
                }
 
                // Find acceptable charset
-               String h = request.getHeader("accept-charset");
-               String charset = null;
+               String h = request.getHeader("accept-charset").orElse(null);
+               Charset charset = null;
                if (h == null)
                        charset = opContext.getDefaultCharset();
                else for (StringRange r : StringRanges.of(h).getRanges()) {
@@ -105,7 +105,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
                                if (r.getName().equals("*"))
                                        charset = opContext.getDefaultCharset();
                                else if (Charset.isSupported(r.getName()))
-                                       charset = r.getName();
+                                       charset = Charset.forName(r.getName());
                                if (charset != null)
                                        break;
                        }
@@ -117,8 +117,8 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
                        setHeaderSafe(e.getName(), stringify(e.getValue()));
 
                if (charset == null)
-                       throw new NotAcceptable("No supported charsets in 
header ''Accept-Charset'': ''{0}''", request.getHeader("Accept-Charset"));
-               super.setCharacterEncoding(charset);
+                       throw new NotAcceptable("No supported charsets in 
header ''Accept-Charset'': ''{0}''", 
request.getStringHeader("Accept-Charset").orElse(null));
+               inner.setCharacterEncoding(charset.name());
 
        }
 
@@ -259,7 +259,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
                        Encoder encoder = null;
                        EncoderGroup encoders = 
request.getOpContext().getEncoders();
 
-                       String ae = request.getHeader("Accept-Encoding");
+                       String ae = 
request.getHeader("Accept-Encoding").orElse(null);
                        if (! (ae == null || ae.isEmpty())) {
                                EncoderMatch match = 
encoders.getEncoderMatch(ae);
                                if (match == null) {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/assertions/FluentProtocolVersionAssertion.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/assertions/FluentProtocolVersionAssertion.java
new file mode 100644
index 0000000..1a8aaad
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/assertions/FluentProtocolVersionAssertion.java
@@ -0,0 +1,90 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.rest.assertions;
+
+import org.apache.http.*;
+import org.apache.juneau.assertions.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Used for fluent assertion calls against {@link ProtocolVersion} objects.
+ *
+ * @param <R> The return type.
+ */
+@FluentSetters(returns="FluentProtocolVersionAssertion<R>")
+public class FluentProtocolVersionAssertion<R> extends FluentAssertion<R> {
+
+       private final ProtocolVersion value;
+
+       /**
+        * Constructor.
+        *
+        * @param value The object being tested.
+        * @param returns The object to return after the test.
+        */
+       public FluentProtocolVersionAssertion(ProtocolVersion value, R returns) 
{
+               super(null, returns);
+               this.value = value;
+               throwable(BadRequest.class);
+       }
+
+       /**
+        * Returns the protocol string as a new assertion.
+        *
+        * @return A new assertion.
+        */
+       public FluentStringAssertion<R> protocol() {
+               return new FluentStringAssertion<>(value.getProtocol(), 
returns());
+       }
+
+       /**
+        * Returns the protocol major version as a new assertion.
+        *
+        * @return A new assertion.
+        */
+       public FluentIntegerAssertion<R> major() {
+               return new FluentIntegerAssertion<>(value.getMajor(), 
returns());
+       }
+
+       /**
+        * Returns the protocol minor version as a new assertion.
+        *
+        * @return A new assertion.
+        */
+       public FluentIntegerAssertion<R> minor() {
+               return new FluentIntegerAssertion<>(value.getMinor(), 
returns());
+       }
+
+       // <FluentSetters>
+
+       @Override /* GENERATED - Assertion */
+       public FluentProtocolVersionAssertion<R> msg(String msg, Object...args) 
{
+               super.msg(msg, args);
+               return this;
+       }
+
+       @Override /* GENERATED - Assertion */
+       public FluentProtocolVersionAssertion<R> stderr() {
+               super.stderr();
+               return this;
+       }
+
+       @Override /* GENERATED - Assertion */
+       public FluentProtocolVersionAssertion<R> stdout() {
+               super.stdout();
+               return this;
+       }
+
+       // </FluentSetters>
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/assertions/FluentRequestLineAssertion.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/assertions/FluentRequestLineAssertion.java
new file mode 100644
index 0000000..cc73691
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/assertions/FluentRequestLineAssertion.java
@@ -0,0 +1,91 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.rest.assertions;
+
+import org.apache.http.*;
+import org.apache.juneau.assertions.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Used for fluent assertion calls against {@link RequestLine} objects.
+ *
+ * @param <R> The return type.
+ */
+@FluentSetters(returns="FluentRequestLineAssertion<R>")
+public class FluentRequestLineAssertion<R> extends FluentAssertion<R> {
+
+       private final RequestLine value;
+
+       /**
+        * Constructor.
+        *
+        * @param value The object being tested.
+        * @param returns The object to return after the test.
+        */
+       public FluentRequestLineAssertion(RequestLine value, R returns) {
+               super(null, returns);
+               this.value = value;
+               throwable(BadRequest.class);
+       }
+
+       /**
+        * Returns the request line method string as a new assertion.
+        *
+        * @return A new assertion.
+        */
+       public FluentStringAssertion<R> method() {
+               return new FluentStringAssertion<>(value.getMethod(), 
returns());
+       }
+
+       /**
+        * Returns the request line uri string as a new assertion.
+        *
+        * @return A new assertion.
+        */
+       public FluentStringAssertion<R> uri() {
+               return new FluentStringAssertion<>(value.getUri(), returns());
+       }
+
+       /**
+        * Returns the request line protocol version as a new assertion.
+        *
+        * @return A new assertion.
+        */
+       public FluentProtocolVersionAssertion<R> protocolVersion() {
+               return new 
FluentProtocolVersionAssertion<>(value.getProtocolVersion(), returns());
+       }
+
+       // <FluentSetters>
+
+       @Override /* GENERATED - Assertion */
+       public FluentRequestLineAssertion<R> msg(String msg, Object...args) {
+               super.msg(msg, args);
+               return this;
+       }
+
+       @Override /* GENERATED - Assertion */
+       public FluentRequestLineAssertion<R> stderr() {
+               super.stderr();
+               return this;
+       }
+
+       @Override /* GENERATED - Assertion */
+       public FluentRequestLineAssertion<R> stdout() {
+               super.stdout();
+               return this;
+       }
+
+
+       // </FluentSetters>
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logging/BasicRestLogger.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logging/BasicRestLogger.java
index 3a72bfd..2af886a 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logging/BasicRestLogger.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logging/BasicRestLogger.java
@@ -335,8 +335,6 @@ public class BasicRestLogger implements RestLogger {
        }
 
        private byte[] getRequestBody(HttpServletRequest req) {
-               if (req instanceof RestRequest)
-                       req = ((RestRequest)req).getHttpServletRequest();
                if (req instanceof CachingHttpServletRequest)
                        return ((CachingHttpServletRequest)req).getBody();
                return castOrNull(req.getAttribute("RequestBody"), 
byte[].class);
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestHeaderVar.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestHeaderVar.java
index 49bf226..0a34d85 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestHeaderVar.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestHeaderVar.java
@@ -67,7 +67,7 @@ public class RequestHeaderVar extends MultipartResolvingVar {
 
        @Override /* Var */
        public String resolve(VarResolverSession session, String key) {
-               return 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new).getHeader(key);
+               return 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new).getHeader(key).orElse(null);
        }
 
        @Override /* Var */
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestQueryVar.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestQueryVar.java
index c2a166a..613ed50 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestQueryVar.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestQueryVar.java
@@ -68,7 +68,7 @@ public class RequestQueryVar extends MultipartResolvingVar {
 
        @Override /* Var */
        public String resolve(VarResolverSession session, String key) {
-               return 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new).getRequestQueryParam(key).asString().orElse(null);
+               return 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new).getRequestQueryParam(key).orElse(null);
        }
 
        @Override /* Var */
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
index c2d8fd5..aa7063e 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
@@ -172,11 +172,7 @@ public abstract class MenuItemWidget extends Widget {
        }
 
        private Integer getId(RestRequest req) {
-               Integer id = (Integer)req.getAttribute("LastMenuItemId");
-               if (id == null)
-                       id = 1;
-               else
-                       id = id + 1;
+               Integer id = 
(Integer)req.getAttribute("LastMenuItemId").orElse(0) + 1;
                req.setAttribute("LastMenuItemId", id);
                return id;
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/http/SerializedHttpEntity_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/http/SerializedHttpEntity_Test.java
index 1f26306..fd97286 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/http/SerializedHttpEntity_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/http/SerializedHttpEntity_Test.java
@@ -41,7 +41,7 @@ public class SerializedHttpEntity_Test {
        public static class A extends BasicRestObject {
                @RestPost
                public String[] checkHeader(org.apache.juneau.rest.RestRequest 
req) {
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getStringHeader("Check").get()).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
                @RestPost
                public Reader checkBody(org.apache.juneau.rest.RestRequest req) 
throws IOException {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java
index 4a8e628..b00359a 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_Test.java
@@ -567,7 +567,7 @@ public class Remote_Test {
        public static class F extends BasicRestObject {
                @RestGet
                public String[] headers(org.apache.juneau.rest.RestRequest req) 
{
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getHeader("Check").orElse(null)).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/Header_AcceptCharset_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/Header_AcceptCharset_Test.java
index 6f992d7..31ed25a 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/Header_AcceptCharset_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/Header_AcceptCharset_Test.java
@@ -45,15 +45,15 @@ public class Header_AcceptCharset_Test {
        public void a01_qValues() throws Exception {
                RestClient a = MockRestClient.build(A.class);
                
a.get("/a").accept("text/plain").acceptCharset("utf-8").run().assertCharset().is("utf-8");
-               
a.get("/a").accept("text/plain").acceptCharset("iso-8859-1").run().assertCharset().is("iso-8859-1");
+               
a.get("/a").accept("text/plain").acceptCharset("iso-8859-1").run().assertCharset().is("ISO-8859-1");
                
a.get("/a").accept("text/plain").acceptCharset("bad,utf-8").run().assertCharset().is("utf-8");
                
a.get("/a").accept("text/plain").acceptCharset("utf-8,bad").run().assertCharset().is("utf-8");
                
a.get("/a").accept("text/plain").acceptCharset("bad;q=0.9,utf-8;q=0.1").run().assertCharset().is("utf-8");
                
a.get("/a").accept("text/plain").acceptCharset("bad;q=0.1,utf-8;q=0.9").run().assertCharset().is("utf-8");
                
a.get("/a").accept("text/plain").acceptCharset("utf-8;q=0.9,iso-8859-1;q=0.1").run().assertCharset().is("utf-8");
-               
a.get("/a").accept("text/plain").acceptCharset("utf-8;q=0.1,iso-8859-1;q=0.9").run().assertCharset().is("iso-8859-1");
+               
a.get("/a").accept("text/plain").acceptCharset("utf-8;q=0.1,iso-8859-1;q=0.9").run().assertCharset().is("ISO-8859-1");
                
a.get("/a").accept("text/plain").acceptCharset("*").run().assertCharset().is("utf-8");
-               
a.get("/a").accept("text/plain").acceptCharset("bad,iso-8859-1;q=0.5,*;q=0.1").run().assertCharset().is("iso-8859-1");
+               
a.get("/a").accept("text/plain").acceptCharset("bad,iso-8859-1;q=0.5,*;q=0.1").run().assertCharset().is("ISO-8859-1");
                
a.get("/a").accept("text/plain").acceptCharset("bad,iso-8859-1;q=0.1,*;q=0.5").run().assertCharset().is("utf-8");
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
index f711850..dcf6af9 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Params_Test.java
@@ -386,7 +386,7 @@ public class RestOp_Params_Test {
 
                @Override
                public Object resolve(RestCall call) throws Exception {
-                       return new 
B2b(call.getRestRequest().getHeader("Custom"));
+                       return new 
B2b(call.getRestRequest().getStringHeader("Custom").orElse(null));
                }
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestHook_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestHook_Test.java
index 30cb8e3..053153e 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestHook_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestHook_Test.java
@@ -58,9 +58,9 @@ public class RestHook_Test {
                        attrs.put("p2", "xp2");
                        attrs.put("p4", "xp4");
                        attrs.put("p5", "xp5"); // New property
-                       String overrideContentType = 
req.getHeader("Override-Content-Type");
+                       String overrideContentType = 
req.getStringHeader("Override-Content-Type").orElse(null);
                        if (overrideContentType != null)
-                               req.getRequestHeaders().put("Content-Type", 
overrideContentType);
+                               req.getRequestHeaders().set("Content-Type", 
overrideContentType);
                }
 
                @RestPut(
@@ -128,10 +128,10 @@ public class RestHook_Test {
                        attrs.put("p2", "xp2");
                        attrs.put("p4", "xp4");
                        attrs.put("p5", "xp5"); // New property
-                       String overrideAccept = 
req.getHeader("Override-Accept");
+                       String overrideAccept = 
req.getStringHeader("Override-Accept").orElse(null);
                        if (overrideAccept != null)
-                               req.getRequestHeaders().put("Accept", 
overrideAccept);
-                       String overrideContentType = 
req.getHeader("Override-Content-Type");
+                               req.getRequestHeaders().set("Accept", 
overrideAccept);
+                       String overrideContentType = 
req.getStringHeader("Override-Content-Type").orElse(null);
                        if (overrideContentType != null)
                                attrs.put("Override-Content-Type", 
overrideContentType);
                }
@@ -151,9 +151,9 @@ public class RestHook_Test {
                public String b(RestRequest req, RequestAttributes attrs) 
throws Exception {
                        attrs.put("p3", "pp3");
                        attrs.put("p4", "pp4");
-                       String accept = req.getHeader("Accept");
+                       String accept = 
req.getStringHeader("Accept").orElse(null);
                        if (accept == null || accept.isEmpty())
-                               req.getRequestHeaders().put("Accept", 
"text/s2");
+                               req.getRequestHeaders().set("Accept", 
"text/s2");
                        return null;
                }
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
index 0df76cc..4e8c688 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
@@ -83,7 +83,7 @@ public class RestClient_BasicCalls_Test {
                }
                @RestOp(path="/checkHeader")
                public String[] postHeader(org.apache.juneau.rest.RestRequest 
req) {
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getStringHeader("Check").orElse(null)).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
                @RestOp(path="/",method="*")
                public Reader echoMethod(@Method String method) {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_BeanContext_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_BeanContext_Test.java
index 8ba94d1..092cbb0 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_BeanContext_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_BeanContext_Test.java
@@ -41,7 +41,7 @@ public class RestClient_Config_BeanContext_Test {
                }
                @RestGet
                public String[] checkHeader(org.apache.juneau.rest.RestRequest 
req) {
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getStringHeader("Check").orElse(null)).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
                @RestGet
                public Reader checkQuery(org.apache.juneau.rest.RestRequest 
req) {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_OpenApi_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_OpenApi_Test.java
index 455ea2a..3050f5a 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_OpenApi_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_OpenApi_Test.java
@@ -36,7 +36,7 @@ public class RestClient_Config_OpenApi_Test {
                }
                @RestGet
                public String[] checkHeader(org.apache.juneau.rest.RestRequest 
req) {
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getStringHeader("Check").orElse(null)).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
                @RestGet
                public Reader checkQuery(org.apache.juneau.rest.RestRequest 
req) {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
index 86db999..92653e3 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
@@ -86,7 +86,7 @@ public class RestClient_Config_RestClient_Test {
                }
                @RestOp(path="/checkHeader")
                public String[] getHeader(org.apache.juneau.rest.RestRequest 
req) {
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getStringHeader("Check").orElse(null)).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
index f29355d..08b3fc8 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
@@ -58,7 +58,7 @@ public class RestClient_Headers_Test {
        public static class A extends BasicRestObject {
                @RestGet
                public String[] headers(org.apache.juneau.rest.RestRequest req) 
{
-                       return 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       return 
req.getRequestHeaders().getAll(req.getStringHeader("Check").get()).stream().map(x
 -> x.getValue()).toArray(String[]::new);
                }
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Headers_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Headers_Test.java
index bac5c35..57892dc 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Headers_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Headers_Test.java
@@ -36,8 +36,8 @@ public class RestClient_Response_Headers_Test {
        public static class A extends BasicRestObject {
                @RestGet
                public String echo(org.apache.juneau.rest.RestRequest req, 
org.apache.juneau.rest.RestResponse res) {
-                       String c = req.getHeader("Check");
-                       String[] h = 
req.getRequestHeaders().getAll(req.getHeader("Check")).stream().map(x -> 
x.getValue()).toArray(String[]::new);
+                       String c = req.getStringHeader("Check").get();
+                       String[] h = 
req.getRequestHeaders().getAll(c).stream().map(x -> 
x.getValue()).toArray(String[]::new);
                        if (h != null)
                                for (String hh : h)
                                        res.addHeader(c, hh);
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Test.java
index 14ab963..3b43a3c 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Response_Test.java
@@ -149,7 +149,7 @@ public class RestClient_Response_Test {
        public static class C extends BasicRestObject {
                @RestGet(path="/")
                public String getHeader(org.apache.juneau.rest.RestRequest req, 
org.apache.juneau.rest.RestResponse res) {
-                       String n = req.getHeader("Check");
+                       String n = req.getStringHeader("Check").get();
                        String v = 
req.getRequestHeaders().getString(n).orElse(null);
                        res.setHeader(n,v);
                        return v;

Reply via email to