From 527dc2cc48edc9d895319889e559af4bbbb7b3ca Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Fri, 22 Sep 2017 17:41:42 +0100 Subject: [PATCH] SpringMvcContract: Ignore RequestMapping headers with no value. According to: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#headers-- the `headers` field can take several formats: 1. Require the header with a concrete value: `"My-Header=myValue"` 2. Require the header with any value: `"My-Header"` 3. Require the header is absent: `"!My-Header"` 4. Require the header with a negated value : `"My-Header!=myValue"` This fixes `parseHeaders` for cases 2-4 (which previously threw a `StringIndexOutOfBoundsException`). Fixes gh-1778. --- .../feign/support/SpringMvcContract.java | 4 +++- .../feign/support/SpringMvcContractTests.java | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java index fd7cfeb3..aa480655 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/support/SpringMvcContract.java @@ -275,8 +275,10 @@ public class SpringMvcContract extends Contract.BaseContract if (annotation.headers() != null && annotation.headers().length > 0) { for (String header : annotation.headers()) { int index = header.indexOf('='); - md.template().header(resolve(header.substring(0, index)), + if (!header.contains("!=") && index >= 0) { + md.template().header(resolve(header.substring(0, index)), resolve(header.substring(index + 1).trim())); + } } } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/support/SpringMvcContractTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/support/SpringMvcContractTests.java index 8b0a51c7..b786c6f0 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/support/SpringMvcContractTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/support/SpringMvcContractTests.java @@ -332,6 +332,18 @@ public class SpringMvcContractTests { assertEquals("bar", data.template().headers().get("X-Foo").iterator().next()); } + @Test + public void testProcessHeadersWithoutValues() throws Exception { + Method method = TestTemplate_HeadersWithoutValues.class.getDeclaredMethod("getTest", + String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertEquals("/test/{id}", data.template().url()); + assertEquals("GET", data.template().method()); + assertEquals(true, data.template().headers().isEmpty()); + } + @Test public void testProcessAnnotations_Fallback() throws Exception { Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTestFallback", @@ -462,6 +474,11 @@ public class SpringMvcContractTests { ResponseEntity getTest(@PathVariable("id") String id); } + public interface TestTemplate_HeadersWithoutValues { + @RequestMapping(value = "/test/{id}", method = RequestMethod.GET, headers = { "X-Foo", "!X-Bar", "X-Baz!=fooBar" }) + ResponseEntity getTest(@PathVariable("id") String id); + } + public interface TestTemplate_ListParams { @RequestMapping(value = "/test", method = RequestMethod.GET) ResponseEntity getTest(@RequestParam("id") List id);