1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.embedded;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.RandomAccessFile;
24 import java.nio.ByteBuffer;
25 import java.nio.channels.FileChannel;
26 import java.nio.channels.FileChannel.MapMode;
27 import java.nio.file.StandardOpenOption;
28
29 import javax.servlet.AsyncContext;
30 import javax.servlet.ServletException;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33
34 import org.eclipse.jetty.http.MimeTypes;
35 import org.eclipse.jetty.server.Handler;
36 import org.eclipse.jetty.server.HttpOutput;
37 import org.eclipse.jetty.server.Request;
38 import org.eclipse.jetty.server.Server;
39 import org.eclipse.jetty.server.handler.AbstractHandler;
40 import org.eclipse.jetty.server.handler.DefaultHandler;
41 import org.eclipse.jetty.server.handler.HandlerList;
42 import org.eclipse.jetty.server.handler.ResourceHandler;
43 import org.eclipse.jetty.servlet.DefaultServlet;
44 import org.eclipse.jetty.util.Callback;
45 import org.eclipse.jetty.util.URIUtil;
46 import org.eclipse.jetty.util.resource.Resource;
47
48 /* ------------------------------------------------------------ */
49 /** Fast FileServer.
50 *
51 * <p>This example shows how to use the Jetty APIs for sending static
52 * as fast as possible using various strategies for small, medium and
53 * large content.</p>
54 * <p>The Jetty {@link DefaultServlet} does all this and more, and to
55 * a lesser extent so does the {@link ResourceHandler}, so unless you
56 * have exceptional circumstances it is best to use those classes for
57 * static content</p>
58 */
59 public class FastFileServer
60 {
61 public static void main(String[] args) throws Exception
62 {
63 Server server = new Server(8080);
64
65 HandlerList handlers = new HandlerList();
66 handlers.setHandlers(new Handler[] { new FastFileHandler(new File(".")), new DefaultHandler() });
67 server.setHandler(handlers);
68
69 server.start();
70 server.join();
71 }
72
73 static class FastFileHandler extends AbstractHandler
74 {
75 private final MimeTypes _mimeTypes = new MimeTypes();
76 private final File _dir;
77
78 FastFileHandler(File dir)
79 {
80 _dir=dir;
81 }
82
83 @Override
84 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
85 {
86 // define small medium and large.
87 // This should be turned for your content, JVM and OS, but we will huge HTTP response buffer size as a measure
88 final int SMALL=response.getBufferSize();
89 final int MEDIUM=8*SMALL;
90
91
92 // What file to serve?
93 final File file = new File(_dir,request.getPathInfo());
94
95 // Only handle existing files
96 if (!file.exists())
97 return;
98
99 // we will handle this request
100 baseRequest.setHandled(true);
101
102 // Handle directories
103 if (file.isDirectory())
104 {
105 if (!request.getPathInfo().endsWith(URIUtil.SLASH))
106 {
107 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
108 return;
109 }
110 String listing = Resource.newResource(file).getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
111 response.setContentType("text/html; charset=UTF-8");
112 response.getWriter().println(listing);
113 return;
114 }
115
116 // Set some content headers
117 // Jetty DefaultServlet will cache formatted date strings, but we will reformat for each request here
118 response.setDateHeader("Last-Modified",file.lastModified());
119 response.setDateHeader("Content-Length",file.length());
120 response.setContentType(_mimeTypes.getMimeByExtension(file.getName()));
121
122
123
124 // send "small" files blocking directly from an input stream
125 if (file.length()<SMALL)
126 {
127 // need to caste to Jetty output stream for best API
128 ((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ));
129 return;
130 }
131
132
133 // send not "small" files asynchronously so we don't hold threads if the client is slow
134 final AsyncContext async = request.startAsync();
135 Callback completionCB = new Callback()
136 {
137 @Override
138 public void succeeded()
139 {
140 // Async content write succeeded, so complete async response
141 async.complete();
142 }
143
144 @Override
145 public void failed(Throwable x)
146 {
147 // log error and complete async response;
148 x.printStackTrace();
149 async.complete();
150 }
151 };
152
153
154
155 // send "medium" files from an input stream
156 if (file.length()<MEDIUM)
157 {
158 // the file channel is closed by the async send
159 ((HttpOutput)response.getOutputStream()).sendContent(FileChannel.open(file.toPath(),StandardOpenOption.READ),completionCB);
160 return;
161 }
162
163
164 // for "large" files get the file mapped buffer to send
165 // Typically the resulting buffer should be cached as allocating kernel memory
166 // can be hard to GC on some JVMs. But for this example we will create a new buffer per file
167 ByteBuffer buffer;
168 try (RandomAccessFile raf = new RandomAccessFile(file,"r");)
169 {
170 buffer=raf.getChannel().map(MapMode.READ_ONLY,0,raf.length());
171 }
172
173 // Assuming the file buffer might be shared cached version, so lets take our own view of it
174 buffer=buffer.asReadOnlyBuffer();
175
176
177 // send the content as a buffer with a callback to complete the async request
178 // need to caste to Jetty output stream for best API
179 ((HttpOutput)response.getOutputStream()).sendContent(buffer,completionCB);
180 }
181 }
182 }