Refactored complexity out of HttpFields
In preparation of merging Fields class and supporting HTTP/2.0 HPACK
This commit is contained in:
parent
e408bd64c7
commit
f3b393aa5d
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue