You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2019/11/02 15:56:43 UTC
[juneau] branch master updated: JUNEAU-161 Exeption-Message header
can contain CR/LF characters.
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 1bfcd29 JUNEAU-161 Exeption-Message header can contain CR/LF characters.
1bfcd29 is described below
commit 1bfcd2924318f21bff7ad4b0ec975b0259014b33
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sat Nov 2 11:56:28 2019 -0400
JUNEAU-161 Exeption-Message header can contain CR/LF characters.
---
.../org/apache/juneau/utils/StringUtilsTest.java | 9 ++++++
.../main/java/org/apache/juneau/ObjectList.java | 2 +-
.../src/main/java/org/apache/juneau/ObjectMap.java | 2 +-
.../org/apache/juneau/internal/StringUtils.java | 35 ++++++++++++++++++++++
juneau-doc/docs/ReleaseNotes/8.1.2.html | 8 +++--
.../apache/juneau/rest/BasicRestCallHandler.java | 4 +--
.../java/org/apache/juneau/rest/RestResponse.java | 26 ++++++++++++++--
.../juneau/rest/reshandlers/DefaultHandler.java | 6 ++--
8 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
index 1f8bd5f..427a33e 100755
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
@@ -985,4 +985,13 @@ public class StringUtilsTest {
assertEquals(10*w, getDuration("10 W"));
assertEquals(10*w, getDuration(" 10 W "));
}
+
+ //====================================================================================================
+ // getDuration(String)
+ //====================================================================================================
+ @Test
+ public void testStripInvalidHttpHeaderChars() throws Exception {
+ assertEquals("xxx", stripInvalidHttpHeaderChars("xxx"));
+ assertEquals("\t []^x", stripInvalidHttpHeaderChars("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u0020\\[]^x"));
+ }
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
index 8d64b17..21c8379 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
@@ -141,7 +141,7 @@ public class ObjectList extends LinkedList<Object> {
* @return A new {@link ObjectList} object, or <jk>null</jk> if the input is <jk>null</jk>.
* @throws ParseException Invalid JSON string.
*/
- public static ObjectList create(CharSequence s) throws ParseException {
+ public static ObjectList parse(CharSequence s) throws ParseException {
return s == null ? null : new ObjectList(s);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
index d56a6cf..f0e7145 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
@@ -162,7 +162,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
* @return A new {@link ObjectMap} object, or <jk>null</jk> if the input is <jk>null</jk>.
* @throws ParseException Invalid JSON string.
*/
- public static ObjectMap create(CharSequence s) throws ParseException {
+ public static ObjectMap parse(CharSequence s) throws ParseException {
return s == null ? null : new ObjectMap(s);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 0b5baa8..9735bd7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -57,6 +57,13 @@ public final class StringUtils {
.chars("{}|\\^[]`") // unwise characters.
.build();
+ // Valid HTTP header characters (including quoted strings and comments).
+ private static final AsciiSet httpHeaderChars = AsciiSet
+ .create()
+ .chars("\t -")
+ .ranges("!-[","]-}")
+ .build();
+
// Maps BASE64 characters to 6-bit nibbles.
private static final byte[] base64m2 = new byte[128];
static {
@@ -2681,4 +2688,32 @@ public final class StringUtils {
return new String(sArray);
}
+ /**
+ * Strips invalid characters such as CTRL characters from a string meant to be encoded
+ * as an HTTP header value.
+ *
+ * @param s The string to strip chars from.
+ * @return The string with invalid characters removed.
+ */
+ public static String stripInvalidHttpHeaderChars(String s) {
+ if (s == null)
+ return null;
+
+ boolean needsReplace = false;
+ for (int i = 0; i < s.length() && ! needsReplace; i++)
+ needsReplace |= httpHeaderChars.contains(s.charAt(i));
+
+ if (! needsReplace)
+ return s;
+
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (httpHeaderChars.contains(c))
+ sb.append(c);
+ }
+
+ return sb.toString();
+ }
+
}
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.2.html b/juneau-doc/docs/ReleaseNotes/8.1.2.html
index 8f1f16c..76c23a8 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.2.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.2.html
@@ -24,8 +24,8 @@
<li>
New convenience methods:
<ul>
- <li class='jm'>{@link oaj.ObjectMap.create(String)}
- <li class='jm'>{@link oaj.ObjectList.create(String)}
+ <li class='jm'>{@link oaj.ObjectMap.parse(String)}
+ <li class='jm'>{@link oaj.ObjectList.parse(String)}
</ul>
<li>
{@link oaj.marshall.CharMarshall} and {@link oaj.marshall.StreamMarshall} now have public constructors.
@@ -33,6 +33,10 @@
<h5 class='topic w800'>juneau-rest-server</h5>
<ul class='spaced-list'>
+ <li>
+ New method {@link oajr.RestResponse.setHeaderSafe(String,String)} to strip invalid characters from header values.
+ <li>
+ Fixed issues related to invalid characters being set on HTTP header values.
</ul>
<h5 class='topic w800'>juneau-rest-client</h5>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 4b287e3..d0dacf1 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -337,8 +337,8 @@ public class BasicRestCallHandler implements RestCallHandler {
if (t == null)
t = e2.getRootCause();
if (t != null) {
- res.setHeader("Exception-Name", t.getClass().getName());
- res.setHeader("Exception-Message", t.getMessage());
+ res.setHeader("Exception-Name", stripInvalidHttpHeaderChars(t.getClass().getName()));
+ res.setHeader("Exception-Message", stripInvalidHttpHeaderChars(t.getMessage()));
}
try {
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 f44d3ce..e9940e7 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
@@ -27,6 +27,7 @@ import org.apache.juneau.html.annotation.*;
import org.apache.juneau.http.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.httppart.bean.*;
+import org.apache.juneau.internal.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.http.exception.*;
import org.apache.juneau.rest.util.*;
@@ -81,7 +82,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
this.request = req;
for (Map.Entry<String,Object> e : context.getDefaultResponseHeaders().entrySet())
- setHeader(e.getKey(), stringify(e.getValue()));
+ setHeaderSafe(e.getKey(), stringify(e.getValue()));
try {
String passThroughHeaders = req.getHeader("x-response-headers");
@@ -89,7 +90,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
HttpPartParser p = context.getPartParser();
ObjectMap m = p.createPartSession(req.getParserSessionArgs()).parse(HttpPartType.HEADER, null, passThroughHeaders, context.getClassMeta(ObjectMap.class));
for (Map.Entry<String,Object> e : m.entrySet())
- setHeader(e.getKey(), e.getValue().toString());
+ setHeaderSafe(e.getKey(), e.getValue().toString());
}
} catch (Exception e1) {
throw new BadRequest(e1, "Invalid format for header 'x-response-headers'. Must be in URL-encoded format.");
@@ -536,6 +537,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
@Override /* ServletResponse */
public void setHeader(String name, String value) {
+
// Jetty doesn't set the content type correctly if set through this method.
// Tomcat/WAS does.
if (name.equalsIgnoreCase("Content-Type"))
@@ -545,6 +547,24 @@ public final class RestResponse extends HttpServletResponseWrapper {
}
/**
+ * Same as {@link #setHeader(String, String)} but strips invalid characters from the value if present.
+ *
+ * These include CTRL characters, newlines, and non-ISO8859-1 characters.
+ *
+ * @param name Header name.
+ * @param value Header value.
+ */
+ public void setHeaderSafe(String name, String value) {
+
+ // Jetty doesn't set the content type correctly if set through this method.
+ // Tomcat/WAS does.
+ if (name.equalsIgnoreCase("Content-Type"))
+ super.setContentType(value);
+ else
+ super.setHeader(name, StringUtils.stripInvalidHttpHeaderChars(value));
+ }
+
+ /**
* Same as {@link #setHeader(String, String)} but header is defined as a response part
*
* @param h Header to set.
@@ -552,7 +572,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
* @throws SerializeException Header part could not be serialized.
*/
public void setHeader(HttpPart h) throws SchemaValidationException, SerializeException {
- setHeader(h.getName(), h.asString());
+ setHeaderSafe(h.getName(), h.asString());
}
/**
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
index e0f9a71..d0f6690 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
@@ -67,8 +67,8 @@ public class DefaultHandler implements ResponseHandler {
boolean isThrowable = rm.getClassMeta().isType(Throwable.class);
if (isThrowable) {
- res.setHeader("Exception-Name", rm.getClassMeta().getName());
- res.setHeader("Exception-Message", ((Throwable)o).getMessage());
+ res.setHeaderSafe("Exception-Name", rm.getClassMeta().getName());
+ res.setHeaderSafe("Exception-Message", ((Throwable)o).getMessage());
}
ResponseBeanPropertyMeta stm = rm.getStatusMethod();
@@ -157,7 +157,7 @@ public class DefaultHandler implements ResponseHandler {
);
for (Map.Entry<String,String> h : session.getResponseHeaders().entrySet())
- res.setHeader(h.getKey(), h.getValue());
+ res.setHeaderSafe(h.getKey(), h.getValue());
if (! session.isWriterSerializer()) {
if (req.isPlainText()) {