jetty-9 URI parsing passed ByteBuffer so that strange query string encodings can be supported

This commit is contained in:
Greg Wilkins 2012-12-24 19:17:00 +11:00
parent 73d90623c7
commit 31def06214
18 changed files with 258 additions and 215 deletions

View File

@ -78,7 +78,7 @@ public class HttpParser
private HttpMethod _method;
private String _methodString;
private HttpVersion _version;
private String _uri;
private ByteBuffer _uri=ByteBuffer.allocate(128); // Tune?
private byte _eol;
private EndOfContent _endOfContent;
private long _contentLength;
@ -87,12 +87,10 @@ public class HttpParser
private int _chunkPosition;
private boolean _headResponse;
private ByteBuffer _contentChunk;
// private final Trie<HttpField> _connectionFields=(Trie<HttpField>)HttpField.CONNECTION.clone();
private final Trie<HttpField> _connectionFields=new Trie(512);
private final Trie<HttpField> _connectionFields=new Trie<>(512);
private int _length;
private final StringBuilder _string=new StringBuilder();
private final Utf8StringBuilder _utf8=new Utf8StringBuilder();
/* ------------------------------------------------------------------------------- */
public HttpParser(RequestHandler<ByteBuffer> handler)
@ -360,9 +358,39 @@ public class HttpParser
}
else
{
_uri.clear();
setState(State.URI);
_utf8.reset();
_utf8.append(ch);
// quick scan for space or EoBuffer
if (buffer.hasArray())
{
byte[] array=buffer.array();
int p=buffer.arrayOffset()+buffer.position();
int l=buffer.arrayOffset()+buffer.limit();
int i=p;
while (i<l && array[i]>HttpTokens.SPACE)
i++;
int len=i-p;
_headerBytes+=len;
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
{
LOG.warn("URI is too large >"+_maxHeaderBytes);
badMessage(buffer,HttpStatus.REQUEST_URI_TOO_LONG_414,null);
return true;
}
if (_uri.remaining()<len)
{
ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()+2*len);
_uri.flip();
uri.put(_uri);
_uri=uri;
}
_uri.put(array,p-1,len+1);
buffer.position(i-buffer.arrayOffset());
}
else
_uri.put(ch);
}
}
else if (ch < HttpTokens.SPACE)
@ -396,15 +424,12 @@ public class HttpParser
case URI:
if (ch == HttpTokens.SPACE)
{
_uri=_utf8.toString();
_utf8.reset();
setState(State.SPACE2);
}
else if (ch < HttpTokens.SPACE && ch>=0)
{
// HTTP/0.9
_uri=_utf8.toString();
_utf8.reset();
_uri.flip();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri,null);
setState(State.END);
BufferUtil.clear(buffer);
@ -412,7 +437,16 @@ public class HttpParser
return_from_parse|=_handler.messageComplete();
}
else
_utf8.append(ch);
{
if (!_uri.hasRemaining())
{
ByteBuffer uri = ByteBuffer.allocate(_uri.capacity()*2);
_uri.flip();
uri.put(_uri);
_uri=uri;
}
_uri.put(ch);
}
break;
case SPACE2:
@ -439,7 +473,8 @@ public class HttpParser
buffer.position(buffer.position()+_version.asString().length()-1);
_eol=buffer.get();
setState(State.HEADER);
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, _version);
_uri.flip();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri, _version);
}
}
}
@ -455,7 +490,8 @@ public class HttpParser
else
{
// HTTP/0.9
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, null);
_uri.flip();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri, null);
setState(State.END);
BufferUtil.clear(buffer);
return_from_parse|=_handler.headerComplete();
@ -477,7 +513,8 @@ public class HttpParser
_eol=ch;
setState(State.HEADER);
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, _version);
_uri.flip();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri, _version);
continue;
}
else
@ -1001,7 +1038,6 @@ public class HttpParser
_version=null;
_method=null;
_methodString=null;
_uri=null;
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
_header=null;
quickStart(buffer);
@ -1214,7 +1250,6 @@ public class HttpParser
}
catch(Exception e)
{
e.printStackTrace();
BufferUtil.clear(buffer);
if (isClosed())
{
@ -1360,8 +1395,13 @@ public class HttpParser
{
/**
* This is the method called by parser when the HTTP request line is parsed
* @param method The method as enum if of a known type
* @param methodString The method as a string
* @param uri The raw bytes of the URI. These are copied into a ByteBuffer that will not be changed until this parser is reset and reused.
* @param version
* @return true if handling parsing should return.
*/
public abstract boolean startRequest(HttpMethod method, String methodString, String uri, HttpVersion version);
public abstract boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version);
/**
* This is the method called by the parser after it has parsed the host header (and checked it's format). This is

View File

@ -237,10 +237,10 @@ public class HttpTester
private String _uri;
@Override
public boolean startRequest(HttpMethod method, String methodString, String uri, HttpVersion version)
public boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion version)
{
_method=methodString;
_uri=uri;
_uri=BufferUtil.toUTF8String(uri);
_version=version;
return false;
}

View File

@ -127,7 +127,7 @@ public class HttpURI
public void parse(String raw)
{
byte[] b = StringUtil.getBytes(raw);
byte[] b = StringUtil.getUtf8Bytes(raw);
parse2(b,0,b.length);
_rawString=raw;
}
@ -680,12 +680,6 @@ public class HttpURI
return StringUtil.toString(bytes,0,n,encoding);
}
public String getPathAndParam()
{
@ -734,17 +728,17 @@ public class HttpURI
return new String(_raw,_fragment+1,_end-_fragment-1,_charset);
}
public void decodeQueryTo(MultiMap parameters)
public void decodeQueryTo(MultiMap<String> parameters)
{
if (_query==_fragment)
return;
if (_charset==StringUtil.__UTF8_CHARSET)
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
else
UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,_charset.toString()),parameters,_charset.toString());
UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,_charset.toString()),parameters,_charset.toString(),-1);
}
public void decodeQueryTo(MultiMap parameters, String encoding) throws UnsupportedEncodingException
public void decodeQueryTo(MultiMap<String> parameters, String encoding) throws UnsupportedEncodingException
{
if (_query==_fragment)
return;
@ -752,7 +746,7 @@ public class HttpURI
if (encoding==null || StringUtil.isUTF8(encoding))
UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
else
UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding);
UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
}
public void clear()

View File

@ -843,14 +843,14 @@ public class HttpParserTest
}
@Override
public boolean startRequest(HttpMethod httpMethod, String method, String uri, HttpVersion version)
public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
{
request=true;
_h= -1;
_hdr= new String[10];
_val= new String[10];
_methodOrVersion= method;
_uriOrStatus= uri;
_uriOrStatus= BufferUtil.toUTF8String(uri);
_versionOrReason= version==null?null:version.asString();
fields=new HttpFields();

View File

@ -87,7 +87,7 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
if (test[5]!=null)
{
MultiMap<String> params=new MultiMap<String>();
UrlEncoded.decodeTo(test[5],params,StringUtil.__UTF8);
UrlEncoded.decodeTo(test[5],params,StringUtil.__UTF8_CHARSET,-1);
for (String n:params.keySet())
assertEquals(params.getString(n),_request.getParameter(n));

View File

@ -147,7 +147,7 @@ public class Dispatcher implements RequestDispatcher
}
MultiMap<String> parameters=new MultiMap<>();
UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding(),-1);
if(old_params != null) {
// Merge parameters.

View File

@ -373,7 +373,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
@Override
public boolean startRequest(HttpMethod httpMethod, String method, String uri, HttpVersion version)
public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
{
_expect = false;
_expect100Continue = false;
@ -384,9 +384,9 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
_request.setMethod(httpMethod, method);
if (httpMethod == HttpMethod.CONNECT)
_uri.parseConnect(uri);
_uri.parseConnect(uri.array(),uri.arrayOffset()+uri.position(),uri.remaining());
else
_uri.parse(uri);
_uri.parse(uri.array(),uri.arrayOffset()+uri.position(),uri.remaining());
_request.setUri(_uri);
String path;

View File

@ -2102,7 +2102,7 @@ public class Request implements HttpServletRequest
{
// extract parameters from dispatch query
MultiMap<String> parameters = new MultiMap<>();
UrlEncoded.decodeTo(query,parameters, StringUtil.__UTF8); //have to assume UTF-8 because we can't know otherwise
UrlEncoded.decodeTo(query,parameters, StringUtil.__UTF8_CHARSET,-1); //have to assume UTF-8 because we can't know otherwise
boolean merge_old_query = false;
@ -2123,11 +2123,11 @@ public class Request implements HttpServletRequest
{
StringBuilder overridden_query_string = new StringBuilder();
MultiMap<String> overridden_old_query = new MultiMap<>();
UrlEncoded.decodeTo(_queryString,overridden_old_query,getQueryEncoding());//decode using any queryencoding set for the request
UrlEncoded.decodeTo(_queryString,overridden_old_query,getQueryEncoding(),-1);//decode using any queryencoding set for the request
MultiMap<String> overridden_new_query = new MultiMap<>();
UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8); //have to assume utf8 as we cannot know otherwise
UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8_CHARSET,-1); //have to assume utf8 as we cannot know otherwise
for(String name: overridden_old_query.keySet())
{

View File

@ -136,7 +136,7 @@ public class ClientUsageTest
Session session = new StandardSession(SPDY.V2, null, null, null, null, null, null, 1, null, null, null);
session.syn(new SynInfo(new Fields(), false), new StreamFrameListener.Adapter()
{
{
// The good of passing the listener to push() is that applications can safely
// accumulate info from the reply headers to be used in the data callback,
// e.g. content-type, charset, etc.
@ -167,16 +167,11 @@ public class ClientUsageTest
{
StringBuilder builder = (StringBuilder)stream.getAttribute("builder");
builder.append(dataInfo.asString("UTF-8", true));
if (dataInfo.isClose())
{
int receivedLength = builder.toString().getBytes(Charset.forName("UTF-8")).length;
assert receivedLength == stream.getAttribute("content-length");
}
}
}, new Promise.Adapter<Stream>()
{
@Override
}, new Promise.Adapter<Stream>()
{
@Override
public void succeeded(Stream stream)
{
stream.data(new BytesDataInfo("wee".getBytes(Charset.forName("UTF-8")), false), new Callback.Adapter());

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -170,10 +171,13 @@ public class HttpChannelOverSPDY extends HttpChannel<DataInfo>
HttpMethod httpMethod = HttpMethod.fromString(methodHeader.value());
HttpVersion httpVersion = HttpVersion.fromString(versionHeader.value());
String uriString = uriHeader.value();
// TODO should handle URI as byte buffer as some bad clients send WRONG encodings in query string
// that we have to deal with
ByteBuffer uri = BufferUtil.toBuffer(uriHeader.value());
LOG.debug("HTTP > {} {} {}", httpMethod, uriString, httpVersion);
startRequest(httpMethod, httpMethod.asString(), uriString, httpVersion);
LOG.debug("HTTP > {} {} {}", httpMethod, uriHeader.value(), httpVersion);
startRequest(httpMethod, httpMethod.asString(), uri, httpVersion);
Fields.Field schemeHeader = headers.get(HTTPSPDYHeader.SCHEME.name(version));
if (schemeHeader != null)

View File

@ -52,6 +52,7 @@ import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.server.http.HTTPSPDYHeader;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.Promise;
@ -80,13 +81,13 @@ public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParse
}
@Override
public boolean startRequest(HttpMethod method, String methodString, String uri, HttpVersion httpVersion)
public boolean startRequest(HttpMethod method, String methodString, ByteBuffer uri, HttpVersion httpVersion)
{
Connector connector = getConnector();
String scheme = connector.getConnectionFactory(SslConnectionFactory.class) != null ? "https" : "http";
headers.put(HTTPSPDYHeader.SCHEME.name(version), scheme);
headers.put(HTTPSPDYHeader.METHOD.name(version), methodString);
headers.put(HTTPSPDYHeader.URI.name(version), uri);
headers.put(HTTPSPDYHeader.URI.name(version), BufferUtil.toUTF8String(uri)); // TODO handle bad encodings
headers.put(HTTPSPDYHeader.VERSION.name(version), httpVersion.asString());
return false;
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.util;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
/* ------------------------------------------------------------ */
/** ByteArrayOutputStream with public internals
@ -46,4 +47,8 @@ public class ByteArrayOutputStream2 extends ByteArrayOutputStream
buf[count++]=(byte)b;
}
public String toString(Charset charset)
{
return new String(buf, 0, count, charset);
}
}

View File

@ -52,11 +52,13 @@ public class StringUtil
public final static Charset __UTF8_CHARSET;
public final static Charset __ISO_8859_1_CHARSET;
public final static Charset __UTF16_CHARSET;
static
{
__UTF8_CHARSET=Charset.forName(__UTF8);
__ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1);
__UTF16_CHARSET=Charset.forName(__UTF16);
CHARSETS.put("UTF-8",__UTF8);
CHARSETS.put("UTF8",__UTF8);

View File

@ -25,6 +25,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
@ -56,8 +57,22 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
{
static final Logger LOG = Log.getLogger(UrlEncoded.class);
public static final String ENCODING = System.getProperty("org.eclipse.jetty.util.UrlEncoding.charset",StringUtil.__UTF8);
public static final Charset ENCODING;
static
{
Charset encoding=null;
try
{
encoding=Charset.forName(System.getProperty("org.eclipse.jetty.util.UrlEncoding.charset",StringUtil.__UTF8));
}
catch(Exception e)
{
LOG.warn(e);
encoding=StringUtil.__UTF8_CHARSET;
}
ENCODING=encoding;
}
/* ----------------------------------------------------------------- */
public UrlEncoded(UrlEncoded url)
{
@ -69,20 +84,6 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
{
}
/* ----------------------------------------------------------------- */
public UrlEncoded(String s)
{
this();
decode(s,ENCODING);
}
/* ----------------------------------------------------------------- */
public UrlEncoded(String s, String charset)
{
this();
decode(s,charset);
}
/* ----------------------------------------------------------------- */
public void decode(String query)
{
@ -90,7 +91,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
}
/* ----------------------------------------------------------------- */
public void decode(String query,String charset)
public void decode(String query,Charset charset)
{
decodeTo(query,this,charset,-1);
}
@ -106,7 +107,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
/* -------------------------------------------------------------- */
/** Encode Hashtable with % encoding.
*/
public String encode(String charset)
public String encode(Charset charset)
{
return encode(charset,false);
}
@ -116,7 +117,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
* @param equalsForNullValue if True, then an '=' is always used, even
* for parameters without a value. e.g. "blah?a=&b=&c=".
*/
public synchronized String encode(String charset, boolean equalsForNullValue)
public synchronized String encode(Charset charset, boolean equalsForNullValue)
{
return encode(this,charset,equalsForNullValue);
}
@ -126,7 +127,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
* @param equalsForNullValue if True, then an '=' is always used, even
* for parameters without a value. e.g. "blah?a=&b=&c=".
*/
public static String encode(MultiMap<String> map, String charset, boolean equalsForNullValue)
public static String encode(MultiMap<String> map, Charset charset, boolean equalsForNullValue)
{
if (charset==null)
charset=ENCODING;
@ -180,22 +181,20 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
return result.toString();
}
/* -------------------------------------------------------------- */
/** Decoded parameters to Map.
* @param content the string containing the encoded parameters
*/
public static void decodeTo(String content, MultiMap<String> map, String charset)
public static void decodeTo(String content, MultiMap<String> map, String charset, int maxKeys)
{
decodeTo(content,map,charset,-1);
decodeTo(content,map,charset==null?null:Charset.forName(charset),maxKeys);
}
/* -------------------------------------------------------------- */
/** Decoded parameters to Map.
* @param content the string containing the encoded parameters
*/
public static void decodeTo(String content, MultiMap<String> map, String charset, int maxKeys)
public static void decodeTo(String content, MultiMap<String> map, Charset charset, int maxKeys)
{
if (charset==null)
charset=ENCODING;
@ -570,38 +569,59 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
decodeTo(buf.getBuffer().toString(),map,ENCODING,maxKeys);
}
/* -------------------------------------------------------------- */
/** Decoded parameters to Map.
* @param in the stream containing the encoded parameters
*/
public static void decodeTo(InputStream in, MultiMap<String> map, String charset, int maxLength, int maxKeys)
throws IOException
{
if (charset==null)
{
if (ENCODING==StringUtil.__UTF8_CHARSET)
decodeUtf8To(in,map,maxLength,maxKeys);
else
decodeTo(in,map,ENCODING,maxLength,maxKeys);
}
else if (StringUtil.__UTF8.equalsIgnoreCase(charset))
decodeUtf8To(in,map,maxLength,maxKeys);
else if (StringUtil.__ISO_8859_1.equalsIgnoreCase(charset))
decode88591To(in,map,maxLength,maxKeys);
else if (StringUtil.__UTF16.equalsIgnoreCase(charset))
decodeUtf16To(in,map,maxLength,maxKeys);
else
decodeTo(in,map,Charset.forName(charset),maxLength,maxKeys);
}
/* -------------------------------------------------------------- */
/** Decoded parameters to Map.
* @param in the stream containing the encoded parameters
*/
public static void decodeTo(InputStream in, MultiMap<String> map, Charset charset, int maxLength, int maxKeys)
throws IOException
{
//no charset present, use the configured default
if (charset==null)
{
charset=ENCODING;
}
if (StringUtil.__UTF8.equalsIgnoreCase(charset))
if (StringUtil.__UTF8_CHARSET.equals(charset))
{
decodeUtf8To(in,map,maxLength,maxKeys);
return;
}
if (StringUtil.__ISO_8859_1.equals(charset))
if (StringUtil.__ISO_8859_1_CHARSET.equals(charset))
{
decode88591To(in,map,maxLength,maxKeys);
return;
}
if (StringUtil.__UTF16.equalsIgnoreCase(charset)) // Should be all 2 byte encodings
if (StringUtil.__UTF16_CHARSET.equals(charset)) // Should be all 2 byte encodings
{
decodeUtf16To(in,map,maxLength,maxKeys);
return;
}
synchronized(map)
{
@ -700,9 +720,9 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
* This method makes the assumption that the majority of calls
* will need no decoding.
*/
public static String decodeString(String encoded,int offset,int length,String charset)
public static String decodeString(String encoded,int offset,int length,Charset charset)
{
if (charset==null || StringUtil.isUTF8(charset))
if (charset==null || StringUtil.__UTF8_CHARSET.equals(charset))
{
Utf8StringBuffer buffer=null;
@ -786,117 +806,110 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
{
StringBuffer buffer=null;
try
for (int i=0;i<length;i++)
{
for (int i=0;i<length;i++)
char c = encoded.charAt(offset+i);
if (c<0||c>0xff)
{
char c = encoded.charAt(offset+i);
if (c<0||c>0xff)
if (buffer==null)
{
if (buffer==null)
{
buffer=new StringBuffer(length);
buffer.append(encoded,offset,offset+i+1);
}
else
buffer.append(c);
buffer=new StringBuffer(length);
buffer.append(encoded,offset,offset+i+1);
}
else if (c=='+')
else
buffer.append(c);
}
else if (c=='+')
{
if (buffer==null)
{
if (buffer==null)
{
buffer=new StringBuffer(length);
buffer.append(encoded,offset,offset+i);
}
buffer.append(' ');
buffer=new StringBuffer(length);
buffer.append(encoded,offset,offset+i);
}
else if (c=='%')
{
if (buffer==null)
{
buffer=new StringBuffer(length);
buffer.append(encoded,offset,offset+i);
}
byte[] ba=new byte[length];
int n=0;
while(c>=0 && c<=0xff)
{
if (c=='%')
{
if(i+2<length)
{
try
{
if ('u'==encoded.charAt(offset+i+1))
{
int o=offset+i+2;
i+=6;
String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
byte[] reencoded = unicode.getBytes(charset);
System.arraycopy(reencoded,0,ba,n,reencoded.length);
n+=reencoded.length;
}
else
{
int o=offset+i+1;
i+=3;
ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
n++;
}
}
catch(NumberFormatException nfe)
{
LOG.ignore(nfe);
ba[n++] = (byte)'?';
}
}
else
{
ba[n++] = (byte)'%';
i++;
}
}
else if (c=='+')
buffer.append(' ');
}
else if (c=='%')
{
if (buffer==null)
{
buffer=new StringBuffer(length);
buffer.append(encoded,offset,offset+i);
}
byte[] ba=new byte[length];
int n=0;
while(c>=0 && c<=0xff)
{
if (c=='%')
{
if(i+2<length)
{
ba[n++]=(byte)' ';
i++;
try
{
if ('u'==encoded.charAt(offset+i+1))
{
int o=offset+i+2;
i+=6;
String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
byte[] reencoded = unicode.getBytes(charset);
System.arraycopy(reencoded,0,ba,n,reencoded.length);
n+=reencoded.length;
}
else
{
int o=offset+i+1;
i+=3;
ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
n++;
}
}
catch(NumberFormatException nfe)
{
LOG.ignore(nfe);
ba[n++] = (byte)'?';
}
}
else
{
ba[n++]=(byte)c;
ba[n++] = (byte)'%';
i++;
}
if (i>=length)
break;
c = encoded.charAt(offset+i);
}
else if (c=='+')
{
ba[n++]=(byte)' ';
i++;
}
else
{
ba[n++]=(byte)c;
i++;
}
i--;
buffer.append(new String(ba,0,n,charset));
if (i>=length)
break;
c = encoded.charAt(offset+i);
}
else if (buffer!=null)
buffer.append(c);
}
if (buffer==null)
{
if (offset==0 && encoded.length()==length)
return encoded;
return encoded.substring(offset,offset+length);
}
i--;
buffer.append(new String(ba,0,n,charset));
return buffer.toString();
}
else if (buffer!=null)
buffer.append(c);
}
catch (UnsupportedEncodingException e)
if (buffer==null)
{
throw new RuntimeException(e);
if (offset==0 && encoded.length()==length)
return encoded;
return encoded.substring(offset,offset+length);
}
return buffer.toString();
}
}
/* ------------------------------------------------------------ */
@ -914,20 +927,12 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
* @param string
* @return encoded string.
*/
public static String encodeString(String string,String charset)
public static String encodeString(String string,Charset charset)
{
if (charset==null)
charset=ENCODING;
byte[] bytes=null;
try
{
bytes=string.getBytes(charset);
}
catch(UnsupportedEncodingException e)
{
// LOG.warn(LogSupport.EXCEPTION,e);
bytes=string.getBytes();
}
bytes=string.getBytes(charset);
int len=bytes.length;
byte[] encoded= new byte[bytes.length*3];
@ -969,15 +974,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
if (noEncode)
return string;
try
{
return new String(encoded,0,n,charset);
}
catch(UnsupportedEncodingException e)
{
// LOG.warn(LogSupport.EXCEPTION,e);
return new String(encoded,0,n);
}
return new String(encoded,0,n,charset);
}

View File

@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import org.junit.Assert;
import org.junit.Test;
@ -120,24 +121,24 @@ public class URLEncodedTest
assertEquals("encoded get", url_encoded.getString("Name8"),"xx, yy ,zz");
url_encoded.clear();
url_encoded.decode("Name11=%u30EDxxVerdi+%C6+og+2zz", "ISO-8859-1");
url_encoded.decode("Name11=%u30EDxxVerdi+%C6+og+2zz", StringUtil.__ISO_8859_1_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "?xxVerdi \u00c6 og 2zz",url_encoded.getString("Name11"));
url_encoded.clear();
url_encoded.decode("Name12=%u30EDxxVerdi+%2F+og+2zz", "UTF-8");
url_encoded.decode("Name12=%u30EDxxVerdi+%2F+og+2zz", StringUtil.__UTF8_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", url_encoded.getString("Name12"),"\u30edxxVerdi / og 2zz");
url_encoded.clear();
url_encoded.decode("Name14=%uXXXXa%GGb%+%c%+%d", "ISO-8859-1");
url_encoded.decode("Name14=%uXXXXa%GGb%+%c%+%d", StringUtil.__ISO_8859_1_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get","?a?b?c?d", url_encoded.getString("Name14"));
url_encoded.clear();
url_encoded.decode("Name14=%uXXXX%GG%+%%+%", "UTF-8");
url_encoded.decode("Name14=%uXXXX%GG%+%%+%", StringUtil.__UTF8_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", url_encoded.getString("Name14"),"\ufffd\ufffd\ufffd\ufffd");
assertEquals("encoded get", "\ufffd\ufffd\ufffd\ufffd",url_encoded.getString("Name14"));
/* Not every jvm supports this encoding */
@ -145,7 +146,7 @@ public class URLEncodedTest
if (java.nio.charset.Charset.isSupported("SJIS"))
{
url_encoded.clear();
url_encoded.decode("Name9=%u30ED%83e%83X%83g", "SJIS"); // "Test" in Japanese Katakana
url_encoded.decode("Name9=%u30ED%83e%83X%83g", Charset.forName("SJIS")); // "Test" in Japanese Katakana
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "\u30ed\u30c6\u30b9\u30c8", url_encoded.getString("Name9"));
}
@ -159,11 +160,11 @@ public class URLEncodedTest
public void testBadEncoding()
{
UrlEncoded url_encoded = new UrlEncoded();
url_encoded.decode("Name15=xx%zz", "UTF-8");
url_encoded.decode("Name15=xx%zz", StringUtil.__UTF8_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "xx\ufffd", url_encoded.getString("Name15"));
assertEquals("xxx",UrlEncoded.decodeString("xxx%u123",0,5,"UTF-8"));
assertEquals("xxx",UrlEncoded.decodeString("xxx%u123",0,5,StringUtil.__UTF8_CHARSET));
}
@ -185,12 +186,12 @@ public class URLEncodedTest
{
ByteArrayInputStream in = new ByteArrayInputStream("name\n=value+%30&name1=&name2&n\u00e3me3=value+3".getBytes(charsets[i][0]));
MultiMap<String> m = new MultiMap<>();
UrlEncoded.decodeTo(in, m, charsets[i][1], -1,-1);
assertEquals(i+" stream length",4,m.size());
assertEquals(i+" stream name\\n","value 0",m.getString("name\n"));
assertEquals(i+" stream name1","",m.getString("name1"));
assertEquals(i+" stream name2","",m.getString("name2"));
assertEquals(i+" stream n\u00e3me3","value 3",m.getString("n\u00e3me3"));
UrlEncoded.decodeTo(in, m, charsets[i][1]==null?null:Charset.forName(charsets[i][1]), -1,-1);
assertEquals(charsets[i][1]+" stream length",4,m.size());
assertEquals(charsets[i][1]+" stream name\\n","value 0",m.getString("name\n"));
assertEquals(charsets[i][1]+" stream name1","",m.getString("name1"));
assertEquals(charsets[i][1]+" stream name2","",m.getString("name2"));
assertEquals(charsets[i][1]+" stream n\u00e3me3","value 3",m.getString("n\u00e3me3"));
}
@ -198,7 +199,7 @@ public class URLEncodedTest
{
ByteArrayInputStream in2 = new ByteArrayInputStream ("name=%83e%83X%83g".getBytes(StringUtil.__ISO_8859_1));
MultiMap<String> m2 = new MultiMap<>();
UrlEncoded.decodeTo(in2, m2, "Shift_JIS", -1,-1);
UrlEncoded.decodeTo(in2, m2, Charset.forName("Shift_JIS"), -1,-1);
assertEquals("stream length",1,m2.size());
assertEquals("stream name","\u30c6\u30b9\u30c8",m2.getString("name"));
}

View File

@ -88,7 +88,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
String query = requestURI.getQuery();
if (StringUtil.isNotBlank(query))
{
UrlEncoded.decodeTo(query,params,StringUtil.__UTF8);
UrlEncoded.decodeTo(query,params,StringUtil.__UTF8_CHARSET,-1);
}
for (String name : params.keySet())

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.common.extensions.mux.MuxChannel;
@ -89,7 +90,7 @@ public class MuxAddHandler implements MuxAddServer
HttpMethod method = HttpMethod.fromString(request.getMethod());
HttpVersion version = HttpVersion.fromString(request.getHttpVersion());
httpChannel.startRequest(method,request.getMethod(),request.getRequestURI().toASCIIString(),version);
httpChannel.startRequest(method,request.getMethod(),BufferUtil.toBuffer(request.getRequestURI().toASCIIString()),version);
for (String headerName : request.getHeaders().keySet())
{

View File

@ -22,10 +22,14 @@ import com.acme.DispatchServlet;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletTester;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.hamcrest.Matchers;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
/**
* Simple tests against DispatchServlet.
@ -136,8 +140,7 @@ public class DispatchServletTest
response.startsWith("HTTP/1.1 413 "));
assertFalse(msg + " should not be code 500.", response.startsWith("HTTP/1.1 500 "));
assertTrue(msg + " should return error code 403 (Forbidden)", response.startsWith("HTTP/1.1 403 "));
assertThat(response,Matchers.startsWith("HTTP/1.1 403 "));
}
}
}