Mercurial Hosting > luan
diff src/org/eclipse/jetty/util/URIUtil.java @ 1013:6939226e0ac4
simplify URIUtil
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 24 Oct 2016 01:06:33 -0600 |
parents | 8e9db0bbf4f9 |
children |
line wrap: on
line diff
--- a/src/org/eclipse/jetty/util/URIUtil.java Mon Oct 24 00:47:24 2016 -0600 +++ b/src/org/eclipse/jetty/util/URIUtil.java Mon Oct 24 01:06:33 2016 -0600 @@ -35,655 +35,539 @@ * @see UrlEncoded * */ -public class URIUtil - implements Cloneable -{ - public static final String SLASH="/"; - public static final String HTTP="http"; - public static final String HTTP_COLON="http:"; - public static final String HTTPS="https"; - public static final String HTTPS_COLON="https:"; +public final class URIUtil { + // Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars + public static final String __CHARSET = StringUtil.__UTF8; + + private URIUtil() + {} + + /* ------------------------------------------------------------ */ + /** Encode a URI path. + * This is the same encoding offered by URLEncoder, except that + * the '/' character is not encoded. + * @param path The path the encode + * @return The encoded path + */ + public static String encodePath(String path) + { + if (path==null || path.length()==0) + return path; + + StringBuilder buf = encodePath(null,path); + return buf==null?path:buf.toString(); + } + + /* ------------------------------------------------------------ */ + /** Encode a URI path. + * @param path The path the encode + * @param buf StringBuilder to encode path into (or null) + * @return The StringBuilder or null if no substitutions required. + */ + public static StringBuilder encodePath(StringBuilder buf, String path) + { + byte[] bytes=null; + if (buf==null) + { + loop: + for (int i=0;i<path.length();i++) + { + char c=path.charAt(i); + switch(c) + { + case '%': + case '?': + case ';': + case '#': + case '\'': + case '"': + case '<': + case '>': + case ' ': + buf=new StringBuilder(path.length()*2); + break loop; + default: + if (c>127) + { + try + { + bytes=path.getBytes(URIUtil.__CHARSET); + } + catch (UnsupportedEncodingException e) + { + throw new IllegalStateException(e); + } + buf=new StringBuilder(path.length()*2); + break loop; + } + + } + } + if (buf==null) + return null; + } + + synchronized(buf) + { + if (bytes!=null) + { + for (int i=0;i<bytes.length;i++) + { + byte c=bytes[i]; + switch(c) + { + case '%': + buf.append("%25"); + continue; + case '?': + buf.append("%3F"); + continue; + case ';': + buf.append("%3B"); + continue; + case '#': + buf.append("%23"); + continue; + case '"': + buf.append("%22"); + continue; + case '\'': + buf.append("%27"); + continue; + case '<': + buf.append("%3C"); + continue; + case '>': + buf.append("%3E"); + continue; + case ' ': + buf.append("%20"); + continue; + default: + if (c<0) + { + buf.append('%'); + TypeUtil.toHex(c,buf); + } + else + buf.append((char)c); + continue; + } + } + + } + else + { + for (int i=0;i<path.length();i++) + { + char c=path.charAt(i); + switch(c) + { + case '%': + buf.append("%25"); + continue; + case '?': + buf.append("%3F"); + continue; + case ';': + buf.append("%3B"); + continue; + case '#': + buf.append("%23"); + continue; + case '"': + buf.append("%22"); + continue; + case '\'': + buf.append("%27"); + continue; + case '<': + buf.append("%3C"); + continue; + case '>': + buf.append("%3E"); + continue; + case ' ': + buf.append("%20"); + continue; + default: + buf.append(c); + continue; + } + } + } + } - // Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars - public static final String __CHARSET=System.getProperty("org.eclipse.jetty.util.URI.charset",StringUtil.__UTF8); - - private URIUtil() - {} - - /* ------------------------------------------------------------ */ - /** Encode a URI path. - * This is the same encoding offered by URLEncoder, except that - * the '/' character is not encoded. - * @param path The path the encode - * @return The encoded path - */ - public static String encodePath(String path) - { - if (path==null || path.length()==0) - return path; - - StringBuilder buf = encodePath(null,path); - return buf==null?path:buf.toString(); - } - - /* ------------------------------------------------------------ */ - /** Encode a URI path. - * @param path The path the encode - * @param buf StringBuilder to encode path into (or null) - * @return The StringBuilder or null if no substitutions required. - */ - public static StringBuilder encodePath(StringBuilder buf, String path) - { - byte[] bytes=null; - if (buf==null) - { - loop: - for (int i=0;i<path.length();i++) - { - char c=path.charAt(i); - switch(c) - { - case '%': - case '?': - case ';': - case '#': - case '\'': - case '"': - case '<': - case '>': - case ' ': - buf=new StringBuilder(path.length()*2); - break loop; - default: - if (c>127) - { - try - { - bytes=path.getBytes(URIUtil.__CHARSET); - } - catch (UnsupportedEncodingException e) - { - throw new IllegalStateException(e); - } - buf=new StringBuilder(path.length()*2); - break loop; - } - - } - } - if (buf==null) - return null; - } - - synchronized(buf) - { - if (bytes!=null) - { - for (int i=0;i<bytes.length;i++) - { - byte c=bytes[i]; - switch(c) - { - case '%': - buf.append("%25"); - continue; - case '?': - buf.append("%3F"); - continue; - case ';': - buf.append("%3B"); - continue; - case '#': - buf.append("%23"); - continue; - case '"': - buf.append("%22"); - continue; - case '\'': - buf.append("%27"); - continue; - case '<': - buf.append("%3C"); - continue; - case '>': - buf.append("%3E"); - continue; - case ' ': - buf.append("%20"); - continue; - default: - if (c<0) - { - buf.append('%'); - TypeUtil.toHex(c,buf); - } - else - buf.append((char)c); - continue; - } - } - - } - else - { - for (int i=0;i<path.length();i++) - { - char c=path.charAt(i); - switch(c) - { - case '%': - buf.append("%25"); - continue; - case '?': - buf.append("%3F"); - continue; - case ';': - buf.append("%3B"); - continue; - case '#': - buf.append("%23"); - continue; - case '"': - buf.append("%22"); - continue; - case '\'': - buf.append("%27"); - continue; - case '<': - buf.append("%3C"); - continue; - case '>': - buf.append("%3E"); - continue; - case ' ': - buf.append("%20"); - continue; - default: - buf.append(c); - continue; - } - } - } - } + return buf; + } + + /* ------------------------------------------------------------ */ + /* Decode a URI path and strip parameters + * @param path The path the encode + * @param buf StringBuilder to encode path into + */ + public static String decodePath(String path) + { + if (path==null) + return null; + // Array to hold all converted characters + char[] chars=null; + int n=0; + // Array to hold a sequence of %encodings + byte[] bytes=null; + int b=0; + + int len=path.length(); + + for (int i=0;i<len;i++) + { + char c = path.charAt(i); - return buf; - } - - /* ------------------------------------------------------------ */ - /** Encode a URI path. - * @param path The path the encode - * @param buf StringBuilder to encode path into (or null) - * @param encode String of characters to encode. % is always encoded. - * @return The StringBuilder or null if no substitutions required. - */ - public static StringBuilder encodeString(StringBuilder buf, - String path, - String encode) - { - if (buf==null) - { - loop: - for (int i=0;i<path.length();i++) - { - char c=path.charAt(i); - if (c=='%' || encode.indexOf(c)>=0) - { - buf=new StringBuilder(path.length()<<1); - break loop; - } - } - if (buf==null) - return null; - } - - synchronized(buf) - { - for (int i=0;i<path.length();i++) - { - char c=path.charAt(i); - if (c=='%' || encode.indexOf(c)>=0) - { - buf.append('%'); - StringUtil.append(buf,(byte)(0xff&c),16); - } - else - buf.append(c); - } - } + if (c=='%' && (i+2)<len) + { + if (chars==null) + { + chars=new char[len]; + bytes=new byte[len]; + path.getChars(0,i,chars,0); + } + bytes[b++]=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16)); + i+=2; + continue; + } + else if (c==';') + { + if (chars==null) + { + chars=new char[len]; + path.getChars(0,i,chars,0); + n=i; + } + break; + } + else if (bytes==null) + { + n++; + continue; + } + + // Do we have some bytes to convert? + if (b>0) + { + // convert series of bytes and add to chars + String s; + try + { + s=new String(bytes,0,b,__CHARSET); + } + catch (UnsupportedEncodingException e) + { + s=new String(bytes,0,b); + } + s.getChars(0,s.length(),chars,n); + n+=s.length(); + b=0; + } + + chars[n++]=c; + } - return buf; - } - - /* ------------------------------------------------------------ */ - /* Decode a URI path and strip parameters - * @param path The path the encode - * @param buf StringBuilder to encode path into - */ - public static String decodePath(String path) - { - if (path==null) - return null; - // Array to hold all converted characters - char[] chars=null; - int n=0; - // Array to hold a sequence of %encodings - byte[] bytes=null; - int b=0; - - int len=path.length(); - - for (int i=0;i<len;i++) - { - char c = path.charAt(i); - - if (c=='%' && (i+2)<len) - { - if (chars==null) - { - chars=new char[len]; - bytes=new byte[len]; - path.getChars(0,i,chars,0); - } - bytes[b++]=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16)); - i+=2; - continue; - } - else if (c==';') - { - if (chars==null) - { - chars=new char[len]; - path.getChars(0,i,chars,0); - n=i; - } - break; - } - else if (bytes==null) - { - n++; - continue; - } - - // Do we have some bytes to convert? - if (b>0) - { - // convert series of bytes and add to chars - String s; - try - { - s=new String(bytes,0,b,__CHARSET); - } - catch (UnsupportedEncodingException e) - { - s=new String(bytes,0,b); - } - s.getChars(0,s.length(),chars,n); - n+=s.length(); - b=0; - } - - chars[n++]=c; - } - - if (chars==null) - return path; + if (chars==null) + return path; - // if we have a remaining sequence of bytes - if (b>0) - { - // convert series of bytes and add to chars - String s; - try - { - s=new String(bytes,0,b,__CHARSET); - } - catch (UnsupportedEncodingException e) - { - s=new String(bytes,0,b); - } - s.getChars(0,s.length(),chars,n); - n+=s.length(); - } - - return new String(chars,0,n); - } - - /* ------------------------------------------------------------ */ - /* Decode a URI path and strip parameters. - * @param path The path the encode - * @param buf StringBuilder to encode path into - */ - public static String decodePath(byte[] buf, int offset, int length) - { - byte[] bytes=null; - int n=0; - - for (int i=0;i<length;i++) - { - byte b = buf[i + offset]; - - if (b=='%' && (i+2)<length) - { - b=(byte)(0xff&TypeUtil.parseInt(buf,i+offset+1,2,16)); - i+=2; - } - else if (b==';') - { - length=i; - break; - } - else if (bytes==null) - { - n++; - continue; - } - - if (bytes==null) - { - bytes=new byte[length]; - for (int j=0;j<n;j++) - bytes[j]=buf[j + offset]; - } - - bytes[n++]=b; - } + // if we have a remaining sequence of bytes + if (b>0) + { + // convert series of bytes and add to chars + String s; + try + { + s=new String(bytes,0,b,__CHARSET); + } + catch (UnsupportedEncodingException e) + { + s=new String(bytes,0,b); + } + s.getChars(0,s.length(),chars,n); + n+=s.length(); + } + + return new String(chars,0,n); + } + + /* ------------------------------------------------------------ */ + /* Decode a URI path and strip parameters. + * @param path The path the encode + * @param buf StringBuilder to encode path into + */ + public static String decodePath(byte[] buf, int offset, int length) + { + byte[] bytes=null; + int n=0; + + for (int i=0;i<length;i++) + { + byte b = buf[i + offset]; + + if (b=='%' && (i+2)<length) + { + b=(byte)(0xff&TypeUtil.parseInt(buf,i+offset+1,2,16)); + i+=2; + } + else if (b==';') + { + length=i; + break; + } + else if (bytes==null) + { + n++; + continue; + } + + if (bytes==null) + { + bytes=new byte[length]; + for (int j=0;j<n;j++) + bytes[j]=buf[j + offset]; + } + + bytes[n++]=b; + } - if (bytes==null) - return StringUtil.toString(buf,offset,length,__CHARSET); - return StringUtil.toString(bytes,0,n,__CHARSET); - } + if (bytes==null) + return StringUtil.toString(buf,offset,length,__CHARSET); + return StringUtil.toString(bytes,0,n,__CHARSET); + } - - /* ------------------------------------------------------------ */ - /** Add two URI path segments. - * Handles null and empty paths, path and query params (eg ?a=b or - * ;JSESSIONID=xxx) and avoids duplicate '/' - * @param p1 URI path segment (should be encoded) - * @param p2 URI path segment (should be encoded) - * @return Legally combined path segments. - */ - public static String addPaths(String p1, String p2) - { - if (p1==null || p1.length()==0) - { - if (p1!=null && p2==null) - return p1; - return p2; - } - if (p2==null || p2.length()==0) - return p1; - - int split=p1.indexOf(';'); - if (split<0) - split=p1.indexOf('?'); - if (split==0) - return p2+p1; - if (split<0) - split=p1.length(); + + /* ------------------------------------------------------------ */ + /** Add two URI path segments. + * Handles null and empty paths, path and query params (eg ?a=b or + * ;JSESSIONID=xxx) and avoids duplicate '/' + * @param p1 URI path segment (should be encoded) + * @param p2 URI path segment (should be encoded) + * @return Legally combined path segments. + */ + public static String addPaths(String p1, String p2) + { + if (p1==null || p1.length()==0) + { + if (p1!=null && p2==null) + return p1; + return p2; + } + if (p2==null || p2.length()==0) + return p1; + + int split=p1.indexOf(';'); + if (split<0) + split=p1.indexOf('?'); + if (split==0) + return p2+p1; + if (split<0) + split=p1.length(); - StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2); - buf.append(p1); - - if (buf.charAt(split-1)=='/') - { - if (p2.startsWith(URIUtil.SLASH)) - { - buf.deleteCharAt(split-1); - buf.insert(split-1,p2); - } - else - buf.insert(split,p2); - } - else - { - if (p2.startsWith(URIUtil.SLASH)) - buf.insert(split,p2); - else - { - buf.insert(split,'/'); - buf.insert(split+1,p2); - } - } + StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2); + buf.append(p1); + + if (buf.charAt(split-1)=='/') + { + if (p2.startsWith("/")) + { + buf.deleteCharAt(split-1); + buf.insert(split-1,p2); + } + else + buf.insert(split,p2); + } + else + { + if (p2.startsWith("/")) + buf.insert(split,p2); + else + { + buf.insert(split,'/'); + buf.insert(split+1,p2); + } + } - return buf.toString(); - } - - /* ------------------------------------------------------------ */ - /** Return the parent Path. - * Treat a URI like a directory path and return the parent directory. - */ - public static String parentPath(String p) - { - if (p==null || URIUtil.SLASH.equals(p)) - return null; - int slash=p.lastIndexOf('/',p.length()-2); - if (slash>=0) - return p.substring(0,slash+1); - return null; - } - - /* ------------------------------------------------------------ */ - /** Convert a path to a cananonical form. - * All instances of "." and ".." are factored out. Null is returned - * if the path tries to .. above its root. - * @param path - * @return path or null. - */ - public static String canonicalPath(String path) - { - if (path==null || path.length()==0) - return path; - - int end=path.length(); - int start = path.lastIndexOf('/', end); - - search: - while (end>0) - { - switch(end-start) - { - case 2: // possible single dot - if (path.charAt(start+1)!='.') - break; - break search; - case 3: // possible double dot - if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.') - break; - break search; - } - - end=start; - start=path.lastIndexOf('/',end-1); - } + return buf.toString(); + } + + /* ------------------------------------------------------------ */ + /** Return the parent Path. + * Treat a URI like a directory path and return the parent directory. + */ + public static String parentPath(String p) + { + if (p==null || "/".equals(p)) + return null; + int slash=p.lastIndexOf('/',p.length()-2); + if (slash>=0) + return p.substring(0,slash+1); + return null; + } + + /* ------------------------------------------------------------ */ + /** Convert a path to a cananonical form. + * All instances of "." and ".." are factored out. Null is returned + * if the path tries to .. above its root. + * @param path + * @return path or null. + */ + public static String canonicalPath(String path) + { + if (path==null || path.length()==0) + return path; - // If we have checked the entire string - if (start>=end) - return path; - - StringBuilder buf = new StringBuilder(path); - int delStart=-1; - int delEnd=-1; - int skip=0; - - while (end>0) - { - switch(end-start) - { - case 2: // possible single dot - if (buf.charAt(start+1)!='.') - { - if (skip>0 && --skip==0) - { - delStart=start>=0?start:0; - if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') - delStart++; - } - break; - } - - if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/') - break; - - if(delEnd<0) - delEnd=end; - delStart=start; - if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/') - { - delStart++; - if (delEnd<buf.length() && buf.charAt(delEnd)=='/') - delEnd++; - break; - } - if (end==buf.length()) - delStart++; - - end=start--; - while (start>=0 && buf.charAt(start)!='/') - start--; - continue; - - case 3: // possible double dot - if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.') - { - if (skip>0 && --skip==0) - { delStart=start>=0?start:0; - if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') - delStart++; - } - break; - } - - delStart=start; - if (delEnd<0) - delEnd=end; + int end=path.length(); + int start = path.lastIndexOf('/', end); - skip++; - end=start--; - while (start>=0 && buf.charAt(start)!='/') - start--; - continue; - - default: - if (skip>0 && --skip==0) - { - delStart=start>=0?start:0; - if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.') - delStart++; - } - } - - // Do the delete - if (skip<=0 && delStart>=0 && delEnd>=delStart) - { - buf.delete(delStart,delEnd); - delStart=delEnd=-1; - if (skip>0) - delEnd=end; - } - - end=start--; - while (start>=0 && buf.charAt(start)!='/') - start--; - } + search: + while (end>0) + { + switch(end-start) + { + case 2: // possible single dot + if (path.charAt(start+1)!='.') + break; + break search; + case 3: // possible double dot + if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.') + break; + break search; + } + + end=start; + start=path.lastIndexOf('/',end-1); + } - // Too many .. - if (skip>0) - return null; - - // Do the delete - if (delEnd>=0) - buf.delete(delStart,delEnd); - - return buf.toString(); - } - - /* ------------------------------------------------------------ */ - /** Convert a path to a compact form. - * All instances of "//" and "///" etc. are factored out to single "/" - * @param path - * @return path - */ - public static String compactPath(String path) - { - if (path==null || path.length()==0) - return path; + // If we have checked the entire string + if (start>=end) + return path; + + StringBuilder buf = new StringBuilder(path); + int delStart=-1; + int delEnd=-1; + int skip=0; + + while (end>0) + { + switch(end-start) + { + case 2: // possible single dot + if (buf.charAt(start+1)!='.') + { + if (skip>0 && --skip==0) + { + delStart=start>=0?start:0; + if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') + delStart++; + } + break; + } + + if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/') + break; + + if(delEnd<0) + delEnd=end; + delStart=start; + if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/') + { + delStart++; + if (delEnd<buf.length() && buf.charAt(delEnd)=='/') + delEnd++; + break; + } + if (end==buf.length()) + delStart++; + + end=start--; + while (start>=0 && buf.charAt(start)!='/') + start--; + continue; + + case 3: // possible double dot + if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.') + { + if (skip>0 && --skip==0) + { delStart=start>=0?start:0; + if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') + delStart++; + } + break; + } + + delStart=start; + if (delEnd<0) + delEnd=end; - int state=0; - int end=path.length(); - int i=0; - - loop: - while (i<end) - { - char c=path.charAt(i); - switch(c) - { - case '?': - return path; - case '/': - state++; - if (state==2) - break loop; - break; - default: - state=0; - } - i++; - } - - if (state<2) - return path; - - StringBuffer buf = new StringBuffer(path.length()); - buf.append(path,0,i); - - loop2: - while (i<end) - { - char c=path.charAt(i); - switch(c) - { - case '?': - buf.append(path,i,end); - break loop2; - case '/': - if (state++==0) - buf.append(c); - break; - default: - state=0; - buf.append(c); - } - i++; - } - - return buf.toString(); - } + skip++; + end=start--; + while (start>=0 && buf.charAt(start)!='/') + start--; + continue; + + default: + if (skip>0 && --skip==0) + { + delStart=start>=0?start:0; + if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.') + delStart++; + } + } + + // Do the delete + if (skip<=0 && delStart>=0 && delEnd>=delStart) + { + buf.delete(delStart,delEnd); + delStart=delEnd=-1; + if (skip>0) + delEnd=end; + } + + end=start--; + while (start>=0 && buf.charAt(start)!='/') + start--; + } - /* ------------------------------------------------------------ */ - /** - * @param uri URI - * @return True if the uri has a scheme - */ - public static boolean hasScheme(String uri) - { - for (int i=0;i<uri.length();i++) - { - char c=uri.charAt(i); - if (c==':') - return true; - if (!(c>='a'&&c<='z' || - c>='A'&&c<='Z' || - (i>0 &&(c>='0'&&c<='9' || - c=='.' || - c=='+' || - c=='-')) - )) - break; - } - return false; - } - + // Too many .. + if (skip>0) + return null; + + // Do the delete + if (delEnd>=0) + buf.delete(delStart,delEnd); + + return buf.toString(); + } + + /* ------------------------------------------------------------ */ + /** + * @param uri URI + * @return True if the uri has a scheme + */ + public static boolean hasScheme(String uri) + { + for (int i=0;i<uri.length();i++) + { + char c=uri.charAt(i); + if (c==':') + return true; + if (!(c>='a'&&c<='z' || + c>='A'&&c<='Z' || + (i>0 &&(c>='0'&&c<='9' || + c=='.' || + c=='+' || + c=='-')) + )) + break; + } + return false; + } + }