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:
commit
aa2064f02f
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,63 +21,72 @@ 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
|
||||
* 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) -> {
|
||||
for (int i=0;i<preferredOrder.length;++i)
|
||||
this((s) ->
|
||||
{
|
||||
for (int i = 0; i < preferredOrder.length; ++i)
|
||||
if (preferredOrder[i].equals(s))
|
||||
return preferredOrder.length-i;
|
||||
return preferredOrder.length - i;
|
||||
|
||||
if ("*".equals(s))
|
||||
return preferredOrder.length;
|
||||
|
@ -87,51 +96,57 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void parsedValue(StringBuffer buffer)
|
||||
{
|
||||
super.parsedValue(buffer);
|
||||
_quality.add(ONE);
|
||||
|
||||
// Assume a quality of ONE
|
||||
_quality.add(1.0D);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
|
||||
{
|
||||
if (paramName<0)
|
||||
if (paramName < 0)
|
||||
{
|
||||
if (buffer.charAt(buffer.length()-1)==';')
|
||||
buffer.setLength(buffer.length()-1);
|
||||
if (buffer.charAt(buffer.length() - 1) == ';')
|
||||
buffer.setLength(buffer.length() - 1);
|
||||
}
|
||||
else if (paramValue>=0 &&
|
||||
buffer.charAt(paramName)=='q' && paramValue>paramName &&
|
||||
buffer.length()>=paramName && buffer.charAt(paramName+1)=='=')
|
||||
else if (paramValue >= 0 &&
|
||||
buffer.charAt(paramName) == 'q' && paramValue > paramName &&
|
||||
buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=')
|
||||
{
|
||||
Double q;
|
||||
try
|
||||
{
|
||||
q=(_keepQuotes && buffer.charAt(paramValue)=='"')
|
||||
? (Double) Double.parseDouble(buffer.substring(paramValue + 1, buffer.length() - 1))
|
||||
: (Double) Double.parseDouble(buffer.substring(paramValue));
|
||||
q = (_keepQuotes && buffer.charAt(paramValue) == '"')
|
||||
? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1))
|
||||
: Double.valueOf(buffer.substring(paramValue));
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
q=ZERO;
|
||||
}
|
||||
buffer.setLength(Math.max(0,paramName-1));
|
||||
|
||||
if (!ONE.equals(q))
|
||||
_quality.set(_quality.size()-1,q);
|
||||
Log.getLogger(QuotedQualityCSV.class).ignore(e);
|
||||
q = 0.0D;
|
||||
}
|
||||
buffer.setLength(Math.max(0, paramName - 1));
|
||||
|
||||
if (q != 1.0D)
|
||||
// replace assumed quality
|
||||
_quality.set(_quality.size() - 1, q);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +157,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
sort();
|
||||
return _values;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator()
|
||||
{
|
||||
|
@ -153,35 +168,35 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
|
||||
protected void sort()
|
||||
{
|
||||
_sorted=true;
|
||||
_sorted = true;
|
||||
|
||||
Double last = ZERO;
|
||||
Double last = 0.0D;
|
||||
int lastSecondaryOrder = Integer.MIN_VALUE;
|
||||
|
||||
for (int i = _values.size(); i-- > 0;)
|
||||
for (int i = _values.size(); i-- > 0; )
|
||||
{
|
||||
String v = _values.get(i);
|
||||
Double q = _quality.get(i);
|
||||
|
||||
int compare=last.compareTo(q);
|
||||
if (compare>0 || (compare==0 && _secondaryOrdering.apply(v)<lastSecondaryOrder))
|
||||
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 = ZERO;
|
||||
lastSecondaryOrder=0;
|
||||
last = 0.0D;
|
||||
lastSecondaryOrder = 0;
|
||||
i = _values.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
last=q;
|
||||
lastSecondaryOrder=_secondaryOrdering.apply(v);
|
||||
last = q;
|
||||
lastSecondaryOrder = _secondaryOrdering.applyAsInt(v);
|
||||
}
|
||||
|
||||
int last_element=_quality.size();
|
||||
while(last_element>0 && _quality.get(--last_element).equals(ZERO))
|
||||
|
||||
int last_element = _quality.size();
|
||||
while (last_element > 0 && _quality.get(--last_element).equals(0.0D))
|
||||
{
|
||||
_quality.remove(last_element);
|
||||
_values.remove(last_element);
|
||||
|
|
|
@ -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/*","*/*"));
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue