001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002 // The Molly framework is freely distributable under the terms of an 003 // MIT-style license. For details, see the molly pages web site at: 004 // http://www.mollypages.org/. Use, modify, have fun ! 005 006 package fc.web.servlet; 007 008 import javax.servlet.*; 009 import javax.servlet.http.*; 010 import java.io.*; 011 import java.util.*; 012 import fc.web.servlet.*; 013 import fc.io.*; 014 015 /** 016 Sends the specified file as a binary stream after setting the 017 ContenEncoding to be gzip. (the specified file should have 018 a .gz extension, this is mandated because it helps catch 019 user errors, for example forgetting to gzip the file). 020 <p> 021 <b>Note, this servlet does not Gzip data, it only serves data that 022 has already been gzip'ed.</b> 023 <p> 024 To use, add something like the following to web.xml: 025 <blockquote> 026 <pre> 027 <servlet> 028 <servlet-name>gzip</servlet-name> 029 <servlet-class>fc.web.servlet.GzipFileServlet</servlet-class> 030 </servlet> 031 ... 032 <!-- an example mapping, your taste may vary --> 033 <servlet-mapping> 034 <servlet-name>gzip</servlet-name> 035 <url-pattern>/gzip</url-pattern> 036 </servlet-mapping> 037 </pre> 038 </blockquote> 039 Then, in this example, one can say, in your HTML document: 040 <blockquote> 041 <pre> 042 <html> 043 <head> 044 <title>Untitled</title> 045 <font color=blue><script src="/gzip?js=myscript.js"></script></font> 046 </head> 047 <body> 048 hello 049 </body> 050 </html> 051 </pre> 052 </blockquote> 053 That will load the <i><code>myscript.js.gz</code></i> 054 file (and if myscript.js.gz is not present, a servlet exception 055 will be logged). Note, the ".gz" extension is added automatically 056 if not specified in the filename. 057 <p> 058 There are two modes in serving files. In both cases, the file 059 to be retrieved <u>must already exist as a gzip file on 060 the server</u>. 061 <ol> 062 <li> 063 Retrieve a arbitrary gzip'ed file from the server. There are 3 064 required parameters: 065 <dl> 066 <dt>file</dt> 067 <dd>the filename (if the specified name does not have a 068 .gz extension, ".gz" will be added automatically to the 069 specified name). [see <a href="#path"> path note below</a>] 070 </dd> 071 <dt>mimetype</dt> 072 <dd>The mimetype of the retrieved file (<i>after</i> it has been 073 gunzipped). This will commonly be <code>text/html</code> or 074 <code>text/javascript</code></dd> 075 <dt>encoding</dt> 076 <dd>The text encoding of the specified file. Commonly, it is 077 US-ASCII, ISO-8859-1 or UTF-8</dd> 078 </dl> 079 Example: <code> 080 /gzip?file=foo.html.gz&mimetype=text/html&encoding=ISO-8859-1 081 </code> 082 </li> 083 084 <li> 085 Retrieve a Javascript file (convenience method): This will automatically 086 set the content-type to be <i>text/javascript; charset=utf-8</i> 087 (so the mime or charset do not have to be specified). One need 088 only specify: 089 <dd> 090 <dt>js</dt> 091 <dl>path to the gzip'ed file. [see <a href="#path"> path note below</a>]</dl> 092 </dd> 093 </li> 094 </ol> 095 <hr> 096 <h3>Path name for the specified file</h3> 097 098 This path to the included file must be in absolute (start with a 099 '/') or relative form. 100 <p> 101 Absolute path names are like HTML absolute names and 102 start from the document root directory of the web server. 103 <p> 104 Relative names (relative to the invoking page) are <b>not</b> 105 supported by this servlet. 106 107 @author hursh jain 108 */ 109 public final class GzipFileServlet extends javax.servlet.http.HttpServlet 110 { 111 /* 112 context page 113 /WEBAPP/ /bar/baz/foo.js 114 /WEBAPP/ /bar/img/zap.img 115 116 from foo.js -> 117 a) "../img/zap.img" [relative] 118 b) "/WEBAPP/bar/img/zap.img" [absolute #1] 119 c) "/bar/img/zap.img" [absolute #2, WEBAPP added by us] 120 121 we really want (a) and (b), (c) is not-intuitive. 122 (a) and (b) correspond to tradional relative, absolute paths 123 (where absolute is always from the web server document root] 124 125 (a) is too tricky, we don't know the path to the invoking 126 page in this servlet (so cannot do relative to that 127 unknown path). 128 */ 129 130 File docroot; 131 132 String d_charset = "text/html; charset=ISO-8859-1"; 133 134 public void init(ServletConfig conf) throws ServletException 135 { 136 super.init(); 137 ServletContext context = conf.getServletContext(); 138 ServletContext root_context = context.getContext("/"); 139 docroot = new File(root_context.getRealPath("/")); 140 } 141 142 /** 143 Returns the specified gzip file. If the specified name 144 does not end with ".gz", ".gz" will automatically 145 be tacked on to the specified name (so files served by 146 this method must always end with ".gz" 147 */ 148 public void doGet(final HttpServletRequest req, final HttpServletResponse res) 149 throws ServletException, IOException 150 { 151 final String file = req.getParameter("file"); 152 if (file != null) { 153 sendFile(file, req, res); 154 return; 155 } 156 157 final String js = req.getParameter("js"); 158 if (js != null) { 159 sendJSFile(js, req, res); 160 return; 161 } 162 163 throw new ServletException("Bad request. I need either the \"file\" or the \"jsfile\" parameter."); 164 } 165 166 void sendFile(final String file, final HttpServletRequest req, final HttpServletResponse res) 167 throws ServletException, IOException 168 { 169 final String mime = WebUtil.getRequiredParam(req, "mimetype"); 170 final String encoding = WebUtil.getRequiredParam(req, "encoding"); 171 final String content_type = mime + "; charset=" + encoding; 172 res.setContentType(content_type); 173 res.setHeader("Content-Encoding", "gzip"); 174 ServletOutputStream out = res.getOutputStream(); 175 push(out, file); 176 } 177 178 String js_content_type = "text/javascript; charset=utf-8"; 179 180 void sendJSFile(final String file, final HttpServletRequest req, final HttpServletResponse res) 181 throws ServletException, IOException 182 { 183 res.setContentType(js_content_type); 184 res.setHeader("Content-Encoding", "gzip"); 185 final ServletOutputStream out = res.getOutputStream(); 186 push(out, file); 187 } 188 189 void push(final ServletOutputStream out, final String file) 190 throws IOException 191 { 192 final File f = makeFile(file); 193 final byte[] buf = IOUtil.fileToByteArray(f); 194 final BufferedOutputStream bout = new BufferedOutputStream(out); 195 bout.write(buf, 0, buf.length); 196 bout.flush(); 197 bout.close(); 198 } 199 200 File makeFile(String path) throws IOException 201 { 202 if (! path.endsWith(".gz")) { 203 path += ".gz"; 204 } 205 206 File f = new File(docroot, path); 207 208 if (! f.exists() || ! f.isFile()) { 209 throw new IOException("\nERROR: file: [" + path + "] not found"); 210 } 211 return f; 212 } 213 }