DefaultHttpHeader.names().toArray(...) may throw ArrayStoreException

Motivation:

DefaultHttpHeader.names() exposes HTTP header names as a Set<String>. Converting the resulting set to an array using toArray(String[]) throws an exception: java.lang.ArrayStoreException: io.netty.util.AsciiString.

Modifications:

- Remove our custom implementation of toArray(...) (and others) by just extending AbstractCollection.
- Add unit test

Result:

Fixes [#7428].
This commit is contained in:
Norman Maurer 2017-11-21 19:25:51 +01:00
parent 7d213240ca
commit 7aca99f986
2 changed files with 18 additions and 70 deletions

View File

@ -23,16 +23,13 @@ import org.junit.Test;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; import static io.netty.handler.codec.http.HttpHeadersTestUtils.of;
import static io.netty.util.AsciiString.contentEquals; import static io.netty.util.AsciiString.contentEquals;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class DefaultHttpHeadersTest { public class DefaultHttpHeadersTest {
private static final CharSequence HEADER_NAME = "testHeader"; private static final CharSequence HEADER_NAME = "testHeader";
@ -229,6 +226,16 @@ public class DefaultHttpHeadersTest {
.toString()); .toString());
} }
@Test
public void providesHeaderNamesAsArray() throws Exception {
Set<String> nettyHeaders = new DefaultHttpHeaders()
.add(HttpHeaderNames.CONTENT_LENGTH, 10)
.names();
String[] namesArray = nettyHeaders.toArray(new String[nettyHeaders.size()]);
assertArrayEquals(namesArray, new String[] { HttpHeaderNames.CONTENT_LENGTH.toString() });
}
private static void assertDefaultValues(final DefaultHttpHeaders headers, final HeaderValue headerValue) { private static void assertDefaultValues(final DefaultHttpHeaders headers, final HeaderValue headerValue) {
assertTrue(contentEquals(headerValue.asList().get(0), headers.get(HEADER_NAME))); assertTrue(contentEquals(headerValue.asList().get(0), headers.get(HEADER_NAME)));
List<CharSequence> expected = headerValue.asList(); List<CharSequence> expected = headerValue.asList();

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import java.util.AbstractCollection;
import java.util.AbstractList; import java.util.AbstractList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@ -109,7 +110,7 @@ public final class HeadersUtils {
private static final class StringEntryIterator implements Iterator<Entry<String, String>> { private static final class StringEntryIterator implements Iterator<Entry<String, String>> {
private final Iterator<Entry<CharSequence, CharSequence>> iter; private final Iterator<Entry<CharSequence, CharSequence>> iter;
public StringEntryIterator(Iterator<Entry<CharSequence, CharSequence>> iter) { StringEntryIterator(Iterator<Entry<CharSequence, CharSequence>> iter) {
this.iter = iter; this.iter = iter;
} }
@ -170,7 +171,7 @@ public final class HeadersUtils {
private static final class StringIterator<T> implements Iterator<String> { private static final class StringIterator<T> implements Iterator<String> {
private final Iterator<T> iter; private final Iterator<T> iter;
public StringIterator(Iterator<T> iter) { StringIterator(Iterator<T> iter) {
this.iter = iter; this.iter = iter;
} }
@ -192,7 +193,7 @@ public final class HeadersUtils {
} }
private static final class CharSequenceDelegatingStringSet extends DelegatingStringSet<CharSequence> { private static final class CharSequenceDelegatingStringSet extends DelegatingStringSet<CharSequence> {
public CharSequenceDelegatingStringSet(Set<CharSequence> allNames) { CharSequenceDelegatingStringSet(Set<CharSequence> allNames) {
super(allNames); super(allNames);
} }
@ -207,10 +208,10 @@ public final class HeadersUtils {
} }
} }
private abstract static class DelegatingStringSet<T> implements Set<String> { private abstract static class DelegatingStringSet<T> extends AbstractCollection<String> implements Set<String> {
protected final Set<T> allNames; protected final Set<T> allNames;
public DelegatingStringSet(Set<T> allNames) { DelegatingStringSet(Set<T> allNames) {
this.allNames = checkNotNull(allNames, "allNames"); this.allNames = checkNotNull(allNames, "allNames");
} }
@ -234,71 +235,11 @@ public final class HeadersUtils {
return new StringIterator<T>(allNames.iterator()); return new StringIterator<T>(allNames.iterator());
} }
@Override
public Object[] toArray() {
Object[] arr = new Object[size()];
fillArray(arr);
return arr;
}
@SuppressWarnings("unchecked")
@Override
public <X> X[] toArray(X[] a) {
if (a == null || a.length < size()) {
X[] arr = (X[]) new Object[size()];
fillArray(arr);
return arr;
}
fillArray(a);
return a;
}
private void fillArray(Object[] arr) {
Iterator<T> itr = allNames.iterator();
for (int i = 0; i < size(); ++i) {
arr[i] = itr.next();
}
}
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
return allNames.remove(o); return allNames.remove(o);
} }
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean modified = false;
for (Object o : c) {
if (remove(o)) {
modified = true;
}
}
return modified;
}
@Override
public boolean retainAll(Collection<?> c) {
boolean modified = false;
Iterator<String> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
@Override @Override
public void clear() { public void clear() {
allNames.clear(); allNames.clear();