diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index d373a29184a..f77012a8238 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -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 _connectionFields=(Trie)HttpField.CONNECTION.clone(); - private final Trie _connectionFields=new Trie(512); + private final Trie _connectionFields=new Trie<>(512); private int _length; private final StringBuilder _string=new StringBuilder(); - private final Utf8StringBuilder _utf8=new Utf8StringBuilder(); /* ------------------------------------------------------------------------------- */ public HttpParser(RequestHandler 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 (iHttpTokens.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()=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 diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java index c038ebe4296..414007de35e 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java @@ -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; } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java index 27d83bc5161..e4593ce1dab 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java @@ -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 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 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() diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 3000e8d91da..3a949b6f14e 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -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(); diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java index e847b29a985..0f9483c4f96 100644 --- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java +++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRuleTest.java @@ -87,7 +87,7 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase if (test[5]!=null) { MultiMap params=new MultiMap(); - 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)); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java index 46e5cb5d863..4c5a1051fe1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java @@ -147,7 +147,7 @@ public class Dispatcher implements RequestDispatcher } MultiMap parameters=new MultiMap<>(); - UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding()); + UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding(),-1); if(old_params != null) { // Merge parameters. diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index 2eeab021698..92d67ed1019 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -373,7 +373,7 @@ public class HttpChannel implements HttpParser.RequestHandler, 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 implements HttpParser.RequestHandler, 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; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 6ce69d268f5..31678f55be4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -2102,7 +2102,7 @@ public class Request implements HttpServletRequest { // extract parameters from dispatch query MultiMap 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 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 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()) { diff --git a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java index e7111b79e3e..9e74f2777fe 100644 --- a/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java +++ b/jetty-spdy/spdy-core/src/test/java/org/eclipse/jetty/spdy/api/ClientUsageTest.java @@ -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() - { - @Override + }, new Promise.Adapter() + { + @Override public void succeeded(Stream stream) { stream.data(new BytesDataInfo("wee".getBytes(Charset.forName("UTF-8")), false), new Callback.Adapter()); diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java index 54dcc664387..75acdf1a615 100644 --- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java +++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpChannelOverSPDY.java @@ -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 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) diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java index b601af5108c..304812327cc 100644 --- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java +++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java @@ -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; } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayOutputStream2.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayOutputStream2.java index b370bfddbd4..2da49421f9c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayOutputStream2.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayOutputStream2.java @@ -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); + } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 25661424528..d7ca17bb2ad 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -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); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java index 01e0e9c1455..58474f83f9d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java @@ -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 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 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 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 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 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 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 map, String charset, boolean equalsForNullValue) + public static String encode(MultiMap map, Charset charset, boolean equalsForNullValue) { if (charset==null) charset=ENCODING; @@ -180,22 +181,20 @@ public class UrlEncoded extends MultiMap implements Cloneable return result.toString(); } - - /* -------------------------------------------------------------- */ /** Decoded parameters to Map. * @param content the string containing the encoded parameters */ - public static void decodeTo(String content, MultiMap map, String charset) + public static void decodeTo(String content, MultiMap 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 map, String charset, int maxKeys) + public static void decodeTo(String content, MultiMap map, Charset charset, int maxKeys) { if (charset==null) charset=ENCODING; @@ -570,38 +569,59 @@ public class UrlEncoded extends MultiMap 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 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 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 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 implements Cloneable { StringBuffer buffer=null; - try + for (int i=0;i0xff) { - 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=0 && c<=0xff) + { + if (c=='%') + { + if(i+2=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 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 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); } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java index dfa5b6e3fc0..f152bf65ba2 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java @@ -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 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 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")); } diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index cead6208ad1..95d02d4043e 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -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()) diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/MuxAddHandler.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/MuxAddHandler.java index 306531cd0dc..31cd5e0d9a9 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/MuxAddHandler.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/mux/MuxAddHandler.java @@ -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()) { diff --git a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java index 6f800eb7de7..8aff82d723b 100644 --- a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java +++ b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java @@ -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 ")); } } }