NIFI-12516 Corrected Cluster Replicated Response Headers for HTTP/2

The headers in the map that come back when replicating a request used to be in the case given; however they can be lowercased in the OkHttp Headers object when using HTTP/2 instead of HTTP/1.1. As a result, we need to ensure that we always use lower-case header names or check the map case-insensitive.

This closes #8163

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Mark Payne 2023-12-15 12:12:48 -05:00 committed by exceptionfactory
parent b0f30d6860
commit 231dbde4b3
No known key found for this signature in database
GPG Key ID: 29B6A52D2AAE8DBA
2 changed files with 41 additions and 4 deletions

View File

@ -231,7 +231,12 @@ public class JacksonResponse extends Response {
}
@Override
public String getHeaderString(String name) {
return responseHeaders.getFirst(name);
public String getHeaderString(final String name) {
final String headerValue = responseHeaders.getFirst(name);
if (headerValue != null) {
return headerValue;
}
return responseHeaders.getFirst(name.toLowerCase());
}
}

View File

@ -35,6 +35,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
@ -113,11 +114,11 @@ public class StandardNiFiContentAccess implements ContentAccess {
}
// get the file name
final String contentDisposition = responseHeaders.getFirst("Content-Disposition");
final String contentDisposition = getHeader(responseHeaders, "content-disposition");
final String filename = StringUtils.substringBetween(contentDisposition, "filename=\"", "\"");
// get the content type
final String contentType = responseHeaders.getFirst("Content-Type");
final String contentType = getHeader(responseHeaders, "content-type");
// create the downloadable content
return new DownloadableContent(filename, contentType, nodeResponse.getInputStream());
@ -159,6 +160,37 @@ public class StandardNiFiContentAccess implements ContentAccess {
}
}
/**
* Returns the value of the first header in the given header map whose name matches the given header name.
* In HTTP 2.0, all header names should be lower-case. Before that, they were not necessarily. The spec, however,
* indicates that header names are case-insensitive. That said, the code has them stored in a Map, and the keys in Maps
* are not case-insensitive. This method allows us to access the value of the first header with the given name,
* ignoring case.
*
* @param headers the map containing all headers
* @param headerName the case-insensitive name of the header to retrieve
* @return the value of the first header with the given name, or <code>null</code> if the header is not found
*/
private String getHeader(final MultivaluedMap<String, String> headers, final String headerName) {
if (headers == null || headers.isEmpty() || headerName == null || headerName.isBlank()) {
return null;
}
final String exactMatch = headers.getFirst(headerName);
if (exactMatch != null) {
return exactMatch;
}
for (final Map.Entry<String, List<String>> entry : headers.entrySet()) {
if (entry.getKey().equalsIgnoreCase(headerName)) {
final List<String> values = entry.getValue();
return values == null || values.isEmpty() ? null : values.get(0);
}
}
return null;
}
private DownloadableContent getFlowFileContent(final String connectionId, final String flowfileId, final String dataUri) {
// user authorization is handled once we have the actual content so we can utilize the flow file attributes in the resource context
return serviceFacade.getContent(connectionId, flowfileId, dataUri);