2022-09-14 10:23:06 +08:00
|
|
|
From: Markus Koschany <apo@debian.org>
|
|
|
|
|
Date: Sat, 31 Jul 2021 17:21:57 +0200
|
|
|
|
|
Subject: CVE-2020-27223
|
2021-03-18 16:09:03 +08:00
|
|
|
|
|
|
|
|
---
|
2022-09-14 10:23:06 +08:00
|
|
|
.../java/org/eclipse/jetty/http/QuotedCSV.java | 280 ++-----------------
|
|
|
|
|
.../org/eclipse/jetty/http/QuotedCSVParser.java | 303 +++++++++++++++++++++
|
|
|
|
|
.../org/eclipse/jetty/http/QuotedQualityCSV.java | 140 ++++++----
|
|
|
|
|
.../eclipse/jetty/http/QuotedQualityCSVTest.java | 143 +++++-----
|
|
|
|
|
4 files changed, 479 insertions(+), 387 deletions(-)
|
|
|
|
|
create mode 100644 jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java
|
2021-03-18 16:09:03 +08:00
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java
|
|
|
|
|
index 9ca7dbe..a356213 100644
|
|
|
|
|
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java
|
|
|
|
|
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java
|
|
|
|
|
@@ -1,6 +1,6 @@
|
|
|
|
|
//
|
|
|
|
|
// ========================================================================
|
|
|
|
|
-// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
|
|
|
|
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// All rights reserved. This program and the accompanying materials
|
|
|
|
|
// are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
|
@@ -22,236 +22,36 @@ import java.util.ArrayList;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
-import org.eclipse.jetty.util.QuotedStringTokenizer;
|
|
|
|
|
-
|
|
|
|
|
-/* ------------------------------------------------------------ */
|
|
|
|
|
/**
|
|
|
|
|
* Implements a quoted comma separated list of values
|
|
|
|
|
* in accordance with RFC7230.
|
|
|
|
|
* OWS is removed and quoted characters ignored for parsing.
|
|
|
|
|
+ *
|
|
|
|
|
* @see "https://tools.ietf.org/html/rfc7230#section-3.2.6"
|
|
|
|
|
* @see "https://tools.ietf.org/html/rfc7230#section-7"
|
|
|
|
|
*/
|
|
|
|
|
-public class QuotedCSV implements Iterable<String>
|
|
|
|
|
-{
|
|
|
|
|
- private enum State { VALUE, PARAM_NAME, PARAM_VALUE};
|
|
|
|
|
-
|
|
|
|
|
+public class QuotedCSV extends QuotedCSVParser implements Iterable<String>
|
|
|
|
|
+{
|
|
|
|
|
protected final List<String> _values = new ArrayList<>();
|
|
|
|
|
- protected final boolean _keepQuotes;
|
|
|
|
|
-
|
|
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
+
|
|
|
|
|
public QuotedCSV(String... values)
|
|
|
|
|
{
|
|
|
|
|
- this(true,values);
|
|
|
|
|
+ this(true, values);
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
- public QuotedCSV(boolean keepQuotes,String... values)
|
|
|
|
|
- {
|
|
|
|
|
- _keepQuotes=keepQuotes;
|
|
|
|
|
- for (String v:values)
|
|
|
|
|
- addValue(v);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
- /** Add and parse a value string(s)
|
|
|
|
|
- * @param value A value that may contain one or more Quoted CSV items.
|
|
|
|
|
- */
|
|
|
|
|
- public void addValue(String value)
|
|
|
|
|
+
|
|
|
|
|
+ public QuotedCSV(boolean keepQuotes, String... values)
|
|
|
|
|
{
|
|
|
|
|
- if (value == null)
|
|
|
|
|
- return;
|
|
|
|
|
-
|
|
|
|
|
- StringBuffer buffer = new StringBuffer();
|
|
|
|
|
-
|
|
|
|
|
- int l=value.length();
|
|
|
|
|
- State state=State.VALUE;
|
|
|
|
|
- boolean quoted=false;
|
|
|
|
|
- boolean sloshed=false;
|
|
|
|
|
- int nws_length=0;
|
|
|
|
|
- int last_length=0;
|
|
|
|
|
- int value_length=-1;
|
|
|
|
|
- int param_name=-1;
|
|
|
|
|
- int param_value=-1;
|
|
|
|
|
-
|
|
|
|
|
- for (int i=0;i<=l;i++)
|
|
|
|
|
+ super(keepQuotes);
|
|
|
|
|
+ for (String v : values)
|
|
|
|
|
{
|
|
|
|
|
- char c=i==l?0:value.charAt(i);
|
|
|
|
|
-
|
|
|
|
|
- // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
|
|
|
- if (quoted && c!=0)
|
|
|
|
|
- {
|
|
|
|
|
- if (sloshed)
|
|
|
|
|
- sloshed=false;
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- switch(c)
|
|
|
|
|
- {
|
|
|
|
|
- case '\\':
|
|
|
|
|
- sloshed=true;
|
|
|
|
|
- if (!_keepQuotes)
|
|
|
|
|
- continue;
|
|
|
|
|
- break;
|
|
|
|
|
- case '"':
|
|
|
|
|
- quoted=false;
|
|
|
|
|
- if (!_keepQuotes)
|
|
|
|
|
- continue;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Handle common cases
|
|
|
|
|
- switch(c)
|
|
|
|
|
- {
|
|
|
|
|
- case ' ':
|
|
|
|
|
- case '\t':
|
|
|
|
|
- if (buffer.length()>last_length) // not leading OWS
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- case '"':
|
|
|
|
|
- quoted=true;
|
|
|
|
|
- if (_keepQuotes)
|
|
|
|
|
- {
|
|
|
|
|
- if (state==State.PARAM_VALUE && param_value<0)
|
|
|
|
|
- param_value=nws_length;
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- }
|
|
|
|
|
- else if (state==State.PARAM_VALUE && param_value<0)
|
|
|
|
|
- param_value=nws_length;
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- case ';':
|
|
|
|
|
- buffer.setLength(nws_length); // trim following OWS
|
|
|
|
|
- if (state==State.VALUE)
|
|
|
|
|
- {
|
|
|
|
|
- parsedValue(buffer);
|
|
|
|
|
- value_length=buffer.length();
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- parsedParam(buffer,value_length,param_name,param_value);
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- param_name=param_value=-1;
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- last_length=++nws_length;
|
|
|
|
|
- state=State.PARAM_NAME;
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- case ',':
|
|
|
|
|
- case 0:
|
|
|
|
|
- if (nws_length>0)
|
|
|
|
|
- {
|
|
|
|
|
- buffer.setLength(nws_length); // trim following OWS
|
|
|
|
|
- switch(state)
|
|
|
|
|
- {
|
|
|
|
|
- case VALUE:
|
|
|
|
|
- parsedValue(buffer);
|
|
|
|
|
- value_length=buffer.length();
|
|
|
|
|
- break;
|
|
|
|
|
- case PARAM_NAME:
|
|
|
|
|
- case PARAM_VALUE:
|
|
|
|
|
- parsedParam(buffer,value_length,param_name,param_value);
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- _values.add(buffer.toString());
|
|
|
|
|
- }
|
|
|
|
|
- buffer.setLength(0);
|
|
|
|
|
- last_length=0;
|
|
|
|
|
- nws_length=0;
|
|
|
|
|
- value_length=param_name=param_value=-1;
|
|
|
|
|
- state=State.VALUE;
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- case '=':
|
|
|
|
|
- switch (state)
|
|
|
|
|
- {
|
|
|
|
|
- case VALUE:
|
|
|
|
|
- // It wasn't really a value, it was a param name
|
|
|
|
|
- value_length=param_name=0;
|
|
|
|
|
- buffer.setLength(nws_length); // trim following OWS
|
|
|
|
|
- String param = buffer.toString();
|
|
|
|
|
- buffer.setLength(0);
|
|
|
|
|
- parsedValue(buffer);
|
|
|
|
|
- value_length=buffer.length();
|
|
|
|
|
- buffer.append(param);
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- last_length=++nws_length;
|
|
|
|
|
- state=State.PARAM_VALUE;
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- case PARAM_NAME:
|
|
|
|
|
- buffer.setLength(nws_length); // trim following OWS
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- last_length=++nws_length;
|
|
|
|
|
- state=State.PARAM_VALUE;
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- case PARAM_VALUE:
|
|
|
|
|
- if (param_value<0)
|
|
|
|
|
- param_value=nws_length;
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- continue;
|
|
|
|
|
-
|
|
|
|
|
- default:
|
|
|
|
|
- {
|
|
|
|
|
- switch (state)
|
|
|
|
|
- {
|
|
|
|
|
- case VALUE:
|
|
|
|
|
- {
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case PARAM_NAME:
|
|
|
|
|
- {
|
|
|
|
|
- if (param_name<0)
|
|
|
|
|
- param_name=nws_length;
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- case PARAM_VALUE:
|
|
|
|
|
- {
|
|
|
|
|
- if (param_value<0)
|
|
|
|
|
- param_value=nws_length;
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- nws_length=buffer.length();
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
+ addValue(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Called when a value has been parsed
|
|
|
|
|
- * @param buffer Containing the trimmed value, which may be mutated
|
|
|
|
|
- */
|
|
|
|
|
- protected void parsedValue(StringBuffer buffer)
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Called when a parameter has been parsed
|
|
|
|
|
- * @param buffer Containing the trimmed value and all parameters, which may be mutated
|
|
|
|
|
- * @param valueLength The length of the value
|
|
|
|
|
- * @param paramName The index of the start of the parameter just parsed
|
|
|
|
|
- * @param paramValue The index of the start of the parameter value just parsed, or -1
|
|
|
|
|
- */
|
|
|
|
|
- protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
|
|
|
|
|
+ @Override
|
|
|
|
|
+ protected void parsedValueAndParams(StringBuffer buffer)
|
|
|
|
|
{
|
|
|
|
|
+ _values.add(buffer.toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int size()
|
|
|
|
|
@@ -268,67 +68,21 @@ public class QuotedCSV implements Iterable<String>
|
|
|
|
|
{
|
|
|
|
|
return _values;
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Override
|
|
|
|
|
public Iterator<String> iterator()
|
|
|
|
|
{
|
|
|
|
|
return _values.iterator();
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
- public static String unquote(String s)
|
|
|
|
|
- {
|
|
|
|
|
- // handle trivial cases
|
|
|
|
|
- int l=s.length();
|
|
|
|
|
- if (s==null || l==0)
|
|
|
|
|
- return s;
|
|
|
|
|
-
|
|
|
|
|
- // Look for any quotes
|
|
|
|
|
- int i=0;
|
|
|
|
|
- for (;i<l;i++)
|
|
|
|
|
- {
|
|
|
|
|
- char c=s.charAt(i);
|
|
|
|
|
- if (c=='"')
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- if (i==l)
|
|
|
|
|
- return s;
|
|
|
|
|
|
|
|
|
|
- boolean quoted=true;
|
|
|
|
|
- boolean sloshed=false;
|
|
|
|
|
- StringBuffer buffer = new StringBuffer();
|
|
|
|
|
- buffer.append(s,0,i);
|
|
|
|
|
- i++;
|
|
|
|
|
- for (;i<l;i++)
|
|
|
|
|
- {
|
|
|
|
|
- char c=s.charAt(i);
|
|
|
|
|
- if (quoted)
|
|
|
|
|
- {
|
|
|
|
|
- if (sloshed)
|
|
|
|
|
- {
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- sloshed=false;
|
|
|
|
|
- }
|
|
|
|
|
- else if (c=='"')
|
|
|
|
|
- quoted=false;
|
|
|
|
|
- else if (c=='\\')
|
|
|
|
|
- sloshed=true;
|
|
|
|
|
- else
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- }
|
|
|
|
|
- else if (c=='"')
|
|
|
|
|
- quoted=true;
|
|
|
|
|
- else
|
|
|
|
|
- buffer.append(c);
|
|
|
|
|
- }
|
|
|
|
|
- return buffer.toString();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
@Override
|
|
|
|
|
public String toString()
|
|
|
|
|
{
|
|
|
|
|
List<String> list = new ArrayList<>();
|
|
|
|
|
for (String s : this)
|
|
|
|
|
+ {
|
|
|
|
|
list.add(s);
|
|
|
|
|
+ }
|
|
|
|
|
return list.toString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java
|
|
|
|
|
new file mode 100644
|
|
|
|
|
index 0000000..7aefcf7
|
|
|
|
|
--- /dev/null
|
|
|
|
|
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java
|
|
|
|
|
@@ -0,0 +1,303 @@
|
|
|
|
|
+//
|
|
|
|
|
+// ========================================================================
|
|
|
|
|
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
|
|
|
|
+// ------------------------------------------------------------------------
|
|
|
|
|
+// All rights reserved. This program and the accompanying materials
|
|
|
|
|
+// are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
|
+// and Apache License v2.0 which accompanies this distribution.
|
|
|
|
|
+//
|
|
|
|
|
+// The Eclipse Public License is available at
|
|
|
|
|
+// http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
|
+//
|
|
|
|
|
+// The Apache License v2.0 is available at
|
|
|
|
|
+// http://www.opensource.org/licenses/apache2.0.php
|
|
|
|
|
+//
|
|
|
|
|
+// You may elect to redistribute this code under either of these licenses.
|
|
|
|
|
+// ========================================================================
|
|
|
|
|
+//
|
|
|
|
|
+
|
|
|
|
|
+package org.eclipse.jetty.http;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Implements a quoted comma separated list parser
|
|
|
|
|
+ * in accordance with RFC7230.
|
|
|
|
|
+ * OWS is removed and quoted characters ignored for parsing.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6"
|
|
|
|
|
+ * @see "https://tools.ietf.org/html/rfc7230#section-7"
|
|
|
|
|
+ */
|
|
|
|
|
+public abstract class QuotedCSVParser
|
|
|
|
|
+{
|
|
|
|
|
+ private enum State
|
|
|
|
|
+ {
|
|
|
|
|
+ VALUE, PARAM_NAME, PARAM_VALUE
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ protected final boolean _keepQuotes;
|
|
|
|
|
+
|
|
|
|
|
+ public QuotedCSVParser(boolean keepQuotes)
|
|
|
|
|
+ {
|
|
|
|
|
+ _keepQuotes = keepQuotes;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static String unquote(String s)
|
|
|
|
|
+ {
|
|
|
|
|
+ // handle trivial cases
|
|
|
|
|
+ int l = s.length();
|
|
|
|
|
+ if (s == null || l == 0)
|
|
|
|
|
+ return s;
|
|
|
|
|
+
|
|
|
|
|
+ // Look for any quotes
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ for (; i < l; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ char c = s.charAt(i);
|
|
|
|
|
+ if (c == '"')
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (i == l)
|
|
|
|
|
+ return s;
|
|
|
|
|
+
|
|
|
|
|
+ boolean quoted = true;
|
|
|
|
|
+ boolean sloshed = false;
|
|
|
|
|
+ StringBuffer buffer = new StringBuffer();
|
|
|
|
|
+ buffer.append(s, 0, i);
|
|
|
|
|
+ i++;
|
|
|
|
|
+ for (; i < l; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ char c = s.charAt(i);
|
|
|
|
|
+ if (quoted)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (sloshed)
|
|
|
|
|
+ {
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ sloshed = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (c == '"')
|
|
|
|
|
+ quoted = false;
|
|
|
|
|
+ else if (c == '\\')
|
|
|
|
|
+ sloshed = true;
|
|
|
|
|
+ else
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (c == '"')
|
|
|
|
|
+ quoted = true;
|
|
|
|
|
+ else
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ }
|
|
|
|
|
+ return buffer.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Add and parse a value string(s)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param value A value that may contain one or more Quoted CSV items.
|
|
|
|
|
+ */
|
|
|
|
|
+ public void addValue(String value)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (value == null)
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ StringBuffer buffer = new StringBuffer();
|
|
|
|
|
+
|
|
|
|
|
+ int l = value.length();
|
|
|
|
|
+ State state = State.VALUE;
|
|
|
|
|
+ boolean quoted = false;
|
|
|
|
|
+ boolean sloshed = false;
|
|
|
|
|
+ int nwsLength = 0;
|
|
|
|
|
+ int lastLength = 0;
|
|
|
|
|
+ int valueLength = -1;
|
|
|
|
|
+ int paramName = -1;
|
|
|
|
|
+ int paramValue = -1;
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i <= l; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ char c = i == l ? 0 : value.charAt(i);
|
|
|
|
|
+
|
|
|
|
|
+ // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
|
|
|
+ if (quoted && c != 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (sloshed)
|
|
|
|
|
+ sloshed = false;
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (c)
|
|
|
|
|
+ {
|
|
|
|
|
+ case '\\':
|
|
|
|
|
+ sloshed = true;
|
|
|
|
|
+ if (!_keepQuotes)
|
|
|
|
|
+ continue;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case '"':
|
|
|
|
|
+ quoted = false;
|
|
|
|
|
+ if (!_keepQuotes)
|
|
|
|
|
+ continue;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Handle common cases
|
|
|
|
|
+ switch (c)
|
|
|
|
|
+ {
|
|
|
|
|
+ case ' ':
|
|
|
|
|
+ case '\t':
|
|
|
|
|
+ if (buffer.length() > lastLength) // not leading OWS
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ case '"':
|
|
|
|
|
+ quoted = true;
|
|
|
|
|
+ if (_keepQuotes)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (state == State.PARAM_VALUE && paramValue < 0)
|
|
|
|
|
+ paramValue = nwsLength;
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (state == State.PARAM_VALUE && paramValue < 0)
|
|
|
|
|
+ paramValue = nwsLength;
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ case ';':
|
|
|
|
|
+ buffer.setLength(nwsLength); // trim following OWS
|
|
|
|
|
+ if (state == State.VALUE)
|
|
|
|
|
+ {
|
|
|
|
|
+ parsedValue(buffer);
|
|
|
|
|
+ valueLength = buffer.length();
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ parsedParam(buffer, valueLength, paramName, paramValue);
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ paramName = paramValue = -1;
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ lastLength = ++nwsLength;
|
|
|
|
|
+ state = State.PARAM_NAME;
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ case ',':
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ if (nwsLength > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ buffer.setLength(nwsLength); // trim following OWS
|
|
|
|
|
+ switch (state)
|
|
|
|
|
+ {
|
|
|
|
|
+ case VALUE:
|
|
|
|
|
+ parsedValue(buffer);
|
|
|
|
|
+ valueLength = buffer.length();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case PARAM_NAME:
|
|
|
|
|
+ case PARAM_VALUE:
|
|
|
|
|
+ parsedParam(buffer, valueLength, paramName, paramValue);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ parsedValueAndParams(buffer);
|
|
|
|
|
+ }
|
|
|
|
|
+ buffer.setLength(0);
|
|
|
|
|
+ lastLength = 0;
|
|
|
|
|
+ nwsLength = 0;
|
|
|
|
|
+ valueLength = paramName = paramValue = -1;
|
|
|
|
|
+ state = State.VALUE;
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ case '=':
|
|
|
|
|
+ switch (state)
|
|
|
|
|
+ {
|
|
|
|
|
+ case VALUE:
|
|
|
|
|
+ // It wasn't really a value, it was a param name
|
|
|
|
|
+ valueLength = paramName = 0;
|
|
|
|
|
+ buffer.setLength(nwsLength); // trim following OWS
|
|
|
|
|
+ String param = buffer.toString();
|
|
|
|
|
+ buffer.setLength(0);
|
|
|
|
|
+ parsedValue(buffer);
|
|
|
|
|
+ valueLength = buffer.length();
|
|
|
|
|
+ buffer.append(param);
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ lastLength = ++nwsLength;
|
|
|
|
|
+ state = State.PARAM_VALUE;
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ case PARAM_NAME:
|
|
|
|
|
+ buffer.setLength(nwsLength); // trim following OWS
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ lastLength = ++nwsLength;
|
|
|
|
|
+ state = State.PARAM_VALUE;
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ case PARAM_VALUE:
|
|
|
|
|
+ if (paramValue < 0)
|
|
|
|
|
+ paramValue = nwsLength;
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (state)
|
|
|
|
|
+ {
|
|
|
|
|
+ case VALUE:
|
|
|
|
|
+ {
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ case PARAM_NAME:
|
|
|
|
|
+ {
|
|
|
|
|
+ if (paramName < 0)
|
|
|
|
|
+ paramName = nwsLength;
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ case PARAM_VALUE:
|
|
|
|
|
+ {
|
|
|
|
|
+ if (paramValue < 0)
|
|
|
|
|
+ paramValue = nwsLength;
|
|
|
|
|
+ buffer.append(c);
|
|
|
|
|
+ nwsLength = buffer.length();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Called when a value and it's parameters has been parsed
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param buffer Containing the trimmed value and parameters
|
|
|
|
|
+ */
|
|
|
|
|
+ protected void parsedValueAndParams(StringBuffer buffer)
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Called when a value has been parsed (prior to any parameters)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param buffer Containing the trimmed value, which may be mutated
|
|
|
|
|
+ */
|
|
|
|
|
+ protected void parsedValue(StringBuffer buffer)
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Called when a parameter has been parsed
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param buffer Containing the trimmed value and all parameters, which may be mutated
|
|
|
|
|
+ * @param valueLength The length of the value
|
|
|
|
|
+ * @param paramName The index of the start of the parameter just parsed
|
|
|
|
|
+ * @param paramValue The index of the start of the parameter value just parsed, or -1
|
|
|
|
|
+ */
|
|
|
|
|
+ protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
2021-03-18 16:09:03 +08:00
|
|
|
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
|
2022-09-14 10:23:06 +08:00
|
|
|
index d148d9e..5bc9985 100644
|
2021-03-18 16:09:03 +08:00
|
|
|
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java
|
|
|
|
|
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java
|
2022-09-14 10:23:06 +08:00
|
|
|
@@ -1,6 +1,6 @@
|
|
|
|
|
//
|
|
|
|
|
// ========================================================================
|
|
|
|
|
-// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
|
|
|
|
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// All rights reserved. This program and the accompanying materials
|
|
|
|
|
// are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
|
@@ -21,14 +21,12 @@ package org.eclipse.jetty.http;
|
2021-03-18 16:09:03 +08:00
|
|
|
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;
|
|
|
|
|
-
|
2022-09-14 10:23:06 +08:00
|
|
|
-/* ------------------------------------------------------------ */
|
|
|
|
|
-
|
2021-03-18 16:09:03 +08:00
|
|
|
/**
|
2022-09-14 10:23:06 +08:00
|
|
|
* Implements a quoted comma separated list of quality values
|
|
|
|
|
* in accordance with RFC7230 and RFC7231.
|
|
|
|
|
@@ -57,22 +55,19 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
2021-03-18 16:09:03 +08:00
|
|
|
return 3;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
- private final List<Double> _quality = new ArrayList<>();
|
|
|
|
|
+ private final List<QualityValue> _qualities = new ArrayList<>();
|
|
|
|
|
+ private QualityValue _lastQuality;
|
|
|
|
|
private boolean _sorted = false;
|
|
|
|
|
private final ToIntFunction<String> _secondaryOrdering;
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
|
|
|
* Sorts values with equal quality according to the length of the value String.
|
2021-03-18 16:09:03 +08:00
|
|
|
*/
|
|
|
|
|
public QuotedQualityCSV()
|
|
|
|
|
{
|
|
|
|
|
- this((ToIntFunction)null);
|
|
|
|
|
+ this((ToIntFunction<String>)null);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
|
|
|
* Sorts values with equal quality according to given order.
|
|
|
|
|
*
|
|
|
|
|
@@ -83,57 +78,71 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|
|
|
|
this((s) ->
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < preferredOrder.length; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
if (preferredOrder[i].equals(s))
|
|
|
|
|
return preferredOrder.length - i;
|
|
|
|
|
+ }
|
|
|
|
|
|
2021-03-18 16:09:03 +08:00
|
|
|
if ("*".equals(s))
|
|
|
|
|
return preferredOrder.length;
|
|
|
|
|
|
|
|
|
|
- return MIN_VALUE;
|
|
|
|
|
+ return 0;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
-
|
2021-03-18 16:09:03 +08:00
|
|
|
/**
|
|
|
|
|
* 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<String> secondaryOrdering)
|
|
|
|
|
{
|
|
|
|
|
this._secondaryOrdering = secondaryOrdering == null ? s -> 0 : secondaryOrdering;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
- /* ------------------------------------------------------------ */
|
2021-03-18 16:09:03 +08:00
|
|
|
+ @Override
|
|
|
|
|
+ protected void parsedValueAndParams(StringBuffer buffer)
|
|
|
|
|
+ {
|
|
|
|
|
+ super.parsedValueAndParams(buffer);
|
|
|
|
|
+
|
2022-09-14 10:23:06 +08:00
|
|
|
+ // Collect full value with parameters
|
|
|
|
|
+ _lastQuality = new QualityValue(_lastQuality._quality, buffer.toString(), _lastQuality._index);
|
|
|
|
|
+ _qualities.set(_lastQuality._index, _lastQuality);
|
2021-03-18 16:09:03 +08:00
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
@Override
|
|
|
|
|
protected void parsedValue(StringBuffer buffer)
|
|
|
|
|
{
|
|
|
|
|
super.parsedValue(buffer);
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
+ _sorted = false;
|
2021-03-18 16:09:03 +08:00
|
|
|
+
|
|
|
|
|
+ // This is the just the value, without parameters.
|
|
|
|
|
// Assume a quality of ONE
|
|
|
|
|
- _quality.add(1.0D);
|
2022-09-14 10:23:06 +08:00
|
|
|
+ _lastQuality = new QualityValue(1.0D, buffer.toString(), _qualities.size());
|
|
|
|
|
+ _qualities.add(_lastQuality);
|
2021-03-18 16:09:03 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
- /* ------------------------------------------------------------ */
|
2021-03-18 16:09:03 +08:00
|
|
|
@Override
|
|
|
|
|
protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
|
|
|
|
|
{
|
2022-09-14 10:23:06 +08:00
|
|
|
+ _sorted = false;
|
2021-03-18 16:09:03 +08:00
|
|
|
+
|
|
|
|
|
if (paramName < 0)
|
|
|
|
|
{
|
|
|
|
|
if (buffer.charAt(buffer.length() - 1) == ';')
|
2022-09-14 10:23:06 +08:00
|
|
|
buffer.setLength(buffer.length() - 1);
|
|
|
|
|
}
|
|
|
|
|
else if (paramValue >= 0 &&
|
|
|
|
|
- buffer.charAt(paramName) == 'q' && paramValue > paramName &&
|
|
|
|
|
- buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=')
|
|
|
|
|
+ buffer.charAt(paramName) == 'q' && paramValue > paramName &&
|
|
|
|
|
+ buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=')
|
2021-03-18 16:09:03 +08:00
|
|
|
{
|
|
|
|
|
- Double q;
|
|
|
|
|
+ double q;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
q = (_keepQuotes && buffer.charAt(paramValue) == '"')
|
2022-09-14 10:23:06 +08:00
|
|
|
- ? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1))
|
|
|
|
|
- : Double.valueOf(buffer.substring(paramValue));
|
|
|
|
|
+ ? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1))
|
|
|
|
|
+ : Double.valueOf(buffer.substring(paramValue));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
@@ -143,8 +152,10 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
2021-03-18 16:09:03 +08:00
|
|
|
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);
|
|
|
|
|
+ }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-14 10:23:06 +08:00
|
|
|
@@ -166,38 +177,73 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
2021-03-18 16:09:03 +08:00
|
|
|
|
|
|
|
|
protected void sort()
|
|
|
|
|
{
|
2022-09-14 10:23:06 +08:00
|
|
|
+ _values.clear();
|
|
|
|
|
+ _qualities.stream()
|
|
|
|
|
+ .filter((qv) -> qv._quality != 0.0D)
|
|
|
|
|
+ .sorted()
|
|
|
|
|
+ .map(QualityValue::getValue)
|
|
|
|
|
+ .collect(Collectors.toCollection(() -> _values));
|
2021-03-18 16:09:03 +08:00
|
|
|
_sorted = true;
|
|
|
|
|
+ }
|
2022-09-14 10:23:06 +08:00
|
|
|
+
|
2021-03-18 16:09:03 +08:00
|
|
|
+ private class QualityValue implements Comparable<QualityValue>
|
|
|
|
|
+ {
|
|
|
|
|
+ private final double _quality;
|
2022-09-14 10:23:06 +08:00
|
|
|
+ private final String _value;
|
|
|
|
|
+ private final int _index;
|
|
|
|
|
|
|
|
|
|
- Double last = 0.0D;
|
|
|
|
|
- int lastSecondaryOrder = Integer.MIN_VALUE;
|
|
|
|
|
+ private QualityValue(double quality, String value, int index)
|
|
|
|
|
+ {
|
|
|
|
|
+ _quality = quality;
|
|
|
|
|
+ _value = value;
|
|
|
|
|
+ _index = index;
|
|
|
|
|
+ }
|
2021-03-18 16:09:03 +08:00
|
|
|
|
|
|
|
|
- for (int i = _values.size(); i-- > 0; )
|
2022-09-14 10:23:06 +08:00
|
|
|
+ @Override
|
|
|
|
|
+ public int hashCode()
|
2021-03-18 16:09:03 +08:00
|
|
|
{
|
|
|
|
|
- String v = _values.get(i);
|
|
|
|
|
- Double q = _quality.get(i);
|
2022-09-14 10:23:06 +08:00
|
|
|
+ return Double.hashCode(_quality) ^ Objects.hash(_value, _index);
|
|
|
|
|
+ }
|
2021-03-18 16:09:03 +08:00
|
|
|
|
|
|
|
|
- int compare = last.compareTo(q);
|
|
|
|
|
- if (compare > 0 || (compare == 0 && _secondaryOrdering.applyAsInt(v) < lastSecondaryOrder))
|
|
|
|
|
+ @Override
|
2022-09-14 10:23:06 +08:00
|
|
|
+ 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)
|
2021-03-18 16:09:03 +08:00
|
|
|
{
|
|
|
|
|
- _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;
|
2022-09-14 10:23:06 +08:00
|
|
|
+ // 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);
|
2021-03-18 16:09:03 +08:00
|
|
|
}
|
2022-09-14 10:23:06 +08:00
|
|
|
-
|
2021-03-18 16:09:03 +08:00
|
|
|
- last = q;
|
|
|
|
|
- lastSecondaryOrder = _secondaryOrdering.applyAsInt(v);
|
2022-09-14 10:23:06 +08:00
|
|
|
+ return compare;
|
2021-03-18 16:09:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- int last_element = _quality.size();
|
|
|
|
|
- while (last_element > 0 && _quality.get(--last_element).equals(0.0D))
|
2022-09-14 10:23:06 +08:00
|
|
|
+ @Override
|
|
|
|
|
+ public String toString()
|
2021-03-18 16:09:03 +08:00
|
|
|
{
|
|
|
|
|
- _quality.remove(last_element);
|
|
|
|
|
- _values.remove(last_element);
|
|
|
|
|
+ return String.format("%s@%x[%s,q=%f,i=%d]",
|
|
|
|
|
+ getClass().getSimpleName(),
|
2022-09-14 10:23:06 +08:00
|
|
|
+ hashCode(),
|
|
|
|
|
+ _value,
|
|
|
|
|
+ _quality,
|
|
|
|
|
+ _index);
|
2021-03-18 16:09:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-14 10:23:06 +08:00
|
|
|
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 f03657b..b941e95 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
|
|
|
|
|
@@ -1,6 +1,6 @@
|
|
|
|
|
//
|
|
|
|
|
// ========================================================================
|
|
|
|
|
-// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
|
|
|
|
+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
// All rights reserved. This program and the accompanying materials
|
|
|
|
|
// are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
|
@@ -18,13 +18,12 @@
|
|
|
|
|
|
|
|
|
|
package org.eclipse.jetty.http;
|
|
|
|
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
|
|
|
|
|
import org.hamcrest.Matchers;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
|
|
|
|
|
-import java.util.ArrayList;
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
-
|
|
|
|
|
import static org.hamcrest.MatcherAssert.assertThat;
|
|
|
|
|
import static org.hamcrest.Matchers.contains;
|
|
|
|
|
|
|
|
|
|
@@ -32,58 +31,58 @@ public class QuotedQualityCSVTest
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
- public void test7231_5_3_2_example1()
|
|
|
|
|
+ public void test7231Sec532Example1()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue(" audio/*; q=0.2, audio/basic");
|
|
|
|
|
- assertThat(values,Matchers.contains("audio/basic","audio/*"));
|
|
|
|
|
+ assertThat(values, Matchers.contains("audio/basic", "audio/*"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
- public void test7231_5_3_2_example2()
|
|
|
|
|
+ public void test7231Sec532Example2()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("text/plain; q=0.5, text/html,");
|
|
|
|
|
values.addValue("text/x-dvi; q=0.8, text/x-c");
|
|
|
|
|
- assertThat(values,Matchers.contains("text/html","text/x-c","text/x-dvi","text/plain"));
|
|
|
|
|
+ assertThat(values, Matchers.contains("text/html", "text/x-c", "text/x-dvi", "text/plain"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
- public void test7231_5_3_2_example3()
|
|
|
|
|
+ public void test7231Sec532Example3()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
// Note this sort is only on quality and not the most specific type as per 5.3.2
|
|
|
|
|
- assertThat(values,Matchers.contains("text/*","text/plain","text/plain;format=flowed","*/*"));
|
|
|
|
|
+ assertThat(values, Matchers.contains("text/*", "text/plain", "text/plain;format=flowed", "*/*"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
- public void test7231_5_3_2_example3_most_specific()
|
|
|
|
|
+ public void test7231532Example3MostSpecific()
|
|
|
|
|
{
|
|
|
|
|
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/*","*/*"));
|
|
|
|
|
+
|
|
|
|
|
+ assertThat(values, Matchers.contains("text/plain;format=flowed", "text/plain", "text/*", "*/*"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
- public void test7231_5_3_2_example4()
|
|
|
|
|
+ public void test7231Sec532Example4()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("text/*;q=0.3, text/html;q=0.7, text/html;level=1,");
|
|
|
|
|
values.addValue("text/html;level=2;q=0.4, */*;q=0.5");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "text/html;level=1",
|
|
|
|
|
- "text/html",
|
|
|
|
|
- "*/*",
|
|
|
|
|
- "text/html;level=2",
|
|
|
|
|
- "text/*"
|
|
|
|
|
- ));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "text/html;level=1",
|
|
|
|
|
+ "text/html",
|
|
|
|
|
+ "*/*",
|
|
|
|
|
+ "text/html;level=2",
|
|
|
|
|
+ "text/*"
|
|
|
|
|
+ ));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
- public void test7231_5_3_4_example1()
|
|
|
|
|
+ public void test7231Sec534Example1()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("compress, gzip");
|
|
|
|
|
@@ -91,16 +90,16 @@ public class QuotedQualityCSVTest
|
|
|
|
|
values.addValue("*");
|
|
|
|
|
values.addValue("compress;q=0.5, gzip;q=1.0");
|
|
|
|
|
values.addValue("gzip;q=1.0, identity; q=0.5, *;q=0");
|
|
|
|
|
-
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "compress",
|
|
|
|
|
- "gzip",
|
|
|
|
|
- "*",
|
|
|
|
|
- "gzip",
|
|
|
|
|
- "gzip",
|
|
|
|
|
- "compress",
|
|
|
|
|
- "identity"
|
|
|
|
|
- ));
|
|
|
|
|
+
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "compress",
|
|
|
|
|
+ "gzip",
|
|
|
|
|
+ "*",
|
|
|
|
|
+ "gzip",
|
|
|
|
|
+ "gzip",
|
|
|
|
|
+ "compress",
|
|
|
|
|
+ "identity"
|
|
|
|
|
+ ));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@@ -108,66 +107,65 @@ public class QuotedQualityCSVTest
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue(" value 0.5 ; p = v ; q =0.5 , value 1.0 ");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "value 1.0",
|
|
|
|
|
- "value 0.5;p=v"));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "value 1.0",
|
|
|
|
|
+ "value 0.5;p=v"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
public void testEmpty()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue(",aaaa, , bbbb ,,cccc,");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "aaaa",
|
|
|
|
|
- "bbbb",
|
|
|
|
|
- "cccc"));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "aaaa",
|
|
|
|
|
+ "bbbb",
|
|
|
|
|
+ "cccc"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
public void testQuoted()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue(" value 0.5 ; p = \"v ; q = \\\"0.5\\\" , value 1.0 \" ");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "value 0.5;p=\"v ; q = \\\"0.5\\\" , value 1.0 \""));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "value 0.5;p=\"v ; q = \\\"0.5\\\" , value 1.0 \""));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
public void testOpenQuote()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("value;p=\"v");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "value;p=\"v"));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "value;p=\"v"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
public void testQuotedQuality()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue(" value 0.5 ; p = v ; q = \"0.5\" , value 1.0 ");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "value 1.0",
|
|
|
|
|
- "value 0.5;p=v"));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "value 1.0",
|
|
|
|
|
+ "value 0.5;p=v"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
public void testBadQuality()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("value0.5;p=v;q=0.5,value1.0,valueBad;q=X");
|
|
|
|
|
- assertThat(values,Matchers.contains(
|
|
|
|
|
- "value1.0",
|
|
|
|
|
- "value0.5;p=v"));
|
|
|
|
|
+ assertThat(values, Matchers.contains(
|
|
|
|
|
+ "value1.0",
|
|
|
|
|
+ "value0.5;p=v"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
+
|
|
|
|
|
@Test
|
|
|
|
|
public void testBad()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
// None of these should throw exceptions
|
|
|
|
|
values.addValue(null);
|
|
|
|
|
values.addValue("");
|
|
|
|
|
@@ -223,13 +221,10 @@ public class QuotedQualityCSVTest
|
|
|
|
|
values.addValue("q=");
|
|
|
|
|
values.addValue("q=,");
|
|
|
|
|
values.addValue("q=;");
|
|
|
|
|
-
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- /* ------------------------------------------------------------ */
|
|
|
|
|
-
|
|
|
|
|
- private static final String[] preferBrotli = {"br","gzip"};
|
|
|
|
|
- private static final String[] preferGzip = {"gzip","br"};
|
|
|
|
|
+ private static final String[] preferBrotli = {"br", "gzip"};
|
|
|
|
|
+ private static final String[] preferGzip = {"gzip", "br"};
|
|
|
|
|
private static final String[] noFormats = {};
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@@ -295,14 +290,13 @@ public class QuotedQualityCSVTest
|
|
|
|
|
values.addValue("gzip, *");
|
|
|
|
|
assertThat(values, contains("*", "gzip"));
|
|
|
|
|
}
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void testSameQuality()
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("one;q=0.5,two;q=0.5,three;q=0.5");
|
|
|
|
|
- assertThat(values.getValues(),Matchers.contains("one","two","three"));
|
|
|
|
|
+ assertThat(values.getValues(), Matchers.contains("one", "two", "three"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@@ -310,10 +304,9 @@ public class QuotedQualityCSVTest
|
|
|
|
|
{
|
|
|
|
|
QuotedQualityCSV values = new QuotedQualityCSV();
|
|
|
|
|
values.addValue("one,two;,three;x=y");
|
|
|
|
|
- assertThat(values.getValues(),Matchers.contains("one","two","three;x=y"));
|
|
|
|
|
+ assertThat(values.getValues(), Matchers.contains("one", "two", "three;x=y"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
@Test
|
|
|
|
|
public void testQuality()
|
|
|
|
|
{
|
|
|
|
|
@@ -339,19 +332,15 @@ public class QuotedQualityCSVTest
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
// The provided string is not legal according to some RFCs ( not a token because of = and not a parameter because not preceded by ; )
|
|
|
|
|
// The string is legal according to RFC7239 which allows for just parameters (called forwarded-pairs)
|
|
|
|
|
values.addValue("p=0.5,q=0.5");
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
// The QuotedCSV implementation is lenient and adopts the later interpretation and thus sees q=0.5 and p=0.5 both as parameters
|
|
|
|
|
- assertThat(results,contains("parsedValue: ", "parsedParam: p=0.5",
|
|
|
|
|
- "parsedValue: ", "parsedParam: q=0.5"));
|
|
|
|
|
-
|
|
|
|
|
+ assertThat(results, contains("parsedValue: ", "parsedParam: p=0.5",
|
|
|
|
|
+ "parsedValue: ", "parsedParam: q=0.5"));
|
|
|
|
|
|
|
|
|
|
// However the QuotedQualityCSV only handles the q parameter and that is consumed from the parameter string.
|
|
|
|
|
- assertThat(values,contains("p=0.5", ""));
|
|
|
|
|
-
|
|
|
|
|
+ assertThat(values, contains("p=0.5", ""));
|
|
|
|
|
}
|
|
|
|
|
}
|