This is an automated email from the ASF dual-hosted git repository. exceptionfactory pushed a commit to branch support/nifi-1.x in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/support/nifi-1.x by this push: new b3fb0b2713 NIFI-12516 Corrected Cluster Replicated Response Headers for HTTP/2 b3fb0b2713 is described below commit b3fb0b2713d3e6f1c7ae6578b71d9e31a2aca195 Author: Mark Payne <marka...@hotmail.com> AuthorDate: Fri Dec 15 12:12:48 2023 -0500 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 <exceptionfact...@apache.org> (cherry picked from commit 231dbde4b30bfea5b711169fe6f125c9279ec450) --- .../http/replication/okhttp/JacksonResponse.java | 9 ++++-- .../apache/nifi/web/StandardNiFiContentAccess.java | 36 ++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/JacksonResponse.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/JacksonResponse.java index e4eb45cf1f..3f10d42f89 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/JacksonResponse.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/JacksonResponse.java @@ -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()); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java index 79973fe147..7271b7ffaa 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java @@ -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);