Skip to content

Commit 8f1aaf8

Browse files
committed
HttpHeaders should not leak pseudo headers in MultiMap API.
1 parent 02f2d1d commit 8f1aaf8

File tree

2 files changed

+118
-85
lines changed

2 files changed

+118
-85
lines changed

vertx-core/src/main/java/io/vertx/core/http/impl/headers/HttpHeaders.java

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@
1818
import io.vertx.core.http.impl.HttpUtils;
1919
import io.vertx.core.internal.http.HttpHeadersInternal;
2020

21-
import java.util.AbstractList;
22-
import java.util.Iterator;
23-
import java.util.List;
24-
import java.util.Map;
25-
import java.util.Set;
26-
import java.util.TreeSet;
21+
import java.util.*;
2722
import java.util.stream.Collectors;
2823

2924
/**
@@ -217,45 +212,72 @@ public HttpHeaders clear() {
217212

218213
@Override
219214
public Iterator<Map.Entry<String, String>> iterator() {
220-
Iterator<Map.Entry<CharSequence, CharSequence>> i = headers.iterator();
221-
return new Iterator<Map.Entry<String, String>>() {
222-
@Override
223-
public boolean hasNext() {
224-
return i.hasNext();
225-
}
226-
@Override
227-
public Map.Entry<String, String> next() {
215+
return new EntryIterator(headers.iterator());
216+
}
217+
218+
private class EntryIterator implements Iterator<Map.Entry<String, String>> {
219+
220+
private Map.Entry<String, String> nextEntry(Iterator<Map.Entry<CharSequence, CharSequence>> i) {
221+
while (i.hasNext()) {
228222
Map.Entry<CharSequence, CharSequence> next = i.next();
229-
return new Map.Entry<String, String>() {
230-
@Override
231-
public String getKey() {
232-
return next.getKey().toString();
233-
}
234-
@Override
235-
public String getValue() {
236-
return next.getValue().toString();
237-
}
238-
@Override
239-
public String setValue(String value) {
240-
if (!mutable) {
241-
throw new IllegalStateException("Read only");
223+
CharSequence name = next.getKey();
224+
if (name.length() == 0 || name.charAt(0) != ':') {
225+
return new Map.Entry<>() {
226+
@Override
227+
public String getKey() {
228+
return next.getKey().toString();
229+
}
230+
@Override
231+
public String getValue() {
232+
return next.getValue().toString();
242233
}
243-
String old = next.getValue().toString();
244-
next.setValue(value);
245-
return old;
246-
}
247-
@Override
248-
public String toString() {
249-
return next.toString();
250-
}
251-
};
234+
@Override
235+
public String setValue(String value) {
236+
if (!mutable) {
237+
throw new IllegalStateException("Read only");
238+
}
239+
String old = next.getValue().toString();
240+
next.setValue(value);
241+
return old;
242+
}
243+
@Override
244+
public String toString() {
245+
return next.toString();
246+
}
247+
};
248+
}
252249
}
253-
};
250+
return null;
251+
}
252+
253+
private final Iterator<Map.Entry<CharSequence, CharSequence>> iterator;
254+
private Map.Entry<String, String> next;
255+
256+
public EntryIterator(Iterator<Map.Entry<CharSequence, CharSequence>> iterator) {
257+
this.iterator = iterator;
258+
this.next = nextEntry(iterator);
259+
}
260+
261+
@Override
262+
public boolean hasNext() {
263+
return next != null;
264+
}
265+
266+
@Override
267+
public Map.Entry<String, String> next() {
268+
Map.Entry<String, String> ret = next;
269+
next = nextEntry(iterator);
270+
return ret;
271+
}
254272
}
255273

256274
@Override
257275
public int size() {
258-
return names().size();
276+
int size = 0;
277+
for (CharSequence name : headers.names()) {
278+
size += isPseudoHeader(name) ? 0 : 1;
279+
}
280+
return size;
259281
}
260282

261283
@Override
@@ -380,4 +402,8 @@ public HttpHeaders copy(boolean mutable) {
380402
HttpHeaders copy(boolean mutable, Headers<CharSequence, CharSequence, ?> headers) {
381403
return new HttpHeaders(mutable, new DefaultHttp2Headers().setAll(headers));
382404
}
405+
406+
private static boolean isPseudoHeader(CharSequence cs) {
407+
return cs.length() > 0 && cs.charAt(0) == ':';
408+
}
383409
}

vertx-core/src/test/java/io/vertx/tests/http/headers/Http2HeadersAdaptorsTest.java

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,15 @@
1111

1212
package io.vertx.tests.http.headers;
1313

14+
import io.netty.handler.codec.Headers;
1415
import io.netty.handler.codec.http2.DefaultHttp2Headers;
15-
import io.vertx.core.MultiMap;
1616
import io.vertx.core.http.HttpMethod;
1717
import io.vertx.core.http.impl.headers.HttpHeaders;
1818
import io.vertx.core.http.impl.headers.HttpRequestHeaders;
19-
import org.junit.Before;
2019
import org.junit.Ignore;
2120
import org.junit.Test;
2221

23-
import java.util.Arrays;
24-
import java.util.Collections;
25-
import java.util.HashSet;
26-
import java.util.List;
27-
import java.util.Map;
22+
import java.util.*;
2823
import java.util.stream.Collectors;
2924

3025
import static org.junit.Assert.*;
@@ -34,84 +29,82 @@
3429
*/
3530
public class Http2HeadersAdaptorsTest extends HeadersTest {
3631

37-
DefaultHttp2Headers headers;
38-
MultiMap map;
39-
40-
@Before
41-
public void setUp() {
42-
headers = new DefaultHttp2Headers();
43-
map = new HttpHeaders(headers);
44-
}
45-
4632
@Override
4733
protected HttpHeaders newMultiMap() {
4834
return new HttpHeaders(new DefaultHttp2Headers());
4935
}
5036

5137
@Test
5238
public void testGetConvertUpperCase() {
53-
map.set("foo", "foo_value");
54-
assertEquals("foo_value", map.get("Foo"));
55-
assertEquals("foo_value", map.get((CharSequence) "Foo"));
39+
HttpHeaders headers = newMultiMap();
40+
headers.set("foo", "foo_value");
41+
assertEquals("foo_value", headers.get("Foo"));
42+
assertEquals("foo_value", headers.get((CharSequence) "Foo"));
5643
}
5744

5845
@Test
5946
public void testGetAllConvertUpperCase() {
60-
map.set("foo", "foo_value");
61-
assertEquals(Collections.singletonList("foo_value"), map.getAll("Foo"));
62-
assertEquals(Collections.singletonList("foo_value"), map.getAll((CharSequence) "Foo"));
47+
HttpHeaders headers = newMultiMap();
48+
headers.set("foo", "foo_value");
49+
assertEquals(Collections.singletonList("foo_value"), headers.getAll("Foo"));
50+
assertEquals(Collections.singletonList("foo_value"), headers.getAll((CharSequence) "Foo"));
6351
}
6452

6553
@Test
6654
public void testContainsConvertUpperCase() {
67-
map.set("foo", "foo_value");
68-
assertTrue(map.contains("Foo"));
69-
assertTrue(map.contains((CharSequence) "Foo"));
55+
HttpHeaders headers = newMultiMap();
56+
headers.set("foo", "foo_value");
57+
assertTrue(headers.contains("Foo"));
58+
assertTrue(headers.contains((CharSequence) "Foo"));
7059
}
7160

7261
@Test
7362
public void testSetConvertUpperCase() {
74-
map.set("Foo", "foo_value");
75-
map.set((CharSequence) "Bar", "bar_value");
76-
map.set("Juu", (Iterable<String>)Collections.singletonList("juu_value"));
77-
map.set("Daa", Collections.singletonList((CharSequence)"daa_value"));
78-
assertHeaderNames("foo","bar", "juu", "daa");
63+
HttpHeaders headers = newMultiMap();
64+
headers.set("Foo", "foo_value");
65+
headers.set((CharSequence) "Bar", "bar_value");
66+
headers.set("Juu", (Iterable<String>)Collections.singletonList("juu_value"));
67+
headers.set("Daa", Collections.singletonList((CharSequence)"daa_value"));
68+
assertHeaderNames(headers, "foo","bar", "juu", "daa");
7969
}
8070

8171
@Test
8272
public void testAddConvertUpperCase() {
83-
map.add("Foo", "foo_value");
84-
map.add((CharSequence) "Bar", "bar_value");
85-
map.add("Juu", (Iterable<String>)Collections.singletonList("juu_value"));
86-
map.add("Daa", Collections.singletonList((CharSequence)"daa_value"));
87-
assertHeaderNames("foo","bar", "juu", "daa");
73+
HttpHeaders headers = newMultiMap();
74+
headers.add("Foo", "foo_value");
75+
headers.add((CharSequence) "Bar", "bar_value");
76+
headers.add("Juu", (Iterable<String>)Collections.singletonList("juu_value"));
77+
headers.add("Daa", Collections.singletonList((CharSequence)"daa_value"));
78+
assertHeaderNames(headers, "foo","bar", "juu", "daa");
8879
}
8980

9081
@Test
9182
public void testRemoveConvertUpperCase() {
92-
map.set("foo", "foo_value");
93-
map.remove("Foo");
94-
map.set("bar", "bar_value");
95-
map.remove((CharSequence) "Bar");
96-
assertHeaderNames();
83+
HttpHeaders headers = newMultiMap();
84+
headers.set("foo", "foo_value");
85+
headers.remove("Foo");
86+
headers.set("bar", "bar_value");
87+
headers.remove((CharSequence) "Bar");
88+
assertHeaderNames(headers);
9789
}
9890

9991
@Ignore
10092
@Test
10193
public void testEntries() {
102-
map.set("foo", Arrays.<String>asList("foo_value_1", "foo_value_2"));
103-
List<Map.Entry<String, String>> entries = map.entries();
104-
assertEquals(entries.size(), 1);
94+
HttpHeaders headers = newMultiMap();
95+
headers.set("foo", Arrays.<String>asList("foo_value_1", "foo_value_2"));
96+
List<Map.Entry<String, String>> entries = headers.entries();
97+
assertEquals(1, entries.size());
10598
assertEquals("foo", entries.get(0).getKey());
10699
assertEquals("foo_value_1", entries.get(0).getValue());
107-
map.set("bar", "bar_value");
108-
Map<String, String> collected = map.entries().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
100+
headers.set("bar", "bar_value");
101+
Map<String, String> collected = headers.entries().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
109102
assertEquals("foo_value_1", collected.get("foo"));
110103
assertEquals("bar_value", collected.get("bar"));
111104
}
112105

113-
private void assertHeaderNames(String... expected) {
114-
assertEquals(new HashSet<>(Arrays.asList(expected)), headers.names().stream().map(CharSequence::toString).collect(Collectors.toSet()));
106+
private void assertHeaderNames(HttpHeaders headers, String... expected) {
107+
assertEquals(new HashSet<>(Arrays.asList(expected)), headers.unwrap().names().stream().map(CharSequence::toString).collect(Collectors.toSet()));
115108
}
116109

117110
private HttpHeaders headers(HttpMethod method, String authority, String host) {
@@ -147,4 +140,18 @@ public void testAuthorityValidation() {
147140
assertFalse(headers(HttpMethod.CONNECT, "localhost:a", null).validate());
148141
assertFalse(headers(HttpMethod.CONNECT, null, "localhost:a").validate());
149142
}
143+
144+
@Test
145+
public void testFilterPseudoHeaders() {
146+
HttpHeaders headers = headers(HttpMethod.PUT, "localhost:8080", "another:4443");
147+
List<String> names = new ArrayList<>();
148+
headers.forEach(entry -> {
149+
names.add(entry.getKey());
150+
});
151+
assertEquals(List.of("host"), names);
152+
assertEquals(1, headers.size());
153+
HttpHeaders other = newMultiMap();
154+
other.setAll(headers);
155+
assertEquals(1, other.unwrap().size());
156+
}
150157
}

0 commit comments

Comments
 (0)