Skip to content

Commit ec2a865

Browse files
committed
Fix xml output of webxml
Missing entries, formatting, correct URI encoding
1 parent 045f436 commit ec2a865

3 files changed

Lines changed: 297 additions & 38 deletions

File tree

java/org/apache/tomcat/util/descriptor/web/WebXml.java

Lines changed: 119 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616
*/
1717
package org.apache.tomcat.util.descriptor.web;
1818

19+
import java.io.IOException;
1920
import java.io.UnsupportedEncodingException;
2021
import java.net.URL;
21-
import java.net.URLEncoder;
22-
import java.nio.charset.StandardCharsets;
2322
import java.util.ArrayList;
2423
import java.util.Collection;
2524
import java.util.EnumSet;
@@ -29,6 +28,7 @@
2928
import java.util.LinkedHashMap;
3029
import java.util.LinkedHashSet;
3130
import java.util.List;
31+
import java.util.Locale;
3232
import java.util.Map;
3333
import java.util.Map.Entry;
3434
import java.util.Set;
@@ -45,6 +45,8 @@
4545
import org.apache.juli.logging.LogFactory;
4646
import org.apache.tomcat.util.buf.B2CConverter;
4747
import org.apache.tomcat.util.buf.UDecoder;
48+
import org.apache.tomcat.util.buf.UEncoder;
49+
import org.apache.tomcat.util.buf.UEncoder.SafeCharsSet;
4850
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
4951
import org.apache.tomcat.util.digester.DocumentProperties;
5052
import org.apache.tomcat.util.res.StringManager;
@@ -73,6 +75,8 @@ public WebXml() {
7375

7476
private final Log log = LogFactory.getLog(WebXml.class); // must not be static
7577

78+
private final UEncoder urlEncoder = new UEncoder(SafeCharsSet.WITH_SLASH);
79+
7680
/**
7781
* Global defaults are overridable but Servlets and Servlet mappings need to be unique. Duplicates normally trigger
7882
* an error. This flag indicates if newly added Servlet elements are marked as overridable.
@@ -1352,6 +1356,7 @@ public String toString() {
13521356
private static final String INDENT2 = " ";
13531357
private static final String INDENT4 = " ";
13541358
private static final String INDENT6 = " ";
1359+
private static final String INDENT8 = " ";
13551360

13561361
/**
13571362
* Generate a web.xml in String form that matches the representation stored in this object.
@@ -1452,7 +1457,9 @@ public String toXml() {
14521457
appendElement(sb, INDENT4, "param-value", entry.getValue());
14531458
sb.append(" </context-param>\n");
14541459
}
1455-
sb.append('\n');
1460+
if (!contextParams.isEmpty()) {
1461+
sb.append('\n');
1462+
}
14561463

14571464
// Filters were introduced in Servlet 2.3
14581465
if (getMajorVersion() > 2 || getMinorVersion() > 2) {
@@ -1475,7 +1482,9 @@ public String toXml() {
14751482
}
14761483
sb.append(" </filter>\n");
14771484
}
1478-
sb.append('\n');
1485+
if (!filters.isEmpty()) {
1486+
sb.append('\n');
1487+
}
14791488

14801489
for (FilterMap filterMap : filterMaps) {
14811490
sb.append(" <filter-mapping>\n");
@@ -1505,7 +1514,9 @@ public String toXml() {
15051514
}
15061515
sb.append(" </filter-mapping>\n");
15071516
}
1508-
sb.append('\n');
1517+
if (!filterMaps.isEmpty()) {
1518+
sb.append('\n');
1519+
}
15091520
}
15101521

15111522
// Listeners were introduced in Servlet 2.3
@@ -1515,7 +1526,9 @@ public String toXml() {
15151526
appendElement(sb, INDENT4, "listener-class", listener);
15161527
sb.append(" </listener>\n");
15171528
}
1518-
sb.append('\n');
1529+
if (!listeners.isEmpty()) {
1530+
sb.append('\n');
1531+
}
15191532
}
15201533

15211534
for (Map.Entry<String,ServletDef> entry : servlets.entrySet()) {
@@ -1566,29 +1579,54 @@ public String toXml() {
15661579
}
15671580
sb.append(" </servlet>\n");
15681581
}
1569-
sb.append('\n');
1582+
if (!servlets.isEmpty()) {
1583+
sb.append('\n');
1584+
}
15701585

15711586
for (Map.Entry<String,String> entry : servletMappings.entrySet()) {
15721587
sb.append(" <servlet-mapping>\n");
15731588
appendElement(sb, INDENT4, "servlet-name", entry.getValue());
15741589
appendElement(sb, INDENT4, "url-pattern", encodeUrl(entry.getKey()));
15751590
sb.append(" </servlet-mapping>\n");
15761591
}
1577-
sb.append('\n');
1592+
if (!servletMappings.isEmpty()) {
1593+
sb.append('\n');
1594+
}
15781595

1579-
if (sessionConfig != null) {
1596+
if (sessionConfig.getSessionTimeout() != null || sessionConfig.getCookieName() != null ||
1597+
!sessionConfig.getCookieAttributes().isEmpty() || !sessionConfig.getSessionTrackingModes().isEmpty()) {
15801598
sb.append(" <session-config>\n");
15811599
appendElement(sb, INDENT4, "session-timeout", sessionConfig.getSessionTimeout());
15821600
if (majorVersion >= 3) {
1583-
sb.append(" <cookie-config>\n");
1584-
appendElement(sb, INDENT6, "name", sessionConfig.getCookieName());
1585-
appendElement(sb, INDENT6, "domain", sessionConfig.getCookieDomain());
1586-
appendElement(sb, INDENT6, "path", sessionConfig.getCookiePath());
1587-
appendElement(sb, INDENT6, "comment", sessionConfig.getCookieComment());
1588-
appendElement(sb, INDENT6, "http-only", sessionConfig.getCookieHttpOnly());
1589-
appendElement(sb, INDENT6, "secure", sessionConfig.getCookieSecure());
1590-
appendElement(sb, INDENT6, "max-age", sessionConfig.getCookieMaxAge());
1591-
sb.append(" </cookie-config>\n");
1601+
if (sessionConfig.getCookieName() != null || !sessionConfig.getCookieAttributes().isEmpty()) {
1602+
sb.append(" <cookie-config>\n");
1603+
appendElement(sb, INDENT6, "name", sessionConfig.getCookieName());
1604+
appendElement(sb, INDENT6, "domain", sessionConfig.getCookieDomain());
1605+
appendElement(sb, INDENT6, "path", sessionConfig.getCookiePath());
1606+
appendElement(sb, INDENT6, "comment", sessionConfig.getCookieComment());
1607+
appendElement(sb, INDENT6, "http-only", sessionConfig.getCookieHttpOnly());
1608+
appendElement(sb, INDENT6, "secure", sessionConfig.getCookieSecure());
1609+
appendElement(sb, INDENT6, "max-age", sessionConfig.getCookieMaxAge());
1610+
for (Map.Entry<String,String> entry : sessionConfig.getCookieAttributes().entrySet()) {
1611+
switch (entry.getKey().toLowerCase(Locale.ROOT)) {
1612+
case "domain":
1613+
case "path":
1614+
case "comment":
1615+
case "http-only":
1616+
case "secure":
1617+
case "max-age":
1618+
// NO-OP - handled above
1619+
break;
1620+
default: {
1621+
sb.append(" <attribute>\n");
1622+
appendElement(sb, INDENT8, "attribute-name", entry.getKey());
1623+
appendElement(sb, INDENT8, "attribute-value", entry.getValue());
1624+
sb.append(" </attribute>\n");
1625+
}
1626+
}
1627+
}
1628+
sb.append(" </cookie-config>\n");
1629+
}
15921630
for (SessionTrackingMode stm : sessionConfig.getSessionTrackingModes()) {
15931631
appendElement(sb, INDENT4, "tracking-mode", stm.name());
15941632
}
@@ -1602,7 +1640,9 @@ public String toXml() {
16021640
appendElement(sb, INDENT4, "mime-type", entry.getValue());
16031641
sb.append(" </mime-mapping>\n");
16041642
}
1605-
sb.append('\n');
1643+
if (!mimeMappings.isEmpty()) {
1644+
sb.append('\n');
1645+
}
16061646

16071647
if (!welcomeFiles.isEmpty()) {
16081648
sb.append(" <welcome-file-list>\n");
@@ -1626,10 +1666,12 @@ public String toXml() {
16261666
} else if (errorPage.getErrorCode() > 0) {
16271667
appendElement(sb, INDENT4, "error-code", Integer.toString(errorCode));
16281668
}
1629-
appendElement(sb, INDENT4, "location", errorPage.getLocation());
1669+
appendElement(sb, INDENT4, "location", errorPage.getLocation(), true);
16301670
sb.append(" </error-page>\n");
16311671
}
1632-
sb.append('\n');
1672+
if (!errorPages.isEmpty()) {
1673+
sb.append('\n');
1674+
}
16331675

16341676
// jsp-config was added in Servlet 2.4. Prior to that, tag-libs was used
16351677
// directly and jsp-property-group did not exist
@@ -1687,7 +1729,9 @@ public String toXml() {
16871729
appendElement(sb, INDENT4, "lookup-name", resourceEnvRef.getLookupName());
16881730
sb.append(" </resource-env-ref>\n");
16891731
}
1690-
sb.append('\n');
1732+
if (!resourceEnvRefs.isEmpty()) {
1733+
sb.append('\n');
1734+
}
16911735
}
16921736

16931737
for (ContextResource resourceRef : resourceRefs.values()) {
@@ -1710,7 +1754,9 @@ public String toXml() {
17101754
appendElement(sb, INDENT4, "lookup-name", resourceRef.getLookupName());
17111755
sb.append(" </resource-ref>\n");
17121756
}
1713-
sb.append('\n');
1757+
if (!resourceRefs.isEmpty()) {
1758+
sb.append('\n');
1759+
}
17141760

17151761
for (SecurityConstraint constraint : securityConstraints) {
17161762
sb.append(" <security-constraint>\n");
@@ -1733,12 +1779,20 @@ public String toXml() {
17331779
}
17341780
sb.append(" </web-resource-collection>\n");
17351781
}
1736-
if (constraint.findAuthRoles().length > 0) {
1782+
if (constraint.findAuthRoles().length > 0 || constraint.getAllRoles() || constraint.getAuthenticatedUsers()) {
17371783
sb.append(" <auth-constraint>\n");
17381784
for (String role : constraint.findAuthRoles()) {
17391785
appendElement(sb, INDENT6, "role-name", role);
17401786
}
1787+
if (constraint.getAllRoles()) {
1788+
appendElement(sb, INDENT6, "role-name", SecurityConstraint.ROLE_ALL_ROLES);
1789+
}
1790+
if (constraint.getAuthenticatedUsers()) {
1791+
appendElement(sb, INDENT6, "role-name", SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
1792+
}
17411793
sb.append(" </auth-constraint>\n");
1794+
} else if (constraint.getAuthConstraint()) {
1795+
sb.append(" <auth-constraint/>\n");
17421796
}
17431797
if (constraint.getUserConstraint() != null) {
17441798
sb.append(" <user-data-constraint>\n");
@@ -1747,16 +1801,18 @@ public String toXml() {
17471801
}
17481802
sb.append(" </security-constraint>\n");
17491803
}
1750-
sb.append('\n');
1804+
if (!securityConstraints.isEmpty()) {
1805+
sb.append('\n');
1806+
}
17511807

17521808
if (loginConfig != null) {
17531809
sb.append(" <login-config>\n");
17541810
appendElement(sb, INDENT4, "auth-method", loginConfig.getAuthMethod());
17551811
appendElement(sb, INDENT4, "realm-name", loginConfig.getRealmName());
17561812
if (loginConfig.getErrorPage() != null || loginConfig.getLoginPage() != null) {
17571813
sb.append(" <form-login-config>\n");
1758-
appendElement(sb, INDENT6, "form-login-page", loginConfig.getLoginPage());
1759-
appendElement(sb, INDENT6, "form-error-page", loginConfig.getErrorPage());
1814+
appendElement(sb, INDENT6, "form-login-page", loginConfig.getLoginPage(), true);
1815+
appendElement(sb, INDENT6, "form-error-page", loginConfig.getErrorPage(), true);
17601816
sb.append(" </form-login-config>\n");
17611817
}
17621818
sb.append(" </login-config>\n\n");
@@ -1784,7 +1840,9 @@ public String toXml() {
17841840
appendElement(sb, INDENT4, "lookup-name", envEntry.getLookupName());
17851841
sb.append(" </env-entry>\n");
17861842
}
1787-
sb.append('\n');
1843+
if (!envEntries.isEmpty()) {
1844+
sb.append('\n');
1845+
}
17881846

17891847
for (ContextEjb ejbRef : ejbRefs.values()) {
17901848
sb.append(" <ejb-ref>\n");
@@ -1804,7 +1862,9 @@ public String toXml() {
18041862
appendElement(sb, INDENT4, "lookup-name", ejbRef.getLookupName());
18051863
sb.append(" </ejb-ref>\n");
18061864
}
1807-
sb.append('\n');
1865+
if (!ejbRefs.isEmpty()) {
1866+
sb.append('\n');
1867+
}
18081868

18091869
// ejb-local-ref was introduced in Servlet 2.3
18101870
if (getMajorVersion() > 2 || getMinorVersion() > 2) {
@@ -1826,7 +1886,9 @@ public String toXml() {
18261886
appendElement(sb, INDENT4, "lookup-name", ejbLocalRef.getLookupName());
18271887
sb.append(" </ejb-local-ref>\n");
18281888
}
1829-
sb.append('\n');
1889+
if (!ejbLocalRefs.isEmpty()) {
1890+
sb.append('\n');
1891+
}
18301892
}
18311893

18321894
// service-ref was introduced in Servlet 2.4
@@ -1874,7 +1936,9 @@ public String toXml() {
18741936
appendElement(sb, INDENT4, "lookup-name", serviceRef.getLookupName());
18751937
sb.append(" </service-ref>\n");
18761938
}
1877-
sb.append('\n');
1939+
if (!serviceRefs.isEmpty()) {
1940+
sb.append('\n');
1941+
}
18781942
}
18791943

18801944
if (!postConstructMethods.isEmpty()) {
@@ -1917,7 +1981,9 @@ public String toXml() {
19171981
appendElement(sb, INDENT4, "lookup-name", mdr.getLookupName());
19181982
sb.append(" </message-destination-ref>\n");
19191983
}
1920-
sb.append('\n');
1984+
if (!messageDestinationRefs.isEmpty()) {
1985+
sb.append('\n');
1986+
}
19211987

19221988
for (MessageDestination md : messageDestinations.values()) {
19231989
sb.append(" <message-destination>\n");
@@ -1928,7 +1994,9 @@ public String toXml() {
19281994
appendElement(sb, INDENT4, "lookup-name", md.getLookupName());
19291995
sb.append(" </message-destination>\n");
19301996
}
1931-
sb.append('\n');
1997+
if (!messageDestinations.isEmpty()) {
1998+
sb.append('\n');
1999+
}
19322000
}
19332001

19342002
// locale-encoding-mapping-list was introduced in Servlet 2.4
@@ -1964,12 +2032,21 @@ public String toXml() {
19642032
}
19652033

19662034

1967-
private String encodeUrl(String input) {
1968-
return URLEncoder.encode(input, StandardCharsets.UTF_8);
2035+
private synchronized String encodeUrl(String input) {
2036+
try {
2037+
return urlEncoder.encodeURL(input, 0, input.length()).toString();
2038+
} catch (IOException e) {
2039+
throw new IllegalArgumentException(input, e);
2040+
}
19692041
}
19702042

19712043

1972-
private static void appendElement(StringBuilder sb, String indent, String elementName, String value) {
2044+
private void appendElement(StringBuilder sb, String indent, String elementName, String value) {
2045+
appendElement(sb, indent, elementName, value, false);
2046+
}
2047+
2048+
private void appendElement(StringBuilder sb, String indent, String elementName, String value, boolean encodeValue) {
2049+
19732050
if (value == null) {
19742051
return;
19752052
}
@@ -1983,14 +2060,18 @@ private static void appendElement(StringBuilder sb, String indent, String elemen
19832060
sb.append('<');
19842061
sb.append(elementName);
19852062
sb.append('>');
1986-
sb.append(Escape.xml(value));
2063+
if (encodeValue) {
2064+
sb.append(Escape.xml(encodeUrl(value)));
2065+
} else {
2066+
sb.append(Escape.xml(value));
2067+
}
19872068
sb.append("</");
19882069
sb.append(elementName);
19892070
sb.append(">\n");
19902071
}
19912072
}
19922073

1993-
private static void appendElement(StringBuilder sb, String indent, String elementName, Object value) {
2074+
private void appendElement(StringBuilder sb, String indent, String elementName, Object value) {
19942075
if (value == null) {
19952076
return;
19962077
}

0 commit comments

Comments
 (0)