Skip to content

Commit ed2cef0

Browse files
fmcejudovelo
authored andcommitted
Remove null empty headers (#724)
* Creating headers from Request removing those with null or empty values * Moving back to let the empty strings as valid header * Returning headers filtering null and empty rather removing them for the current map. Supporting with tests as it needs to be LinkedHashMap not to lost the sorting
1 parent a25423c commit ed2cef0

File tree

4 files changed

+87
-3
lines changed

4 files changed

+87
-3
lines changed

core/src/main/java/feign/RequestTemplate.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static feign.Util.emptyToNull;
3535
import static feign.Util.toArray;
3636
import static feign.Util.valuesOrEmpty;
37+
import static java.util.stream.Collectors.toMap;
3738

3839
/**
3940
* Builds a request to an http target. Not thread safe. <br>
@@ -266,7 +267,7 @@ private String encodeValueIfNotEncoded(String key,
266267
/* roughly analogous to {@code javax.ws.rs.client.Target.request()}. */
267268
public Request request() {
268269
Map<String, Collection<String>> safeCopy = new LinkedHashMap<String, Collection<String>>();
269-
safeCopy.putAll(headers);
270+
safeCopy.putAll(headers());
270271
return Request.create(
271272
method, url + queryLine(),
272273
Collections.unmodifiableMap(safeCopy),
@@ -535,7 +536,16 @@ public RequestTemplate headers(Map<String, Collection<String>> headers) {
535536
* @see Request#headers()
536537
*/
537538
public Map<String, Collection<String>> headers() {
538-
return Collections.unmodifiableMap(headers);
539+
540+
return Collections.unmodifiableMap(
541+
headers.entrySet().stream().filter(h -> h.getValue() != null && !h.getValue().isEmpty())
542+
.collect(toMap(
543+
Entry::getKey,
544+
Entry::getValue,
545+
(e1, e2) -> {
546+
throw new IllegalStateException("headers should not have duplicated keys");
547+
},
548+
LinkedHashMap::new)));
539549
}
540550

541551
/**

core/src/test/java/feign/RequestTemplateTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
*/
1414
package feign;
1515

16+
import org.assertj.core.api.Assertions;
1617
import org.junit.Rule;
1718
import org.junit.Test;
1819
import org.junit.rules.ExpectedException;
1920
import java.util.Arrays;
21+
import java.util.Collection;
22+
import java.util.Collections;
2023
import java.util.LinkedHashMap;
2124
import java.util.Map;
2225
import static feign.RequestTemplate.expand;
@@ -344,4 +347,31 @@ public void encodedQueryWithUnsafeCharactersMixedWithUnencoded() throws Exceptio
344347
assertThat(template.queries()).doesNotContain(entry("params[]", asList("not encoded")));
345348
assertThat(template.queries()).contains(entry("params[]", asList("encoded")));
346349
}
350+
351+
@Test
352+
public void shouldRetrieveHeadersWithoutNull() {
353+
RequestTemplate template = new RequestTemplate()
354+
.header("key1", (String) null)
355+
.header("key2", Collections.emptyList())
356+
.header("key3", (Collection) null)
357+
.header("key4", "valid")
358+
.header("key5", "valid")
359+
.header("key6", "valid")
360+
.header("key7", "valid");
361+
362+
assertThat(template.headers()).hasSize(4);
363+
assertThat(template.headers().keySet()).containsExactly("key4", "key5", "key6", "key7");
364+
365+
}
366+
367+
@Test(expected = UnsupportedOperationException.class)
368+
public void shouldNotInsertHeadersImmutableMap() {
369+
RequestTemplate template = new RequestTemplate()
370+
.header("key1", "valid");
371+
372+
assertThat(template.headers()).hasSize(1);
373+
assertThat(template.headers().keySet()).containsExactly("key1");
374+
375+
template.headers().put("key2", asList("other value"));
376+
}
347377
}

core/src/test/java/feign/assertj/RequestTemplateAssert.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,9 @@ public RequestTemplateAssert hasHeaders(MapEntry... entries) {
8282
maps.assertContainsExactly(info, actual.headers(), entries);
8383
return this;
8484
}
85+
86+
public RequestTemplateAssert hasNoHeader(final String encoded) {
87+
objects.assertNull(info, actual.headers().get(encoded));
88+
return this;
89+
}
8590
}

core/src/test/java/feign/client/AbstractClientTest.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import okhttp3.mockwebserver.MockWebServer;
3636
import static java.util.Arrays.asList;
3737
import static org.assertj.core.api.Assertions.assertThat;
38+
import static org.assertj.core.api.Assertions.entry;
3839
import static org.junit.Assert.assertEquals;
3940
import static feign.Util.UTF_8;
4041

@@ -93,7 +94,7 @@ public void parsesRequestAndResponse() throws IOException, InterruptedException
9394

9495
MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST")
9596
.hasPath("/?foo=bar&foo=baz&qux=")
96-
.hasHeaders("Foo: Bar", "Foo: Baz", "Qux: ", "Accept: */*", "Content-Length: 3")
97+
.hasHeaders("Foo: Bar", "Foo: Baz", "Accept: */*", "Content-Length: 3")
9798
.hasBody("foo");
9899
}
99100

@@ -282,6 +283,38 @@ public void testDefaultCollectionFormat() throws Exception {
282283
.hasPath("/?foo=bar&foo=baz");
283284
}
284285

286+
@Test
287+
public void testHeadersWithNullParams() throws InterruptedException {
288+
server.enqueue(new MockResponse().setBody("body"));
289+
290+
TestInterface api = newBuilder()
291+
.target(TestInterface.class, "http://localhost:" + server.getPort());
292+
293+
Response response = api.getWithHeaders(null);
294+
295+
assertThat(response.status()).isEqualTo(200);
296+
assertThat(response.reason()).isEqualTo("OK");
297+
298+
MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("GET")
299+
.hasPath("/").hasNoHeaderNamed("Authorization");
300+
}
301+
302+
@Test
303+
public void testHeadersWithNotEmptyParams() throws InterruptedException {
304+
server.enqueue(new MockResponse().setBody("body"));
305+
306+
TestInterface api = newBuilder()
307+
.target(TestInterface.class, "http://localhost:" + server.getPort());
308+
309+
Response response = api.getWithHeaders("token");
310+
311+
assertThat(response.status()).isEqualTo(200);
312+
assertThat(response.reason()).isEqualTo("OK");
313+
314+
MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("GET")
315+
.hasPath("/").hasHeaders(entry("authorization", asList("token")));
316+
}
317+
285318
@Test
286319
public void testAlternativeCollectionFormat() throws Exception {
287320
server.enqueue(new MockResponse().setBody("body"));
@@ -316,6 +349,12 @@ public interface TestInterface {
316349
@RequestLine("GET /?foo={multiFoo}")
317350
Response get(@Param("multiFoo") List<String> multiFoo);
318351

352+
@Headers({
353+
"Authorization: {authorization}"
354+
})
355+
@RequestLine("GET /")
356+
Response getWithHeaders(@Param("authorization") String authorization);
357+
319358
@RequestLine(value = "GET /?foo={multiFoo}", collectionFormat = CollectionFormat.CSV)
320359
Response getCSV(@Param("multiFoo") List<String> multiFoo);
321360

0 commit comments

Comments
 (0)