1 //
2 // ========================================================================
3 // Copyright (c) 1995-2016 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.util.Callback;
43 import org.eclipse.jetty.util.URIUtil;
44 import org.eclipse.jetty.util.resource.Resource;
45
46 /**
47 * Fast FileServer.
48 * <p>
49 * This example shows how to use the Jetty APIs for sending static as fast as
50 * possible using various strategies for small, medium and large content.
51 * </p>
52 * <p>
53 * The Jetty {@link DefaultServlet} does all this and more, and to a lesser
54 * extent so does the {@link ResourceHandler}, so unless you have exceptional
55 * circumstances it is best to use those classes for static content
56 * </p>
57 */
58 public class FastFileServer
59 {
60 public static void main( String[] args ) throws Exception
61 {
62 Server server = new Server(8080);
63
64 HandlerList handlers = new HandlerList();
65 handlers.setHandlers(new Handler[] {
66 new FastFileHandler(new File(System.getProperty("user.dir"))),
67 new DefaultHandler() });
68 server.setHandler(handlers);
69
70 server.start();
71 server.join();
72 }
73
74 static class FastFileHandler extends AbstractHandler
75 {
76 private final MimeTypes mimeTypes = new MimeTypes();
77 private final File dir;
78
79 private FastFileHandler( File dir )
80 {
81 this.dir = dir;
82 }
83
84 @Override
85 public void handle( String target,
86 Request baseRequest,
87 HttpServletRequest request,
88 HttpServletResponse response ) throws IOException,
89 ServletException
90 {
91 // define small medium and large.
92 // This should be turned for your content, JVM and OS, but we will
93 // huge HTTP response buffer size as a measure
94 final int SMALL = response.getBufferSize();
95 final int MEDIUM = 8 * SMALL;
96
97 // What file to serve?
98 final File file = new File(this.dir, request.getPathInfo());
99
100 // Only handle existing files
101 if (!file.exists())
102 return;
103
104 // we will handle this request
105 baseRequest.setHandled(true);
106
107 // Handle directories
108 if (file.isDirectory())
109 {
110 if (!request.getPathInfo().endsWith(URIUtil.SLASH))
111 {
112 response.sendRedirect(response.encodeRedirectURL(URIUtil
113 .addPaths(request.getRequestURI(), URIUtil.SLASH)));
114 return;
115 }
116 String listing = Resource.newResource(file).getListHTML(
117 request.getRequestURI(),
118 request.getPathInfo().lastIndexOf("/") > 0);
119 response.setContentType("text/html; charset=utf-8");
120 response.getWriter().println(listing);
121 return;
122 }
123
124 // Set some content headers.
125
126 // Jetty DefaultServlet will cache formatted date strings, but we
127 // will reformat for each request here
128 response.setDateHeader("Last-Modified", file.lastModified());
129 response.setDateHeader("Content-Length", file.length());
130 response.setContentType(mimeTypes.getMimeByExtension(file.getName()));
131
132 // send "small" files blocking directly from an input stream
133 if (file.length() < SMALL)
134 {
135 // need to caste to Jetty output stream for best API
136 ((HttpOutput) response.getOutputStream())
137 .sendContent(FileChannel.open(file.toPath(),
138 StandardOpenOption.READ));
139 return;
140 }
141
142 // send not "small" files asynchronously so we don't hold threads if
143 // the client is slow
144 final AsyncContext async = request.startAsync();
145 Callback completionCB = new Callback()
146 {
147 @Override
148 public void succeeded()
149 {
150 // Async content write succeeded, so complete async response
151 async.complete();
152 }
153
154 @Override
155 public void failed( Throwable x )
156 {
157 // log error and complete async response;
158 x.printStackTrace();
159 async.complete();
160 }
161 };
162
163 // send "medium" files from an input stream
164 if (file.length() < MEDIUM)
165 {
166 // the file channel is closed by the async send
167 ((HttpOutput) response.getOutputStream())
168 .sendContent(FileChannel.open(file.toPath(),
169 StandardOpenOption.READ), completionCB);
170 return;
171 }
172
173 // for "large" files get the file mapped buffer to send Typically
174 // the resulting buffer should be cached as allocating kernel memory
175 // can be hard to GC on some JVMs. But for this example we will
176 // create a new buffer per file
177 ByteBuffer buffer;
178 try ( RandomAccessFile raf = new RandomAccessFile(file, "r"); )
179 {
180 buffer = raf.getChannel().map(MapMode.READ_ONLY, 0,
181 raf.length());
182 }
183
184 // Assuming the file buffer might be shared cached version, so lets
185 // take our own view of it
186 buffer = buffer.asReadOnlyBuffer();
187
188 // send the content as a buffer with a callback to complete the
189 // async request need to caste to Jetty output stream for best API
190 ((HttpOutput) response.getOutputStream()).sendContent(buffer,
191 completionCB);
192 }
193 }
194 }