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 2e0d823  org.apache.juneau.http tests.
2e0d823 is described below

commit 2e0d8235e0edfb34f526513f99b09311e3960dc5
Author: JamesBognar <[email protected]>
AuthorDate: Sat Jul 11 13:26:00 2020 -0400

    org.apache.juneau.http tests.
---
 .../org/apache/juneau/http/BasicHttpEntity.java    | 344 +++++++++++++++++++++
 .../org/apache/juneau/http/BasicHttpResource.java  | 198 +++++-------
 .../org/apache/juneau/http/ReaderResource.java     |  62 +++-
 .../juneau/http/ResolvingReaderResource.java       | 123 +++++++-
 .../org/apache/juneau/http/SerializedHeader.java   |  20 ++
 .../apache/juneau/http/SerializedHttpEntity.java   |  79 +++--
 .../juneau/http/SerializedNameValuePair.java       |  19 ++
 .../org/apache/juneau/http/StreamResource.java     |  68 +++-
 .../main/ConfigurablePropertyCodeGenerator.java    |   1 +
 .../apache/juneau/http/BasicHttpResource_Test.java |  16 +
 .../rest/client2/RestClient_FormData_Test.java     |  24 +-
 .../rest/client2/RestClient_Headers_Test.java      |  19 ++
 12 files changed, 807 insertions(+), 166 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpEntity.java
new file mode 100644
index 0000000..c2341f0
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpEntity.java
@@ -0,0 +1,344 @@
+// 
***************************************************************************************************************************
+// * 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.http;
+
+import java.io.*;
+import java.util.function.*;
+
+import org.apache.http.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * An extension of {@link org.apache.http.entity.BasicHttpEntity} with fluent 
setters.
+ * 
+ * <p>
+ * Includes automatic support for a variety of content types.
+ */
+public class BasicHttpEntity extends org.apache.http.entity.BasicHttpEntity {
+       private Object content;
+       private boolean cache;
+
+       /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link ReaderResource} object.
+        */
+       public static BasicHttpEntity of(Object content) {
+               return new BasicHttpEntity().content(content);
+       }
+
+       /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link ReaderResource} object.
+        */
+       public static BasicHttpEntity of(Supplier<?> content) {
+               return new BasicHttpEntity().content(content);
+       }
+
+       /**
+        * Creates a new basic entity.
+        * The content is initially missing, the content length
+        * is set to a negative number.
+        */
+       public BasicHttpEntity() {
+               super();
+       }
+
+       /**
+        * Sets the content on this entity.
+        *
+        * @param value
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity content(Object value) {
+               this.content = value;
+               return this;
+       }
+
+       /**
+        * Sets the content on this entity.
+        *
+        * @param value
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity content(Supplier<?> value) {
+               this.content = value;
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setContentType(String)}.
+        *
+        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity contentType(String value) {
+               super.setContentType(value);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setContentType(Header)}.
+        *
+        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity contentType(Header value) {
+               super.setContentType(value);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setContentLength(long)}.
+        *
+        * @param value The new <c>Content-Length</c> header value.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity contentLength(long value) {
+               super.setContentLength(value);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setContentEncoding(String)}.
+        *
+        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity contentEncoding(String value) {
+               super.setContentEncoding(value);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setContentEncoding(Header)}.
+        *
+        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity contentEncoding(Header value) {
+               super.setContentEncoding(value);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setChunked(boolean)} with <jk>true</jk>.
+        *
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity chunked() {
+               super.setChunked(true);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling {@link #setChunked(boolean)}.
+        *
+        * @param value The new value for this flag.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity chunked(boolean value) {
+               super.setChunked(value);
+               return this;
+       }
+
+       /**
+        * Specifies that the contents of this resource should be cached into 
an internal byte array so that it can
+        * be read multiple times.
+        *
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity cache() {
+               return cache(true);
+       }
+
+       /**
+        * Specifies that the contents of this resource should be cached into 
an internal byte array so that it can
+        * be read multiple times.
+        *
+        * @param value The new value for this flag.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public BasicHttpEntity cache(boolean value) {
+               this.cache = value;
+               return this;
+       }
+
+       @Override
+       public boolean isRepeatable() {
+               return cache || content instanceof File || content instanceof 
CharSequence || content instanceof byte[];
+       }
+
+       @Override
+       public long getContentLength() {
+               try {
+                       tryCache();
+               } catch (IOException e) {}
+               if (content instanceof byte[])
+                       return ((byte[])content).length;
+               if (content instanceof File)
+                       return ((File)content).length();
+               if (content instanceof CharSequence)
+                       return ((CharSequence)content).length();
+               return -1;
+       }
+
+       @Override
+       public InputStream getContent() {
+               try {
+                       tryCache();
+                       if (content == null)
+                               return null;
+                       if (content instanceof File)
+                               return new FileInputStream((File)content);
+                       if (content instanceof byte[])
+                               return new 
ByteArrayInputStream((byte[])content);
+                       if (content instanceof Reader)
+                               return new ReaderInputStream((Reader)content, 
IOUtils.UTF8);
+                       if (content instanceof InputStream)
+                               return (InputStream)content;
+                       return new ReaderInputStream(new 
StringReader(content.toString()),IOUtils.UTF8);
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       @Override
+       public void writeTo(OutputStream os) throws IOException {
+               tryCache();
+               if (content != null)
+                       IOUtils.pipe(content, os);
+               os.flush();
+       }
+
+       @Override
+       public boolean isStreaming() {
+               return (content instanceof InputStream || content instanceof 
Reader);
+       }
+
+       /**
+        * Returns the raw content of this resource.
+        *
+        * @return The raw content of this resource.
+        */
+       protected Object getRawContent() {
+               return unwrap(content);
+       }
+
+       private void tryCache() throws IOException {
+               if (cache && isCacheable(content))
+                       content = readBytes(content);
+       }
+
+       /**
+        * Returns <jk>true</jk> if the specified object is cachable as a byte 
array.
+        *
+        * <p>
+        * The default implementation returns <jk>true</jk> for the following 
types:
+        * <ul>
+        *      <li>{@link File}
+        *      <li>{@link InputStream}
+        *      <li>{@link Reader}
+        *
+        * @param o The object to check.
+        * @return <jk>true</jk> if the specified object is cachable as a byte 
array.
+        */
+       protected boolean isCacheable(Object o) {
+               return (o instanceof File || o instanceof InputStream || o 
instanceof Reader);
+       }
+
+       /**
+        * Reads the contents of the specified object as a byte array.
+        *
+        * @param o The object to read.
+        * @return The byte array contents.
+        * @throws IOException If object could not be read.
+        */
+       protected byte[] readBytes(Object o) throws IOException {
+               return IOUtils.readBytes(o);
+       }
+
+       /**
+        * If the specified object is a {@link Supplier}, returns the supplied 
value, otherwise the same value.
+        *
+        * @param o The object to unwrap.
+        * @return The unwrapped object.
+        */
+       protected Object unwrap(Object o) {
+               while (o instanceof Supplier)
+                       o = ((Supplier<?>)o).get();
+               return o;
+       }
+
+       // <FluentSetters>
+
+       // </FluentSetters>
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpResource.java
index cea51c9..76a454a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpResource.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicHttpResource.java
@@ -12,24 +12,21 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http;
 
