Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2019-03-05 10:40:52 +11:00
commit aa2064f02f
4 changed files with 87 additions and 57 deletions

View File

@ -30,6 +30,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.ToIntFunction;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -419,6 +420,19 @@ public class HttpFields implements Iterable<HttpField>
* @param header The header
*/
public List<String> getQualityCSV(HttpHeader header)
{
return getQualityCSV(header,null);
}
/**
* Get multiple field values of the same name, split and
* sorted as a {@link QuotedQualityCSV}
*
* @param header The header
* @param secondaryOrdering Function to apply an ordering other than specified by quality
* @return List the values in quality order with the q param and OWS stripped
*/
public List<String> getQualityCSV(HttpHeader header, ToIntFunction<String> secondaryOrdering)
{
QuotedQualityCSV values = null;
for (HttpField f : this)
@ -426,7 +440,7 @@ public class HttpFields implements Iterable<HttpField>
if (f.getHeader()==header)
{
if (values==null)
values = new QuotedQualityCSV();
values = new QuotedQualityCSV(secondaryOrdering);
values.addValue(f.getValue());
}
}

View File

@ -21,60 +21,69 @@ package org.eclipse.jetty.http;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import org.eclipse.jetty.util.log.Log;
import static java.lang.Integer.MIN_VALUE;
import static java.lang.Integer.MIN_VALUE;
/* ------------------------------------------------------------ */
/**
* Implements a quoted comma separated list of quality values
* in accordance with RFC7230 and RFC7231.
* Values are returned sorted in quality order, with OWS and the
* quality parameters removed.
*
* @see "https://tools.ietf.org/html/rfc7230#section-3.2.6"
* @see "https://tools.ietf.org/html/rfc7230#section-7"
* @see "https://tools.ietf.org/html/rfc7231#section-5.3.1"
*/
public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
{
private final static Double ZERO = 0.0D;
private final static Double ONE = 1.0D;
/**
* Function to apply a most specific MIME encoding secondary ordering
* Lambda to apply a most specific MIME encoding secondary ordering.
*
* @see "https://tools.ietf.org/html/rfc7231#section-5.3.2"
*/
public static Function<String, Integer> MOST_SPECIFIC = new Function<String, Integer>()
public static ToIntFunction<String> MOST_SPECIFIC_MIME_ORDERING = s ->
{
@Override
public Integer apply(String s)
{
String[] elements = s.split("/");
return 1000000*elements.length+1000*elements[0].length()+elements[elements.length-1].length();
}
if ("*/*".equals(s))
return 0;
if (s.endsWith("/*"))
return 1;
if (s.indexOf(';') < 0)
return 2;
return 3;
};
private final List<Double> _quality = new ArrayList<>();
private boolean _sorted = false;
private final Function<String, Integer> _secondaryOrdering;
private final ToIntFunction<String> _secondaryOrdering;
/* ------------------------------------------------------------ */
/**
* Sorts values with equal quality according to the length of the value String.
*/
public QuotedQualityCSV()
{
this((s) -> 0);
this((ToIntFunction)null);
}
/* ------------------------------------------------------------ */
/**
* Sorts values with equal quality according to given order.
*
* @param preferredOrder Array indicating the preferred order of known values
*/
public QuotedQualityCSV(String[] preferredOrder)
{
this((s) -> {
this((s) ->
{
for (int i = 0; i < preferredOrder.length; ++i)
if (preferredOrder[i].equals(s))
return preferredOrder.length - i;
@ -87,13 +96,15 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
}
/* ------------------------------------------------------------ */
/**
* Orders values with equal quality with the given function.
*
* @param secondaryOrdering Function to apply an ordering other than specified by quality
*/
public QuotedQualityCSV(Function<String, Integer> secondaryOrdering)
public QuotedQualityCSV(ToIntFunction<String> secondaryOrdering)
{
this._secondaryOrdering = secondaryOrdering;
this._secondaryOrdering = secondaryOrdering == null ? s -> 0 : secondaryOrdering;
}
/* ------------------------------------------------------------ */
@ -101,7 +112,9 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
protected void parsedValue(StringBuffer buffer)
{
super.parsedValue(buffer);
_quality.add(ONE);
// Assume a quality of ONE
_quality.add(1.0D);
}
/* ------------------------------------------------------------ */
@ -121,16 +134,18 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
try
{
q = (_keepQuotes && buffer.charAt(paramValue) == '"')
? (Double) Double.parseDouble(buffer.substring(paramValue + 1, buffer.length() - 1))
: (Double) Double.parseDouble(buffer.substring(paramValue));
? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1))
: Double.valueOf(buffer.substring(paramValue));
}
catch (Exception e)
{
q=ZERO;
Log.getLogger(QuotedQualityCSV.class).ignore(e);
q = 0.0D;
}
buffer.setLength(Math.max(0, paramName - 1));
if (!ONE.equals(q))
if (q != 1.0D)
// replace assumed quality
_quality.set(_quality.size() - 1, q);
}
}
@ -155,7 +170,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
{
_sorted = true;
Double last = ZERO;
Double last = 0.0D;
int lastSecondaryOrder = Integer.MIN_VALUE;
for (int i = _values.size(); i-- > 0; )
@ -164,24 +179,24 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
Double q = _quality.get(i);
int compare = last.compareTo(q);
if (compare>0 || (compare==0 && _secondaryOrdering.apply(v)<lastSecondaryOrder))
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 = ZERO;
last = 0.0D;
lastSecondaryOrder = 0;
i = _values.size();
continue;
}
last = q;
lastSecondaryOrder=_secondaryOrdering.apply(v);
lastSecondaryOrder = _secondaryOrdering.applyAsInt(v);
}
int last_element = _quality.size();
while(last_element>0 && _quality.get(--last_element).equals(ZERO))
while (last_element > 0 && _quality.get(--last_element).equals(0.0D))
{
_quality.remove(last_element);
_values.remove(last_element);

View File

@ -61,7 +61,7 @@ public class QuotedQualityCSVTest
@Test
public void test7231_5_3_2_example3_most_specific()
{
QuotedQualityCSV values = new QuotedQualityCSV(QuotedQualityCSV.MOST_SPECIFIC);
QuotedQualityCSV values = new QuotedQualityCSV(QuotedQualityCSV.MOST_SPECIFIC_MIME_ORDERING);
values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
assertThat(values,Matchers.contains("text/plain;format=flowed","text/plain","text/*","*/*"));

View File

@ -38,6 +38,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.QuotedQualityCSV;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
@ -166,7 +167,7 @@ public class ErrorHandler extends AbstractHandler
protected void generateAcceptableResponse(Request baseRequest, HttpServletRequest request, HttpServletResponse response, int code, String message)
throws IOException
{
List<String> acceptable=baseRequest.getHttpFields().getQualityCSV(HttpHeader.ACCEPT);
List<String> acceptable=baseRequest.getHttpFields().getQualityCSV(HttpHeader.ACCEPT, QuotedQualityCSV.MOST_SPECIFIC_MIME_ORDERING);
if (acceptable.isEmpty() && !baseRequest.getHttpFields().contains(HttpHeader.ACCEPT))
{