From 10e531756b972162eed402c44d0244f7f6b85131 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 18 Feb 2021 07:14:38 -0600 Subject: [PATCH] Merge pull request from GHSA-m394-8rww-3jr7 Use comparator based sort Signed-off-by: Joakim Erdfelt Signed-off-by: gregw Co-authored-by: gregw --- .../eclipse/jetty/http/QuotedQualityCSV.java | 124 +++++++++++++----- 1 file changed, 89 insertions(+), 35 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java index da0d5a62a41..5bc998518f9 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java @@ -21,12 +21,12 @@ package org.eclipse.jetty.http; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.function.ToIntFunction; +import java.util.stream.Collectors; import org.eclipse.jetty.util.log.Log; -import static java.lang.Integer.MIN_VALUE; - /** * Implements a quoted comma separated list of quality values * in accordance with RFC7230 and RFC7231. @@ -55,7 +55,8 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable return 3; }; - private final List _quality = new ArrayList<>(); + private final List _qualities = new ArrayList<>(); + private QualityValue _lastQuality; private boolean _sorted = false; private final ToIntFunction _secondaryOrdering; @@ -64,7 +65,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable */ public QuotedQualityCSV() { - this((ToIntFunction)null); + this((ToIntFunction)null); } /** @@ -85,32 +86,48 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable if ("*".equals(s)) return preferredOrder.length; - return MIN_VALUE; + return 0; }); } /** * Orders values with equal quality with the given function. * - * @param secondaryOrdering Function to apply an ordering other than specified by quality + * @param secondaryOrdering Function to apply an ordering other than specified by quality, highest values are sorted first. */ public QuotedQualityCSV(ToIntFunction secondaryOrdering) { this._secondaryOrdering = secondaryOrdering == null ? s -> 0 : secondaryOrdering; } + @Override + protected void parsedValueAndParams(StringBuffer buffer) + { + super.parsedValueAndParams(buffer); + + // Collect full value with parameters + _lastQuality = new QualityValue(_lastQuality._quality, buffer.toString(), _lastQuality._index); + _qualities.set(_lastQuality._index, _lastQuality); + } + @Override protected void parsedValue(StringBuffer buffer) { super.parsedValue(buffer); + _sorted = false; + + // This is the just the value, without parameters. // Assume a quality of ONE - _quality.add(1.0D); + _lastQuality = new QualityValue(1.0D, buffer.toString(), _qualities.size()); + _qualities.add(_lastQuality); } @Override protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) { + _sorted = false; + if (paramName < 0) { if (buffer.charAt(buffer.length() - 1) == ';') @@ -120,7 +137,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable buffer.charAt(paramName) == 'q' && paramValue > paramName && buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=') { - Double q; + double q; try { q = (_keepQuotes && buffer.charAt(paramValue) == '"') @@ -135,8 +152,10 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable buffer.setLength(Math.max(0, paramName - 1)); if (q != 1.0D) - // replace assumed quality - _quality.set(_quality.size() - 1, q); + { + _lastQuality = new QualityValue(q, buffer.toString(), _lastQuality._index); + _qualities.set(_lastQuality._index, _lastQuality); + } } } @@ -158,38 +177,73 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable protected void sort() { + _values.clear(); + _qualities.stream() + .filter((qv) -> qv._quality != 0.0D) + .sorted() + .map(QualityValue::getValue) + .collect(Collectors.toCollection(() -> _values)); _sorted = true; + } - Double last = 0.0D; - int lastSecondaryOrder = Integer.MIN_VALUE; + private class QualityValue implements Comparable + { + private final double _quality; + private final String _value; + private final int _index; - for (int i = _values.size(); i-- > 0; ) + private QualityValue(double quality, String value, int index) { - String v = _values.get(i); - Double q = _quality.get(i); - - int compare = last.compareTo(q); - if (compare > 0 || (compare == 0 && _secondaryOrdering.applyAsInt(v) < lastSecondaryOrder)) - { - _values.set(i, _values.get(i + 1)); - _values.set(i + 1, v); - _quality.set(i, _quality.get(i + 1)); - _quality.set(i + 1, q); - last = 0.0D; - lastSecondaryOrder = 0; - i = _values.size(); - continue; - } - - last = q; - lastSecondaryOrder = _secondaryOrdering.applyAsInt(v); + _quality = quality; + _value = value; + _index = index; } - int lastElement = _quality.size(); - while (lastElement > 0 && _quality.get(--lastElement).equals(0.0D)) + @Override + public int hashCode() { - _quality.remove(lastElement); - _values.remove(lastElement); + return Double.hashCode(_quality) ^ Objects.hash(_value, _index); + } + + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof QualityValue)) + return false; + QualityValue qv = (QualityValue)obj; + return _quality == qv._quality && Objects.equals(_value, qv._value) && Objects.equals(_index, qv._index); + } + + private String getValue() + { + return _value; + } + + @Override + public int compareTo(QualityValue o) + { + // sort highest quality first + int compare = Double.compare(o._quality, _quality); + if (compare == 0) + { + // then sort secondary order highest first + compare = Integer.compare(_secondaryOrdering.applyAsInt(o._value), _secondaryOrdering.applyAsInt(_value)); + if (compare == 0) + // then sort index lowest first + compare = -Integer.compare(o._index, _index); + } + return compare; + } + + @Override + public String toString() + { + return String.format("%s@%x[%s,q=%f,i=%d]", + getClass().getSimpleName(), + hashCode(), + _value, + _quality, + _index); } } }