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;
|
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
|
/** A HTTP Field
|
||||||
*/
|
*/
|
||||||
public class HttpField
|
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 HttpHeader _header;
|
||||||
private final String _name;
|
private final String _name;
|
||||||
|
@ -147,88 +68,6 @@ public class HttpField
|
||||||
return _value;
|
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
|
@Override
|
||||||
public String toString()
|
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;
|
package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.TimeZone;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
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.LazyList;
|
||||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
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.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
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.
|
* 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.
|
* single thread.
|
||||||
*
|
*
|
||||||
* <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
|
* <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>
|
public class HttpFields implements Iterable<HttpField>
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(HttpFields.class);
|
private static final Logger LOG = Log.getLogger(HttpFields.class);
|
||||||
public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
|
private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static String __separators = ", \t";
|
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);
|
private final ArrayList<HttpField> _fields = new ArrayList<>(20);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,12 +132,14 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public boolean contains(HttpHeader header, String value)
|
public boolean contains(HttpHeader header, String value)
|
||||||
{
|
{
|
||||||
for (int i=0;i<_fields.size();i++)
|
for (int i=0;i<_fields.size();i++)
|
||||||
{
|
{
|
||||||
HttpField f=_fields.get(i);
|
HttpField f=_fields.get(i);
|
||||||
if (f.getHeader()==header && f.contains(value))
|
if (f.getHeader()==header && contains(f,value))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -371,12 +150,32 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
for (int i=0;i<_fields.size();i++)
|
for (int i=0;i<_fields.size();i++)
|
||||||
{
|
{
|
||||||
HttpField f=_fields.get(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 true;
|
||||||
}
|
}
|
||||||
return false;
|
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)
|
public boolean containsKey(String name)
|
||||||
{
|
{
|
||||||
for (int i=0;i<_fields.size();i++)
|
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
|
public long getLongField(String name) throws NumberFormatException
|
||||||
{
|
{
|
||||||
HttpField field = getField(name);
|
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)
|
if (val == null)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
final long date = __dateParser.get().parse(val);
|
final long date = DateParser.parseDate(val);
|
||||||
if (date==-1)
|
if (date==-1)
|
||||||
throw new IllegalArgumentException("Cannot convert date: " + val);
|
throw new IllegalArgumentException("Cannot convert date: " + val);
|
||||||
return date;
|
return date;
|
||||||
|
@ -742,7 +541,7 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
*/
|
*/
|
||||||
public void putDateField(HttpHeader name, long date)
|
public void putDateField(HttpHeader name, long date)
|
||||||
{
|
{
|
||||||
String d=formatDate(date);
|
String d=DateGenerator.formatDate(date);
|
||||||
put(name, d);
|
put(name, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,7 +553,7 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
*/
|
*/
|
||||||
public void putDateField(String name, long date)
|
public void putDateField(String name, long date)
|
||||||
{
|
{
|
||||||
String d=formatDate(date);
|
String d=DateGenerator.formatDate(date);
|
||||||
put(name, d);
|
put(name, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,170 +565,10 @@ public class HttpFields implements Iterable<HttpField>
|
||||||
*/
|
*/
|
||||||
public void addDateField(String name, long date)
|
public void addDateField(String name, long date)
|
||||||
{
|
{
|
||||||
String d=formatDate(date);
|
String d=DateGenerator.formatDate(date);
|
||||||
add(name,d);
|
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
|
@Override
|
||||||
public String
|
public String
|
||||||
toString()
|
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 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 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 static final ResponseInfo PROGRESS_102_INFO = new ResponseInfo(HttpVersion.HTTP_1_1,null,-1,102,null,false);
|
||||||
public final static ResponseInfo RESPONSE_500_INFO =
|
public final static ResponseInfo RESPONSE_500_INFO =
|
||||||
|
@ -495,6 +496,9 @@ public class HttpGenerator
|
||||||
case HTTP_1_1:
|
case HTTP_1_1:
|
||||||
header.put((byte)' ');
|
header.put((byte)' ');
|
||||||
header.put(request.getHttpVersion().toBytes());
|
header.put(request.getHttpVersion().toBytes());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
header.put(HttpTokens.CRLF);
|
header.put(HttpTokens.CRLF);
|
||||||
}
|
}
|
||||||
|
@ -585,7 +589,7 @@ public class HttpGenerator
|
||||||
|
|
||||||
// write the field to the header
|
// write the field to the header
|
||||||
content_type=true;
|
content_type=true;
|
||||||
field.putTo(header);
|
putTo(field,header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,7 +604,7 @@ public class HttpGenerator
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
{
|
{
|
||||||
if (request!=null)
|
if (request!=null)
|
||||||
field.putTo(header);
|
putTo(field,header);
|
||||||
|
|
||||||
// Lookup and/or split connection value field
|
// Lookup and/or split connection value field
|
||||||
HttpHeaderValue[] values = new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
|
HttpHeaderValue[] values = new HttpHeaderValue[]{HttpHeaderValue.CACHE.get(field.getValue())};
|
||||||
|
@ -672,12 +676,12 @@ public class HttpGenerator
|
||||||
case SERVER:
|
case SERVER:
|
||||||
{
|
{
|
||||||
send=send&~SEND_SERVER;
|
send=send&~SEND_SERVER;
|
||||||
field.putTo(header);
|
putTo(field,header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
field.putTo(header);
|
putTo(field,header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -776,7 +780,7 @@ public class HttpGenerator
|
||||||
{
|
{
|
||||||
String c = transfer_encoding.getValue();
|
String c = transfer_encoding.getValue();
|
||||||
if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
|
if (c.endsWith(HttpHeaderValue.CHUNKED.toString()))
|
||||||
transfer_encoding.putTo(header);
|
putTo(transfer_encoding,header);
|
||||||
else
|
else
|
||||||
throw new IllegalArgumentException("BAD TE");
|
throw new IllegalArgumentException("BAD TE");
|
||||||
}
|
}
|
||||||
|
@ -1008,4 +1012,87 @@ public class HttpGenerator
|
||||||
return String.format("ResponseInfo{%s %s %s,%d,%b}",_httpVersion,_status,_reason,_contentLength,_head);
|
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.http.HttpTokens.EndOfContent;
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||||
|
import org.eclipse.jetty.util.ArrayTrie;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
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 boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
|
||||||
public final static int INITIAL_URI_LENGTH=256;
|
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
|
// States
|
||||||
public enum State
|
public enum State
|
||||||
{
|
{
|
||||||
|
@ -138,6 +154,59 @@ public class HttpParser
|
||||||
private int _length;
|
private int _length;
|
||||||
private final StringBuilder _string=new StringBuilder();
|
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)
|
public HttpParser(RequestHandler<ByteBuffer> handler)
|
||||||
{
|
{
|
||||||
|
@ -801,7 +870,7 @@ public class HttpParser
|
||||||
|
|
||||||
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
|
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);
|
_connectionFields.put(_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,7 +1013,7 @@ public class HttpParser
|
||||||
// Try a look ahead for the known header name and value.
|
// Try a look ahead for the known header name and value.
|
||||||
HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
|
HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
|
||||||
if (field==null)
|
if (field==null)
|
||||||
field=HttpField.CACHE.getBest(buffer,-1,buffer.remaining());
|
field=CACHE.getBest(buffer,-1,buffer.remaining());
|
||||||
|
|
||||||
if (field!=null)
|
if (field!=null)
|
||||||
{
|
{
|
||||||
|
@ -1547,5 +1616,4 @@ public class HttpParser
|
||||||
{
|
{
|
||||||
return _connectionFields;
|
return _connectionFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,10 +136,10 @@ public class MimeTypes
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
|
ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
|
||||||
Enumeration i = mime.getKeys();
|
Enumeration<String> i = mime.getKeys();
|
||||||
while(i.hasMoreElements())
|
while(i.hasMoreElements())
|
||||||
{
|
{
|
||||||
String ext = (String)i.nextElement();
|
String ext = i.nextElement();
|
||||||
String m = mime.getString(ext);
|
String m = mime.getString(ext);
|
||||||
__dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
|
__dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class HttpFieldsTest
|
||||||
|
|
||||||
ByteBuffer buffer=BufferUtil.allocate(1024);
|
ByteBuffer buffer=BufferUtil.allocate(1024);
|
||||||
BufferUtil.flipToFill(buffer);
|
BufferUtil.flipToFill(buffer);
|
||||||
header.putTo(buffer);
|
HttpGenerator.putTo(header,buffer);
|
||||||
BufferUtil.flipToFlush(buffer,0);
|
BufferUtil.flipToFlush(buffer,0);
|
||||||
String result=BufferUtil.toString(buffer);
|
String result=BufferUtil.toString(buffer);
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ public class HttpFieldsTest
|
||||||
|
|
||||||
ByteBuffer buffer = BufferUtil.allocate(1024);
|
ByteBuffer buffer = BufferUtil.allocate(1024);
|
||||||
BufferUtil.flipToFill(buffer);
|
BufferUtil.flipToFill(buffer);
|
||||||
header.putTo(buffer);
|
HttpGenerator.putTo(header,buffer);
|
||||||
BufferUtil.flipToFlush(buffer,0);
|
BufferUtil.flipToFlush(buffer,0);
|
||||||
String out = BufferUtil.toString(buffer);
|
String out = BufferUtil.toString(buffer);
|
||||||
assertThat(out,containsString("name0: value??0"));
|
assertThat(out,containsString("name0: value??0"));
|
||||||
|
@ -136,7 +136,7 @@ public class HttpFieldsTest
|
||||||
|
|
||||||
ByteBuffer buffer = BufferUtil.allocate(1024);
|
ByteBuffer buffer = BufferUtil.allocate(1024);
|
||||||
BufferUtil.flipToFill(buffer);
|
BufferUtil.flipToFill(buffer);
|
||||||
header.putTo(buffer);
|
HttpGenerator.putTo(header,buffer);
|
||||||
BufferUtil.flipToFlush(buffer,0);
|
BufferUtil.flipToFlush(buffer,0);
|
||||||
String out = BufferUtil.toString(buffer).toLowerCase();
|
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)
|
private Set<String> enum2set(Enumeration<String> e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -372,7 +372,7 @@ public class HttpGeneratorServerTest
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
|
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);
|
result = gen.generateResponse(info, null, null, null, true);
|
||||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
|
@ -441,7 +441,7 @@ public class HttpGeneratorServerTest
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
|
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);
|
result = gen.generateResponse(info, null, null, content0, false);
|
||||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
@ -503,7 +503,7 @@ public class HttpGeneratorServerTest
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
|
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);
|
result = gen.generateResponse(info, null, null, content0, false);
|
||||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
@ -570,7 +570,7 @@ public class HttpGeneratorServerTest
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||||
|
|
||||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
|
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);
|
result = gen.generateResponse(info, null, null, content0, false);
|
||||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
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
|
public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
|
||||||
{
|
{
|
||||||
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
|
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
|
* The default connect timeout, in milliseconds
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
|
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 Executor executor;
|
||||||
private final Scheduler scheduler;
|
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
|
* Submit a task to update a selector key. If the System property {@link SelectorManager#SUBMIT_KEY_UPDATES}
|
||||||
* "org.eclipse.jetty.io.SelectorManager.SubmitKeyUpdates" is set true (default is false), the
|
* is set true (default is false), the task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
|
||||||
* task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
|
|
||||||
* woken up if need be.
|
* woken up if need be.
|
||||||
* @param update the update to a key
|
* @param update the update to a key
|
||||||
*/
|
*/
|
||||||
|
@ -374,23 +373,10 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
||||||
{
|
{
|
||||||
update.run();
|
update.run();
|
||||||
|
|
||||||
out: while (true)
|
if (_state.compareAndSet(State.SELECT, State.WAKEUP))
|
||||||
{
|
|
||||||
switch (_state.get())
|
|
||||||
{
|
|
||||||
case SELECT:
|
|
||||||
// Avoid multiple wakeup() calls if we the CAS fails
|
|
||||||
if (!_state.compareAndSet(State.SELECT, State.WAKEUP))
|
|
||||||
continue;
|
|
||||||
wakeup();
|
wakeup();
|
||||||
break out;
|
|
||||||
default:
|
|
||||||
break out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Submits a change to be executed in the selector thread.</p>
|
* <p>Submits a change to be executed in the selector thread.</p>
|
||||||
|
|
|
@ -17,11 +17,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.LazyList;
|
|
||||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -41,10 +42,9 @@ public class CookieCutter
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(CookieCutter.class);
|
private static final Logger LOG = Log.getLogger(CookieCutter.class);
|
||||||
|
|
||||||
|
|
||||||
private Cookie[] _cookies;
|
private Cookie[] _cookies;
|
||||||
private Cookie[] _lastCookies;
|
private Cookie[] _lastCookies;
|
||||||
Object _lazyFields;
|
private final List<String> _fieldList = new ArrayList<>();
|
||||||
int _fields;
|
int _fields;
|
||||||
|
|
||||||
public CookieCutter()
|
public CookieCutter()
|
||||||
|
@ -56,9 +56,7 @@ public class CookieCutter
|
||||||
if (_cookies!=null)
|
if (_cookies!=null)
|
||||||
return _cookies;
|
return _cookies;
|
||||||
|
|
||||||
if (_lastCookies!=null &&
|
if (_lastCookies!=null && _fields==_fieldList.size())
|
||||||
_lazyFields!=null &&
|
|
||||||
_fields==LazyList.size(_lazyFields))
|
|
||||||
_cookies=_lastCookies;
|
_cookies=_lastCookies;
|
||||||
else
|
else
|
||||||
parseFields();
|
parseFields();
|
||||||
|
@ -70,7 +68,7 @@ public class CookieCutter
|
||||||
{
|
{
|
||||||
_cookies=cookies;
|
_cookies=cookies;
|
||||||
_lastCookies=null;
|
_lastCookies=null;
|
||||||
_lazyFields=null;
|
_fieldList.clear();
|
||||||
_fields=0;
|
_fields=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,20 +86,20 @@ public class CookieCutter
|
||||||
if (f.length()==0)
|
if (f.length()==0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (LazyList.size(_lazyFields)>_fields)
|
if (_fieldList.size()>_fields)
|
||||||
{
|
{
|
||||||
if (f.equals(LazyList.get(_lazyFields,_fields)))
|
if (f.equals(_fieldList.get(_fields)))
|
||||||
{
|
{
|
||||||
_fields++;
|
_fields++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (LazyList.size(_lazyFields)>_fields)
|
while (_fieldList.size()>_fields)
|
||||||
_lazyFields=LazyList.remove(_lazyFields,_fields);
|
_fieldList.remove(_fields);
|
||||||
}
|
}
|
||||||
_cookies=null;
|
_cookies=null;
|
||||||
_lastCookies=null;
|
_lastCookies=null;
|
||||||
_lazyFields=LazyList.add(_lazyFields,_fields++,f);
|
_fieldList.add(_fields++,f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,19 +108,17 @@ public class CookieCutter
|
||||||
_lastCookies=null;
|
_lastCookies=null;
|
||||||
_cookies=null;
|
_cookies=null;
|
||||||
|
|
||||||
Object cookies = null;
|
List<Cookie> cookies = new ArrayList<>();
|
||||||
|
|
||||||
int version = 0;
|
int version = 0;
|
||||||
|
|
||||||
// delete excess fields
|
// delete excess fields
|
||||||
while (LazyList.size(_lazyFields)>_fields)
|
while (_fieldList.size()>_fields)
|
||||||
_lazyFields=LazyList.remove(_lazyFields,_fields);
|
_fieldList.remove(_fields);
|
||||||
|
|
||||||
// For each cookie field
|
// For each cookie field
|
||||||
for (int f=0;f<_fields;f++)
|
for (String hdr : _fieldList)
|
||||||
{
|
{
|
||||||
String hdr = LazyList.get(_lazyFields,f);
|
|
||||||
|
|
||||||
// Parse the header
|
// Parse the header
|
||||||
String name = null;
|
String name = null;
|
||||||
String value = null;
|
String value = null;
|
||||||
|
@ -311,7 +307,7 @@ public class CookieCutter
|
||||||
cookie = new Cookie(name, value);
|
cookie = new Cookie(name, value);
|
||||||
if (version > 0)
|
if (version > 0)
|
||||||
cookie.setVersion(version);
|
cookie.setVersion(version);
|
||||||
cookies = LazyList.add(cookies, cookie);
|
cookies.add(cookie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
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;
|
_lastCookies=_cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.DateGenerator;
|
||||||
import org.eclipse.jetty.http.HttpContent;
|
import org.eclipse.jetty.http.HttpContent;
|
||||||
import org.eclipse.jetty.http.HttpContent.ResourceAsHttpContent;
|
import org.eclipse.jetty.http.HttpContent.ResourceAsHttpContent;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
|
@ -345,7 +346,7 @@ public class ResourceCache
|
||||||
_contentType=(mimeType==null?null:BufferUtil.toBuffer(mimeType));
|
_contentType=(mimeType==null?null:BufferUtil.toBuffer(mimeType));
|
||||||
boolean exists=resource.exists();
|
boolean exists=resource.exists();
|
||||||
_lastModified=exists?resource.lastModified():-1;
|
_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;
|
_length=exists?(int)resource.length():0;
|
||||||
_cachedSize.addAndGet(_length);
|
_cachedSize.addAndGet(_length);
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import static org.eclipse.jetty.util.QuotedStringTokenizer.isQuoted;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.channels.IllegalSelectorException;
|
import java.nio.channels.IllegalSelectorException;
|
||||||
|
@ -26,6 +28,7 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
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.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpGenerator;
|
import org.eclipse.jetty.http.HttpGenerator;
|
||||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
||||||
|
import org.eclipse.jetty.http.DateGenerator;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
|
import org.eclipse.jetty.http.HttpParser;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.HttpURI;
|
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.ContextHandler;
|
||||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||||
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
||||||
|
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -64,6 +70,9 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
public class Response implements HttpServletResponse
|
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)
|
public static Response getResponse(HttpServletResponse response)
|
||||||
|
@ -186,7 +195,16 @@ public class Response implements HttpServletResponse
|
||||||
|
|
||||||
public void addCookie(HttpCookie cookie)
|
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
|
@Override
|
||||||
|
@ -206,7 +224,7 @@ public class Response implements HttpServletResponse
|
||||||
comment = null;
|
comment = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_fields.addSetCookie(cookie.getName(),
|
addSetCookie(cookie.getName(),
|
||||||
cookie.getValue(),
|
cookie.getValue(),
|
||||||
cookie.getDomain(),
|
cookie.getDomain(),
|
||||||
cookie.getPath(),
|
cookie.getPath(),
|
||||||
|
@ -217,6 +235,175 @@ public class Response implements HttpServletResponse
|
||||||
cookie.getVersion());
|
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
|
@Override
|
||||||
public boolean containsHeader(String name)
|
public boolean containsHeader(String name)
|
||||||
{
|
{
|
||||||
|
@ -877,7 +1064,7 @@ public class Response implements HttpServletResponse
|
||||||
if (_contentType != null)
|
if (_contentType != null)
|
||||||
{
|
{
|
||||||
_contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
|
_contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
|
||||||
HttpField field = HttpField.CONTENT_TYPE.get(_contentType);
|
HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
|
||||||
if (field!=null)
|
if (field!=null)
|
||||||
_fields.put(field);
|
_fields.put(field);
|
||||||
else
|
else
|
||||||
|
@ -893,7 +1080,7 @@ public class Response implements HttpServletResponse
|
||||||
if (_contentType != null)
|
if (_contentType != null)
|
||||||
{
|
{
|
||||||
_contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding;
|
_contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding;
|
||||||
HttpField field = HttpField.CONTENT_TYPE.get(_contentType);
|
HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
|
||||||
if (field!=null)
|
if (field!=null)
|
||||||
_fields.put(field);
|
_fields.put(field);
|
||||||
else
|
else
|
||||||
|
@ -952,7 +1139,7 @@ public class Response implements HttpServletResponse
|
||||||
_explicitEncoding = true;
|
_explicitEncoding = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpField field = HttpField.CONTENT_TYPE.get(_contentType);
|
HttpField field = HttpParser.CONTENT_TYPE.get(_contentType);
|
||||||
if (field!=null)
|
if (field!=null)
|
||||||
_fields.put(field);
|
_fields.put(field);
|
||||||
else
|
else
|
||||||
|
|
|
@ -32,13 +32,14 @@ import java.util.TimerTask;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.DateGenerator;
|
||||||
import org.eclipse.jetty.http.HttpField;
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator;
|
import org.eclipse.jetty.http.HttpGenerator;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
@ -314,17 +315,17 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
|
|
||||||
|
|
||||||
// use DateCache timer for Date field reformat
|
// use DateCache timer for Date field reformat
|
||||||
final HttpFields.DateGenerator date = new HttpFields.DateGenerator();
|
final DateGenerator date = new DateGenerator();
|
||||||
long now=System.currentTimeMillis();
|
long now=System.currentTimeMillis();
|
||||||
long tick=1000*((now/1000)+1)-now;
|
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()
|
DateCache.getTimer().scheduleAtFixedRate(new TimerTask()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
long now=System.currentTimeMillis();
|
long now=System.currentTimeMillis();
|
||||||
_dateField=new HttpField.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
|
_dateField=new HttpGenerator.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
|
||||||
if (!isRunning())
|
if (!isRunning())
|
||||||
this.cancel();
|
this.cancel();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator;
|
import org.eclipse.jetty.http.HttpGenerator;
|
||||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
||||||
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpURI;
|
import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.io.AbstractEndPoint;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@ -645,6 +647,126 @@ public class ResponseTest
|
||||||
output.flush();
|
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()
|
private Response newResponse()
|
||||||
{
|
{
|
||||||
_channel.reset();
|
_channel.reset();
|
||||||
|
|
|
@ -35,6 +35,7 @@ import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.DateGenerator;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
import org.eclipse.jetty.server.LocalConnector;
|
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");
|
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);
|
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);
|
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);
|
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);
|
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);
|
assertResponseContains("412", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.Servlet;
|
import javax.servlet.Servlet;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.DateGenerator;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpTester;
|
import org.eclipse.jetty.http.HttpTester;
|
||||||
|
@ -98,7 +99,7 @@ public class GzipTester
|
||||||
request.setHeader("Host","tester");
|
request.setHeader("Host","tester");
|
||||||
request.setHeader("Accept-Encoding",compressionType);
|
request.setHeader("Accept-Encoding",compressionType);
|
||||||
if (ifmodifiedsince>0)
|
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)
|
if (this.userAgent != null)
|
||||||
request.setHeader("User-Agent", this.userAgent);
|
request.setHeader("User-Agent", this.userAgent);
|
||||||
request.setURI("/context/" + requestedFilename);
|
request.setURI("/context/" + requestedFilename);
|
||||||
|
@ -176,7 +177,7 @@ public class GzipTester
|
||||||
request.setHeader("Host","tester");
|
request.setHeader("Host","tester");
|
||||||
request.setHeader("Accept-Encoding",compressionType);
|
request.setHeader("Accept-Encoding",compressionType);
|
||||||
if (ifmodifiedsince>0)
|
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)
|
if (this.userAgent != null)
|
||||||
request.setHeader("User-Agent", this.userAgent);
|
request.setHeader("User-Agent", this.userAgent);
|
||||||
request.setURI("/context/" + requestedFilename);
|
request.setURI("/context/" + requestedFilename);
|
||||||
|
|
Loading…
Reference in New Issue