Issue #458 Improve Quality list handling
Added QuotedCSV and QuotedQualityCSV that are up to date with RFC7230
This commit is contained in:
parent
80eefb5257
commit
c768828703
|
@ -171,7 +171,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
|
|||
}
|
||||
|
||||
// PHP does not like multiple Cookie headers, coalesce into one.
|
||||
List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE.asString());
|
||||
List<String> cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE);
|
||||
if (cookies.size() > 1)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -32,7 +32,6 @@ import java.util.Set;
|
|||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.Trie;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -50,6 +49,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
*/
|
||||
public class HttpFields implements Iterable<HttpField>
|
||||
{
|
||||
@Deprecated
|
||||
public static final String __separators = ", \t";
|
||||
|
||||
private static final Logger LOG = Log.getLogger(HttpFields.class);
|
||||
|
@ -244,11 +244,26 @@ public class HttpFields implements Iterable<HttpField>
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get multi headers
|
||||
* Get multiple header of the same name
|
||||
*
|
||||
* @return List the values
|
||||
* @param header the header
|
||||
*/
|
||||
public List<String> getValuesList(HttpHeader header)
|
||||
{
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (HttpField f : this)
|
||||
if (f.getHeader()==header)
|
||||
list.add(f.getValue());
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple header of the same name
|
||||
*
|
||||
* @return List the header values
|
||||
* @param name the case-insensitive field name
|
||||
*/
|
||||
public List<String> getValuesList(String name)
|
||||
|
@ -260,6 +275,72 @@ public class HttpFields implements Iterable<HttpField>
|
|||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple field values of the same name, split
|
||||
* as a {@link QuotedCSV}
|
||||
*
|
||||
* @return List the values with OWS stripped
|
||||
* @param header The header
|
||||
* @param keepQuotes True if the fields are kept quoted
|
||||
*/
|
||||
public List<String> getCSV(HttpHeader header,boolean keepQuotes)
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV(keepQuotes);
|
||||
for (HttpField f : this)
|
||||
if (f.getHeader()==header)
|
||||
values.addValue(f.getValue());
|
||||
return values.getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple field values of the same name
|
||||
* as a {@link QuotedCSV}
|
||||
*
|
||||
* @return List the values with OWS stripped
|
||||
* @param name the case-insensitive field name
|
||||
* @param keepQuotes True if the fields are kept quoted
|
||||
*/
|
||||
public List<String> getCSV(String name,boolean keepQuotes)
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV(keepQuotes);
|
||||
for (HttpField f : this)
|
||||
if (f.getName().equalsIgnoreCase(name))
|
||||
values.addValue(f.getValue());
|
||||
return values.getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple field values of the same name, split and
|
||||
* sorted as a {@link QuotedQualityCSV}
|
||||
*
|
||||
* @return List the values in quality order with the q param and OWS stripped
|
||||
* @param header The header
|
||||
*/
|
||||
public List<String> getQualityCSV(HttpHeader header)
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
for (HttpField f : this)
|
||||
if (f.getHeader()==header)
|
||||
values.addValue(f.getValue());
|
||||
return values.getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple field values of the same name, split and
|
||||
* sorted as a {@link QuotedQualityCSV}
|
||||
*
|
||||
* @return List the values in quality order with the q param and OWS stripped
|
||||
* @param name the case-insensitive field name
|
||||
*/
|
||||
public List<String> getQualityCSV(String name)
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
for (HttpField f : this)
|
||||
if (f.getName().equalsIgnoreCase(name))
|
||||
values.addValue(f.getValue());
|
||||
return values.getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multi headers
|
||||
*
|
||||
|
@ -325,6 +406,7 @@ public class HttpFields implements Iterable<HttpField>
|
|||
* @param separators String of separators.
|
||||
* @return Enumeration of the values, or null if no such header.
|
||||
*/
|
||||
@Deprecated
|
||||
public Enumeration<String> getValues(String name, final String separators)
|
||||
{
|
||||
final Enumeration<String> e = getValues(name);
|
||||
|
@ -713,6 +795,28 @@ public class HttpFields implements Iterable<HttpField>
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field value without parameters. Some field values can have parameters. This method separates the
|
||||
* value from the parameters and optionally populates a map with the parameters. For example:
|
||||
*
|
||||
* <PRE>
|
||||
*
|
||||
* FieldName : Value ; param1=val1 ; param2=val2
|
||||
*
|
||||
* </PRE>
|
||||
*
|
||||
* @param value The Field value, possibly with parameters.
|
||||
* @return The value.
|
||||
*/
|
||||
public static String stripParameters(String value)
|
||||
{
|
||||
if (value == null) return null;
|
||||
|
||||
int i = value.indexOf(';');
|
||||
if (i < 0) return value;
|
||||
return value.substring(0, i).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field value parameters. Some field values can have parameters. This method separates the
|
||||
* value from the parameters and optionally populates a map with the parameters. For example:
|
||||
|
@ -723,7 +827,7 @@ public class HttpFields implements Iterable<HttpField>
|
|||
*
|
||||
* </PRE>
|
||||
*
|
||||
* @param value The Field value, possibly with parameteres.
|
||||
* @param value The Field value, possibly with parameters.
|
||||
* @param parameters A map to populate with the parameters, or null
|
||||
* @return The value.
|
||||
*/
|
||||
|
@ -752,8 +856,11 @@ public class HttpFields implements Iterable<HttpField>
|
|||
return value.substring(0, i).trim();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private static final Float __one = new Float("1.0");
|
||||
@Deprecated
|
||||
private static final Float __zero = new Float("0.0");
|
||||
@Deprecated
|
||||
private static final Trie<Float> __qualities = new ArrayTernaryTrie<>();
|
||||
static
|
||||
{
|
||||
|
@ -775,6 +882,7 @@ public class HttpFields implements Iterable<HttpField>
|
|||
__qualities.put("0.0", __zero);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Float getQuality(String value)
|
||||
{
|
||||
if (value == null) return __zero;
|
||||
|
@ -816,53 +924,16 @@ public class HttpFields implements Iterable<HttpField>
|
|||
* @param e Enumeration of values with quality parameters
|
||||
* @return values in quality order.
|
||||
*/
|
||||
@Deprecated
|
||||
public static List<String> qualityList(Enumeration<String> e)
|
||||
{
|
||||
if (e == null || !e.hasMoreElements())
|
||||
return Collections.emptyList();
|
||||
|
||||
Object list = null;
|
||||
Object qual = null;
|
||||
|
||||
// Assume list will be well ordered and just add nonzero
|
||||
while (e.hasMoreElements())
|
||||
{
|
||||
String v = e.nextElement();
|
||||
Float q = getQuality(v);
|
||||
|
||||
if (q >= 0.001)
|
||||
{
|
||||
list = LazyList.add(list, v);
|
||||
qual = LazyList.add(qual, q);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> vl = LazyList.getList(list, false);
|
||||
if (vl.size() < 2)
|
||||
return vl;
|
||||
|
||||
List<Float> ql = LazyList.getList(qual, false);
|
||||
|
||||
// sort list with swaps
|
||||
Float last = __zero;
|
||||
for (int i = vl.size(); i-- > 0;)
|
||||
{
|
||||
Float q = ql.get(i);
|
||||
if (last.compareTo(q) > 0)
|
||||
{
|
||||
String tmp = vl.get(i);
|
||||
vl.set(i, vl.get(i + 1));
|
||||
vl.set(i + 1, tmp);
|
||||
ql.set(i, ql.get(i + 1));
|
||||
ql.set(i + 1, q);
|
||||
last = __zero;
|
||||
i = vl.size();
|
||||
continue;
|
||||
}
|
||||
last = q;
|
||||
}
|
||||
ql.clear();
|
||||
return vl;
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
while(e.hasMoreElements())
|
||||
values.addValue(e.nextElement());
|
||||
return values.getValues();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* 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};
|
||||
|
||||
private final List<String> _values = new ArrayList<>();
|
||||
private final boolean _keepQuotes;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public QuotedCSV(String... values)
|
||||
{
|
||||
this(true,values);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public QuotedCSV(boolean keepQuotes,String... values)
|
||||
{
|
||||
_keepQuotes=keepQuotes;
|
||||
for (String v:values)
|
||||
addValue(v);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void addValue(String value)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
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)
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
|
||||
case ';':
|
||||
buffer.setLength(nws_length); // trim following OWS
|
||||
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
|
||||
_values.add(buffer.toString());
|
||||
}
|
||||
buffer.setLength(0);
|
||||
last_length=0;
|
||||
nws_length=0;
|
||||
state=State.VALUE;
|
||||
continue;
|
||||
|
||||
default:
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case VALUE:
|
||||
{
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
}
|
||||
|
||||
case PARAM_NAME:
|
||||
{
|
||||
if (c=='=')
|
||||
{
|
||||
buffer.setLength(nws_length); // trim following OWS
|
||||
buffer.append(c);
|
||||
last_length=++nws_length;
|
||||
state=State.PARAM_VALUE;
|
||||
continue;
|
||||
}
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
case PARAM_VALUE:
|
||||
{
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getValues()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* 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 implements Iterable<String>
|
||||
{
|
||||
private final static Double ZERO=new Double(0.0);
|
||||
private final static Double ONE=new Double(1.0);
|
||||
private enum State { VALUE, PARAM_NAME, PARAM_VALUE, Q_VALUE};
|
||||
|
||||
private final List<String> _values = new ArrayList<>();
|
||||
private final List<Double> _quality = new ArrayList<>();
|
||||
private boolean _sorted = false;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public QuotedQualityCSV(String... values)
|
||||
{
|
||||
for (String v:values)
|
||||
addValue(v);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void addValue(String value)
|
||||
{
|
||||
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;
|
||||
Double q=ONE;
|
||||
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;
|
||||
break;
|
||||
case '"':
|
||||
quoted=false;
|
||||
if (state==State.Q_VALUE)
|
||||
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 (state==State.Q_VALUE)
|
||||
continue;
|
||||
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
|
||||
case ';':
|
||||
if (state==State.Q_VALUE)
|
||||
{
|
||||
try
|
||||
{
|
||||
q=new Double(buffer.substring(last_length));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
q=ZERO;
|
||||
}
|
||||
nws_length=last_length;
|
||||
}
|
||||
|
||||
buffer.setLength(nws_length); // trim following OWS
|
||||
buffer.append(c);
|
||||
last_length=++nws_length;
|
||||
state=State.PARAM_NAME;
|
||||
continue;
|
||||
|
||||
case ',':
|
||||
case 0:
|
||||
if (state==State.Q_VALUE)
|
||||
{
|
||||
try
|
||||
{
|
||||
q=new Double(buffer.substring(last_length));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
q=ZERO;
|
||||
}
|
||||
nws_length=last_length;
|
||||
}
|
||||
buffer.setLength(nws_length); // trim following OWS
|
||||
if (q>0.0 && nws_length>0)
|
||||
{
|
||||
_values.add(buffer.toString());
|
||||
_quality.add(q);
|
||||
_sorted=false;
|
||||
}
|
||||
buffer.setLength(0);
|
||||
last_length=0;
|
||||
nws_length=0;
|
||||
q=ONE;
|
||||
state=State.VALUE;
|
||||
continue;
|
||||
|
||||
default:
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case VALUE:
|
||||
{
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
}
|
||||
|
||||
case PARAM_NAME:
|
||||
{
|
||||
if (c=='=')
|
||||
{
|
||||
buffer.setLength(nws_length); // trim following OWS
|
||||
if (nws_length-last_length==1 && Character.toLowerCase(buffer.charAt(last_length))=='q')
|
||||
{
|
||||
buffer.setLength(last_length-1);
|
||||
nws_length=buffer.length();
|
||||
last_length=nws_length;
|
||||
state=State.Q_VALUE;
|
||||
continue;
|
||||
}
|
||||
buffer.append(c);
|
||||
last_length=++nws_length;
|
||||
state=State.PARAM_VALUE;
|
||||
continue;
|
||||
}
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
}
|
||||
|
||||
case PARAM_VALUE:
|
||||
case Q_VALUE:
|
||||
{
|
||||
buffer.append(c);
|
||||
nws_length=buffer.length();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getValues()
|
||||
{
|
||||
if (!_sorted)
|
||||
sort();
|
||||
return _values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator()
|
||||
{
|
||||
if (!_sorted)
|
||||
sort();
|
||||
return _values.iterator();
|
||||
}
|
||||
|
||||
protected void sort()
|
||||
{
|
||||
_sorted=true;
|
||||
|
||||
Double last = ZERO;
|
||||
int len = Integer.MIN_VALUE;
|
||||
|
||||
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 && v.length()<len))
|
||||
{
|
||||
_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;
|
||||
len=0;
|
||||
i = _values.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
last=q;
|
||||
len=v.length();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class QuotedCSVTest
|
||||
{
|
||||
@Test
|
||||
public void testOWS()
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV();
|
||||
values.addValue(" value 0.5 ; p = v ; q =0.5 , value 1.0 ");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"value 0.5;p=v;q=0.5",
|
||||
"value 1.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty()
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV();
|
||||
values.addValue(",aaaa, , bbbb ,,cccc,");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"aaaa",
|
||||
"bbbb",
|
||||
"cccc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuoted()
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV();
|
||||
values.addValue("A;p=\"v\",B,\"C, D\"");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"A;p=\"v\"",
|
||||
"B",
|
||||
"\"C, D\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenQuote()
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV();
|
||||
values.addValue("value;p=\"v");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"value;p=\"v"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuotedNoQuotes()
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV(false);
|
||||
values.addValue("A;p=\"v\",B,\"C, D\"");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"A;p=v",
|
||||
"B",
|
||||
"C, D"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenQuoteNoQuotes()
|
||||
{
|
||||
QuotedCSV values = new QuotedCSV(false);
|
||||
values.addValue("value;p=\"v");
|
||||
assertThat(values,Matchers.contains(
|
||||
"value;p=v"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnQuote()
|
||||
{
|
||||
assertThat(QuotedCSV.unquote(""),is(""));
|
||||
assertThat(QuotedCSV.unquote("\"\""),is(""));
|
||||
assertThat(QuotedCSV.unquote("foo"),is("foo"));
|
||||
assertThat(QuotedCSV.unquote("\"foo\""),is("foo"));
|
||||
assertThat(QuotedCSV.unquote("f\"o\"o"),is("foo"));
|
||||
assertThat(QuotedCSV.unquote("\"\\\"foo\""),is("\"foo"));
|
||||
assertThat(QuotedCSV.unquote("\\foo"),is("\\foo"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class QuotedQualityCSVTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void test7231_5_3_2_example1()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue(" audio/*; q=0.2, audio/basic");
|
||||
Assert.assertThat(values,Matchers.contains("audio/basic","audio/*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test7231_5_3_2_example2()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue("text/plain; q=0.5, text/html,");
|
||||
values.addValue("text/x-dvi; q=0.8, text/x-c");
|
||||
Assert.assertThat(values,Matchers.contains("text/html","text/x-c","text/x-dvi","text/plain"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test7231_5_3_2_example3()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
|
||||
Assert.assertThat(values,Matchers.contains("text/plain;format=flowed","text/plain","text/*","*/*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test7231_5_3_2_example4()
|
||||
{
|
||||
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");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"text/html;level=1",
|
||||
"text/html",
|
||||
"*/*",
|
||||
"text/html;level=2",
|
||||
"text/*"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test7231_5_3_4_example1()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue("compress, gzip");
|
||||
values.addValue("");
|
||||
values.addValue("*");
|
||||
values.addValue("compress;q=0.5, gzip;q=1.0");
|
||||
values.addValue("gzip;q=1.0, identity; q=0.5, *;q=0");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"compress",
|
||||
"gzip",
|
||||
"gzip",
|
||||
"gzip",
|
||||
"*",
|
||||
"compress",
|
||||
"identity"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOWS()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue(" value 0.5 ; p = v ; q =0.5 , value 1.0 ");
|
||||
Assert.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,");
|
||||
Assert.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 \" ");
|
||||
Assert.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");
|
||||
Assert.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 ");
|
||||
Assert.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");
|
||||
Assert.assertThat(values,Matchers.contains(
|
||||
"value1.0",
|
||||
"value0.5;p=v"));
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ import java.util.Locale;
|
|||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.http.QuotedCSV;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -35,7 +35,6 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* call to {@link #getCookies()}.
|
||||
* If the added fields are identical to those last added (as strings), then the
|
||||
* cookies are not re parsed.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class CookieCutter
|
||||
|
@ -274,8 +273,9 @@ public class CookieCutter
|
|||
// If after processing the current character we have a value and a name, then it is a cookie
|
||||
if (value!=null && name!=null)
|
||||
{
|
||||
name=QuotedStringTokenizer.unquoteOnly(name);
|
||||
value=QuotedStringTokenizer.unquoteOnly(value);
|
||||
|
||||
name=QuotedCSV.unquote(name);
|
||||
value=QuotedCSV.unquote(value);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncListener;
|
||||
|
@ -701,20 +702,12 @@ public class Request implements HttpServletRequest
|
|||
}
|
||||
|
||||
_cookiesExtracted = true;
|
||||
|
||||
Enumeration<?> enm = _metadata.getFields().getValues(HttpHeader.COOKIE.toString());
|
||||
|
||||
// Handle no cookies
|
||||
if (enm != null)
|
||||
|
||||
for (String c : _metadata.getFields().getValuesList(HttpHeader.COOKIE))
|
||||
{
|
||||
if (_cookies == null)
|
||||
_cookies = new CookieCutter();
|
||||
|
||||
while (enm.hasMoreElements())
|
||||
{
|
||||
String c = (String)enm.nextElement();
|
||||
_cookies.addCookieField(c);
|
||||
}
|
||||
_cookies.addCookieField(c);
|
||||
}
|
||||
|
||||
//Javadoc for Request.getCookies() stipulates null for no cookies
|
||||
|
@ -825,34 +818,22 @@ public class Request implements HttpServletRequest
|
|||
if (_metadata==null)
|
||||
return Locale.getDefault();
|
||||
|
||||
Enumeration<String> enm = _metadata.getFields().getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
|
||||
List<String> acceptable = _metadata.getFields().getQualityCSV(HttpHeader.ACCEPT_LANGUAGE);
|
||||
|
||||
// handle no locale
|
||||
if (enm == null || !enm.hasMoreElements())
|
||||
if (acceptable.isEmpty())
|
||||
return Locale.getDefault();
|
||||
|
||||
// sort the list in quality order
|
||||
List<?> acceptLanguage = HttpFields.qualityList(enm);
|
||||
if (acceptLanguage.size() == 0)
|
||||
return Locale.getDefault();
|
||||
|
||||
int size = acceptLanguage.size();
|
||||
|
||||
if (size > 0)
|
||||
String language = acceptable.get(0);
|
||||
language = HttpFields.stripParameters(language);
|
||||
String country = "";
|
||||
int dash = language.indexOf('-');
|
||||
if (dash > -1)
|
||||
{
|
||||
String language = (String)acceptLanguage.get(0);
|
||||
language = HttpFields.valueParameters(language,null);
|
||||
String country = "";
|
||||
int dash = language.indexOf('-');
|
||||
if (dash > -1)
|
||||
{
|
||||
country = language.substring(dash + 1).trim();
|
||||
language = language.substring(0,dash).trim();
|
||||
}
|
||||
return new Locale(language,country);
|
||||
country = language.substring(dash + 1).trim();
|
||||
language = language.substring(0,dash).trim();
|
||||
}
|
||||
|
||||
return Locale.getDefault();
|
||||
return new Locale(language,country);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -865,38 +846,26 @@ public class Request implements HttpServletRequest
|
|||
if (_metadata==null)
|
||||
return Collections.enumeration(__defaultLocale);
|
||||
|
||||
Enumeration<String> enm = _metadata.getFields().getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
|
||||
List<String> acceptable = _metadata.getFields().getQualityCSV(HttpHeader.ACCEPT_LANGUAGE);
|
||||
|
||||
// handle no locale
|
||||
if (enm == null || !enm.hasMoreElements())
|
||||
if (acceptable.isEmpty())
|
||||
return Collections.enumeration(__defaultLocale);
|
||||
|
||||
// sort the list in quality order
|
||||
List<String> acceptLanguage = HttpFields.qualityList(enm);
|
||||
|
||||
if (acceptLanguage.size() == 0)
|
||||
return Collections.enumeration(__defaultLocale);
|
||||
|
||||
List<Locale> langs = new ArrayList<>();
|
||||
|
||||
// convert to locals
|
||||
for (String language : acceptLanguage)
|
||||
List<Locale> locales = acceptable.stream().map(language->
|
||||
{
|
||||
language = HttpFields.valueParameters(language, null);
|
||||
language = HttpFields.stripParameters(language);
|
||||
String country = "";
|
||||
int dash = language.indexOf('-');
|
||||
if (dash > -1)
|
||||
{
|
||||
country = language.substring(dash + 1).trim();
|
||||
language = language.substring(0, dash).trim();
|
||||
language = language.substring(0,dash).trim();
|
||||
}
|
||||
langs.add(new Locale(language, country));
|
||||
}
|
||||
|
||||
if (langs.size() == 0)
|
||||
return Collections.enumeration(__defaultLocale);
|
||||
|
||||
return Collections.enumeration(langs);
|
||||
return new Locale(language,country);
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
return Collections.enumeration(locales);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
@ -39,6 +40,8 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -129,7 +132,6 @@ public class RequestTest
|
|||
|
||||
}
|
||||
|
||||
@Ignore("Empty headers are not 7230 compliant")
|
||||
@Test
|
||||
public void testEmptyHeaders() throws Exception
|
||||
{
|
||||
|
@ -139,15 +141,24 @@ public class RequestTest
|
|||
public boolean check(HttpServletRequest request,HttpServletResponse response)
|
||||
{
|
||||
assertNotNull(request.getLocale());
|
||||
assertTrue(request.getLocales().hasMoreElements());
|
||||
assertNull(request.getContentType());
|
||||
assertTrue(request.getLocales().hasMoreElements()); // Default locale
|
||||
assertEquals("",request.getContentType());
|
||||
assertNull(request.getCharacterEncoding());
|
||||
assertEquals(0,request.getQueryString().length());
|
||||
assertEquals(-1,request.getContentLength());
|
||||
assertNull(request.getCookies());
|
||||
assertNull(request.getHeader("Name"));
|
||||
assertFalse(request.getHeaders("Name").hasMoreElements());
|
||||
assertEquals(-1,request.getDateHeader("Name"));
|
||||
assertEquals("",request.getHeader("Name"));
|
||||
assertTrue(request.getHeaders("Name").hasMoreElements()); // empty
|
||||
try
|
||||
{
|
||||
request.getDateHeader("Name");
|
||||
assertTrue(false);
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
|
||||
}
|
||||
assertEquals(-1,request.getDateHeader("Other"));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -214,6 +225,43 @@ public class RequestTest
|
|||
assertTrue(responses.startsWith("HTTP/1.1 200"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocale() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException
|
||||
{
|
||||
assertThat(request.getLocale().getLanguage(),is("da"));
|
||||
Enumeration<Locale> locales = request.getLocales();
|
||||
Locale locale=locales.nextElement();
|
||||
assertThat(locale.getLanguage(),is("da"));
|
||||
assertThat(locale.getCountry(),is(""));
|
||||
locale=locales.nextElement();
|
||||
assertThat(locale.getLanguage(),is("en"));
|
||||
assertThat(locale.getCountry(),is("AU"));
|
||||
locale=locales.nextElement();
|
||||
assertThat(locale.getLanguage(),is("en"));
|
||||
assertThat(locale.getCountry(),is("GB"));
|
||||
locale=locales.nextElement();
|
||||
assertThat(locale.getLanguage(),is("en"));
|
||||
assertThat(locale.getCountry(),is(""));
|
||||
assertFalse(locales.hasMoreElements());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String request="GET / HTTP/1.1\r\n"+
|
||||
"Host: whatever\r\n"+
|
||||
"Connection: close\r\n"+
|
||||
"Accept-Language: da, en-gb;q=0.8, en;q=0.7\r\n"+
|
||||
"Accept-Language: XX;q=0, en-au;q=0.9\r\n"+
|
||||
"\r\n";
|
||||
String response = _connector.getResponses(request);
|
||||
assertThat(response,Matchers.containsString(" 200 OK"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMultiPart() throws Exception
|
||||
|
@ -1088,14 +1136,14 @@ public class RequestTest
|
|||
response=_connector.getResponses(
|
||||
"GET / HTTP/1.1\n"+
|
||||
"Host: whatever\n"+
|
||||
"Cookie: name=quoted=\\\"value\\\"\n" +
|
||||
"Cookie: name=quoted=\"\\\"value\\\"\"\n" +
|
||||
"Connection: close\n"+
|
||||
"\n"
|
||||
);
|
||||
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
|
||||
assertEquals(1,cookies.size());
|
||||
assertEquals("name", cookies.get(0).getName());
|
||||
assertEquals("quoted=\\\"value\\\"", cookies.get(0).getValue());
|
||||
assertEquals("quoted=\"value\"", cookies.get(0).getValue());
|
||||
|
||||
cookies.clear();
|
||||
response=_connector.getResponses(
|
||||
|
|
|
@ -43,7 +43,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.DateParser;
|
||||
import org.eclipse.jetty.http.GzipHttpContent;
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -52,7 +51,7 @@ import org.eclipse.jetty.http.HttpMethod;
|
|||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.PathMap.MappedEntry;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.http.ResourceHttpContent;
|
||||
import org.eclipse.jetty.http.QuotedCSV;
|
||||
import org.eclipse.jetty.io.WriterOutputStream;
|
||||
import org.eclipse.jetty.server.HttpOutput;
|
||||
import org.eclipse.jetty.server.InclusiveByteRange;
|
||||
|
@ -65,7 +64,6 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.MultiPartOutputStream;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -748,12 +746,14 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
boolean match=false;
|
||||
if (etag!=null)
|
||||
{
|
||||
QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifm,", ",false,true);
|
||||
while (!match && quoted.hasMoreTokens())
|
||||
QuotedCSV quoted = new QuotedCSV(true,ifm);
|
||||
for (String tag : quoted)
|
||||
{
|
||||
String tag = quoted.nextToken();
|
||||
if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag)))
|
||||
{
|
||||
match=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -775,10 +775,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
}
|
||||
|
||||
// Handle list of tags
|
||||
QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifnm,", ",false,true);
|
||||
while (quoted.hasMoreTokens())
|
||||
QuotedCSV quoted = new QuotedCSV(true,ifnm);
|
||||
for (String tag : quoted)
|
||||
{
|
||||
String tag = quoted.nextToken();
|
||||
if (etag.equals(tag) || tag.endsWith(ETAG_GZIP_QUOTE) && etag.equals(removeGzipFromETag(tag)))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
|
|
|
@ -246,16 +246,15 @@ public class StringUtil
|
|||
if (c<s.length())
|
||||
buf.append(s.substring(c,s.length()));
|
||||
|
||||
return buf.toString();
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Remove single or double quotes.
|
||||
* @param s the input string
|
||||
* @return the string with quotes removed
|
||||
*/
|
||||
@Deprecated
|
||||
public static String unquote(String s)
|
||||
{
|
||||
return QuotedStringTokenizer.unquote(s);
|
||||
|
|
|
@ -378,6 +378,7 @@ public class UpgradeConnection extends AbstractConnection implements Connection.
|
|||
{
|
||||
for (String extVal : extValues)
|
||||
{
|
||||
// TODO use QuotedCSV ???
|
||||
QuotedStringTokenizer tok = new QuotedStringTokenizer(extVal,",");
|
||||
while (tok.hasMoreTokens())
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue