From 8011c48685e78850a88e7cc06650f678a375037d Mon Sep 17 00:00:00 2001
From: Greg Wilkins <gregw@webtide.com>
Date: Thu, 28 Feb 2019 18:37:02 +1100
Subject: [PATCH 1/4] Issue #3404 Updated QCSV Double usage

Signed-off-by: Greg Wilkins <gregw@webtide.com>
---
 .../eclipse/jetty/http/QuotedQualityCSV.java  | 58 ++++++++++---------
 1 file changed, 30 insertions(+), 28 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 61cee88bfd5..c7047c57a4b 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
@@ -25,6 +25,8 @@
 import java.util.List;
 import java.util.function.Function;
 
+import org.eclipse.jetty.util.log.Log;
+
 /* ------------------------------------------------------------ */
 /**
  * Implements a quoted comma separated list of quality values
@@ -37,21 +39,16 @@
  */
 public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
 {    
-    private final static Double ZERO=new Double(0.0);
-    private final static Double ONE=new Double(1.0);
-    
+    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
      */
-    public static Function<String, Integer> MOST_SPECIFIC = new Function<String, Integer>()
+    public static Function<String, Integer> MOST_SPECIFIC = s ->
     {
-        @Override
-        public Integer apply(String s)
-        {
-            String[] elements = s.split("/");
-            return 1000000*elements.length+1000*elements[0].length()+elements[elements.length-1].length();
-        }
+        String[] elements = s.split("/");
+        return 1000000*elements.length+1000*elements[0].length()+elements[elements.length-1].length();
     };
     
     private final List<Double> _quality = new ArrayList<>();
@@ -74,7 +71,8 @@ public QuotedQualityCSV()
      */
     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;
@@ -101,6 +99,8 @@ public QuotedQualityCSV(Function<String, Integer> secondaryOrdering)
     protected void parsedValue(StringBuffer buffer)
     {
         super.parsedValue(buffer);
+
+        // Assume a quality of ONE
         _quality.add(ONE);
     }
 
@@ -108,30 +108,32 @@ protected void parsedValue(StringBuffer buffer)
     @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)=='"')
-                    ?new Double(buffer.substring(paramValue+1,buffer.length()-1))
-                    :new Double(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 = ZERO;
+            }
+            buffer.setLength(Math.max(0, paramName - 1));
+
+            if (!ONE.equals(q))
+                // replace assumed quality
+                _quality.set(_quality.size() - 1, q);
         }
     }
 

From ca8a10a9d5a50e7d814c65134144f776bff1c07a Mon Sep 17 00:00:00 2001
From: Greg Wilkins <gregw@webtide.com>
Date: Fri, 1 Mar 2019 10:17:02 +1100
Subject: [PATCH 2/4] Issue #3404 Cleanup QCSV mime ordering

Signed-off-by: Greg Wilkins <gregw@webtide.com>
---
 .../org/eclipse/jetty/http/HttpFields.java    | 16 ++++++++-
 .../eclipse/jetty/http/QuotedQualityCSV.java  | 35 ++++++++++---------
 .../jetty/http/QuotedQualityCSVTest.java      |  2 +-
 .../jetty/server/handler/ErrorHandler.java    |  3 +-
 4 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
index 14c4335eb5a..5a8b53013a7 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
@@ -31,6 +31,7 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.function.Function;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
@@ -437,6 +438,19 @@ protected String addCSV(QuotedCSV existing,String... values)
      * @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, Function<String, Integer> secondaryOrdering)
     {
         QuotedQualityCSV values = null;
         for (HttpField f : this)
@@ -444,7 +458,7 @@ protected String addCSV(QuotedCSV existing,String... values)
             if (f.getHeader()==header)
             {
                 if (values==null)
-                    values = new QuotedQualityCSV();
+                    values = new QuotedQualityCSV(secondaryOrdering);
                 values.addValue(f.getValue());
             }
         }
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 c7047c57a4b..498d4e01491 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
@@ -38,17 +38,20 @@
  * @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;
-
+{
     /**
-     * Lambda 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 = s ->
+    public static Function<String, Integer> MOST_SPECIFIC_MIME_ORDERING = 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<>();
@@ -61,7 +64,7 @@
      */
     public QuotedQualityCSV()
     {
-        this((s) -> 0);
+        this((Function)null);
     }
 
     /* ------------------------------------------------------------ */
@@ -91,7 +94,7 @@ public QuotedQualityCSV(String[] preferredOrder)
      */
     public QuotedQualityCSV(Function<String, Integer> secondaryOrdering)
     {
-        this._secondaryOrdering = secondaryOrdering;
+        this._secondaryOrdering = secondaryOrdering == null ? s->0 : secondaryOrdering;
     }
     
     /* ------------------------------------------------------------ */
@@ -101,7 +104,7 @@ protected void parsedValue(StringBuffer buffer)
         super.parsedValue(buffer);
 
         // Assume a quality of ONE
-        _quality.add(ONE);
+        _quality.add(1.0D);
     }
 
     /* ------------------------------------------------------------ */
