Browse Source

SPR-6296 Spring:url tag should not use application/x-www-form-urlencoded encoding

Work around for application/x-www-form-urlencoded encoding.  Replaces '+' from java.net.URLDecoder with charset specific encoded space value ('%20' for most charsets).

The custom urlEncode method should be replaced with calls to a true URL encoder once available.
pull/23217/head
Scott Andrews 15 years ago
parent
commit
67b5781dab
  1. 41
      org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/UrlTag.java
  2. 29
      org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java

41
org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/UrlTag.java

@ -16,6 +16,7 @@
package org.springframework.web.servlet.tags; package org.springframework.web.servlet.tags;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -23,6 +24,7 @@ import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
@ -65,7 +67,7 @@ import org.springframework.web.util.TagUtils;
* <spring:param name="variableName" value="more than JSTL c:url" /> * <spring:param name="variableName" value="more than JSTL c:url" />
* &lt;/spring:url&gt;</pre> * &lt;/spring:url&gt;</pre>
* Results in: * Results in:
* <code>/currentApplicationContext/url/path/more+than+JSTL+c%3Aurl</code> * <code>/currentApplicationContext/url/path/more%20than%20JSTL%20c%3Aurl</code>
* *
* @author Scott Andrews * @author Scott Andrews
* @since 3.0 * @since 3.0
@ -269,7 +271,6 @@ public class UrlTag extends HtmlEscapingAwareTag implements ParamAware {
*/ */
protected String replaceUriTemplateParams(String uri, List<Param> params, Set<String> usedParams) protected String replaceUriTemplateParams(String uri, List<Param> params, Set<String> usedParams)
throws JspException { throws JspException {
for (Param param : params) { for (Param param : params) {
String template = URL_TEMPLATE_DELIMITER_PREFIX + param.getName() + URL_TEMPLATE_DELIMITER_SUFFIX; String template = URL_TEMPLATE_DELIMITER_PREFIX + param.getName() + URL_TEMPLATE_DELIMITER_SUFFIX;
if (uri.contains(template)) { if (uri.contains(template)) {
@ -281,7 +282,11 @@ public class UrlTag extends HtmlEscapingAwareTag implements ParamAware {
} }
/** /**
* URL-encode the providedSstring using the character encoding for the response. * URL-encode the provided String using the character encoding for the response.
* <p>This method will <strong>not</strong> URL-encode according to the
* <code>application/x-www-form-urlencoded</code> MIME format. Spaces will
* encoded as regular character instead of <code>+</code>. In <code>UTF-8</code>
* a space encodes to <code>%20</code>.
* @param value the value to encode * @param value the value to encode
* @return the URL encoded value * @return the URL encoded value
* @throws JspException if the character encoding is invalid * @throws JspException if the character encoding is invalid
@ -291,13 +296,41 @@ public class UrlTag extends HtmlEscapingAwareTag implements ParamAware {
return null; return null;
} }
try { try {
return URLEncoder.encode(value, pageContext.getResponse().getCharacterEncoding()); String encoding = pageContext.getResponse().getCharacterEncoding();
String formUrlEncodedValue = URLEncoder.encode(value, encoding);
if (!formUrlEncodedValue.contains("+")) {
return formUrlEncodedValue;
}
String spaceEncoding = this.urlEncode(' ', encoding);
return formUrlEncodedValue.replace("+", spaceEncoding);
} }
catch (UnsupportedEncodingException ex) { catch (UnsupportedEncodingException ex) {
throw new JspException(ex); throw new JspException(ex);
} }
} }
/*
* based on URLCodec from Apache Commons Codec
*/
protected String urlEncode(Character c, String enc) throws UnsupportedEncodingException {
if (c == null) {
return null;
}
byte[] bytes = c.toString().getBytes(enc);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
int b = bytes[i];
if (b < 0) {
b = 256 + b;
}
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
builder.append('%');
builder.append(hex1);
builder.append(hex2);
}
return builder.toString();
}
/** /**
* Internal enum that classifies URLs by type. * Internal enum that classifies URLs by type.

29
org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java

@ -16,7 +16,9 @@
package org.springframework.web.servlet.tags; package org.springframework.web.servlet.tags;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -108,7 +110,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag(); tag.doEndTag();
assertEquals("url/path?n+me=v%26l%3De&name=value2", context assertEquals("url/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var")); .getAttribute("var"));
} }
@ -131,7 +133,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag(); tag.doEndTag();
assertEquals("url/path?n+me=v%26l%3De&name=value2", context assertEquals("url/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var")); .getAttribute("var"));
} }
@ -154,7 +156,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag(); tag.doEndTag();
assertEquals("url/path?n+me=v%26l%3De&amp;name=value2", context assertEquals("url/path?n%20me=v%26l%3De&amp;name=value2", context
.getAttribute("var")); .getAttribute("var"));
} }
@ -177,7 +179,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag(); tag.doEndTag();
assertEquals("url\\/path?n+me=v%26l%3De&name=value2", context assertEquals("url\\/path?n%20me=v%26l%3De&name=value2", context
.getAttribute("var")); .getAttribute("var"));
} }
@ -201,7 +203,7 @@ public class UrlTagTests extends AbstractTagTests {
tag.doEndTag(); tag.doEndTag();
assertEquals("url\\/path?n+me=v%26l%3De&amp;name=value2", context assertEquals("url\\/path?n%20me=v%26l%3De&amp;name=value2", context
.getAttribute("var")); .getAttribute("var"));
} }
@ -322,7 +324,7 @@ public class UrlTagTests extends AbstractTagTests {
String queryString = tag.createQueryString(params, usedParams, true); String queryString = tag.createQueryString(params, usedParams, true);
assertEquals("?n+me=v%26l%3De&name=value2", queryString); assertEquals("?n%20me=v%26l%3De&name=value2", queryString);
} }
public void testCreateQueryStringParamNullName() throws JspException { public void testCreateQueryStringParamNullName() throws JspException {
@ -425,7 +427,7 @@ public class UrlTagTests extends AbstractTagTests {
String uri = tag.replaceUriTemplateParams("url/{name}", params, String uri = tag.replaceUriTemplateParams("url/{name}", params,
usedParams); usedParams);
assertEquals("url/v+lue", uri); assertEquals("url/v%20lue", uri);
assertEquals(1, usedParams.size()); assertEquals(1, usedParams.size());
assertTrue(usedParams.contains("name")); assertTrue(usedParams.contains("name"));
} }
@ -509,7 +511,7 @@ public class UrlTagTests extends AbstractTagTests {
String uri = invokeCreateUrl(tag); String uri = invokeCreateUrl(tag);
assertEquals("url/path?name=value&n+me=v+lue", uri); assertEquals("url/path?name=value&n%20me=v%20lue", uri);
} }
public void testCreateUrlWithTemplateParams() throws JspException { public void testCreateUrlWithTemplateParams() throws JspException {
@ -529,7 +531,7 @@ public class UrlTagTests extends AbstractTagTests {
String uri = invokeCreateUrl(tag); String uri = invokeCreateUrl(tag);
assertEquals("url/value?n+me=v+lue", uri); assertEquals("url/value?n%20me=v%20lue", uri);
} }
public void testCreateUrlWithParamAndExsistingQueryString() public void testCreateUrlWithParamAndExsistingQueryString()
@ -549,7 +551,14 @@ public class UrlTagTests extends AbstractTagTests {
} }
public void testUrlEncode() throws JspException { public void testUrlEncode() throws JspException {
assertEquals("my+name", tag.urlEncode("my name")); assertEquals("my%20name%2Bis", tag.urlEncode("my name+is"));
}
public void testUrlEncode_character() throws UnsupportedEncodingException {
assertEquals("%20", tag.urlEncode(' ', "UTF-8"));
assertEquals(" ", URLDecoder.decode("%20", "UTF-8"));
assertEquals("%FE%FF%00%20", tag.urlEncode(' ', "UTF-16"));
assertEquals("%40", tag.urlEncode(' ', "IBM-Thai"));
} }
public void testUrlEncodeNull() throws JspException { public void testUrlEncodeNull() throws JspException {

Loading…
Cancel
Save