diff --git a/client/rest/src/main/java/org/elasticsearch/client/Response.java b/client/rest/src/main/java/org/elasticsearch/client/Response.java index c5a159eb39da5..7abe24aa59eb5 100644 --- a/client/rest/src/main/java/org/elasticsearch/client/Response.java +++ b/client/rest/src/main/java/org/elasticsearch/client/Response.java @@ -116,6 +116,86 @@ public HttpEntity getEntity() { "GMT" + // GMT "\")?"); // closing quote (optional, since an older version can still send a warn-date) + /** + * Optimized regular expression to test if a string matches the RFC 1123 date + * format (with quotes and leading space). Start/end of line characters and + * atomic groups are used to prevent backtracking. + */ + private static final Pattern WARNING_HEADER_DATE_PATTERN = Pattern.compile( + "^ " + // start of line, leading space + // quoted RFC 1123 date format + "\"" + // opening quote + "(?>Mon|Tue|Wed|Thu|Fri|Sat|Sun), " + // day of week, atomic group to prevent backtracking + "\\d{2} " + // 2-digit day + "(?>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " + // month, atomic group to prevent backtracking + "\\d{4} " + // 4-digit year + "\\d{2}:\\d{2}:\\d{2} " + // (two-digit hour):(two-digit minute):(two-digit second) + "GMT" + // GMT + "\"$"); // closing quote (optional, since an older version can still send a warn-date), end of line + + /** + * Length of RFC 1123 format (with quotes and leading space), used in + * matchWarningHeaderPatternByPrefix(String). + */ + private static final int WARNING_HEADER_DATE_LENGTH = 0 + + 1 + + 1 + + 3 + 1 + 1 + + 2 + 1 + + 3 + 1 + + 4 + 1 + + 2 + 1 + 2 + 1 + 2 + 1 + + 3 + + 1; + + /** + * Tests if a string matches the RFC 7234 specification for warning headers. + * This assumes that the warn code is always 299 and the warn agent is always + * Elasticsearch. + * + * @param s the value of a warning header formatted according to RFC 7234 + * @return {@code true} if the input string matches the specification + */ + private static boolean matchWarningHeaderPatternByPrefix(final String s) { + return s.startsWith("299 Elasticsearch-"); + } + + /** + * Refer to org.elasticsearch.common.logging.DeprecationLogger + */ + private static String extractWarningValueFromWarningHeader(final String s) { + String warningHeader = s; + + /* + * The following block tests for the existence of a RFC 1123 date in the warning header. If the date exists, it is removed for + * extractWarningValueFromWarningHeader(String) to work properly (as it does not handle dates). + */ + if (s.length() > WARNING_HEADER_DATE_LENGTH) { + final String possibleDateString = s.substring(s.length() - WARNING_HEADER_DATE_LENGTH); + final Matcher matcher = WARNING_HEADER_DATE_PATTERN.matcher(possibleDateString); + + if (matcher.matches()) { + warningHeader = warningHeader.substring(0, s.length() - WARNING_HEADER_DATE_LENGTH); + } + } + + final int firstQuote = warningHeader.indexOf('\"'); + final int lastQuote = warningHeader.length() - 1; + final String warningValue = warningHeader.substring(firstQuote + 1, lastQuote); + assert assertWarningValue(s, warningValue); + return warningValue; + } + + /** + * Refer to org.elasticsearch.common.logging.DeprecationLogger + */ + private static boolean assertWarningValue(final String s, final String warningValue) { + final Matcher matcher = WARNING_HEADER_PATTERN.matcher(s); + final boolean matches = matcher.matches(); + assert matches; + return matcher.group(1).equals(warningValue); + } + /** * Returns a list of all warning headers returned in the response. */ @@ -123,9 +203,8 @@ public List getWarnings() { List warnings = new ArrayList<>(); for (Header header : response.getHeaders("Warning")) { String warning = header.getValue(); - final Matcher matcher = WARNING_HEADER_PATTERN.matcher(warning); - if (matcher.matches()) { - warnings.add(matcher.group(1)); + if (matchWarningHeaderPatternByPrefix(warning)) { + warnings.add(extractWarningValueFromWarningHeader(warning)); } else { warnings.add(warning); } diff --git a/client/rest/src/test/java/org/elasticsearch/client/RestClientSingleHostTests.java b/client/rest/src/test/java/org/elasticsearch/client/RestClientSingleHostTests.java index dd133f90daadb..fa5e9bcc6b43c 100644 --- a/client/rest/src/test/java/org/elasticsearch/client/RestClientSingleHostTests.java +++ b/client/rest/src/test/java/org/elasticsearch/client/RestClientSingleHostTests.java @@ -424,6 +424,7 @@ public void testHeaders() throws Exception { public void testDeprecationWarnings() throws Exception { String chars = randomAsciiAlphanumOfLength(5); assertDeprecationWarnings(singletonList("poorly formatted " + chars), singletonList("poorly formatted " + chars)); + assertDeprecationWarnings(singletonList(formatWarningWithoutDate(chars)), singletonList(chars)); assertDeprecationWarnings(singletonList(formatWarning(chars)), singletonList(chars)); assertDeprecationWarnings( Arrays.asList(formatWarning(chars), "another one", "and another"), @@ -433,6 +434,9 @@ public void testDeprecationWarnings() throws Exception { Arrays.asList("ignorable one", "and another")); assertDeprecationWarnings(singletonList("exact"), singletonList("exact")); assertDeprecationWarnings(Collections.emptyList(), Collections.emptyList()); + + String proxyWarning = "112 - \"network down\" \"Sat, 25 Aug 2012 23:34:45 GMT\""; + assertDeprecationWarnings(singletonList(proxyWarning), singletonList(proxyWarning)); } private enum DeprecationWarningOption { @@ -518,9 +522,13 @@ private void assertDeprecationWarnings(List warningHeaderTexts, List