jetty-9 URI parsing passed ByteBuffer so that strange query string encodings can be supported
This commit is contained in:
parent
73d90623c7
commit
31def06214
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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 "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue