Mercurial Hosting > luan
diff src/org/eclipse/jetty/util/resource/Resource.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children | 8e9db0bbf4f9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/eclipse/jetty/util/resource/Resource.java Wed Sep 07 21:15:48 2016 -0600 @@ -0,0 +1,678 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util.resource; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.text.DateFormat; +import java.util.Arrays; +import java.util.Date; + +import org.eclipse.jetty.util.B64Code; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/* ------------------------------------------------------------ */ +/** + * Abstract resource class. + */ +public abstract class Resource implements ResourceFactory +{ + private static final Logger LOG = Log.getLogger(Resource.class); + public static boolean __defaultUseCaches = true; + volatile Object _associate; + + /* ------------------------------------------------------------ */ + /** + * Change the default setting for url connection caches. + * Subsequent URLConnections will use this default. + * @param useCaches + */ + public static void setDefaultUseCaches (boolean useCaches) + { + __defaultUseCaches=useCaches; + } + + /* ------------------------------------------------------------ */ + public static boolean getDefaultUseCaches () + { + return __defaultUseCaches; + } + + /* ------------------------------------------------------------ */ + /** Construct a resource from a uri. + * @param uri A URI. + * @return A Resource object. + * @throws IOException Problem accessing URI + */ + public static Resource newResource(URI uri) + throws IOException + { + return newResource(uri.toURL()); + } + + /* ------------------------------------------------------------ */ + /** Construct a resource from a url. + * @param url A URL. + * @return A Resource object. + * @throws IOException Problem accessing URL + */ + public static Resource newResource(URL url) + throws IOException + { + return newResource(url, __defaultUseCaches); + } + + /* ------------------------------------------------------------ */ + /** + * Construct a resource from a url. + * @param url the url for which to make the resource + * @param useCaches true enables URLConnection caching if applicable to the type of resource + * @return + */ + static Resource newResource(URL url, boolean useCaches) + { + if (url==null) + return null; + + String url_string=url.toExternalForm(); + if( url_string.startsWith( "file:")) + { + try + { + FileResource fileResource= new FileResource(url); + return fileResource; + } + catch(Exception e) + { + LOG.debug(Log.EXCEPTION,e); + return new BadResource(url,e.toString()); + } + } + else if( url_string.startsWith( "jar:file:")) + { + return new JarFileResource(url, useCaches); + } + else if( url_string.startsWith( "jar:")) + { + return new JarResource(url, useCaches); + } + + return new URLResource(url,null,useCaches); + } + + + + /* ------------------------------------------------------------ */ + /** Construct a resource from a string. + * @param resource A URL or filename. + * @return A Resource object. + */ + public static Resource newResource(String resource) + throws MalformedURLException, IOException + { + return newResource(resource, __defaultUseCaches); + } + + /* ------------------------------------------------------------ */ + /** Construct a resource from a string. + * @param resource A URL or filename. + * @param useCaches controls URLConnection caching + * @return A Resource object. + */ + public static Resource newResource (String resource, boolean useCaches) + throws MalformedURLException, IOException + { + URL url=null; + try + { + // Try to format as a URL? + url = new URL(resource); + } + catch(MalformedURLException e) + { + if(!resource.startsWith("ftp:") && + !resource.startsWith("file:") && + !resource.startsWith("jar:")) + { + try + { + // It's a file. + if (resource.startsWith("./")) + resource=resource.substring(2); + + File file=new File(resource).getCanonicalFile(); + url=Resource.toURL(file); + + URLConnection connection=url.openConnection(); + connection.setUseCaches(useCaches); + return new FileResource(url,connection,file); + } + catch(Exception e2) + { + LOG.debug(Log.EXCEPTION,e2); + throw e; + } + } + else + { + LOG.warn("Bad Resource: "+resource); + throw e; + } + } + + return newResource(url); + } + + /* ------------------------------------------------------------ */ + public static Resource newResource (File file) + throws MalformedURLException, IOException + { + file = file.getCanonicalFile(); + URL url = Resource.toURL(file); + + URLConnection connection = url.openConnection(); + FileResource fileResource = new FileResource(url, connection, file); + return fileResource; + } + + /* ------------------------------------------------------------ */ + /** Construct a system resource from a string. + * The resource is tried as classloader resource before being + * treated as a normal resource. + * @param resource Resource as string representation + * @return The new Resource + * @throws IOException Problem accessing resource. + */ + public static Resource newSystemResource(String resource) + throws IOException + { + URL url=null; + // Try to format as a URL? + ClassLoader loader=Thread.currentThread().getContextClassLoader(); + if (loader!=null) + { + try + { + url = loader.getResource(resource); + if (url == null && resource.startsWith("/")) + url = loader.getResource(resource.substring(1)); + } + catch (IllegalArgumentException e) + { + // Catches scenario where a bad Windows path like "C:\dev" is + // improperly escaped, which various downstream classloaders + // tend to have a problem with + url = null; + } + } + if (url==null) + { + loader=Resource.class.getClassLoader(); + if (loader!=null) + { + url=loader.getResource(resource); + if (url==null && resource.startsWith("/")) + url=loader.getResource(resource.substring(1)); + } + } + + if (url==null) + { + url=ClassLoader.getSystemResource(resource); + if (url==null && resource.startsWith("/")) + url=ClassLoader.getSystemResource(resource.substring(1)); + } + + if (url==null) + return null; + + return newResource(url); + } + + /* ------------------------------------------------------------ */ + /** Find a classpath resource. + */ + public static Resource newClassPathResource(String resource) + { + return newClassPathResource(resource,true,false); + } + + /* ------------------------------------------------------------ */ + /** Find a classpath resource. + * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not + * found, then the {@link Loader#getResource(Class, String, boolean)} method is used. + * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used. + * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources. + * @param name The relative name of the resource + * @param useCaches True if URL caches are to be used. + * @param checkParents True if forced searching of parent Classloaders is performed to work around + * loaders with inverted priorities + * @return Resource or null + */ + public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents) + { + URL url=Resource.class.getResource(name); + + if (url==null) + url=Loader.getResource(Resource.class,name,checkParents); + if (url==null) + return null; + return newResource(url,useCaches); + } + + /* ------------------------------------------------------------ */ + public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException + { + return r.isContainedIn(containingResource); + } + + /* ------------------------------------------------------------ */ + @Override + protected void finalize() + { + release(); + } + + /* ------------------------------------------------------------ */ + public abstract boolean isContainedIn (Resource r) throws MalformedURLException; + + + /* ------------------------------------------------------------ */ + /** Release any temporary resources held by the resource. + */ + public abstract void release(); + + + /* ------------------------------------------------------------ */ + /** + * Returns true if the respresened resource exists. + */ + public abstract boolean exists(); + + + /* ------------------------------------------------------------ */ + /** + * Returns true if the respresenetd resource is a container/directory. + * If the resource is not a file, resources ending with "/" are + * considered directories. + */ + public abstract boolean isDirectory(); + + /* ------------------------------------------------------------ */ + /** + * Returns the last modified time + */ + public abstract long lastModified(); + + + /* ------------------------------------------------------------ */ + /** + * Return the length of the resource + */ + public abstract long length(); + + + /* ------------------------------------------------------------ */ + /** + * Returns an URL representing the given resource + */ + public abstract URL getURL(); + + /* ------------------------------------------------------------ */ + /** + * Returns an URI representing the given resource + */ + public URI getURI() + { + try + { + return getURL().toURI(); + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + + /* ------------------------------------------------------------ */ + /** + * Returns an File representing the given resource or NULL if this + * is not possible. + */ + public abstract File getFile() + throws IOException; + + + /* ------------------------------------------------------------ */ + /** + * Returns the name of the resource + */ + public abstract String getName(); + + + /* ------------------------------------------------------------ */ + /** + * Returns an input stream to the resource + */ + public abstract InputStream getInputStream() + throws java.io.IOException; + + /* ------------------------------------------------------------ */ + /** + * Returns an output stream to the resource + */ + public abstract OutputStream getOutputStream() + throws java.io.IOException, SecurityException; + + /* ------------------------------------------------------------ */ + /** + * Deletes the given resource + */ + public abstract boolean delete() + throws SecurityException; + + /* ------------------------------------------------------------ */ + /** + * Rename the given resource + */ + public abstract boolean renameTo( Resource dest) + throws SecurityException; + + /* ------------------------------------------------------------ */ + /** + * Returns a list of resource names contained in the given resource + * The resource names are not URL encoded. + */ + public abstract String[] list(); + + /* ------------------------------------------------------------ */ + /** + * Returns the resource contained inside the current resource with the + * given name. + * @param path The path segment to add, which should be encoded by the + * encode method. + */ + public abstract Resource addPath(String path) + throws IOException,MalformedURLException; + + /* ------------------------------------------------------------ */ + /** Get a resource from withing this resource. + * <p> + * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions. + * This method satisfied the {@link ResourceFactory} interface. + * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String) + */ + public Resource getResource(String path) + { + try + { + return addPath(path); + } + catch(Exception e) + { + LOG.debug(e); + return null; + } + } + + /* ------------------------------------------------------------ */ + /** Encode according to this resource type. + * The default implementation calls URI.encodePath(uri) + * @param uri + * @return String encoded for this resource type. + */ + public String encode(String uri) + { + return URIUtil.encodePath(uri); + } + + /* ------------------------------------------------------------ */ + public Object getAssociate() + { + return _associate; + } + + /* ------------------------------------------------------------ */ + public void setAssociate(Object o) + { + _associate=o; + } + + /* ------------------------------------------------------------ */ + /** + * @return The canonical Alias of this resource or null if none. + */ + public URL getAlias() + { + return null; + } + + /* ------------------------------------------------------------ */ + /** Get the resource list as a HTML directory listing. + * @param base The base URL + * @param parent True if the parent directory should be included + * @return String of HTML + */ + public String getListHTML(String base,boolean parent) + throws IOException + { + base=URIUtil.canonicalPath(base); + if (base==null || !isDirectory()) + return null; + + String[] ls = list(); + if (ls==null) + return null; + Arrays.sort(ls); + + String decodedBase = URIUtil.decodePath(base); + String title = "Directory: "+deTag(decodedBase); + + StringBuilder buf=new StringBuilder(4096); + buf.append("<HTML><HEAD>"); + buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>"); + buf.append(title); + buf.append("</TITLE></HEAD><BODY>\n<H1>"); + buf.append(title); + buf.append("</H1>\n<TABLE BORDER=0>\n"); + + if (parent) + { + buf.append("<TR><TD><A HREF=\""); + buf.append(URIUtil.addPaths(base,"../")); + buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n"); + } + + String encodedBase = hrefEncodeURI(base); + + DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM, + DateFormat.MEDIUM); + for (int i=0 ; i< ls.length ; i++) + { + Resource item = addPath(ls[i]); + + buf.append("\n<TR><TD><A HREF=\""); + String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i])); + + buf.append(path); + + if (item.isDirectory() && !path.endsWith("/")) + buf.append(URIUtil.SLASH); + + // URIUtil.encodePath(buf,path); + buf.append("\">"); + buf.append(deTag(ls[i])); + buf.append(" "); + buf.append("</A></TD><TD ALIGN=right>"); + buf.append(item.length()); + buf.append(" bytes </TD><TD>"); + buf.append(dfmt.format(new Date(item.lastModified()))); + buf.append("</TD></TR>"); + } + buf.append("</TABLE>\n"); + buf.append("</BODY></HTML>\n"); + + return buf.toString(); + } + + /** + * Encode any characters that could break the URI string in an HREF. + * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a> + * + * The above example would parse incorrectly on various browsers as the "<" or '"' characters + * would end the href attribute value string prematurely. + * + * @param raw the raw text to encode. + * @return the defanged text. + */ + private static String hrefEncodeURI(String raw) + { + StringBuffer buf = null; + + loop: + for (int i=0;i<raw.length();i++) + { + char c=raw.charAt(i); + switch(c) + { + case '\'': + case '"': + case '<': + case '>': + buf=new StringBuffer(raw.length()<<1); + break loop; + } + } + if (buf==null) + return raw; + + for (int i=0;i<raw.length();i++) + { + char c=raw.charAt(i); + switch(c) + { + case '"': + buf.append("%22"); + continue; + case '\'': + buf.append("%27"); + continue; + case '<': + buf.append("%3C"); + continue; + case '>': + buf.append("%3E"); + continue; + default: + buf.append(c); + continue; + } + } + + return buf.toString(); + } + + private static String deTag(String raw) + { + return StringUtil.replace( StringUtil.replace(raw,"<","<"), ">", ">"); + } + + /* ------------------------------------------------------------ */ + /** + * @param out + * @param start First byte to write + * @param count Bytes to write or -1 for all of them. + */ + public void writeTo(OutputStream out,long start,long count) + throws IOException + { + InputStream in = getInputStream(); + try + { + in.skip(start); + if (count<0) + IO.copy(in,out); + else + IO.copy(in,out,count); + } + finally + { + in.close(); + } + } + + /* ------------------------------------------------------------ */ + public void copyTo(File destination) + throws IOException + { + if (destination.exists()) + throw new IllegalArgumentException(destination+" exists"); + writeTo(new FileOutputStream(destination),0,-1); + } + + /* ------------------------------------------------------------ */ + public String getWeakETag() + { + try + { + StringBuilder b = new StringBuilder(32); + b.append("W/\""); + + String name=getName(); + int length=name.length(); + long lhash=0; + for (int i=0; i<length;i++) + lhash=31*lhash+name.charAt(i); + + B64Code.encode(lastModified()^lhash,b); + B64Code.encode(length()^lhash,b); + b.append('"'); + return b.toString(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /* ------------------------------------------------------------ */ + /** Generate a properly encoded URL from a {@link File} instance. + * @param file Target file. + * @return URL of the target file. + * @throws MalformedURLException + */ + public static URL toURL(File file) throws MalformedURLException + { + return file.toURI().toURL(); + } +}