blob: 5bbbaf872b6407b4dd436857a9e75ba8be71880c [file] [log] [blame]
//
// ========================================================================
// 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.embedded;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.StandardOpenOption;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
/** Fast FileServer.
*
* <p>This example shows how to use the Jetty APIs for sending static
* as fast as possible using various strategies for small, medium and
* large content.</p>
* <p>The Jetty {@link DefaultServlet} does all this and more, and to
* a lesser extent so does the {@link ResourceHandler}, so unless you
* have exceptional circumstances it is best to use those classes for
* static content</p>
*/
public class FastFileServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { new FastFileHandler(new File(".")), new DefaultHandler() });
server.setHandler(handlers);
server.start();
server.join();
}
static class FastFileHandler extends AbstractHandler
{
private final MimeTypes _mimeTypes = new MimeTypes();
private final File _dir;
FastFileHandler(File dir)
{
_dir=dir;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// define small medium and large.
// This should be turned for your content, JVM and OS, but we will huge HTTP response buffer size as a measure
final int SMALL=response.getBufferSize();
final int MEDIUM=8*SMALL;
// What file to serve?
final File file = new File(_dir,request.getPathInfo());
// Only handle existing files
if (!file.exists())
return;
// we will handle this request
baseRequest.setHandled(true);
// Handle directories
if (file.isDirectory())
{
if (!request.getPathInfo().endsWith(URIUtil.SLASH))
{
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
return;
}
String listing = Resource.newResource(file).getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().println(listing);
return;
}
// Set some content headers
// Jetty DefaultServlet will cache formatted date strings, but we will reformat for each request here
response.setDateHeader("Last-Modified",file.lastModified());
response.setDateHeader("Content-Length",file.length());
response.setContentType(_mimeTypes.getMimeByExtension(file.getName()));
// send "small" files blocking directly from an input stream
if (file.length()<SMALL)
{
// need to caste to Jetty output stream for best API
((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ));
return;
}
// send not "small" files asynchronously so we don't hold threads if the client is slow
final AsyncContext async = request.startAsync();
Callback completionCB = new Callback()
{
@Override
public void succeeded()
{
// Async content write succeeded, so complete async response
async.complete();
}
@Override
public void failed(Throwable x)
{
// log error and complete async response;
x.printStackTrace();
async.complete();
}
};
// send "medium" files from an input stream
if (file.length()<MEDIUM)
{
// the file channel is closed by the async send
((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ),completionCB);
return;
}
// for "large" files get the file mapped buffer to send
// Typically the resulting buffer should be cached as allocating kernel memory
// can be hard to GC on some JVMs. But for this example we will create a new buffer per file
ByteBuffer buffer;
try (RandomAccessFile raf = new RandomAccessFile(file,"r");)
{
buffer=raf.getChannel().map(MapMode.READ_ONLY,0,raf.length());
}
// Assuming the file buffer might be shared cached version, so lets take our own view of it
buffer=buffer.asReadOnlyBuffer();
// send the content as a buffer with a callback to complete the async request
// need to caste to Jetty output stream for best API
((HttpOutput)response.getOutputStream()).sendContent(buffer,completionCB);
}
}
}