-import java.io.*;
 import java.util.*;
+import java.util.function.*;
 
 import org.apache.http.*;
-import org.apache.http.entity.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.header.ContentType;
 
 /**
- * An abstract subclass of all {@link HttpResource} objects.
+ * An extension of an {@link HttpEntity} with support for arbitrary headers.
  */
-public class BasicHttpResource extends AbstractHttpEntity implements 
HttpResource  {
+public class BasicHttpResource extends BasicHttpEntity implements HttpResource 
{
 
        private final List<Header> headers = AList.of();
-       private Object content;
-       private boolean cache;
 
        /**
         * Creator.
@@ -52,6 +49,7 @@ public class BasicHttpResource extends AbstractHttpEntity 
implements HttpResourc
         *              <li><c>File</c>
         *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
         * @return A new empty {@link ReaderResource} object.
@@ -61,6 +59,27 @@ public class BasicHttpResource extends AbstractHttpEntity 
implements HttpResourc
        }
 
        /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link ReaderResource} object.
+        */
+       public static BasicHttpResource of(Supplier<?> content) {
+               return new BasicHttpResource().content(content);
+       }
+
+       /**
         * Constructor.
         */
        public BasicHttpResource() {
@@ -85,67 +104,15 @@ public class BasicHttpResource extends AbstractHttpEntity 
implements HttpResourc
         *              <li><c>File</c>
         *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
         */
        public BasicHttpResource(ContentType contentType, ContentEncoding 
contentEncoding, Object content) {
-               setContentType(contentType);
-               setContentEncoding(contentEncoding);
-               this.content = content;
-       }
-
-       /**
-        * Returns the raw content of this resource.
-        *
-        * @return The raw content of this resource.
-        */
-       protected Object getRawContent() {
-               return content;
-       }
-
-       @Override
-       @FluentSetter
-       public BasicHttpResource chunked() {
-               super.setChunked(true);
-               return this;
-       }
-
-       @Override
-       @FluentSetter
-       public BasicHttpResource contentType(Header value) {
-               super.setContentType(value);
-               return this;
-       }
-
-       /**
-        * Shortcut for calling {@link #contentType(String)}.
-        *
-        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpResource contentType(String value) {
-               super.setContentType(value);
-               return this;
-       }
-
-       @Override
-       @FluentSetter
-       public BasicHttpResource contentEncoding(Header value) {
-               super.setContentEncoding(value);
-               return this;
-       }
-
-       /**
-        * Shortcut for calling {@link #setContentEncoding(String)}.
-        *
-        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpResource contentEncoding(String value) {
-               super.setContentEncoding(value);
-               return this;
+               super();
+               content(content);
+               contentType(contentType);
+               contentEncoding(contentEncoding);
        }
 
        /**
@@ -233,79 +200,78 @@ public class BasicHttpResource extends AbstractHttpEntity 
implements HttpResourc
                return null;
        }
 
-       @Override
-       @FluentSetter
-       public BasicHttpResource content(Object value) {
-               this.content = value;
-               return this;
+       @Override /* Resource */
+       public List<Header> getHeaders() {
+               return Collections.unmodifiableList(headers);
        }
 
-       @Override
-       @FluentSetter
+       // <FluentSetters>
+
+       @Override /* GENERATED - BasicHttpEntity */
        public BasicHttpResource cache() {
-               this.cache = true;
+               super.cache();
                return this;
        }
 
-       @Override /* Resource */
-       public List<Header> getHeaders() {
-               return Collections.unmodifiableList(headers);
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource cache(boolean value) {
+               super.cache(value);
+               return this;
        }
 
-       @Override
-       public boolean isRepeatable() {
-               return cache || content instanceof File || content instanceof 
CharSequence || content instanceof byte[];
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource chunked() {
+               super.chunked();
+               return this;
        }
 
-       @Override
-       public long getContentLength() {
-               try {
-                       tryCache();
-               } catch (IOException e) {}
-               if (content instanceof byte[])
-                       return ((byte[])content).length;
-               if (content instanceof File)
-                       return ((File)content).length();
-               if (content instanceof CharSequence)
-                       return ((CharSequence)content).length();
-               return -1;
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource chunked(boolean value) {
+               super.chunked(value);
+               return this;
        }
 
-       @Override
-       public InputStream getContent() throws IOException, 
UnsupportedOperationException {
-               tryCache();
-               if (content == null)
-                       return null;
-               if (content instanceof File)
-                       return new FileInputStream((File)content);
-               if (content instanceof byte[])
-                       return new ByteArrayInputStream((byte[])content);
-               if (content instanceof Reader)
-                       return new ReaderInputStream((Reader)content, 
IOUtils.UTF8);
-               if (content instanceof InputStream)
-                       return (InputStream)content;
-               return new ReaderInputStream(new 
StringReader(content.toString()),IOUtils.UTF8);
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource content(Object value) {
+               super.content(value);
+               return this;
        }
 
-       @Override
-       public void writeTo(OutputStream os) throws IOException {
-               if (content != null)
-                       IOUtils.pipe(content, os);
-               os.flush();
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource content(Supplier<?> value) {
+               super.content(value);
+               return this;
        }
 
-       @Override
-       public boolean isStreaming() {
-               return (content instanceof InputStream || content instanceof 
Reader);
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource contentEncoding(String value) {
+               super.contentEncoding(value);
+               return this;
        }
 
-       private void tryCache() throws IOException {
-               if (cache)
-                       if (content instanceof File || content instanceof 
InputStream || content instanceof Reader)
-                               content = IOUtils.readBytes(content);
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource contentEncoding(Header value) {
+               super.contentEncoding(value);
+               return this;
        }
 
-       // <FluentSetters>
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource contentLength(long value) {
+               super.contentLength(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource contentType(String value) {
+               super.contentType(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
+       public BasicHttpResource contentType(Header value) {
+               super.contentType(value);
+               return this;
+       }
 
        // </FluentSetters>
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
index 86148fd..0fda49a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ReaderResource.java
@@ -14,6 +14,7 @@ package org.apache.juneau.http;
 
 import java.io.*;
 import java.util.*;
+import java.util.function.*;
 
 import org.apache.http.Header;
 import org.apache.juneau.assertions.*;
@@ -60,6 +61,7 @@ public class ReaderResource extends BasicHttpResource {
         *              <li><c>File</c>
         *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
         * @return A new empty {@link ReaderResource} object.
@@ -69,6 +71,27 @@ public class ReaderResource extends BasicHttpResource {
        }
 
        /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link ReaderResource} object.
+        */
+       public static ReaderResource of(Supplier<?> content) {
+               return new ReaderResource().content(content);
+       }
+
+       /**
         * Constructor.
         */
        public ReaderResource() {
@@ -93,6 +116,7 @@ public class ReaderResource extends BasicHttpResource {
         *              <li><c>File</c>
         *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
         */
@@ -122,43 +146,67 @@ public class ReaderResource extends BasicHttpResource {
 
        // <FluentSetters>
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource cache() {
                super.cache();
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ReaderResource cache(boolean value) {
+               super.cache(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource chunked() {
                super.chunked();
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ReaderResource chunked(boolean value) {
+               super.chunked(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource content(Object value) {
                super.content(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ReaderResource content(Supplier<?> value) {
+               super.content(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource contentEncoding(String value) {
                super.contentEncoding(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource contentEncoding(Header value) {
                super.contentEncoding(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ReaderResource contentLength(long value) {
+               super.contentLength(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource contentType(String value) {
                super.contentType(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public ReaderResource contentType(Header value) {
                super.contentType(value);
                return this;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ResolvingReaderResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ResolvingReaderResource.java
index e4c9673..b7ff536 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ResolvingReaderResource.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/ResolvingReaderResource.java
@@ -14,6 +14,7 @@ package org.apache.juneau.http;
 
 import java.io.*;
 import java.util.*;
+import java.util.function.*;
 
 import org.apache.http.*;
 import org.apache.juneau.http.header.*;
@@ -25,6 +26,7 @@ import org.apache.juneau.svl.*;
  */
 public class ResolvingReaderResource extends ReaderResource {
 
+       private VarResolver varResolver;
        private VarResolverSession varSession;
 
        /**
@@ -37,6 +39,48 @@ public class ResolvingReaderResource extends ReaderResource {
        }
 
        /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link ResolvingReaderResource} object.
+        */
+       public static ResolvingReaderResource of(Object content) {
+               return new ResolvingReaderResource().content(content);
+       }
+
+       /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link ResolvingReaderResource} object.
+        */
+       public static ResolvingReaderResource of(Supplier<?> content) {
+               return new ResolvingReaderResource().content(content);
+       }
+
+       /**
         * Constructor.
         */
        public ResolvingReaderResource() {
@@ -52,7 +96,12 @@ public class ResolvingReaderResource extends ReaderResource {
         * @param contentEncoding
         *      The content encoding of the contents.
         *      <br>Can be <jk>null</jk>.
-        * @param varSession Var resolver session for resolving SVL variables.
+        * @param varResolver
+        *      The var resolver for resolving SVL variables.
+        *      <br>Only one of <c>varResolver</c> and <c>varSession</c> needs 
to be specified.
+        * @param varSession
+        *      Var resolver session for resolving SVL variables.
+        *      <br>Only one of <c>varResolver</c> and <c>varSession</c> needs 
to be specified.
         * @param content
         *      The content.
         *      <br>Can be any of the following:
@@ -62,11 +111,13 @@ public class ResolvingReaderResource extends 
ReaderResource {
         *              <li><c>File</c>
         *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
         */
-       public ResolvingReaderResource(ContentType contentType, ContentEncoding 
contentEncoding, VarResolverSession varSession, Object content) {
+       public ResolvingReaderResource(ContentType contentType, ContentEncoding 
contentEncoding, VarResolver varResolver, VarResolverSession varSession, Object 
content) {
                super(contentType, contentEncoding, content);
+               this.varResolver = varResolver;
                this.varSession = varSession;
        }
 
@@ -78,22 +129,24 @@ public class ResolvingReaderResource extends 
ReaderResource {
         */
        @Override
        public String asString() throws IOException {
-               if (varSession == null)
+               VarResolverSession vr = getVarSession();
+               if (vr == null)
                        return super.asString();
                StringWriter sw = new StringWriter();
                String s = IOUtils.read(getRawContent());
-               varSession.resolveTo(s, sw);
+               vr.resolveTo(s, sw);
                return sw.toString();
        }
 
        @Override
        public void writeTo(OutputStream os) throws IOException {
-               if (varSession == null)
+               VarResolverSession vr = getVarSession();
+               if (vr == null)
                        super.writeTo(os);
                else {
                        try (OutputStreamWriter osw = new 
OutputStreamWriter(os, IOUtils.UTF8)) {
                                String s = IOUtils.read(getRawContent());
-                               varSession.resolveTo(s, osw);
+                               vr.resolveTo(s, osw);
                                osw.flush();
                        }
                }
@@ -103,6 +156,18 @@ public class ResolvingReaderResource extends 
ReaderResource {
        /**
         * Sets the var resolver for resolving SVL variables.
         *
+        * @param varResolver - The var resolver.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public ResolvingReaderResource varResolver(VarResolver varResolver) {
+               this.varResolver = varResolver;
+               return this;
+       }
+
+       /**
+        * Sets the var resolver session for resolving SVL variables.
+        *
         * @param varSession - The var resolver session.
         * @return This object (for method chaining).
         */
@@ -112,45 +177,77 @@ public class ResolvingReaderResource extends 
ReaderResource {
                return this;
        }
 
+       private VarResolverSession getVarSession() {
+               if (varSession != null)
+                       return varSession;
+               if (varResolver != null)
+                       return varResolver.createSession();
+               return null;
+       }
+
        // <FluentSetters>
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource cache() {
                super.cache();
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ResolvingReaderResource cache(boolean value) {
+               super.cache(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource chunked() {
                super.chunked();
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ResolvingReaderResource chunked(boolean value) {
+               super.chunked(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource content(Object value) {
                super.content(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ResolvingReaderResource content(Supplier<?> value) {
+               super.content(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource contentEncoding(String value) {
                super.contentEncoding(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource contentEncoding(Header value) {
                super.contentEncoding(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public ResolvingReaderResource contentLength(long value) {
+               super.contentLength(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource contentType(String value) {
                super.contentType(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public ResolvingReaderResource contentType(Header value) {
                super.contentType(value);
                return this;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHeader.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHeader.java
index d3409d7..387c278 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHeader.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHeader.java
@@ -131,6 +131,26 @@ public class SerializedHeader extends BasicStringHeader {
                return this;
        }
 
+       /**
+        * Don't serialize this header if the value is <jk>null</jk> or an 
empty string.
+        *
+        * @return This object (for method chaining).
+        */
+       public SerializedHeader skipIfEmpty() {
+               return skipIfEmpty(true);
+       }
+
+       /**
+        * Don't serialize this header if the value is <jk>null</jk> or an 
empty string.
+        *
+        * @param value The new value of this setting.
+        * @return This object (for method chaining).
+        */
+       public SerializedHeader skipIfEmpty(boolean value) {
+               this.skipIfEmpty = value;
+               return this;
+       }
+
        @Override /* NameValuePair */
        public String getValue() {
                try {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
index b0bdefb..76ce356 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedHttpEntity.java
@@ -16,7 +16,6 @@ import static org.apache.juneau.internal.IOUtils.*;
 
 import java.io.*;
 
-import org.apache.http.entity.*;
 import org.apache.juneau.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.internal.*;
@@ -27,36 +26,72 @@ import org.apache.juneau.utils.*;
  * HttpEntity for serializing POJOs as the body of HTTP requests.
  */
 public class SerializedHttpEntity extends BasicHttpEntity {
-       final Object output;
-       final Serializer serializer;
-       final HttpPartSchema schema;
-       byte[] outputBytes;
+       private Serializer serializer;
+       private HttpPartSchema schema;
+       private byte[] cache;
+
+       /**
+        * Creator.
+        *
+        * @param content The POJO to serialize.  Can also be a {@link Reader} 
or {@link InputStream}.
+        * @return A new {@link SerializedHttpEntity} with uninitialized 
serializer and schema.
+        */
+       public static SerializedHttpEntity of(Object content) {
+               return new SerializedHttpEntity(content, null, null, null);
+       }
 
        /**
         * Constructor.
         *
-        * @param input The POJO to serialize.  Can also be a {@link Reader} or 
{@link InputStream}.
+        * @param content The POJO to serialize.  Can also be a {@link Reader} 
or {@link InputStream}.
         * @param serializer The serializer to use to serialize this response.
         * @param schema The optional schema information about the serialized 
part.
         * @param contentType Override the content type defined on the 
serializer.
         */
-       public SerializedHttpEntity(Object input, Serializer serializer, 
HttpPartSchema schema, String contentType) {
-               this.output = input;
+       public SerializedHttpEntity(Object content, Serializer serializer, 
HttpPartSchema schema, String contentType) {
+               content(content);
                this.serializer = serializer;
                this.schema = schema;
                if (serializer != null && serializer.getResponseContentType() 
!= null)
                        setContentType(new BasicHeader("Content-Type", 
contentType != null ? contentType : 
serializer.getResponseContentType().toString()));
        }
 
+       /**
+        * Sets the serializer to use to serialize the content.
+        *
+        * <p>
+        * Value is ignored if the content is a stream or reader.
+        *
+        * @param value The serializer.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public SerializedHttpEntity serializer(Serializer value) {
+               this.serializer = value;
+               return this;
+       }
+
+       /**
+        * Sets the schema to use to serialize the content.
+        *
+        * <p>
+        * Value is ignored if the serializer is not schema-aware.
+        *
+        * @param value The schema.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public SerializedHttpEntity schema(HttpPartSchema value) {
+               this.schema = value;
+               return this;
+       }
+
        @Override /* BasicHttpEntity */
        public void writeTo(OutputStream os) throws IOException {
                os = new NoCloseOutputStream(os);
-               if (output instanceof InputStream) {
-                       IOPipe.create(output, os).run();
-               } else if (output instanceof Reader) {
-                       try (OutputStreamWriter osw = new 
OutputStreamWriter(os, UTF8)) {
-                               IOPipe.create(output, osw).run();
-                       }
+               Object content = getRawContent();
+               if (content instanceof InputStream || content instanceof Reader 
|| content instanceof File) {
+                       IOPipe.create(content, os).run();
                } else {
                        try {
                                if (serializer == null) {
@@ -66,7 +101,7 @@ public class SerializedHttpEntity extends BasicHttpEntity {
                                        SerializerSessionArgs sArgs = 
SerializerSessionArgs.create().schema(schema);
                                        SerializerSession session = 
serializer.createSession(sArgs);
                                        try (Closeable c = 
session.isWriterSerializer() ? new OutputStreamWriter(os, UTF8) : os) {
-                                               session.serialize(output, c);
+                                               session.serialize(content, c);
                                        }
                                }
                        } catch (SerializeException e) {
@@ -77,19 +112,25 @@ public class SerializedHttpEntity extends BasicHttpEntity {
 
        @Override /* BasicHttpEntity */
        public boolean isRepeatable() {
-               return true;
+               Object content = getRawContent();
+               return (! (content instanceof InputStream || content instanceof 
Reader));
+       }
+
+       @Override
+       public long getContentLength() {
+               return -1;
        }
 
        @Override /* BasicHttpEntity */
        public InputStream getContent() {
-               if (outputBytes == null) {
+               if (cache == null) {
                        try (ByteArrayOutputStream baos = new 
ByteArrayOutputStream()) {
                                writeTo(baos);
-                               outputBytes = baos.toByteArray();
+                               cache = baos.toByteArray();
                        } catch (IOException e) {
                                throw new RuntimeException(e);
                        }
                }
-               return new ByteArrayInputStream(outputBytes);
+               return new ByteArrayInputStream(cache);
        }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedNameValuePair.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedNameValuePair.java
index 4d366d2..bc3c9e7 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedNameValuePair.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/SerializedNameValuePair.java
@@ -141,6 +141,25 @@ public class SerializedNameValuePair extends 
BasicNameValuePair implements Heade
                return this;
        }
 
+       /**
+        * Don't serialize this pair if the value is <jk>null</jk> or an empty 
string.
+        *
+        * @return This object (for method chaining).
+        */
+       public SerializedNameValuePair skipIfEmpty() {
+               return skipIfEmpty(true);
+       }
+
+       /**
+        * Don't serialize this pair if the value is <jk>null</jk> or an empty 
string.
+        *
+        * @param value The new value of this setting.
+        * @return This object (for method chaining).
+        */
+       public SerializedNameValuePair skipIfEmpty(boolean value) {
+               this.skipIfEmpty = value;
+               return this;
+       }
 
        @Override /* Headerable */
        public SerializedHeader asHeader() {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/StreamResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/StreamResource.java
index 1dfad8f..4cff804 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/StreamResource.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/StreamResource.java
@@ -14,6 +14,7 @@ package org.apache.juneau.http;
 
 import java.io.*;
 import java.util.*;
+import java.util.function.*;
 
 import org.apache.http.Header;
 import org.apache.juneau.assertions.*;
@@ -41,7 +42,7 @@ public class StreamResource extends BasicHttpResource {
        /**
         * Creator.
         *
-        * @return A new empty {@link ReaderResource} object.
+        * @return A new empty {@link StreamResource} object.
         */
        public static StreamResource create() {
                return new StreamResource();
@@ -55,17 +56,41 @@ public class StreamResource extends BasicHttpResource {
         *      <br>Can be any of the following:
         *      <ul>
         *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
         *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
-        * @return A new empty {@link ReaderResource} object.
+        * @return A new empty {@link StreamResource} object.
         */
        public static StreamResource of(Object content) {
                return new StreamResource().content(content);
        }
 
        /**
+        * Creator.
+        *
+        * @param content
+        *      The content.
+        *      <br>Can be any of the following:
+        *      <ul>
+        *              <li><c>InputStream</c>
+        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
+        *              <li><c>File</c>
+        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
+        *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
+        *      </ul>
+        * </ul>
+        * @return A new empty {@link StreamResource} object.
+        */
+       public static StreamResource of(Supplier<?> content) {
+               return new StreamResource().content(content);
+       }
+
+       /**
         * Constructor.
         */
        public StreamResource() {
@@ -90,6 +115,7 @@ public class StreamResource extends BasicHttpResource {
         *              <li><c>File</c>
         *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
         *              <li><c><jk>byte</jk>[]</c>.
+        *              <li>A {@link Supplier} of anything on this list.
         *      </ul>
         * </ul>
         */
@@ -119,43 +145,67 @@ public class StreamResource extends BasicHttpResource {
 
        // <FluentSetters>
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource cache() {
                super.cache();
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public StreamResource cache(boolean value) {
+               super.cache(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource chunked() {
                super.chunked();
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public StreamResource chunked(boolean value) {
+               super.chunked(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource content(Object value) {
                super.content(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public StreamResource content(Supplier<?> value) {
+               super.content(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource contentEncoding(String value) {
                super.contentEncoding(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource contentEncoding(Header value) {
                super.contentEncoding(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
+       public StreamResource contentLength(long value) {
+               super.contentLength(value);
+               return this;
+       }
+
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource contentType(String value) {
                super.contentType(value);
                return this;
        }
 
-       @Override /* GENERATED - BasicHttpResource */
+       @Override /* GENERATED - BasicHttpEntity */
        public StreamResource contentType(Header value) {
                super.contentType(value);
                return this;
diff --git 
a/juneau-releng/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java 
b/juneau-releng/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java
index 7502f62..4e33cf0 100644
--- 
a/juneau-releng/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java
+++ 
b/juneau-releng/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java
@@ -186,6 +186,7 @@ public class ConfigurablePropertyCodeGenerator {
                CollectionAssertion.class,
                ListAssertion.class,
 
+               BasicHttpEntity.class,
                BasicHttpResource.class,
                StreamResource.class,
                ReaderResource.class,
diff --git 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
index 9d0bf5a..d02d777 100644
--- 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
+++ 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
@@ -87,6 +87,12 @@ public class BasicHttpResource_Test {
                assertTrue(x.isRepeatable());
                x.writeTo(new ByteArrayOutputStream());
 
+               x = of(f).cache();
+               assertStream(x.getContent()).string().isEmpty();
+               assertStream(x.getContent()).string().isEmpty();
+               assertTrue(x.isRepeatable());
+               x.writeTo(new ByteArrayOutputStream());
+
                assertLong(of("foo").getContentLength()).is(3l);
                assertLong(of("foo".getBytes()).getContentLength()).is(3l);
                assertLong(of(f).getContentLength()).is(0l);
@@ -99,5 +105,15 @@ public class BasicHttpResource_Test {
                assertObject(x.getFirstHeader("Bar")).doesNotExist();
                assertObject(x.getLastHeader("Bar")).doesNotExist();
                assertObject(x.getHeaders()).json().is("['Foo: bar','Foo: 
baz']");
+
+               BasicHttpResource x2 = new BasicHttpResource() {
+                       @Override
+                       protected byte[] readBytes(Object o) throws IOException 
{
+                               throw new IOException("bad");
+                       }
+               };
+               x2.cache().content(new StringReader("foo"));
+               assertLong(x2.getContentLength()).is(-1l);
+               assertThrown(()->x2.writeTo(new 
ByteArrayOutputStream())).contains("bad");
        }
 }
diff --git 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_FormData_Test.java
 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_FormData_Test.java
index 31aae74..bf67232 100644
--- 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_FormData_Test.java
+++ 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_FormData_Test.java
@@ -27,10 +27,10 @@ import org.apache.juneau.httppart.*;
 import org.apache.juneau.marshall.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.rest.client2.RestClient_Test.*;
 import org.apache.juneau.rest.mock2.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.testutils.*;
+import org.apache.juneau.testutils.pojos.ABean;
 import org.apache.juneau.uon.*;
 import org.junit.*;
 
@@ -89,7 +89,7 @@ public class RestClient_FormData_Test {
                
client().build().post("/formData").formDatas(AList.of(pair("foo","bar"),pair("foo","baz"))).run().assertBody().is("foo=bar&foo=baz");
                client().build().post("/formData").formDatas((Object)new 
NameValuePair[]{pair("foo","bar")}).run().assertBody().is("foo=bar");
 
-               
client().build().post("/formData").formDatas(ABean.get()).run().assertBody().is("f=1");
+               
client().build().post("/formData").formDatas(ABean.get()).run().assertBody().is("a=1&b=foo");
 
                
client().formDatas(pair("foo","bar"),null).build().post("/formData").run().assertBody().is("foo=bar");
                
client().build().post("/formData").formDatas(pair("foo","bar"),null).run().assertBody().is("foo=bar");
@@ -102,6 +102,9 @@ public class RestClient_FormData_Test {
                
client().build().post("/formData").formDatas(pair(null,null)).run().assertBody().is("");
 
                
client().formDatas(SerializedHeader.of("foo","bar")).build().post("/formData").run().assertBody().is("foo=bar");
+               
client().formDatas(SerializedNameValuePair.of("foo","bar").schema(null)).build().post("/formData").run().assertBody().is("foo=bar");
+               
client().formDatas(SerializedNameValuePair.of("foo",null).schema(null)).build().post("/formData").run().assertBody().is("");
+               
client().formDatas(SerializedNameValuePair.of("foo",null).skipIfEmpty().schema(HttpPartSchema.create()._default("bar").build())).build().post("/formData").run().assertBody().is("foo=bar");
 
                
assertThrown(()->client().build().post("/formData").formDatas("bad")).is("Invalid
 type passed to formDatas(): java.lang.String");
                
assertThrown(()->client().formDatas(pair("foo","bar"),"baz")).is("Invalid type 
passed to formData():  java.lang.String");
@@ -197,6 +200,23 @@ public class RestClient_FormData_Test {
                
x2.post("/formData").formData("foo",s,T_ARRAY_PIPES).run().assertBody().urlDecode().is("foo=bar|baz");
        }
 
+       public static class A12 implements HttpPartSerializer {
+               @Override
+               public HttpPartSerializerSession 
createPartSession(SerializerSessionArgs args) {
+                       return new HttpPartSerializerSession() {
+                               @Override
+                               public String serialize(HttpPartType type, 
HttpPartSchema schema, Object value) throws SerializeException, 
SchemaValidationException {
+                                       throw new SerializeException("bad");
+                               }
+                       };
+               }
+       }
+
+       @Test
+       public void a12_formData_BadSerialization() throws Exception {
+               
assertThrown(()->client().formData(SerializedNameValuePair.of("Foo","bar").serializer(new
 A12())).build().get()).contains("bad");
+       }
+
        
//------------------------------------------------------------------------------------------------------------------
        // Helper methods.
        
//------------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_Headers_Test.java
 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_Headers_Test.java
index c3d4131..20d5921 100644
--- 
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_Headers_Test.java
+++ 
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClient_Headers_Test.java
@@ -120,6 +120,8 @@ public class RestClient_Headers_Test {
                
checkClient("f").build().get("/headers").headers((Object)null).debug().run().assertBody().is("null");
                assertThrown(()->client().headers("Foo")).contains("Invalid 
type");
                
assertThrown(()->client().build().get("").headers("Foo")).contains("Invalid 
type");
+
+               
checkFooClient().headers(SerializedHeader.of("Foo",null).skipIfEmpty().schema(HttpPartSchema.create()._default("bar").build())).build().get("/headers").run().assertBody().is("['bar']");
        }
 
        @Test
@@ -185,6 +187,23 @@ public class RestClient_Headers_Test {
                
checkFooClient().header("Foo",s,T_ARRAY_PIPES,UonSerializer.DEFAULT).build().get("/headers").run().assertBody().is("['@(foo,bar)']");
        }
 
+       public static class A12 implements HttpPartSerializer {
+               @Override
+               public HttpPartSerializerSession 
createPartSession(SerializerSessionArgs args) {
+                       return new HttpPartSerializerSession() {
+                               @Override
+                               public String serialize(HttpPartType type, 
HttpPartSchema schema, Object value) throws SerializeException, 
SchemaValidationException {
+                                       throw new SerializeException("bad");
+                               }
+                       };
+               }
+       }
+
+       @Test
+       public void a12_headers_BadSerialization() throws Exception {
+               
assertThrown(()->checkFooClient().header(SerializedHeader.of("Foo","bar").serializer(new
 A12())).build().get()).contains("bad");
+       }
+
        
//------------------------------------------------------------------------------------------------------------------
        // Other tests
        
//------------------------------------------------------------------------------------------------------------------

Reply via email to