Refactored complexity out of HttpFields

In preparation of merging Fields class and supporting HTTP/2.0 HPACK
This commit is contained in:
Greg Wilkins 2013-10-17 21:35:11 +11:00
parent e408bd64c7
commit f3b393aa5d
17 changed files with 821 additions and 807 deletions

View File

@ -0,0 +1,160 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.eclipse.jetty.util.StringUtil;
public class DateGenerator
{
private static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
static
{
__GMT.setID("GMT");
}
static final String[] DAYS =
{ "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static final String[] MONTHS =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
private static final ThreadLocal<DateGenerator> __dateGenerator =new ThreadLocal<DateGenerator>()
{
@Override
protected DateGenerator initialValue()
{
return new DateGenerator();
}
};
public final static String __01Jan1970=DateGenerator.formatDate(0);
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public static String formatDate(long date)
{
return __dateGenerator.get().doFormatDate(date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static void formatCookieDate(StringBuilder buf, long date)
{
__dateGenerator.get().doFormatCookieDate(buf,date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static String formatCookieDate(long date)
{
StringBuilder buf = new StringBuilder(28);
formatCookieDate(buf, date);
return buf.toString();
}
private final StringBuilder buf = new StringBuilder(32);
private final GregorianCalendar gc = new GregorianCalendar(__GMT);
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public String doFormatDate(long date)
{
buf.setLength(0);
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
int century = year / 100;
year = year % 100;
int hours = gc.get(Calendar.HOUR_OF_DAY);
int minutes = gc.get(Calendar.MINUTE);
int seconds = gc.get(Calendar.SECOND);
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append(' ');
buf.append(MONTHS[month]);
buf.append(' ');
StringUtil.append2digits(buf, century);
StringUtil.append2digits(buf, year);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
return buf.toString();
}
/**
* Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies
*/
public void doFormatCookieDate(StringBuilder buf, long date)
{
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
year = year % 10000;
int epoch = (int) ((date / 1000) % (60 * 60 * 24));
int seconds = epoch % 60;
epoch = epoch / 60;
int minutes = epoch % 60;
int hours = epoch / 60;
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append('-');
buf.append(MONTHS[month]);
buf.append('-');
StringUtil.append2digits(buf, year/100);
StringUtil.append2digits(buf, year%100);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
}
}

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
class DateParser
{
private static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
static
{
__GMT.setID("GMT");
}
final static String __dateReceiveFmt[] =
{
"EEE, dd MMM yyyy HH:mm:ss zzz",
"EEE, dd-MMM-yy HH:mm:ss",
"EEE MMM dd HH:mm:ss yyyy",
"EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz",
"EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
"EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz",
"dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz",
"MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",
"EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
"EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
};
public static long parseDate(String date)
{
return __dateParser.get().parse(date);
}
private static final ThreadLocal<DateParser> __dateParser =new ThreadLocal<DateParser>()
{
@Override
protected DateParser initialValue()
{
return new DateParser();
}
};
final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
private long parse(final String dateVal)
{
for (int i = 0; i < _dateReceive.length; i++)
{
if (_dateReceive[i] == null)
{
_dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
_dateReceive[i].setTimeZone(__GMT);
}
try
{
Date date = (Date) _dateReceive[i].parseObject(dateVal);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
if (dateVal.endsWith(" GMT"))
{
final String val = dateVal.substring(0, dateVal.length() - 4);
for (SimpleDateFormat element : _dateReceive)
{
try
{
Date date = (Date) element.parseObject(val);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
}
return -1;
}
}

View File

@ -18,92 +18,13 @@
package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
/* ------------------------------------------------------------ */
/** A HTTP Field
*/
public class HttpField
{
/**
* Cache of common {@link HttpField}s including: <UL>
* <LI>Common static combinations such as:<UL>
* <li>Connection: close
* <li>Accept-Encoding: gzip
* <li>Content-Length: 0
* </ul>
* <li>Combinations of Content-Type header for common mime types by common charsets
* <li>Most common headers with null values so that a lookup will at least
* determine the header name even if the name:value combination is not cached
* </ul>
*/
public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
public final static Trie<HttpField> CONTENT_TYPE = new ArrayTrie<>(512);
static
{
CACHE.put(new CachedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
CACHE.put(new CachedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
CACHE.put(new CachedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT,"*/*"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
CACHE.put(new CachedHttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
CACHE.put(new CachedHttpField(HttpHeader.PRAGMA,"no-cache"));
CACHE.put(new CachedHttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
CACHE.put(new CachedHttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_LENGTH,"0"));
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
CACHE.put(new CachedHttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
CACHE.put(new CachedHttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
// Content types
for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/x-www-form-urlencoded"})
{
HttpField field=new CachedHttpField(HttpHeader.CONTENT_TYPE,type);
CACHE.put(field);
CONTENT_TYPE.put(type,field);
for (String charset : new String[]{"UTF-8","ISO-8859-1"})
{
String type_charset=type+"; charset="+charset;
field=new CachedHttpField(HttpHeader.CONTENT_TYPE,type_charset);
CACHE.put(field);
CACHE.put(new CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
CONTENT_TYPE.put(type_charset,field);
CONTENT_TYPE.put(type+";charset="+charset,field);
}
}
// Add headers with null values so HttpParser can avoid looking up name again for unknown values
for (HttpHeader h:HttpHeader.values())
if (!CACHE.put(new HttpField(h,(String)null)))
throw new IllegalStateException("CACHE FULL");
// Add some more common headers
CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
}
private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");
private final static byte[] __colon_space = new byte[] {':',' '};
private final HttpHeader _header;
private final String _name;
@ -146,89 +67,7 @@ public class HttpField
{
return _value;
}
public boolean contains(String value)
{
if (_value==null)
return false;
if (value.equalsIgnoreCase(_value))
return true;
String[] split = __splitter.split(_value);
for (int i = 0; split!=null && i < split.length; i++)
{
if (value.equalsIgnoreCase(split[i]))
return true;
}
return false;
}
public int getIntValue()
{
return StringUtil.toInt(_value);
}
public long getLongValue()
{
return StringUtil.toLong(_value);
}
private static byte[] toSanitisedName(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
case ':' :
bytes[i]=(byte)'?';
}
}
return bytes;
}
private static byte[] toSanitisedValue(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
bytes[i]=(byte)'?';
}
}
return bytes;
}
public void putTo(ByteBuffer bufferInFillMode)
{
if (_header!=null)
{
bufferInFillMode.put(_header.getBytesColonSpace());
bufferInFillMode.put(toSanitisedValue(_value));
}
else
{
bufferInFillMode.put(toSanitisedName(_name));
bufferInFillMode.put(__colon_space);
bufferInFillMode.put(toSanitisedValue(_value));
}
BufferUtil.putCRLF(bufferInFillMode);
}
public void putValueTo(ByteBuffer buffer)
{
buffer.put(toSanitisedValue(_value));
}
@Override
public String toString()
{
@ -250,31 +89,4 @@ public class HttpField
}
/* ------------------------------------------------------------ */
/** A HTTP Field optimised to be reused.
*/
public static class CachedHttpField extends HttpField
{
final byte[] _bytes;
public CachedHttpField(HttpHeader header, String value)
{
super(header,value);
_bytes=new byte[header.asString().length()+2+value.length()+2];
System.arraycopy(header.getBytesColonSpace(),0,_bytes,0,header.asString().length()+2);
System.arraycopy(toSanitisedValue(value),0,_bytes,header.asString().length()+2,value.length());
_bytes[_bytes.length-2]='\r';
_bytes[_bytes.length-1]='\n';
}
CachedHttpField(HttpHeader header, HttpHeaderValue value)
{
this(header,value.asString());
}
@Override
public void putTo(ByteBuffer bufferInFillMode)
{
bufferInFillMode.put(_bytes);
}
}
}

View File

@ -18,29 +18,21 @@
package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
@ -48,13 +40,11 @@ import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
/**
* HTTP Fields. A collection of HTTP header and or Trailer fields.
*
* <p>This class is not synchronised as it is expected that modifications will only be performed by a
* <p>This class is not synchronized as it is expected that modifications will only be performed by a
* single thread.
*
* <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
@ -63,222 +53,9 @@ import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
public class HttpFields implements Iterable<HttpField>
{
private static final Logger LOG = Log.getLogger(HttpFields.class);
public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
public static final DateCache __dateCache = new DateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
public static final String __COOKIE_DELIM="\",;\\ \t";
static
{
__GMT.setID("GMT");
__dateCache.setTimeZone(__GMT);
}
private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");
public final static String __separators = ", \t";
private static final String[] DAYS =
{ "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
private static final String[] MONTHS =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
public static class DateGenerator
{
private final StringBuilder buf = new StringBuilder(32);
private final GregorianCalendar gc = new GregorianCalendar(__GMT);
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public String formatDate(long date)
{
buf.setLength(0);
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
int century = year / 100;
year = year % 100;
int hours = gc.get(Calendar.HOUR_OF_DAY);
int minutes = gc.get(Calendar.MINUTE);
int seconds = gc.get(Calendar.SECOND);
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append(' ');
buf.append(MONTHS[month]);
buf.append(' ');
StringUtil.append2digits(buf, century);
StringUtil.append2digits(buf, year);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
return buf.toString();
}
/**
* Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies
*/
public void formatCookieDate(StringBuilder buf, long date)
{
gc.setTimeInMillis(date);
int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
year = year % 10000;
int epoch = (int) ((date / 1000) % (60 * 60 * 24));
int seconds = epoch % 60;
epoch = epoch / 60;
int minutes = epoch % 60;
int hours = epoch / 60;
buf.append(DAYS[day_of_week]);
buf.append(',');
buf.append(' ');
StringUtil.append2digits(buf, day_of_month);
buf.append('-');
buf.append(MONTHS[month]);
buf.append('-');
StringUtil.append2digits(buf, year/100);
StringUtil.append2digits(buf, year%100);
buf.append(' ');
StringUtil.append2digits(buf, hours);
buf.append(':');
StringUtil.append2digits(buf, minutes);
buf.append(':');
StringUtil.append2digits(buf, seconds);
buf.append(" GMT");
}
}
private static final ThreadLocal<DateGenerator> __dateGenerator =new ThreadLocal<DateGenerator>()
{
@Override
protected DateGenerator initialValue()
{
return new DateGenerator();
}
};
/**
* Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
*/
public static String formatDate(long date)
{
return __dateGenerator.get().formatDate(date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static void formatCookieDate(StringBuilder buf, long date)
{
__dateGenerator.get().formatCookieDate(buf,date);
}
/**
* Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
*/
public static String formatCookieDate(long date)
{
StringBuilder buf = new StringBuilder(28);
formatCookieDate(buf, date);
return buf.toString();
}
private final static String __dateReceiveFmt[] =
{
"EEE, dd MMM yyyy HH:mm:ss zzz",
"EEE, dd-MMM-yy HH:mm:ss",
"EEE MMM dd HH:mm:ss yyyy",
"EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz",
"EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
"EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz",
"dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz",
"MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",
"EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
"EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
};
private static class DateParser
{
final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
long parse(final String dateVal)
{
for (int i = 0; i < _dateReceive.length; i++)
{
if (_dateReceive[i] == null)
{
_dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
_dateReceive[i].setTimeZone(__GMT);
}
try
{
Date date = (Date) _dateReceive[i].parseObject(dateVal);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
if (dateVal.endsWith(" GMT"))
{
final String val = dateVal.substring(0, dateVal.length() - 4);
for (SimpleDateFormat element : _dateReceive)
{
try
{
Date date = (Date) element.parseObject(val);
return date.getTime();
}
catch (java.lang.Exception e)
{
// LOG.ignore(e);
}
}
}
return -1;
}
}
public static long parseDate(String date)
{
return __dateParser.get().parse(date);
}
private static final ThreadLocal<DateParser> __dateParser =new ThreadLocal<DateParser>()
{
@Override
protected DateParser initialValue()
{
return new DateParser();
}
};
public final static String __01Jan1970=formatDate(0);
public final static ByteBuffer __01Jan1970_BUFFER=BufferUtil.toBuffer(__01Jan1970);
public final static String __01Jan1970_COOKIE = formatCookieDate(0).trim();
private final ArrayList<HttpField> _fields = new ArrayList<>(20);
/**
@ -355,12 +132,14 @@ public class HttpFields implements Iterable<HttpField>
return null;
}
public boolean contains(HttpHeader header, String value)
{
for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getHeader()==header && f.contains(value))
if (f.getHeader()==header && contains(f,value))
return true;
}
return false;
@ -371,12 +150,32 @@ public class HttpFields implements Iterable<HttpField>
for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name) && f.contains(value))
if (f.getName().equalsIgnoreCase(name) && contains(f,value))
return true;
}
return false;
}
private boolean contains(HttpField field,String value)
{
String v = field.getValue();
if (v==null)
return false;
if (value.equalsIgnoreCase(v))
return true;
String[] split = __splitter.split(v);
for (int i = 0; split!=null && i < split.length; i++)
{
if (value.equals(split[i]))
return true;
}
return false;
}
public boolean containsKey(String name)
{
for (int i=0;i<_fields.size();i++)
@ -683,7 +482,7 @@ public class HttpFields implements Iterable<HttpField>
public long getLongField(String name) throws NumberFormatException
{
HttpField field = getField(name);
return field==null?-1L:field.getLongValue();
return field==null?-1L:StringUtil.toLong(field.getValue());
}
/**
@ -702,7 +501,7 @@ public class HttpFields implements Iterable<HttpField>
if (val == null)
return -1;
final long date = __dateParser.get().parse(val);
final long date = DateParser.parseDate(val);
if (date==-1)
throw new IllegalArgumentException("Cannot convert date: " + val);
return date;
@ -742,7 +541,7 @@ public class HttpFields implements Iterable<HttpField>
*/
public void putDateField(HttpHeader name, long date)
{
String d=formatDate(date);
String d=DateGenerator.formatDate(date);
put(name, d);
}
@ -754,7 +553,7 @@ public class HttpFields implements Iterable<HttpField>
*/
public void putDateField(String name, long date)
{
String d=formatDate(date);
String d=DateGenerator.formatDate(date);
put(name, d);
}
@ -766,170 +565,10 @@ public class HttpFields implements Iterable<HttpField>
*/
public void addDateField(String name, long date)
{
String d=formatDate(date);
String d=DateGenerator.formatDate(date);
add(name,d);
}
/**
* Format a set cookie value
*
* @param cookie The cookie.
*/
public void addSetCookie(HttpCookie cookie)
{
addSetCookie(
cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
cookie.getMaxAge(),
cookie.getComment(),
cookie.isSecure(),
cookie.isHttpOnly(),
cookie.getVersion());
}
/**
* Format a set cookie value
*
* @param name the name
* @param value the value
* @param domain the domain
* @param path the path
* @param maxAge the maximum age
* @param comment the comment (only present on versions > 0)
* @param isSecure true if secure cookie
* @param isHttpOnly true if for http only
* @param version version of cookie logic to use (0 == default behavior)
*/
public void addSetCookie(
final String name,
final String value,
final String domain,
final String path,
final long maxAge,
final String comment,
final boolean isSecure,
final boolean isHttpOnly,
int version)
{
// Check arguments
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Bad cookie name");
// Format value and params
StringBuilder buf = new StringBuilder(128);
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
boolean quote_name=isQuoteNeededForCookie(name);
quoteOnlyOrAppend(buf,name,quote_name);
buf.append('=');
// Remember name= part to look for other matching set-cookie
String name_equals=buf.toString();
// Append the value
boolean quote_value=isQuoteNeededForCookie(value);
quoteOnlyOrAppend(buf,value,quote_value);
// Look for domain and path fields and check if they need to be quoted
boolean has_domain = domain!=null && domain.length()>0;
boolean quote_domain = has_domain && isQuoteNeededForCookie(domain);
boolean has_path = path!=null && path.length()>0;
boolean quote_path = has_path && isQuoteNeededForCookie(path);
// Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || isQuoted(name) || isQuoted(value) || isQuoted(path) || isQuoted(domain)))
version=1;
// Append version
if (version==1)
buf.append (";Version=1");
else if (version>1)
buf.append (";Version=").append(version);
// Append path
if (has_path)
{
buf.append(";Path=");
quoteOnlyOrAppend(buf,path,quote_path);
}
// Append domain
if (has_domain)
{
buf.append(";Domain=");
quoteOnlyOrAppend(buf,domain,quote_domain);
}
// Handle max-age and/or expires
if (maxAge >= 0)
{
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append(";Expires=");
if (maxAge == 0)
buf.append(__01Jan1970_COOKIE);
else
formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
// for v1 cookies, also send max-age
if (version>=1)
{
buf.append(";Max-Age=");
buf.append(maxAge);
}
}
// add the other fields
if (isSecure)
buf.append(";Secure");
if (isHttpOnly)
buf.append(";HttpOnly");
if (comment != null)
{
buf.append(";Comment=");
quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
}
// remove any existing set-cookie fields of same name
Iterator<HttpField> i=_fields.iterator();
while (i.hasNext())
{
HttpField field=i.next();
if (field.getHeader()==HttpHeader.SET_COOKIE)
{
String val = field.getValue();
if (val!=null && val.startsWith(name_equals))
{
//existing cookie has same name, does it also match domain and path?
if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
{
i.remove();
}
}
}
}
// add the set cookie
add(HttpHeader.SET_COOKIE.toString(), buf.toString());
// Expire responses with set-cookie headers so they do not get cached.
put(HttpHeader.EXPIRES.toString(), __01Jan1970);
}
public void putTo(ByteBuffer bufferInFillMode)
{
for (HttpField field : _fields)
{
if (field != null)
field.putTo(bufferInFillMode);
}
BufferUtil.putCRLF(bufferInFillMode);
}
@Override
public String
toString()
@ -1148,39 +787,4 @@ public class HttpFields implements Iterable<HttpField>
/* ------------------------------------------------------------ */
/** Does a cookie value need to be quoted?
* @param s value string
* @return true if quoted;
* @throws IllegalArgumentException If there a control characters in the string
*/
public static boolean isQuoteNeededForCookie(String s)
{
if (s==null || s.length()==0)
return true;
if (QuotedStringTokenizer.isQuoted(s))
return false;
for (int i=0;i<s.length();i++)
{
char c = s.charAt(i);
if (__COOKIE_DELIM.indexOf(c)>=0)
return true;
if (c<0x20 || c>=0x7f)
throw new IllegalArgumentException("Illegal character in cookie value");
}
return false;
}
private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote)
{
if (quote)
QuotedStringTokenizer.quoteOnly(buf,s);
else
buf.append(s);
}
}

View File

@ -37,6 +37,7 @@ public class HttpGenerator
{
private static final Logger LOG = Log.getLogger(HttpGenerator.class);
private final static byte[] __colon_space = new byte[] {':',' '};
public static final ResponseInfo CONTINUE_100_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,100,null,false);
public static final ResponseInfo PROGRESS_102_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,102,null,false);
public final static ResponseInfo RESPONSE_500_INFO =
@ -495,6 +496,9 @@ public class HttpGenerator
case HTTP_1_1:
header.put((byte)' ');
header.put(request.getHttpVersion().toBytes());
break;
default:
throw new IllegalStateException();
}
header.put(HttpTokens.CRLF);
}
@ -585,7 +589,7 @@ public class HttpGenerator
// write the field to the header
content_type=true;
field.putTo(header);
putTo(field,header);
break;
}
@ -600,7 +604,7 @@ public class HttpGenerator
case CONNECTION:
{
if (request!=null)
field.putTo(header);
putTo(field,header);
// Lookup and/or split connection value field
HttpHeaderValue[] values = new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
@ -672,12 +676,12 @@ public class HttpGenerator
case SERVER:
{
send=send&~SEND_SERVER;
field.putTo(header);
putTo(field,header);
break;
}
default:
field.putTo(header);
putTo(field,header);
}
}
}
@ -776,7 +780,7 @@ public class HttpGenerator
{
String c = transfer_encoding.getValue();
if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
transfer_encoding.putTo(header);
putTo(transfer_encoding,header);
else
throw new IllegalArgumentException("BAD TE");
}
@ -1007,5 +1011,88 @@ public class HttpGenerator
{
return String.format("ResponseInfo{%s %s %s,%d,%b}",_httpVersion,_status,_reason,_contentLength,_head);
}
}
private static void putSanitisedName(String s,ByteBuffer buffer)
{
int l=s.length();
for (int i=0;i<l;i++)
{
char c=s.charAt(i);
if (c<0 || c>0xff || c=='\r' || c=='\n'|| c==':')
buffer.put((byte)'?');
else
buffer.put((byte)(0xff&c));
}
}
private static void putSanitisedValue(String s,ByteBuffer buffer)
{
int l=s.length();
for (int i=0;i<l;i++)
{
char c=s.charAt(i);
if (c<0 || c>0xff || c=='\r' || c=='\n')
buffer.put((byte)'?');
else
buffer.put((byte)(0xff&c));
}
}
public static void putTo(HttpField field, ByteBuffer bufferInFillMode)
{
if (field instanceof CachedHttpField)
{
((CachedHttpField)field).putTo(bufferInFillMode);
}
else
{
HttpHeader header=field.getHeader();
if (header!=null)
{
bufferInFillMode.put(header.getBytesColonSpace());
putSanitisedValue(field.getValue(),bufferInFillMode);
}
else
{
putSanitisedName(field.getName(),bufferInFillMode);
bufferInFillMode.put(__colon_space);
putSanitisedValue(field.getValue(),bufferInFillMode);
}
BufferUtil.putCRLF(bufferInFillMode);
}
}
public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode)
{
for (HttpField field : fields)
{
if (field != null)
putTo(field,bufferInFillMode);
}
BufferUtil.putCRLF(bufferInFillMode);
}
public static class CachedHttpField extends HttpField
{
private final byte[] _bytes;
public CachedHttpField(HttpHeader header,String value)
{
super(header,value);
int cbl=header.getBytesColonSpace().length;
_bytes=new byte[cbl+value.length()+2];
System.arraycopy(header.getBytesColonSpace(),0,_bytes,0,cbl);
System.arraycopy(value.getBytes(StringUtil.__ISO_8859_1_CHARSET),0,_bytes,cbl,value.length());
_bytes[_bytes.length-2]=(byte)'\r';
_bytes[_bytes.length-1]=(byte)'\n';
}
public void putTo(ByteBuffer bufferInFillMode)
{
bufferInFillMode.put(_bytes);
}
}
}

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -76,6 +77,21 @@ public class HttpParser
public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
public final static int INITIAL_URI_LENGTH=256;
/**
* Cache of common {@link HttpField}s including: <UL>
* <LI>Common static combinations such as:<UL>
* <li>Connection: close
* <li>Accept-Encoding: gzip
* <li>Content-Length: 0
* </ul>
* <li>Combinations of Content-Type header for common mime types by common charsets
* <li>Most common headers with null values so that a lookup will at least
* determine the header name even if the name:value combination is not cached
* </ul>
*/
public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
public final static Trie<HttpField> CONTENT_TYPE = new ArrayTrie<>(512);
// States
public enum State
{
@ -138,6 +154,59 @@ public class HttpParser
private int _length;
private final StringBuilder _string=new StringBuilder();
static
{
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.UPGRADE));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip,deflate,sdch"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-GB,en-US;q=0.8,en;q=0.6"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.3"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"*/*"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"image/png,image/*;q=0.8,*/*;q=0.5"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING,"chunked"));
CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
// Content types
for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/x-www-form-urlencoded"})
{
HttpField field=new HttpField(HttpHeader.CONTENT_TYPE,type);
CACHE.put(field);
CONTENT_TYPE.put(type,field);
for (String charset : new String[]{"UTF-8","ISO-8859-1"})
{
String type_charset=type+"; charset="+charset;
field=new HttpField(HttpHeader.CONTENT_TYPE,type_charset);
CACHE.put(field);
CACHE.put(new HttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset));
CONTENT_TYPE.put(type_charset,field);
CONTENT_TYPE.put(type+";charset="+charset,field);
}
}
// Add headers with null values so HttpParser can avoid looking up name again for unknown values
for (HttpHeader h:HttpHeader.values())
if (!CACHE.put(new HttpField(h,(String)null)))
throw new IllegalStateException("CACHE FULL");
// Add some more common headers
CACHE.put(new HttpField(HttpHeader.REFERER,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_MODIFIED_SINCE,(String)null));
CACHE.put(new HttpField(HttpHeader.IF_NONE_MATCH,(String)null));
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
}
/* ------------------------------------------------------------------------------- */
public HttpParser(RequestHandler<ByteBuffer> handler)
{
@ -801,7 +870,7 @@ public class HttpParser
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
{
_field=new HttpField.CachedHttpField(_header,_valueString);
_field=new HttpField(_header,_valueString);
_connectionFields.put(_field);
}
@ -944,7 +1013,7 @@ public class HttpParser
// Try a look ahead for the known header name and value.
HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
if (field==null)
field=HttpField.CACHE.getBest(buffer,-1,buffer.remaining());
field=CACHE.getBest(buffer,-1,buffer.remaining());
if (field!=null)
{
@ -1547,5 +1616,4 @@ public class HttpParser
{
return _connectionFields;
}
}

View File

@ -136,10 +136,10 @@ public class MimeTypes
try
{
ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
Enumeration i = mime.getKeys();
Enumeration<String> i = mime.getKeys();
while(i.hasMoreElements())
{
String ext = (String)i.nextElement();
String ext = i.nextElement();
String m = mime.getString(ext);
__dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
}

View File

@ -83,7 +83,7 @@ public class HttpFieldsTest
ByteBuffer buffer=BufferUtil.allocate(1024);
BufferUtil.flipToFill(buffer);
header.putTo(buffer);
HttpGenerator.putTo(header,buffer);
BufferUtil.flipToFlush(buffer,0);
String result=BufferUtil.toString(buffer);
@ -117,7 +117,7 @@ public class HttpFieldsTest
ByteBuffer buffer = BufferUtil.allocate(1024);
BufferUtil.flipToFill(buffer);
header.putTo(buffer);
HttpGenerator.putTo(header,buffer);
BufferUtil.flipToFlush(buffer,0);
String out = BufferUtil.toString(buffer);
assertThat(out,containsString("name0: value??0"));
@ -136,7 +136,7 @@ public class HttpFieldsTest
ByteBuffer buffer = BufferUtil.allocate(1024);
BufferUtil.flipToFill(buffer);
header.putTo(buffer);
HttpGenerator.putTo(header,buffer);
BufferUtil.flipToFlush(buffer,0);
String out = BufferUtil.toString(buffer).toLowerCase();
@ -268,123 +268,6 @@ public class HttpFieldsTest
@Test
public void testSetCookie() throws Exception
{
HttpFields fields = new HttpFields();
fields.addSetCookie("null",null,null,null,-1,null,false,false,-1);
assertEquals("null=",fields.getStringField("Set-Cookie"));
fields.clear();
fields.addSetCookie("minimal","value",null,null,-1,null,false,false,-1);
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
fields.clear();
//test cookies with same name, domain and path, only 1 allowed
fields.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
fields.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
Enumeration<String> e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
assertFalse(e.hasMoreElements());
//test cookies with same name, different domain
fields.clear();
fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, same path, one with domain, one without
fields.clear();
fields.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
fields.addSetCookie("everything","value","","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, different path
fields.clear();
fields.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain1","path2",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, same domain, one with path, one without
fields.clear();
fields.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
fields.addSetCookie("everything","value","domain1","",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies same name only, no path, no domain
fields.clear();
fields.addSetCookie("everything","other","","",0,"blah",true,true,0);
fields.addSetCookie("everything","value","","",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
fields.clear();
fields.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,1);
String setCookie=fields.getStringField("Set-Cookie");
assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Version=1;Path=\"pa th\";Domain=\"do main\";Expires="));
assertThat(setCookie,Matchers.endsWith(" GMT;Max-Age=1;Secure;HttpOnly;Comment=\"co mment\""));
fields.clear();
fields.addSetCookie("name","value",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
assertEquals(-1,setCookie.indexOf("Version="));
fields.clear();
fields.addSetCookie("name","v a l u e",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
fields.clear();
fields.addSetCookie("json","{\"services\":[\"cwa\", \"aa\"]}",null,null,-1,null,false,false,-1);
assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.getStringField("Set-Cookie"));
fields.clear();
fields.addSetCookie("name","value","domain",null,-1,null,false,false,-1);
fields.addSetCookie("name","other","domain",null,-1,null,false,false,-1);
assertEquals("name=other;Domain=domain",fields.getStringField("Set-Cookie"));
fields.addSetCookie("name","more","domain",null,-1,null,false,false,-1);
assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
fields.addSetCookie("foo","bar","domain",null,-1,null,false,false,-1);
fields.addSetCookie("foo","bob","domain",null,-1,null,false,false,-1);
assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
e=fields.getValues("Set-Cookie");
assertEquals("name=more;Domain=domain",e.nextElement());
assertEquals("foo=bob;Domain=domain",e.nextElement());
fields=new HttpFields();
fields.addSetCookie("name","value%=",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
assertEquals("name=value%=",setCookie);
}
private Set<String> enum2set(Enumeration<String> e)
{

View File

@ -372,7 +372,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
@ -441,7 +441,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -503,7 +503,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -570,7 +570,7 @@ public class HttpGeneratorServerTest
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
info.getHttpFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());

View File

@ -59,13 +59,13 @@ import org.eclipse.jetty.util.thread.Scheduler;
public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
{
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
public static final String SUBMIT_KEY_UPDATES="org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
/**
* The default connect timeout, in milliseconds
*/
public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
private final static boolean __submitKeyUpdates=Boolean.valueOf(System.getProperty("org.eclipse.jetty.io.SelectorManager.SubmitKeyUpdates","FALSE"));
private final static boolean __submitKeyUpdates=Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES,"FALSE"));
private final Executor executor;
private final Scheduler scheduler;
@ -360,9 +360,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
}
/**
* Submit a task to update a selector key. If the System property
* "org.eclipse.jetty.io.SelectorManager.SubmitKeyUpdates" is set true (default is false), the
* task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
* Submit a task to update a selector key. If the System property {@link SelectorManager#SUBMIT_KEY_UPDATES}
* is set true (default is false), the task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
* woken up if need be.
* @param update the update to a key
*/
@ -374,22 +373,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
update.run();
out: while (true)
{
switch (_state.get())
{
case SELECT:
// Avoid multiple wakeup() calls if we the CAS fails
if (!_state.compareAndSet(State.SELECT, State.WAKEUP))
continue;
wakeup();
break out;
default:
break out;
}
}
if (_state.compareAndSet(State.SELECT, State.WAKEUP))
wakeup();
}
}
/**

View File

@ -17,11 +17,12 @@
//
package org.eclipse.jetty.server;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -41,10 +42,9 @@ public class CookieCutter
{
private static final Logger LOG = Log.getLogger(CookieCutter.class);
private Cookie[] _cookies;
private Cookie[] _lastCookies;
Object _lazyFields;
private final List<String> _fieldList = new ArrayList<>();
int _fields;
public CookieCutter()
@ -56,9 +56,7 @@ public class CookieCutter
if (_cookies!=null)
return _cookies;
if (_lastCookies!=null &&
_lazyFields!=null &&
_fields==LazyList.size(_lazyFields))
if (_lastCookies!=null && _fields==_fieldList.size())
_cookies=_lastCookies;
else
parseFields();
@ -70,7 +68,7 @@ public class CookieCutter
{
_cookies=cookies;
_lastCookies=null;
_lazyFields=null;
_fieldList.clear();
_fields=0;
}
@ -88,20 +86,20 @@ public class CookieCutter
if (f.length()==0)
return;
if (LazyList.size(_lazyFields)>_fields)
if (_fieldList.size()>_fields)
{
if (f.equals(LazyList.get(_lazyFields,_fields)))
if (f.equals(_fieldList.get(_fields)))
{
_fields++;
return;
}
while (LazyList.size(_lazyFields)>_fields)
_lazyFields=LazyList.remove(_lazyFields,_fields);
while (_fieldList.size()>_fields)
_fieldList.remove(_fields);
}
_cookies=null;
_lastCookies=null;
_lazyFields=LazyList.add(_lazyFields,_fields++,f);
_fieldList.add(_fields++,f);
}
@ -110,19 +108,17 @@ public class CookieCutter
_lastCookies=null;
_cookies=null;
Object cookies = null;
List<Cookie> cookies = new ArrayList<>();
int version = 0;
// delete excess fields
while (LazyList.size(_lazyFields)>_fields)
_lazyFields=LazyList.remove(_lazyFields,_fields);
while (_fieldList.size()>_fields)
_fieldList.remove(_fields);
// For each cookie field
for (int f=0;f<_fields;f++)
for (String hdr : _fieldList)
{
String hdr = LazyList.get(_lazyFields,f);
// Parse the header
String name = null;
String value = null;
@ -311,7 +307,7 @@ public class CookieCutter
cookie = new Cookie(name, value);
if (version > 0)
cookie.setVersion(version);
cookies = LazyList.add(cookies, cookie);
cookies.add(cookie);
}
}
catch (Exception e)
@ -325,7 +321,7 @@ public class CookieCutter
}
}
_cookies = (Cookie[]) LazyList.toArray(cookies,Cookie.class);
_cookies = (Cookie[]) cookies.toArray(new Cookie[cookies.size()]);
_lastCookies=_cookies;
}

View File

@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpContent.ResourceAsHttpContent;
import org.eclipse.jetty.http.HttpFields;
@ -345,7 +346,7 @@ public class ResourceCache
_contentType=(mimeType==null?null:BufferUtil.toBuffer(mimeType));
boolean exists=resource.exists();
_lastModified=exists?resource.lastModified():-1;
_lastModifiedBytes=_lastModified<0?null:BufferUtil.toBuffer(HttpFields.formatDate(_lastModified));
_lastModifiedBytes=_lastModified<0?null:BufferUtil.toBuffer(DateGenerator.formatDate(_lastModified));
_length=exists?(int)resource.length():0;
_cachedSize.addAndGet(_length);

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.server;
import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.IllegalSelectorException;
@ -26,6 +28,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
@ -42,8 +45,10 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
@ -53,6 +58,7 @@ import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
@ -63,7 +69,10 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class Response implements HttpServletResponse
{
private static final Logger LOG = Log.getLogger(Response.class);
private static final Logger LOG = Log.getLogger(Response.class);
private static final String __COOKIE_DELIM="\",;\\ \t";
private final static String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
/* ------------------------------------------------------------ */
public static Response getResponse(HttpServletResponse response)
@ -186,7 +195,16 @@ public class Response implements HttpServletResponse
public void addCookie(HttpCookie cookie)
{
_fields.addSetCookie(cookie);
addSetCookie(
cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
cookie.getMaxAge(),
cookie.getComment(),
cookie.isSecure(),
cookie.isHttpOnly(),
cookie.getVersion());;
}
@Override
@ -206,7 +224,7 @@ public class Response implements HttpServletResponse
comment = null;
}
}
_fields.addSetCookie(cookie.getName(),
addSetCookie(cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
@ -217,6 +235,175 @@ public class Response implements HttpServletResponse
cookie.getVersion());
}
/**
* Format a set cookie value
*
* @param name the name
* @param value the value
* @param domain the domain
* @param path the path
* @param maxAge the maximum age
* @param comment the comment (only present on versions > 0)
* @param isSecure true if secure cookie
* @param isHttpOnly true if for http only
* @param version version of cookie logic to use (0 == default behavior)
*/
public void addSetCookie(
final String name,
final String value,
final String domain,
final String path,
final long maxAge,
final String comment,
final boolean isSecure,
final boolean isHttpOnly,
int version)
{
// Check arguments
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Bad cookie name");
// Format value and params
StringBuilder buf = new StringBuilder(128);
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
boolean quote_name=isQuoteNeededForCookie(name);
quoteOnlyOrAppend(buf,name,quote_name);
buf.append('=');
// Remember name= part to look for other matching set-cookie
String name_equals=buf.toString();
// Append the value
boolean quote_value=isQuoteNeededForCookie(value);
quoteOnlyOrAppend(buf,value,quote_value);
// Look for domain and path fields and check if they need to be quoted
boolean has_domain = domain!=null && domain.length()>0;
boolean quote_domain = has_domain && isQuoteNeededForCookie(domain);
boolean has_path = path!=null && path.length()>0;
boolean quote_path = has_path && isQuoteNeededForCookie(path);
// Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted
if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || isQuoted(name) || isQuoted(value) || isQuoted(path) || isQuoted(domain)))
version=1;
// Append version
if (version==1)
buf.append (";Version=1");
else if (version>1)
buf.append (";Version=").append(version);
// Append path
if (has_path)
{
buf.append(";Path=");
quoteOnlyOrAppend(buf,path,quote_path);
}
// Append domain
if (has_domain)
{
buf.append(";Domain=");
quoteOnlyOrAppend(buf,domain,quote_domain);
}
// Handle max-age and/or expires
if (maxAge >= 0)
{
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append(";Expires=");
if (maxAge == 0)
buf.append(__01Jan1970_COOKIE);
else
DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
// for v1 cookies, also send max-age
if (version>=1)
{
buf.append(";Max-Age=");
buf.append(maxAge);
}
}
// add the other fields
if (isSecure)
buf.append(";Secure");
if (isHttpOnly)
buf.append(";HttpOnly");
if (comment != null)
{
buf.append(";Comment=");
quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
}
// remove any existing set-cookie fields of same name
Iterator<HttpField> i=_fields.iterator();
while (i.hasNext())
{
HttpField field=i.next();
if (field.getHeader()==HttpHeader.SET_COOKIE)
{
String val = field.getValue();
if (val!=null && val.startsWith(name_equals))
{
//existing cookie has same name, does it also match domain and path?
if (((!has_domain && !val.contains("Domain")) || (has_domain && val.contains(domain))) &&
((!has_path && !val.contains("Path")) || (has_path && val.contains(path))))
{
i.remove();
}
}
}
}
// add the set cookie
_fields.add(HttpHeader.SET_COOKIE.toString(), buf.toString());
// Expire responses with set-cookie headers so they do not get cached.
_fields.put(HttpHeader.EXPIRES.toString(), DateGenerator.__01Jan1970);
}
/* ------------------------------------------------------------ */
/** Does a cookie value need to be quoted?
* @param s value string
* @return true if quoted;
* @throws IllegalArgumentException If there a control characters in the string
*/
private static boolean isQuoteNeededForCookie(String s)
{
if (s==null || s.length()==0)
return true;
if (QuotedStringTokenizer.isQuoted(s))
return false;
for (int i=0;i<s.length();i++)
{
char c = s.charAt(i);
if (__COOKIE_DELIM.indexOf(c)>=0)
return true;
if (c<0x20 || c>=0x7f)
throw new IllegalArgumentException("Illegal character in cookie value");
}
return false;
}
private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote)
{
if (quote)
QuotedStringTokenizer.quoteOnly(buf,s);
else
buf.append(s);
}
@Override
public boolean containsHeader(String name)
{
@ -877,7 +1064,7 @@ public class Response implements HttpServletResponse
if (_contentType != null)
{
_contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
HttpField field = HttpField.CONTENT_TYPE.get(_contentType);
HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
if (field!=null)
_fields.put(field);
else
@ -893,7 +1080,7 @@ public class Response implements HttpServletResponse
if (_contentType != null)
{
_contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding;
HttpField field = HttpField.CONTENT_TYPE.get(_contentType);
HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
if (field!=null)
_fields.put(field);
else
@ -952,7 +1139,7 @@ public class Response implements HttpServletResponse
_explicitEncoding = true;
}
HttpField field = HttpField.CONTENT_TYPE.get(_contentType);
HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
if (field!=null)
_fields.put(field);
else

View File

@ -32,13 +32,14 @@ import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
@ -314,17 +315,17 @@ public class Server extends HandlerWrapper implements Attributes
// use DateCache timer for Date field reformat
final HttpFields.DateGenerator date = new HttpFields.DateGenerator();
final DateGenerator date = new DateGenerator();
long now=System.currentTimeMillis();
long tick=1000*((now/1000)+1)-now;
_dateField=new HttpField.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
_dateField=new HttpGenerator.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
DateCache.getTimer().scheduleAtFixedRate(new TimerTask()
{
@Override
public void run()
{
long now=System.currentTimeMillis();
_dateField=new HttpField.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
_dateField=new HttpGenerator.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
if (!isRunning())
this.cancel();
}

View File

@ -39,6 +39,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.AbstractEndPoint;
@ -61,6 +62,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@ -645,6 +647,126 @@ public class ResponseTest
output.flush();
}
@Test
public void testSetCookie() throws Exception
{
Response response = _channel.getResponse();
HttpFields fields = response.getHttpFields();
response.addSetCookie("null",null,null,null,-1,null,false,false,-1);
assertEquals("null=",fields.getStringField("Set-Cookie"));
fields.clear();
response.addSetCookie("minimal","value",null,null,-1,null,false,false,-1);
assertEquals("minimal=value",fields.getStringField("Set-Cookie"));
fields.clear();
//test cookies with same name, domain and path, only 1 allowed
response.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0);
response.addSetCookie("everything","value","domain","path",0,"comment",true,true,0);
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie"));
Enumeration<String> e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires"));
assertFalse(e.hasMoreElements());
//test cookies with same name, different domain
fields.clear();
response.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
response.addSetCookie("everything","value","domain2","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Domain=domain2;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, same path, one with domain, one without
fields.clear();
response.addSetCookie("everything","other","domain1","path",0,"blah",true,true,0);
response.addSetCookie("everything","value","","path",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, different path
fields.clear();
response.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
response.addSetCookie("everything","value","domain1","path2",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Path=path2;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies with same name, same domain, one with path, one without
fields.clear();
response.addSetCookie("everything","other","domain1","path1",0,"blah",true,true,0);
response.addSetCookie("everything","value","domain1","",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=other;Version=1;Path=path1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=blah",e.nextElement());
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Domain=domain1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
//test cookies same name only, no path, no domain
fields.clear();
response.addSetCookie("everything","other","","",0,"blah",true,true,0);
response.addSetCookie("everything","value","","",0,"comment",true,true,0);
e =fields.getValues("Set-Cookie");
assertTrue(e.hasMoreElements());
assertEquals("everything=value;Version=1;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement());
assertFalse(e.hasMoreElements());
fields.clear();
response.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,1);
String setCookie=fields.getStringField("Set-Cookie");
assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Version=1;Path=\"pa th\";Domain=\"do main\";Expires="));
assertThat(setCookie,Matchers.endsWith(" GMT;Max-Age=1;Secure;HttpOnly;Comment=\"co mment\""));
fields.clear();
response.addSetCookie("name","value",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
assertEquals(-1,setCookie.indexOf("Version="));
fields.clear();
response.addSetCookie("name","v a l u e",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
fields.clear();
response.addSetCookie("json","{\"services\":[\"cwa\", \"aa\"]}",null,null,-1,null,false,false,-1);
assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.getStringField("Set-Cookie"));
fields.clear();
response.addSetCookie("name","value","domain",null,-1,null,false,false,-1);
response.addSetCookie("name","other","domain",null,-1,null,false,false,-1);
assertEquals("name=other;Domain=domain",fields.getStringField("Set-Cookie"));
response.addSetCookie("name","more","domain",null,-1,null,false,false,-1);
assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
response.addSetCookie("foo","bar","domain",null,-1,null,false,false,-1);
response.addSetCookie("foo","bob","domain",null,-1,null,false,false,-1);
assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie"));
e=fields.getValues("Set-Cookie");
assertEquals("name=more;Domain=domain",e.nextElement());
assertEquals("foo=bob;Domain=domain",e.nextElement());
fields.clear();
response.addSetCookie("name","value%=",null,null,-1,null,false,false,0);
setCookie=fields.getStringField("Set-Cookie");
assertEquals("name=value%=",setCookie);
}
private Response newResponse()
{
_channel.reset();

View File

@ -35,6 +35,7 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.LocalConnector;
@ -724,16 +725,16 @@ public class DefaultServletTest
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Modified-Since: "+last_modified+"\r\n\r\n");
assertResponseContains("304", response);
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Modified-Since: "+HttpFields.formatDate(System.currentTimeMillis()-10000)+"\r\n\r\n");
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Modified-Since: "+DateGenerator.formatDate(System.currentTimeMillis()-10000)+"\r\n\r\n");
assertResponseContains("200", response);
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Modified-Since: "+HttpFields.formatDate(System.currentTimeMillis()+10000)+"\r\n\r\n");
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Modified-Since: "+DateGenerator.formatDate(System.currentTimeMillis()+10000)+"\r\n\r\n");
assertResponseContains("304", response);
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Unmodified-Since: "+HttpFields.formatDate(System.currentTimeMillis()+10000)+"\r\n\r\n");
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Unmodified-Since: "+DateGenerator.formatDate(System.currentTimeMillis()+10000)+"\r\n\r\n");
assertResponseContains("200", response);
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Unmodified-Since: "+HttpFields.formatDate(System.currentTimeMillis()-10000)+"\r\n\r\n");
response = connector.getResponses("GET /context/file.txt HTTP/1.1\r\nHost:test\r\nConnection:close\r\nIf-Unmodified-Since: "+DateGenerator.formatDate(System.currentTimeMillis()-10000)+"\r\n\r\n");
assertResponseContains("412", response);
}

View File

@ -42,6 +42,7 @@ import javax.servlet.DispatcherType;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpTester;
@ -98,7 +99,7 @@ public class GzipTester
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (ifmodifiedsince>0)
request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),HttpFields.formatDate(ifmodifiedsince));
request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setURI("/context/" + requestedFilename);
@ -176,7 +177,7 @@ public class GzipTester
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (ifmodifiedsince>0)
request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),HttpFields.formatDate(ifmodifiedsince));
request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setURI("/context/" + requestedFilename);