Skip to content

Commit a8f7f75

Browse files
committed
Moved encodeHttpHeaderFieldParam method to HttpHeaders itself (including tests)
This commit also sets the test source encoding to UTF-8. Issue: SPR-14547
1 parent bbd5993 commit a8f7f75

File tree

8 files changed

+96
-114
lines changed

8 files changed

+96
-114
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ configure(allprojects) { project ->
123123
}
124124
}
125125

126-
compileJava.options.encoding = 'UTF-8'
127-
128126
compileJava.options*.compilerArgs = [
129127
"-Xlint:serial", "-Xlint:varargs", "-Xlint:cast", "-Xlint:classfile",
130128
"-Xlint:dep-ann", "-Xlint:divzero", "-Xlint:empty", "-Xlint:finally",
@@ -143,11 +141,13 @@ configure(allprojects) { project ->
143141
compileJava {
144142
sourceCompatibility = 1.8
145143
targetCompatibility = 1.8
144+
options.encoding = 'UTF-8'
146145
}
147146

148147
compileTestJava {
149148
sourceCompatibility = 1.8
150149
targetCompatibility = 1.8
150+
options.encoding = 'UTF-8'
151151
options.compilerArgs += "-parameters"
152152
}
153153

spring-core/src/main/java/org/springframework/util/StringUtils.java

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.util;
1818

19-
import java.nio.charset.Charset;
20-
import java.nio.charset.StandardCharsets;
2119
import java.util.ArrayList;
2220
import java.util.Arrays;
2321
import java.util.Collection;
@@ -1196,44 +1194,4 @@ public static String arrayToCommaDelimitedString(Object[] arr) {
11961194
return arrayToDelimitedString(arr, ",");
11971195
}
11981196

1199-
/**
1200-
* Encode the given header field param as describe in the rfc5987.
1201-
* @param input the header field param
1202-
* @param charset the charset of the header field param string
1203-
* @return the encoded header field param
1204-
* @see <a href="https://tools.ietf.org/html/rfc5987">rfc5987</a>
1205-
* @since 5.0
1206-
*/
1207-
public static String encodeHttpHeaderFieldParam(String input, Charset charset) {
1208-
Assert.notNull(charset, "charset should not be null");
1209-
if(StandardCharsets.US_ASCII.equals(charset)) {
1210-
return input;
1211-
}
1212-
Assert.isTrue(StandardCharsets.UTF_8.equals(charset) || StandardCharsets.ISO_8859_1.equals(charset),
1213-
"charset should be UTF-8 or ISO-8859-1");
1214-
final byte[] source = input.getBytes(charset);
1215-
final int len = source.length;
1216-
final StringBuilder sb = new StringBuilder(len << 1);
1217-
sb.append(charset.name());
1218-
sb.append("''");
1219-
for (byte b : source) {
1220-
if (isRFC5987AttrChar(b)) {
1221-
sb.append((char) b);
1222-
}
1223-
else {
1224-
sb.append('%');
1225-
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
1226-
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
1227-
sb.append(hex1);
1228-
sb.append(hex2);
1229-
}
1230-
}
1231-
return sb.toString();
1232-
}
1233-
1234-
private static boolean isRFC5987AttrChar(byte c) {
1235-
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
1236-
|| c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-'
1237-
|| c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
1238-
}
12391197
}

spring-core/src/test/java/org/springframework/util/StringUtilsTests.java

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -628,53 +628,47 @@ public void testParseLocaleWithMultiSpecialCharactersInVariant() throws Exceptio
628628
assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant());
629629
}
630630

631-
// SPR-3671
632-
@Test
631+
@Test // SPR-3671
633632
public void testParseLocaleWithMultiValuedVariant() throws Exception {
634633
final String variant = "proper_northern";
635634
final String localeString = "en_GB_" + variant;
636635
Locale locale = StringUtils.parseLocaleString(localeString);
637636
assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant());
638637
}
639638

640-
// SPR-3671
641-
@Test
639+
@Test // SPR-3671
642640
public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparators() throws Exception {
643641
final String variant = "proper northern";
644642
final String localeString = "en GB " + variant;
645643
Locale locale = StringUtils.parseLocaleString(localeString);
646644
assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant());
647645
}
648646

649-
// SPR-3671
650-
@Test
647+
@Test // SPR-3671
651648
public void testParseLocaleWithMultiValuedVariantUsingMixtureOfUnderscoresAndSpacesAsSeparators() throws Exception {
652649
final String variant = "proper northern";
653650
final String localeString = "en_GB_" + variant;
654651
Locale locale = StringUtils.parseLocaleString(localeString);
655652
assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant());
656653
}
657654

658-
// SPR-3671
659-
@Test
655+
@Test // SPR-3671
660656
public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparatorsWithLotsOfLeadingWhitespace() throws Exception {
661657
final String variant = "proper northern";
662658
final String localeString = "en GB " + variant; // lots of whitespace
663659
Locale locale = StringUtils.parseLocaleString(localeString);
664660
assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant());
665661
}
666662

667-
// SPR-3671
668-
@Test
663+
@Test // SPR-3671
669664
public void testParseLocaleWithMultiValuedVariantUsingUnderscoresAsSeparatorsWithLotsOfLeadingWhitespace() throws Exception {
670665
final String variant = "proper_northern";
671666
final String localeString = "en_GB_____" + variant; // lots of underscores
672667
Locale locale = StringUtils.parseLocaleString(localeString);
673668
assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant());
674669
}
675670

676-
// SPR-7779
677-
@Test
671+
@Test // SPR-7779
678672
public void testParseLocaleWithInvalidCharacters() {
679673
try {
680674
StringUtils.parseLocaleString("%0D%0AContent-length:30%0D%0A%0D%0A%3Cscript%3Ealert%28123%29%3C/script%3E");
@@ -685,35 +679,18 @@ public void testParseLocaleWithInvalidCharacters() {
685679
}
686680
}
687681

688-
// SPR-9420
689-
@Test
682+
@Test // SPR-9420
690683
public void testParseLocaleWithSameLowercaseTokenForLanguageAndCountry() {
691684
assertEquals("tr_TR", StringUtils.parseLocaleString("tr_tr").toString());
692685
assertEquals("bg_BG_vnt", StringUtils.parseLocaleString("bg_bg_vnt").toString());
693686
}
694687

695-
// SPR-11806
696-
@Test
688+
@Test // SPR-11806
697689
public void testParseLocaleWithVariantContainingCountryCode() {
698690
String variant = "GBtest";
699691
String localeString = "en_GB_" + variant;
700692
Locale locale = StringUtils.parseLocaleString(localeString);
701693
assertEquals("Variant containing country code not extracted correctly", variant, locale.getVariant());
702694
}
703695

704-
// SPR-14547
705-
@Test
706-
public void encodeHttpHeaderFieldParam() {
707-
String result = StringUtils.encodeHttpHeaderFieldParam("test.txt", StandardCharsets.US_ASCII);
708-
assertEquals("test.txt", result);
709-
710-
result = StringUtils.encodeHttpHeaderFieldParam("中文.txt", StandardCharsets.UTF_8);
711-
assertEquals("UTF-8''%E4%B8%AD%E6%96%87.txt", result);
712-
}
713-
714-
@Test(expected = IllegalArgumentException.class)
715-
public void encodeHttpHeaderFieldParamInvalidCharset() {
716-
StringUtils.encodeHttpHeaderFieldParam("test", StandardCharsets.UTF_16);
717-
}
718-
719696
}

spring-web/src/main/java/org/springframework/http/HttpHeaders.java

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -679,13 +679,14 @@ public void setContentDispositionFormData(String name, String filename) {
679679

680680
/**
681681
* Set the (new) value of the {@code Content-Disposition} header
682-
* for {@code form-data}, optionally encoding the filename using the rfc5987.
682+
* for {@code form-data}, optionally encoding the filename using the RFC 5987.
683683
* <p>Only the US-ASCII, UTF-8 and ISO-8859-1 charsets are supported.
684684
* @param name the control name
685685
* @param filename the filename (may be {@code null})
686686
* @param charset the charset used for the filename (may be {@code null})
687-
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.2.4">rfc7230 Section 3.2.4</a>
688-
* @since 5.0
687+
* @since 4.3.3
688+
* @see #setContentDispositionFormData(String, String)
689+
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.2.4">RFC 7230 Section 3.2.4</a>
689690
*/
690691
public void setContentDispositionFormData(String name, String filename, Charset charset) {
691692
Assert.notNull(name, "'name' must not be null");
@@ -698,7 +699,7 @@ public void setContentDispositionFormData(String name, String filename, Charset
698699
}
699700
else {
700701
builder.append("; filename*=");
701-
builder.append(StringUtils.encodeHttpHeaderFieldParam(filename, charset));
702+
builder.append(encodeHeaderFieldParam(filename, charset));
702703
}
703704
}
704705
set(CONTENT_DISPOSITION, builder.toString());
@@ -1345,4 +1346,45 @@ public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
13451346
return new HttpHeaders(headers, true);
13461347
}
13471348

1349+
/**
1350+
* Encode the given header field param as describe in RFC 5987.
1351+
* @param input the header field param
1352+
* @param charset the charset of the header field param string
1353+
* @return the encoded header field param
1354+
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
1355+
*/
1356+
static String encodeHeaderFieldParam(String input, Charset charset) {
1357+
Assert.notNull(input, "Input String should not be null");
1358+
Assert.notNull(charset, "Charset should not be null");
1359+
if (StandardCharsets.US_ASCII.equals(charset)) {
1360+
return input;
1361+
}
1362+
Assert.isTrue(StandardCharsets.UTF_8.equals(charset) || StandardCharsets.ISO_8859_1.equals(charset),
1363+
"Charset should be UTF-8 or ISO-8859-1");
1364+
byte[] source = input.getBytes(charset);
1365+
int len = source.length;
1366+
StringBuilder sb = new StringBuilder(len << 1);
1367+
sb.append(charset.name());
1368+
sb.append("''");
1369+
for (byte b : source) {
1370+
if (isRFC5987AttrChar(b)) {
1371+
sb.append((char) b);
1372+
}
1373+
else {
1374+
sb.append('%');
1375+
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
1376+
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
1377+
sb.append(hex1);
1378+
sb.append(hex2);
1379+
}
1380+
}
1381+
return sb.toString();
1382+
}
1383+
1384+
private static boolean isRFC5987AttrChar(byte c) {
1385+
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1386+
c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' ||
1387+
c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
1388+
}
1389+
13481390
}

spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ public void host() {
150150
headers.setHost(host);
151151
assertEquals("Invalid Host header", host, headers.getHost());
152152
assertEquals("Invalid Host header", "localhost:8080", headers.getFirst("Host"));
153-
154153
}
155154

156155
@Test
@@ -159,7 +158,6 @@ public void hostNoPort() {
159158
headers.setHost(host);
160159
assertEquals("Invalid Host header", host, headers.getHost());
161160
assertEquals("Invalid Host header", "localhost", headers.getFirst("Host"));
162-
163161
}
164162

165163
@Test(expected = IllegalArgumentException.class)
@@ -429,4 +427,18 @@ public void accessControlRequestMethod() {
429427
assertEquals(HttpMethod.POST, headers.getAccessControlRequestMethod());
430428
}
431429

430+
@Test // SPR-14547
431+
public void encodeHeaderFieldParam() {
432+
String result = HttpHeaders.encodeHeaderFieldParam("test.txt", StandardCharsets.US_ASCII);
433+
assertEquals("test.txt", result);
434+
435+
result = HttpHeaders.encodeHeaderFieldParam("中文.txt", StandardCharsets.UTF_8);
436+
assertEquals("UTF-8''%E4%B8%AD%E6%96%87.txt", result);
437+
}
438+
439+
@Test(expected = IllegalArgumentException.class)
440+
public void encodeHeaderFieldParamInvalidCharset() {
441+
HttpHeaders.encodeHeaderFieldParam("test", StandardCharsets.UTF_16);
442+
}
443+
432444
}

spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@
4545
* @see GroovyMarkupViewResolver
4646
* @see GroovyMarkupConfigurer
4747
* @see <a href="http://groovy-lang.org/templating.html#_the_markuptemplateengine">
48-
* Groovy Markup Template engine documentation</a>
48+
* Groovy Markup Template engine documentation</a>
4949
*/
5050
public class GroovyMarkupView extends AbstractTemplateView {
5151

@@ -63,17 +63,6 @@ public void setTemplateEngine(MarkupTemplateEngine engine) {
6363
this.engine = engine;
6464
}
6565

66-
@Override
67-
public boolean checkResource(Locale locale) throws Exception {
68-
try {
69-
this.engine.resolveTemplate(getUrl());
70-
}
71-
catch (IOException exception) {
72-
return false;
73-
}
74-
return true;
75-
}
76-
7766
/**
7867
* Invoked at startup.
7968
* If no {@link #setTemplateEngine(MarkupTemplateEngine) templateEngine} has
@@ -107,6 +96,17 @@ protected MarkupTemplateEngine autodetectMarkupTemplateEngine() throws BeansExce
10796
}
10897

10998

99+
@Override
100+
public boolean checkResource(Locale locale) throws Exception {
101+
try {
102+
this.engine.resolveTemplate(getUrl());
103+
}
104+
catch (IOException ex) {
105+
return false;
106+
}
107+
return true;
108+
}
109+
110110
@Override
111111
protected void renderMergedTemplateModel(Map<String, Object> model,
112112
HttpServletRequest request, HttpServletResponse response) throws Exception {
@@ -125,8 +125,9 @@ protected Template getTemplate(String viewUrl) throws Exception {
125125
}
126126
catch (ClassNotFoundException ex) {
127127
Throwable cause = (ex.getCause() != null ? ex.getCause() : ex);
128-
throw new NestedServletException("Could not find class while rendering Groovy Markup view with name '" +
129-
getUrl() + "': " + ex.getMessage() + "'", cause);
128+
throw new NestedServletException(
129+
"Could not find class while rendering Groovy Markup view with name '" +
130+
getUrl() + "': " + ex.getMessage() + "'", cause);
130131
}
131132
}
132133

0 commit comments

Comments
 (0)