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.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.function.ToIntFunction;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
@ -419,6 +420,19 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
* @param header The header
|
* @param header The header
|
||||||
*/
|
*/
|
||||||
public List<String> getQualityCSV(HttpHeader 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;
|
QuotedQualityCSV values = null;
|
||||||
for (HttpField f : this)
|
for (HttpField f : this)
|
||||||
|
@ -426,7 +440,7 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
if (f.getHeader()==header)
|
if (f.getHeader()==header)
|
||||||
{
|
{
|
||||||
if (values==null)
|
if (values==null)
|
||||||
values = new QuotedQualityCSV();
|
values = new QuotedQualityCSV(secondaryOrdering);
|
||||||
values.addValue(f.getValue());
|
values.addValue(f.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,60 +21,69 @@ package org.eclipse.jetty.http;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
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;
|
import static java.lang.Integer.MIN_VALUE;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a quoted comma separated list of quality values
|
* Implements a quoted comma separated list of quality values
|
||||||
* in accordance with RFC7230 and RFC7231.
|
* 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.
|
* quality parameters removed.
|
||||||
|
*
|
||||||
* @see "https://tools.ietf.org/html/rfc7230#section-3.2.6"
|
* @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/rfc7230#section-7"
|
||||||
* @see "https://tools.ietf.org/html/rfc7231#section-5.3.1"
|
* @see "https://tools.ietf.org/html/rfc7231#section-5.3.1"
|
||||||
*/
|
*/
|
||||||
public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
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
|
if ("*/*".equals(s))
|
||||||
public Integer apply(String s)
|
return 0;
|
||||||
{
|
if (s.endsWith("/*"))
|
||||||
String[] elements = s.split("/");
|
return 1;
|
||||||
return 1000000*elements.length+1000*elements[0].length()+elements[elements.length-1].length();
|
if (s.indexOf(';') < 0)
|
||||||
}
|
return 2;
|
||||||
|
return 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
private final List<Double> _quality = new ArrayList<>();
|
private final List<Double> _quality = new ArrayList<>();
|
||||||
private boolean _sorted = false;
|
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.
|
* Sorts values with equal quality according to the length of the value String.
|
||||||
*/
|
*/
|
||||||
public QuotedQualityCSV()
|
public QuotedQualityCSV()
|
||||||
{
|
{
|
||||||
this((s) -> 0);
|
this((ToIntFunction)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts values with equal quality according to given order.
|
* Sorts values with equal quality according to given order.
|
||||||
|
*
|
||||||
* @param preferredOrder Array indicating the preferred order of known values
|
* @param preferredOrder Array indicating the preferred order of known values
|
||||||
*/
|
*/
|
||||||
public QuotedQualityCSV(String[] preferredOrder)
|
public QuotedQualityCSV(String[] preferredOrder)
|
||||||
{
|
{
|
||||||
this((s) -> {
|
this((s) ->
|
||||||
|
{
|
||||||
for (int i = 0; i < preferredOrder.length; ++i)
|
for (int i = 0; i < preferredOrder.length; ++i)
|
||||||
if (preferredOrder[i].equals(s))
|
if (preferredOrder[i].equals(s))
|
||||||
return preferredOrder.length - i;
|
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.
|
* 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
|
||||||
*/
|
*/
|
||||||
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)
|
protected void parsedValue(StringBuffer buffer)
|
||||||
{
|
{
|
||||||
super.parsedValue(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
|
try
|
||||||
{
|
{
|
||||||
q = (_keepQuotes && buffer.charAt(paramValue) == '"')
|
q = (_keepQuotes && buffer.charAt(paramValue) == '"')
|
||||||
? (Double) Double.parseDouble(buffer.substring(paramValue + 1, buffer.length() - 1))
|
? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1))
|
||||||
: (Double) Double.parseDouble(buffer.substring(paramValue));
|
: Double.valueOf(buffer.substring(paramValue));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
q=ZERO;
|
Log.getLogger(QuotedQualityCSV.class).ignore(e);
|
||||||
|
q = 0.0D;
|
||||||
}
|
}
|
||||||
buffer.setLength(Math.max(0, paramName - 1));
|
buffer.setLength(Math.max(0, paramName - 1));
|
||||||
|
|
||||||
if (!ONE.equals(q))
|
if (q != 1.0D)
|
||||||
|
// replace assumed quality
|
||||||
_quality.set(_quality.size() - 1, q);
|
_quality.set(_quality.size() - 1, q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +170,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
||||||
{
|
{
|
||||||
_sorted = true;
|
_sorted = true;
|
||||||
|
|
||||||
Double last = ZERO;
|
Double last = 0.0D;
|
||||||
int lastSecondaryOrder = Integer.MIN_VALUE;
|
int lastSecondaryOrder = Integer.MIN_VALUE;
|
||||||
|
|
||||||
for (int i = _values.size(); i-- > 0; )
|
for (int i = _values.size(); i-- > 0; )
|
||||||
|
@ -164,24 +179,24 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
||||||
Double q = _quality.get(i);
|
Double q = _quality.get(i);
|
||||||
|
|
||||||
int compare = last.compareTo(q);
|
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, _values.get(i + 1));
|
||||||
_values.set(i + 1, v);
|
_values.set(i + 1, v);
|
||||||
_quality.set(i, _quality.get(i + 1));
|
_quality.set(i, _quality.get(i + 1));
|
||||||
_quality.set(i + 1, q);
|
_quality.set(i + 1, q);
|
||||||
last = ZERO;
|
last = 0.0D;
|
||||||
lastSecondaryOrder = 0;
|
lastSecondaryOrder = 0;
|
||||||
i = _values.size();
|
i = _values.size();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
last = q;
|
last = q;
|
||||||
lastSecondaryOrder=_secondaryOrdering.apply(v);
|
lastSecondaryOrder = _secondaryOrdering.applyAsInt(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
int last_element = _quality.size();
|
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);
|
_quality.remove(last_element);
|
||||||
_values.remove(last_element);
|
_values.remove(last_element);
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class QuotedQualityCSVTest
|
||||||
@Test
|
@Test
|
||||||
public void test7231_5_3_2_example3_most_specific()
|
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, */*");
|
values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
|
||||||
|
|
||||||
assertThat(values,Matchers.contains("text/plain;format=flowed","text/plain","text/*","*/*"));
|
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.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
|
import org.eclipse.jetty.http.QuotedQualityCSV;
|
||||||
import org.eclipse.jetty.server.Dispatcher;
|
import org.eclipse.jetty.server.Dispatcher;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Server;
|
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)
|
protected void generateAcceptableResponse(Request baseRequest, HttpServletRequest request, HttpServletResponse response, int code, String message)
|
||||||
throws IOException
|
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))
|
if (acceptable.isEmpty() && !baseRequest.getHttpFields().contains(HttpHeader.ACCEPT))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue