Merged branch 'jetty-9.4.x-5104-incorrect_via_header-2' into 'jetty-9.4.x-5104-incorrect_via_header'.

This commit is contained in:
Simone Bordet 2020-08-13 11:33:50 +02:00
commit c3598e814b
3 changed files with 97 additions and 40 deletions

View File

@ -292,6 +292,11 @@ public class HttpField
return _name.equalsIgnoreCase(field.getName()); return _name.equalsIgnoreCase(field.getName());
} }
public boolean is(String name)
{
return _name.equalsIgnoreCase(name);
}
private int nameHashCode() private int nameHashCode()
{ {
int h = this.hash; int h = this.hash;

View File

@ -166,36 +166,56 @@ public class HttpFields implements Iterable<HttpField>
*/ */
public void computeField(String name, BiFunction<String, List<HttpField>, HttpField> computeFn) public void computeField(String name, BiFunction<String, List<HttpField>, HttpField> computeFn)
{ {
boolean found = false; // Look for first occurrence
ListIterator<HttpField> iterator = listIterator(); int first = -1;
while (iterator.hasNext()) for (int i = 0; i < _size; i++)
{ {
HttpField field = iterator.next(); HttpField f = _fields[i];
if (field.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
if (found) first = i;
{ break;
// Remove other headers with the same name, since
// we have computed one from all of them already.
iterator.remove();
}
else
{
found = true;
HttpField newField = computeFn.apply(name, Collections.unmodifiableList(getFields(name)));
if (newField == null)
iterator.remove();
else
iterator.set(newField);
}
} }
} }
if (!found)
// If the header is not found, add a new one;
if (first < 0)
{ {
HttpField newField = computeFn.apply(name, null); HttpField newField = computeFn.apply(name, null);
if (newField != null) if (newField != null)
put(newField); add(newField);
return;
} }
// Are there any more occurrences?
List<HttpField> found = null;
for (int i = first + 1; i < _size; i++)
{
HttpField f = _fields[i];
if (f.is(name))
{
if (found == null)
{
found = new ArrayList<>();
found.add(_fields[first]);
}
// Remember and remove additional fields
found.add(f);
remove(i);
}
}
// If no additional fields were found, handle singleton case
if (found == null)
found = Collections.singletonList(_fields[first]);
else
found = Collections.unmodifiableList(found);
HttpField newField = computeFn.apply(name, found);
if (newField == null)
remove(first);
else
_fields[first] = newField;
} }
public int size() public int size()
@ -277,7 +297,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = 0; i < _size; i++) for (int i = 0; i < _size; i++)
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
return f; return f;
} }
return null; return null;
@ -305,7 +325,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = 0; i < _size; i++) for (int i = 0; i < _size; i++)
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
if (fields == null) if (fields == null)
fields = new ArrayList<>(); fields = new ArrayList<>();
@ -342,7 +362,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = _size; i-- > 0; ) for (int i = _size; i-- > 0; )
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name) && f.contains(value)) if (f.is(name) && f.contains(value))
return true; return true;
} }
return false; return false;
@ -364,7 +384,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = _size; i-- > 0; ) for (int i = _size; i-- > 0; )
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
return true; return true;
} }
return false; return false;
@ -398,7 +418,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = 0; i < _size; i++) for (int i = 0; i < _size; i++)
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(header)) if (f.is(header))
return f.getValue(); return f.getValue();
} }
return null; return null;
@ -434,7 +454,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = 0; i < _size; i++) for (int i = 0; i < _size; i++)
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
if (list == null) if (list == null)
list = new ArrayList<>(size() - i); list = new ArrayList<>(size() - i);
@ -489,7 +509,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i = 0; i < _size; i++) for (int i = 0; i < _size; i++)
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
if (existing == null) if (existing == null)
existing = new QuotedCSV(false); existing = new QuotedCSV(false);
@ -577,7 +597,7 @@ public class HttpFields implements Iterable<HttpField>
QuotedCSV values = null; QuotedCSV values = null;
for (HttpField f : this) for (HttpField f : this)
{ {
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
if (values == null) if (values == null)
values = new QuotedCSV(keepQuotes); values = new QuotedCSV(keepQuotes);
@ -635,7 +655,7 @@ public class HttpFields implements Iterable<HttpField>
QuotedQualityCSV values = null; QuotedQualityCSV values = null;
for (HttpField f : this) for (HttpField f : this)
{ {
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
if (values == null) if (values == null)
values = new QuotedQualityCSV(); values = new QuotedQualityCSV();
@ -657,7 +677,7 @@ public class HttpFields implements Iterable<HttpField>
{ {
final HttpField f = _fields[i]; final HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name) && f.getValue() != null) if (f.is(name) && f.getValue() != null)
{ {
final int first = i; final int first = i;
return new Enumeration<String>() return new Enumeration<String>()
@ -673,7 +693,7 @@ public class HttpFields implements Iterable<HttpField>
while (i < _size) while (i < _size)
{ {
field = _fields[i++]; field = _fields[i++];
if (field.getName().equalsIgnoreCase(name) && field.getValue() != null) if (field.is(name) && field.getValue() != null)
return true; return true;
} }
field = null; field = null;
@ -887,8 +907,7 @@ public class HttpFields implements Iterable<HttpField>
if (f.getHeader() == name) if (f.getHeader() == name)
{ {
removed = f; removed = f;
_size--; remove(i);
System.arraycopy(_fields, i + 1, _fields, i, _size - i);
} }
} }
return removed; return removed;
@ -906,16 +925,22 @@ public class HttpFields implements Iterable<HttpField>
for (int i = _size; i-- > 0; ) for (int i = _size; i-- > 0; )
{ {
HttpField f = _fields[i]; HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name)) if (f.is(name))
{ {
removed = f; removed = f;
_size--; remove(i);
System.arraycopy(_fields, i + 1, _fields, i, _size - i);
} }
} }
return removed; return removed;
} }
private void remove(int i)
{
_size--;
System.arraycopy(_fields, i + 1, _fields, i, _size - i);
_fields[_size] = null;
}
/** /**
* Get a header as an long value. Returns the value of an integer field or -1 if not found. The * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
* case of the field name is ignored. * case of the field name is ignored.
@ -1307,8 +1332,7 @@ public class HttpFields implements Iterable<HttpField>
{ {
if (_current < 0) if (_current < 0)
throw new IllegalStateException(); throw new IllegalStateException();
_size--; HttpFields.this.remove(_current);
System.arraycopy(_fields, _current + 1, _fields, _current, _size - _current);
_fields[_size] = null; _fields[_size] = null;
_cursor = _current; _cursor = _current;
_current = -1; _current = -1;

View File

@ -866,4 +866,32 @@ public class HttpFieldsTest
assertThat(header.stream().count(), is(3L)); assertThat(header.stream().count(), is(3L));
assertThat(header.stream().map(HttpField::getName).filter("name2"::equalsIgnoreCase).count(), is(1L)); assertThat(header.stream().map(HttpField::getName).filter("name2"::equalsIgnoreCase).count(), is(1L));
} }
@Test
public void testCompute()
{
HttpFields header = new HttpFields();
assertThat(header.size(), is(0));
header.computeField("Test", (n, f) -> null);
assertThat(header.size(), is(0));
header.add(new HttpField("Before", "value"));
assertThat(header.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value"));
header.computeField("Test", (n, f) -> new HttpField(n, "one"));
assertThat(header.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "Test: one"));
header.add(new HttpField("After", "value"));
assertThat(header.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "Test: one", "After: value"));
header.add(new HttpField("Test", "extra"));
assertThat(header.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "Test: one", "After: value", "Test: extra"));
header.computeField("Test", (n, f) -> new HttpField("TEST", "count=" + f.size()));
assertThat(header.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "TEST: count=2", "After: value"));
header.computeField("TEST", (n, f) -> null);
assertThat(header.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Before: value", "After: value"));
}
} }