@@ -127,11 +130,11 @@ else if (paramValue >= 0 &&
             catch (Exception e)
             {
                 Log.getLogger(QuotedQualityCSV.class).ignore(e);
-                q = ZERO;
+                q = 0.0D;
             }
             buffer.setLength(Math.max(0, paramName - 1));
 
-            if (!ONE.equals(q))
+            if (!((Double)1.0D).equals(q))
                 // replace assumed quality
                 _quality.set(_quality.size() - 1, q);
         }
@@ -157,7 +160,7 @@ protected void sort()
     {
         _sorted=true;
 
-        Double last = ZERO;
+        Double last = 0.0D;
         int lastSecondaryOrder = Integer.MIN_VALUE;
 
         for (int i = _values.size(); i-- > 0;)
@@ -172,7 +175,7 @@ protected void sort()
                 _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;
@@ -183,7 +186,7 @@ protected void sort()
         }
         
         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);
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java
index ba0db4a8972..f03657ba3e5 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java
@@ -61,7 +61,7 @@ public void test7231_5_3_2_example3()
     @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/*","*/*"));
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
index 84088146cd3..2820dca3a0c 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
@@ -38,6 +38,7 @@
 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;
@@ -159,7 +160,7 @@ else if (old_error_page!=null && old_error_page.equals(error_page))
     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))
         {

From b925380ede948ab7d8757e95a0ce384b2441625b Mon Sep 17 00:00:00 2001
From: Greg Wilkins <gregw@webtide.com>
Date: Fri, 1 Mar 2019 10:20:10 +1100
Subject: [PATCH 3/4] Issue #3404 Updated QCSV Double usage

Signed-off-by: Greg Wilkins <gregw@webtide.com>
---
 .../src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 498d4e01491..a7407d34199 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
@@ -134,7 +134,7 @@ else if (paramValue >= 0 &&
             }
             buffer.setLength(Math.max(0, paramName - 1));
 
-            if (!((Double)1.0D).equals(q))
+            if (q!=1.0D)
                 // replace assumed quality
                 _quality.set(_quality.size() - 1, q);
         }

From 69f6b3b6164228485744918a28ab6a0d4c3facae Mon Sep 17 00:00:00 2001
From: Greg Wilkins <gregw@webtide.com>
Date: Tue, 5 Mar 2019 08:59:55 +1100
Subject: [PATCH 4/4] Issue #3404

updates after review:
 + use ToIntFunction
 + reformat

Signed-off-by: Greg Wilkins <gregw@webtide.com>
---
 .../org/eclipse/jetty/http/HttpFields.java    |  4 +-
 .../eclipse/jetty/http/QuotedQualityCSV.java  | 62 +++++++++++--------
 2 files changed, 37 insertions(+), 29 deletions(-)

diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
index 5a8b53013a7..395b87bde35 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
@@ -31,7 +31,7 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.function.Function;
+import java.util.function.ToIntFunction;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
@@ -450,7 +450,7 @@ protected String addCSV(QuotedCSV existing,String... values)
      * @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, Function<String, Integer> secondaryOrdering)
+    public List<String> getQualityCSV(HttpHeader header, ToIntFunction<String> secondaryOrdering)
     {
         QuotedQualityCSV values = null;
         for (HttpField f : this)
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 a7407d34199..d148d9e65e1 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
@@ -18,21 +18,23 @@
 
 package org.eclipse.jetty.http;
 
-import static java.lang.Integer.MIN_VALUE;
-
 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;
+
 /* ------------------------------------------------------------ */
+
 /**
  * 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"
@@ -41,44 +43,48 @@
 {
     /**
      * 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_MIME_ORDERING = s ->
+    public static ToIntFunction<String> MOST_SPECIFIC_MIME_ORDERING = s ->
     {
         if ("*/*".equals(s))
             return 0;
         if (s.endsWith("/*"))
             return 1;
-        if (s.indexOf(';')<0)
+        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((Function)null);
+        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)
+            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;
@@ -88,15 +94,17 @@ public QuotedQualityCSV(String[] preferredOrder)
     }
 
     /* ------------------------------------------------------------ */
+
     /**
      * 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 == null ? s->0 : secondaryOrdering;
+        this._secondaryOrdering = secondaryOrdering == null ? s -> 0 : secondaryOrdering;
     }
-    
+
     /* ------------------------------------------------------------ */
     @Override
     protected void parsedValue(StringBuffer buffer)
@@ -134,7 +142,7 @@ else if (paramValue >= 0 &&
             }
             buffer.setLength(Math.max(0, paramName - 1));
 
-            if (q!=1.0D)
+            if (q != 1.0D)
                 // replace assumed quality
                 _quality.set(_quality.size() - 1, q);
         }
@@ -147,7 +155,7 @@ else if (paramValue >= 0 &&
             sort();
         return _values;
     }
-    
+
     @Override
     public Iterator<String> iterator()
     {
@@ -158,35 +166,35 @@ else if (paramValue >= 0 &&
 
     protected void sort()
     {
-        _sorted=true;
+        _sorted = true;
 
         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 = 0.0D;
-                lastSecondaryOrder=0;
+                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(0.0D))
+
+        int last_element = _quality.size();
+        while (last_element > 0 && _quality.get(--last_element).equals(0.0D))
         {
             _quality.remove(last_element);
             _values.remove(last_element);
