19
19
import java .nio .charset .StandardCharsets ;
20
20
import java .time .ZonedDateTime ;
21
21
import java .time .format .DateTimeFormatter ;
22
+ import java .util .function .BiConsumer ;
22
23
23
24
import org .junit .jupiter .api .Test ;
24
25
25
26
import static org .assertj .core .api .Assertions .assertThat ;
26
27
import static org .assertj .core .api .Assertions .assertThatIllegalArgumentException ;
28
+ import static org .springframework .http .ContentDisposition .builder ;
27
29
28
30
/**
29
31
* Unit tests for {@link ContentDisposition}
@@ -38,7 +40,7 @@ public class ContentDispositionTests {
38
40
@ Test
39
41
public void parse () {
40
42
assertThat (parse ("form-data; name=\" foo\" ; filename=\" foo.txt\" ; size=123" ))
41
- .isEqualTo (ContentDisposition . builder ("form-data" )
43
+ .isEqualTo (builder ("form-data" )
42
44
.name ("foo" )
43
45
.filename ("foo.txt" )
44
46
.size (123L )
@@ -48,23 +50,23 @@ public void parse() {
48
50
@ Test
49
51
public void parseFilenameUnquoted () {
50
52
assertThat (parse ("form-data; filename=unquoted" ))
51
- .isEqualTo (ContentDisposition . builder ("form-data" )
53
+ .isEqualTo (builder ("form-data" )
52
54
.filename ("unquoted" )
53
55
.build ());
54
56
}
55
57
56
58
@ Test // SPR-16091
57
59
public void parseFilenameWithSemicolon () {
58
60
assertThat (parse ("attachment; filename=\" filename with ; semicolon.txt\" " ))
59
- .isEqualTo (ContentDisposition . builder ("attachment" )
61
+ .isEqualTo (builder ("attachment" )
60
62
.filename ("filename with ; semicolon.txt" )
61
63
.build ());
62
64
}
63
65
64
66
@ Test
65
67
public void parseEncodedFilename () {
66
68
assertThat (parse ("form-data; name=\" name\" ; filename*=UTF-8''%E4%B8%AD%E6%96%87.txt" ))
67
- .isEqualTo (ContentDisposition . builder ("form-data" )
69
+ .isEqualTo (builder ("form-data" )
68
70
.name ("name" )
69
71
.filename ("中文.txt" , StandardCharsets .UTF_8 )
70
72
.build ());
@@ -73,15 +75,15 @@ public void parseEncodedFilename() {
73
75
@ Test // gh-24112
74
76
public void parseEncodedFilenameWithPaddedCharset () {
75
77
assertThat (parse ("attachment; filename*= UTF-8''some-file.zip" ))
76
- .isEqualTo (ContentDisposition . builder ("attachment" )
78
+ .isEqualTo (builder ("attachment" )
77
79
.filename ("some-file.zip" , StandardCharsets .UTF_8 )
78
80
.build ());
79
81
}
80
82
81
83
@ Test
82
84
public void parseEncodedFilenameWithoutCharset () {
83
85
assertThat (parse ("form-data; name=\" name\" ; filename*=test.txt" ))
84
- .isEqualTo (ContentDisposition . builder ("form-data" )
86
+ .isEqualTo (builder ("form-data" )
85
87
.name ("name" )
86
88
.filename ("test.txt" )
87
89
.build ());
@@ -104,18 +106,30 @@ public void parseEncodedFilenameWithInvalidName() {
104
106
105
107
@ Test // gh-23077
106
108
public void parseWithEscapedQuote () {
107
- assertThat (parse ("form-data; name=\" file\" ; filename=\" \\ \" The Twilight Zone\\ \" .txt\" ; size=123" ))
108
- .isEqualTo (ContentDisposition .builder ("form-data" )
109
- .name ("file" )
110
- .filename ("\\ \" The Twilight Zone\\ \" .txt" )
111
- .size (123L )
112
- .build ());
109
+
110
+ BiConsumer <String , String > tester = (description , filename ) -> {
111
+ assertThat (parse ("form-data; name=\" file\" ; filename=\" " + filename + "\" ; size=123" ))
112
+ .as (description )
113
+ .isEqualTo (builder ("form-data" ).name ("file" ).filename (filename ).size (123L ).build ());
114
+ };
115
+
116
+ tester .accept ("Escaped quotes should be ignored" ,
117
+ "\\ \" The Twilight Zone\\ \" .txt" );
118
+
119
+ tester .accept ("Escaped quotes preceded by escaped backslashes should be ignored" ,
120
+ "\\ \\ \\ \" The Twilight Zone\\ \\ \\ \" .txt" );
121
+
122
+ tester .accept ("Escaped backslashes should not suppress quote" ,
123
+ "The Twilight Zone \\ \\ " );
124
+
125
+ tester .accept ("Escaped backslashes should not suppress quote" ,
126
+ "The Twilight Zone \\ \\ \\ \\ " );
113
127
}
114
128
115
129
@ Test
116
130
public void parseWithExtraSemicolons () {
117
131
assertThat (parse ("form-data; name=\" foo\" ;; ; filename=\" foo.txt\" ; size=123" ))
118
- .isEqualTo (ContentDisposition . builder ("form-data" )
132
+ .isEqualTo (builder ("form-data" )
119
133
.name ("foo" )
120
134
.filename ("foo.txt" )
121
135
.size (123L )
@@ -133,7 +147,7 @@ public void parseDates() {
133
147
"creation-date=\" " + creationTime .format (formatter ) + "\" ; " +
134
148
"modification-date=\" " + modificationTime .format (formatter ) + "\" ; " +
135
149
"read-date=\" " + readTime .format (formatter ) + "\" " )).isEqualTo (
136
- ContentDisposition . builder ("attachment" )
150
+ builder ("attachment" )
137
151
.creationDate (creationTime )
138
152
.modificationDate (modificationTime )
139
153
.readDate (readTime )
@@ -149,7 +163,7 @@ public void parseIgnoresInvalidDates() {
149
163
"creation-date=\" -1\" ; " +
150
164
"modification-date=\" -1\" ; " +
151
165
"read-date=\" " + readTime .format (formatter ) + "\" " )).isEqualTo (
152
- ContentDisposition . builder ("attachment" )
166
+ builder ("attachment" )
153
167
.readDate (readTime )
154
168
.build ());
155
169
}
@@ -177,7 +191,7 @@ private static ContentDisposition parse(String input) {
177
191
@ Test
178
192
public void format () {
179
193
assertThat (
180
- ContentDisposition . builder ("form-data" )
194
+ builder ("form-data" )
181
195
.name ("foo" )
182
196
.filename ("foo.txt" )
183
197
.size (123L )
@@ -188,7 +202,7 @@ public void format() {
188
202
@ Test
189
203
public void formatWithEncodedFilename () {
190
204
assertThat (
191
- ContentDisposition . builder ("form-data" )
205
+ builder ("form-data" )
192
206
.name ("name" )
193
207
.filename ("中文.txt" , StandardCharsets .UTF_8 )
194
208
.build ().toString ())
@@ -198,18 +212,45 @@ public void formatWithEncodedFilename() {
198
212
@ Test
199
213
public void formatWithEncodedFilenameUsingUsAscii () {
200
214
assertThat (
201
- ContentDisposition . builder ("form-data" )
215
+ builder ("form-data" )
202
216
.name ("name" )
203
217
.filename ("test.txt" , StandardCharsets .US_ASCII )
204
218
.build ()
205
219
.toString ())
206
220
.isEqualTo ("form-data; name=\" name\" ; filename=\" test.txt\" " );
207
221
}
208
222
223
+ @ Test // gh-24220
224
+ public void formatWithFilenameWithQuotes () {
225
+
226
+ BiConsumer <String , String > tester = (input , output ) -> {
227
+ assertThat (builder ("form-data" ).filename (input ).build ().toString ())
228
+ .isEqualTo ("form-data; filename=\" " + output + "\" " );
229
+ };
230
+
231
+ String filename = "\" foo.txt" ;
232
+ tester .accept (filename , "\\ " + filename );
233
+
234
+ filename = "\\ \" foo.txt" ;
235
+ tester .accept (filename , filename );
236
+
237
+ filename = "\\ \\ \" foo.txt" ;
238
+ tester .accept (filename , "\\ " + filename );
239
+
240
+ filename = "\\ \\ \\ \" foo.txt" ;
241
+ tester .accept (filename , filename );
242
+
243
+ filename = "\\ \\ \\ \\ \" foo.txt" ;
244
+ tester .accept (filename , "\\ " + filename );
245
+
246
+ tester .accept ("\" \" foo.txt" , "\\ \" \\ \" foo.txt" );
247
+ tester .accept ("\" \" \" foo.txt" , "\\ \" \\ \" \\ \" foo.txt" );
248
+ }
249
+
209
250
@ Test
210
251
public void formatWithEncodedFilenameUsingInvalidCharset () {
211
252
assertThatIllegalArgumentException ().isThrownBy (() ->
212
- ContentDisposition . builder ("form-data" )
253
+ builder ("form-data" )
213
254
.name ("name" )
214
255
.filename ("test.txt" , StandardCharsets .UTF_16 )
215
256
.build ()
0 commit comments