Support double-quoted in HttpHeaders::getValuesAsList
This commit introduces support for double-quoted HTTP header values in HttpHeaders::getValuesAsList, as described in RFC 9110 section 5.5. Closes gh-29785
This commit is contained in:
parent
fb0aa5abb3
commit
20c79e1481
|
|
@ -1537,8 +1537,11 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all values of a given header name,
|
* Return all values of a given header name, even if this header is set
|
||||||
* even if this header is set multiple times.
|
* multiple times.
|
||||||
|
* <p>This method supports double-quoted values, as described in
|
||||||
|
* <a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-8">RFC
|
||||||
|
* 9110, section 5.5</a>.
|
||||||
* @param headerName the header name
|
* @param headerName the header name
|
||||||
* @return all associated values
|
* @return all associated values
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
|
|
@ -1549,7 +1552,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
for (String value : values) {
|
for (String value : values) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
Collections.addAll(result, StringUtils.tokenizeToStringArray(value, ","));
|
result.addAll(tokenizeQuoted(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1557,6 +1560,53 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<String> tokenizeQuoted(String str) {
|
||||||
|
List<String> tokens = new ArrayList<>();
|
||||||
|
boolean quoted = false;
|
||||||
|
boolean trim = true;
|
||||||
|
StringBuilder builder = new StringBuilder(str.length());
|
||||||
|
for (int i = 0; i < str.length(); ++i) {
|
||||||
|
char ch = str.charAt(i);
|
||||||
|
if (ch == '"') {
|
||||||
|
if (builder.isEmpty()) {
|
||||||
|
quoted = true;
|
||||||
|
}
|
||||||
|
else if (quoted) {
|
||||||
|
quoted = false;
|
||||||
|
trim = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == '\\' && quoted && i < str.length() - 1) {
|
||||||
|
builder.append(str.charAt(++i));
|
||||||
|
}
|
||||||
|
else if (ch == ',' && !quoted) {
|
||||||
|
addToken(builder, tokens, trim);
|
||||||
|
builder.setLength(0);
|
||||||
|
trim = false;
|
||||||
|
}
|
||||||
|
else if (quoted || (!builder.isEmpty() && trim) || !Character.isWhitespace(ch)) {
|
||||||
|
builder.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!builder.isEmpty()) {
|
||||||
|
addToken(builder, tokens, trim);
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addToken(StringBuilder builder, List<String> tokens, boolean trim) {
|
||||||
|
String token = builder.toString();
|
||||||
|
if (trim) {
|
||||||
|
token = token.trim();
|
||||||
|
}
|
||||||
|
if (!token.isEmpty()) {
|
||||||
|
tokens.add(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the well-known {@code "Content-*"} HTTP headers.
|
* Remove the well-known {@code "Content-*"} HTTP headers.
|
||||||
* <p>Such headers should be cleared from the response if the intended
|
* <p>Such headers should be cleared from the response if the intended
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -713,4 +713,19 @@ public class HttpHeadersTests {
|
||||||
assertThat(headers2).isEqualTo(headers1);
|
assertThat(headers2).isEqualTo(headers1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getValuesAsList() {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Foo", "Bar");
|
||||||
|
headers.add("Foo", "Baz, Qux");
|
||||||
|
headers.add("Quux", "\t\"Corge\", \"Grault\"");
|
||||||
|
headers.add("Garply", " Waldo \"Fred\\!\", \"\tPlugh, Xyzzy! \"");
|
||||||
|
headers.add("Example-Dates", "\"Sat, 04 May 1996\", \"Wed, 14 Sep 2005\"");
|
||||||
|
|
||||||
|
assertThat(headers.getValuesAsList("Foo")).containsExactly("Bar", "Baz", "Qux");
|
||||||
|
assertThat(headers.getValuesAsList("Quux")).containsExactly("Corge", "Grault");
|
||||||
|
assertThat(headers.getValuesAsList("Garply")).containsExactly("Waldo \"Fred\\!\"", "\tPlugh, Xyzzy! ");
|
||||||
|
assertThat(headers.getValuesAsList("Example-Dates")).containsExactly("Sat, 04 May 1996", "Wed, 14 Sep 2005");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue