/examples/kilim/examples/HttpFileServer.java

http://github.com/kilim/kilim · Java · 299 lines · 249 code · 19 blank · 31 comment · 30 complexity · 3dfe0e692883c072edd4824af86099f7 MD5 · raw file

  1. /* Copyright (c) 2006, Sriram Srinivasan
  2. *
  3. * You may distribute this software under the terms of the license
  4. * specified in the file "License"
  5. */
  6. package kilim.examples;
  7. import java.io.EOFException;
  8. import java.io.File;
  9. import java.io.FileInputStream;
  10. import java.io.IOException;
  11. import java.io.PrintStream;
  12. import java.nio.channels.FileChannel;
  13. import java.util.HashMap;
  14. import kilim.Pausable;
  15. import kilim.http.HttpRequest;
  16. import kilim.http.HttpResponse;
  17. import kilim.http.HttpServer;
  18. import kilim.http.HttpSession;
  19. /**
  20. * A simple file server over http
  21. *
  22. * Usage: Run java kilim.examples.HttpFileServer [base directory name] From a browser, go to "http://localhost:7262".
  23. *
  24. * A HttpFileServer object is a SessionTask, and is thus a thin wrapper over the socket connection. Its execute() method
  25. * is called once on connection establishment. The HttpRequest and HttpResponse objects are wrappers over a bytebuffer,
  26. * and unrelated to the socket. The request object is "filled in" by HttpSession.readRequest() and the response object
  27. * is sent by HttpSession.sendResponse(). The rest of the code is related to the mechanics of file serving, common to
  28. * Kilim and non-Kilim approaches alike. The objective of this example is merely to demonstrate Kilim API, not to have a
  29. * fully functioning file server.
  30. */
  31. public class HttpFileServer extends HttpSession {
  32. public static File baseDirectory;
  33. public static String baseDirectoryName;
  34. public static void main(String[] args) throws IOException {
  35. baseDirectoryName = ".";
  36. if (args.length > 0) {
  37. baseDirectoryName = args[0];
  38. }
  39. baseDirectory = new File(baseDirectoryName);
  40. if (!baseDirectory.isDirectory()) {
  41. usage();
  42. }
  43. baseDirectoryName = baseDirectory.getCanonicalPath();
  44. // create a listener on port 7262. An instance of HttpFileServer is created upon
  45. // every new socket connection to this port.
  46. new HttpServer(7262, HttpFileServer.class);
  47. System.out.println("HttpFileServer listening on http://localhost:7262");
  48. }
  49. public static void usage() {
  50. System.err.println("Usage: java kilim.examples.HttpFileServer [<baseDirectory>]");
  51. System.exit(1);
  52. }
  53. @Override
  54. public void execute() throws Pausable, Exception {
  55. try {
  56. // We will reuse the req and resp objects
  57. HttpRequest req = new HttpRequest();
  58. HttpResponse resp = new HttpResponse();
  59. while (true) {
  60. // Fill up the request object. This pauses until the entire request has
  61. // been read in, including all chunks.
  62. super.readRequest(req);
  63. // System.out.println(req);
  64. if (req.method.equals("GET") || req.method.equals("HEAD")) {
  65. File f = urlToPath(req);
  66. System.out.println("[" + this.id + "] Read: " + f.getPath());
  67. if (check(resp, f)) {
  68. boolean headOnly = req.method.equals("HEAD");
  69. if (f.isDirectory())
  70. sendDirectory(resp, f, headOnly);
  71. else
  72. sendFile(resp, f, headOnly);
  73. }
  74. } else {
  75. super.problem(resp, HttpResponse.ST_FORBIDDEN, "Only GET and HEAD accepted");
  76. }
  77. if (!req.keepAlive()) {
  78. break;
  79. }
  80. }
  81. } catch (EOFException e) {
  82. System.out.println("[" + this.id + "] Connection Terminated");
  83. } catch (IOException ioe) {
  84. System.out.println("[" + this.id + "] IO Exception:" + ioe.getMessage());
  85. }
  86. super.close();
  87. }
  88. private File urlToPath(HttpRequest req) {
  89. return (req.uriPath == null) ? baseDirectory : new File(baseDirectory, req.uriPath);
  90. }
  91. public boolean check(HttpResponse resp, File file) throws IOException, Pausable {
  92. byte[] status = HttpResponse.ST_OK;
  93. String msg = "";
  94. if (!file.exists()) {
  95. status = HttpResponse.ST_NOT_FOUND;
  96. msg = "File Not Found: " + file.getName();
  97. } else if (!file.canRead()) {
  98. status = HttpResponse.ST_FORBIDDEN;
  99. msg = "Unable to read file " + file.getName();
  100. } else {
  101. try {
  102. String path = file.getCanonicalPath();
  103. if (!path.startsWith(baseDirectoryName)) {
  104. throw new SecurityException();
  105. }
  106. } catch (Exception e) {
  107. status = HttpResponse.ST_FORBIDDEN;
  108. msg = "Error retrieving " + file.getName() + ":<br>" + e.getMessage();
  109. }
  110. }
  111. if (status != HttpResponse.ST_OK) {
  112. problem(file, resp, status, msg);
  113. return false;
  114. } else {
  115. return true;
  116. }
  117. }
  118. public void sendFile(HttpResponse resp, File file, boolean headOnly) throws IOException, Pausable {
  119. FileInputStream fis;
  120. FileChannel fc;
  121. try {
  122. fis = new FileInputStream(file);
  123. fc = fis.getChannel();
  124. } catch (IOException ioe) {
  125. problem(file, resp, HttpResponse.ST_NOT_FOUND, "Send exception: " + ioe.getMessage());
  126. return;
  127. }
  128. try {
  129. String contentType = mimeType(file);
  130. if (contentType != null) {
  131. resp.setContentType(contentType);
  132. }
  133. resp.setContentLength(file.length());
  134. // Send the header first (with the content type and length)
  135. super.sendResponse(resp);
  136. // Send the contents; this uses sendfile or equivalent underneath.
  137. endpoint.write(fc, 0, file.length());
  138. } finally {
  139. fc.close();
  140. fis.close();
  141. }
  142. }
  143. public void sendDirectory(HttpResponse resp, File file, boolean headOnly) throws Pausable, IOException {
  144. PrintStream p = new PrintStream(resp.getOutputStream());
  145. String relDir = getRelPath(file);
  146. p.print("<html><head><title>Index of ");
  147. p.print(relDir);
  148. p.print("</title></head><body ");
  149. p.print("><h2>Index of ");
  150. p.print(relDir.equals(".") ? "/" : relDir);
  151. p.print("</h2>");
  152. String names[] = file.list();
  153. if (names == null) {
  154. p.print("No files found");
  155. } else {
  156. for (int i = 0; i < names.length; i++) {
  157. // <a href="webpath">name</a>
  158. p.print("<a href=\"");
  159. p.print(relDir);
  160. p.print('/');
  161. p.print(names[i]);
  162. p.print("\">");
  163. p.print(names[i]);
  164. p.print("</a><br>");
  165. }
  166. }
  167. p.print("</body></html>");
  168. p.flush();
  169. super.sendResponse(resp);
  170. }
  171. public void problem(File file, HttpResponse resp, byte[] statusCode, String msg) throws IOException, Pausable {
  172. System.out.println("[" + id + "]. Error retrieving " + file.getAbsolutePath() + "':\n " + msg);
  173. super.problem(resp, statusCode, msg);
  174. }
  175. private String getRelPath(File file) throws IOException {
  176. String path = file.getCanonicalPath();
  177. if (!path.startsWith(baseDirectoryName)) {
  178. throw new SecurityException();
  179. }
  180. path = path.substring(baseDirectoryName.length()); // include the "/"
  181. return (path.length() == 0) ? "." : path;
  182. }
  183. public static HashMap<String, String> mimeTypes = new HashMap<String, String>();
  184. static {
  185. mimeTypes.put("html", "text/html");
  186. mimeTypes.put("htm", "text/html");
  187. mimeTypes.put("txt", "text/plain");
  188. mimeTypes.put("xml", "text/xml");
  189. mimeTypes.put("css", "text/css");
  190. mimeTypes.put("sgml", "text/x-sgml");
  191. mimeTypes.put("sgm", "text/x-sgml");
  192. // Images
  193. mimeTypes.put("gif", "image/gif");
  194. mimeTypes.put("jpg", "image/jpeg");
  195. mimeTypes.put("jpeg", "image/jpeg");
  196. mimeTypes.put("png", "image/png");
  197. mimeTypes.put("bmp", "image/bmp");
  198. mimeTypes.put("tif", "image/tiff");
  199. mimeTypes.put("tiff", "image/tiff");
  200. mimeTypes.put("rgb", "image/x-rgb");
  201. mimeTypes.put("xpm", "image/x-xpixmap");
  202. mimeTypes.put("xbm", "image/x-xbitmap");
  203. mimeTypes.put("svg", "image/svg-xml ");
  204. mimeTypes.put("svgz", "image/svg-xml ");
  205. // Audio
  206. mimeTypes.put("au", "audio/basic");
  207. mimeTypes.put("snd", "audio/basic");
  208. mimeTypes.put("mid", "audio/mid");
  209. mimeTypes.put("midi", "audio/mid");
  210. mimeTypes.put("rmi", "audio/mid");
  211. mimeTypes.put("kar", "audio/mid");
  212. mimeTypes.put("mpga", "audio/mpeg");
  213. mimeTypes.put("mp2", "audio/mpeg");
  214. mimeTypes.put("mp3", "audio/mpeg");
  215. mimeTypes.put("wav", "audio/wav");
  216. mimeTypes.put("aiff", "audio/aiff");
  217. mimeTypes.put("aifc", "audio/aiff");
  218. mimeTypes.put("aif", "audio/x-aiff");
  219. mimeTypes.put("ra", "audio/x-realaudio");
  220. mimeTypes.put("rpm", "audio/x-pn-realaudio-plugin");
  221. mimeTypes.put("ram", "audio/x-pn-realaudio");
  222. mimeTypes.put("sd2", "audio/x-sd2");
  223. // Applications
  224. mimeTypes.put("bin", "application/octet-stream");
  225. mimeTypes.put("dms", "application/octet-stream");
  226. mimeTypes.put("lha", "application/octet-stream");
  227. mimeTypes.put("lzh", "application/octet-stream");
  228. mimeTypes.put("exe", "application/octet-stream");
  229. mimeTypes.put("dll", "application/octet-stream");
  230. mimeTypes.put("class", "application/octet-stream");
  231. mimeTypes.put("hqx", "application/mac-binhex40");
  232. mimeTypes.put("ps", "application/postscript");
  233. mimeTypes.put("eps", "application/postscript");
  234. mimeTypes.put("pdf", "application/pdf");
  235. mimeTypes.put("rtf", "application/rtf");
  236. mimeTypes.put("doc", "application/msword");
  237. mimeTypes.put("ppt", "application/powerpoint");
  238. mimeTypes.put("fif", "application/fractals");
  239. mimeTypes.put("p7c", "application/pkcs7-mime");
  240. // Application/x
  241. mimeTypes.put("js", "application/x-javascript");
  242. mimeTypes.put("z", "application/x-compress");
  243. mimeTypes.put("gz", "application/x-gzip");
  244. mimeTypes.put("tar", "application/x-tar");
  245. mimeTypes.put("tgz", "application/x-compressed");
  246. mimeTypes.put("zip", "application/x-zip-compressed");
  247. mimeTypes.put("dvi", "application/x-dvi");
  248. mimeTypes.put("tex", "application/x-tex");
  249. mimeTypes.put("latex", "application/x-latex");
  250. mimeTypes.put("tcl", "application/x-tcl");
  251. mimeTypes.put("cer", "application/x-x509-ca-cert");
  252. mimeTypes.put("crt", "application/x-x509-ca-cert");
  253. mimeTypes.put("der", "application/x-x509-ca-cert");
  254. mimeTypes.put("iso", "application/x-iso9660-image");
  255. // Video
  256. mimeTypes.put("mpg", "video/mpeg");
  257. mimeTypes.put("mpe", "video/mpeg");
  258. mimeTypes.put("mpeg", "video/mpeg");
  259. mimeTypes.put("qt", "video/quicktime");
  260. mimeTypes.put("mov", "video/quicktime");
  261. mimeTypes.put("avi", "video/x-msvideo");
  262. mimeTypes.put("movie", "video/x-sgi-movie");
  263. mimeTypes.put("jnlp", "application/x-java-jnlp-file");
  264. mimeTypes.put("wrl", "x-world/x-vrml");
  265. mimeTypes.put("vrml", "x-world/x-vrml");
  266. mimeTypes.put("wml", "text/vnd.wap.wml");
  267. mimeTypes.put("wmlc", "application/vnd.wap.wmlc");
  268. mimeTypes.put("wmls", "text/vnd.wap.wmlscript");
  269. }
  270. public static String mimeType(File file) {
  271. String name = file.getName();
  272. int dotpos = name.lastIndexOf('.');
  273. if (dotpos == -1)
  274. return "text/plain";
  275. else {
  276. String mimeType = mimeTypes.get(name.substring(dotpos + 1).toLowerCase());
  277. return (mimeType == null) ? "text/plain" : mimeType;
  278. }
  279. }
  280. }