330417 Atomic PUT in PutFilter

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2526 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2010-11-17 01:52:33 +00:00
parent a9a56e8716
commit d955fa0c3a
3 changed files with 91 additions and 21 deletions

View File

@ -3,6 +3,7 @@ jetty-7.2.2-SNAPSHOT
+ 330208 Support new wording on servlet-mapping and filter-mapping merging from servlet3.0a
+ 330188 Reject web-fragment.xml with same <name> as another already loaded one
+ 330229 Jetty tries to parse META-INF/*.tld when jsp-api is not on classpath, causing DTD entity resoluton to fail
+ 330417 Atomic PUT in PutFilter
jetty-7.2.1.v20101111 11 November 2010
+ 324679 Fixed dedection of write before static content

View File

@ -20,6 +20,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -32,9 +33,11 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;
@ -45,9 +48,10 @@ import org.eclipse.jetty.util.URIUtil;
* A Filter that handles PUT, DELETE and MOVE methods.
* Files are hidden during PUT operations, so that 404's result.
*
* The following init paramters pay be used:<ul>
* The following init parameters pay be used:<ul>
* <li><b>baseURI</b> - The file URI of the document root for put content.
* <li><b>delAllowed</b> - boolean, if true DELETE and MOVE methods are supported.
* <li><b>putAtomic</b> - boolean, if true PUT files are written to a temp location and moved into place.
* </ul>
*
*/
@ -59,17 +63,23 @@ public class PutFilter implements Filter
public final static String __OPTIONS="OPTIONS";
Set<String> _operations = new HashSet<String>();
protected ConcurrentMap<String,String> _hidden = new ConcurrentHashMap<String, String>();
protected String _options;
private ConcurrentMap<String,String> _hidden = new ConcurrentHashMap<String, String>();
private String _options=null;
protected ServletContext _context;
protected String _baseURI;
protected boolean _delAllowed;
private ServletContext _context;
private String _baseURI;
private boolean _delAllowed;
private boolean _putAtomic;
private File _tmpdir;
/* ------------------------------------------------------------ */
public void init(FilterConfig config) throws ServletException
{
_context=config.getServletContext();
_tmpdir=(File)_context.getAttribute("javax.servlet.context.tempdir");
if (_context.getRealPath("/")==null)
throw new UnavailableException("Packed war");
@ -83,17 +93,16 @@ public class PutFilter implements Filter
File base=new File(_context.getRealPath("/"));
_baseURI=base.toURI().toString();
}
_delAllowed = getInitBoolean(config,"delAllowed");
_putAtomic = getInitBoolean(config,"putAtomic");
_operations.add(__OPTIONS);
_operations.add(__PUT);
_options="GET,HEAD,POST,OPTIONS,PUT";
if (_delAllowed)
{
_operations.add(__DELETE);
_operations.add(__MOVE);
_options+=",DELETE";
}
}
@ -107,7 +116,6 @@ public class PutFilter implements Filter
/* ------------------------------------------------------------ */
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse)res;
@ -126,7 +134,7 @@ public class PutFilter implements Filter
try
{
if (method.equals(__OPTIONS))
handleOptions(request, response);
handleOptions(chain,request, response);
else
{
file=new File(new URI(resource));
@ -203,12 +211,30 @@ public class PutFilter implements Filter
parent.mkdirs();
int toRead = request.getContentLength();
InputStream in = request.getInputStream();
OutputStream out = new FileOutputStream(file,false);
if (toRead >= 0)
IO.copy(in, out, toRead);
if (_putAtomic)
{
File tmp=File.createTempFile(file.getName(),null,_tmpdir);
OutputStream out = new FileOutputStream(tmp,false);
if (toRead >= 0)
IO.copy(in, out, toRead);
else
IO.copy(in, out);
out.close();
if (!tmp.renameTo(file))
throw new IOException("rename from "+tmp+" to "+file+" failed");
}
else
IO.copy(in, out);
out.close();
{
OutputStream out = new FileOutputStream(file,false);
if (toRead >= 0)
IO.copy(in, out, toRead);
else
IO.copy(in, out);
out.close();
}
response.setStatus(exists ? HttpServletResponse.SC_OK : HttpServletResponse.SC_CREATED);
response.flushBuffer();
@ -290,10 +316,27 @@ public class PutFilter implements Filter
}
/* ------------------------------------------------------------ */
public void handleOptions(HttpServletRequest request, HttpServletResponse response) throws IOException
public void handleOptions(FilterChain chain, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// TODO filter real options and add PUT & DELETE
response.setHeader("Allow", _options);
chain.doFilter(request,new HttpServletResponseWrapper(response)
{
@Override
public void setHeader(String name, String value)
{
if ("Allow".equalsIgnoreCase(name))
{
Set<String> options = new HashSet<String>();
options.addAll(Arrays.asList(value.split(" *, *")));
options.addAll(_operations);
value=null;
for (String o : options)
value=value==null?o:(value+", "+o);
}
super.setHeader(name,value);
}
});
}
/* ------------------------------------------------------------ */

View File

@ -18,6 +18,10 @@ import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlet.FilterHolder;
@ -51,6 +55,7 @@ public class PutFilterTest
tester.addServlet(org.eclipse.jetty.servlet.DefaultServlet.class, "/");
FilterHolder holder = tester.addFilter(PutFilter.class,"/*",0);
holder.setInitParameter("delAllowed","true");
holder.setInitParameter("putAtomic","true");
tester.start();
}
@ -58,6 +63,7 @@ public class PutFilterTest
public void tearDown() throws Exception
{
tester.stop();
IO.delete(_dir);
}
@Test
@ -233,9 +239,29 @@ public class PutFilterTest
}
@Test
public void testHandleOptions()
public void testHandleOptions() throws Exception
{
// TODO implement
// generated and parsed test
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
// test PUT1
request.setMethod("OPTIONS");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/file.txt");
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
Set<String> options = new HashSet<String>();
options.addAll(Arrays.asList(response.getHeader("Allow").split(" *, *")));
assertTrue(options.contains("GET"));
assertTrue(options.contains("POST"));
assertTrue(options.contains("PUT"));
assertTrue(options.contains("MOVE"));
}
@Test