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 006package fc.web.servlet; 007 008import javax.servlet.*; 009import javax.servlet.http.*; 010import java.io.*; 011import java.util.*; 012import fc.web.servlet.*; 013import fc.io.*; 014 015/** 016Sends the specified file as a binary stream after setting the 017ContenEncoding to be gzip. (the specified file should have 018a .gz extension, this is mandated because it helps catch 019user errors, for example forgetting to gzip the file). 020<p> 021<b>Note, this servlet does not Gzip data, it only serves data that 022has already been gzip'ed.</b> 023<p> 024To 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> 039Then, 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> 048hello 049</body> 050</html> 051</pre> 052</blockquote> 053That will load the <i><code>myscript.js.gz</code></i> 054file (and if myscript.js.gz is not present, a servlet exception 055will be logged). Note, the ".gz" extension is added automatically 056if not specified in the filename. 057<p> 058There are two modes in serving files. In both cases, the file 059to be retrieved <u>must already exist as a gzip file on 060the server</u>. 061<ol> 062<li> 063Retrieve a arbitrary gzip'ed file from the server. There are 3 064required 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 069specified 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 073gunzipped). 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 077US-ASCII, ISO-8859-1 or UTF-8</dd> 078</dl> 079Example: <code> 080/gzip?file=foo.html.gz&mimetype=text/html&encoding=ISO-8859-1 081</code> 082</li> 083 084<li> 085Retrieve a Javascript file (convenience method): This will automatically 086set 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 088only 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 098This path to the included file must be in absolute (start with a 099'/') or relative form. 100<p> 101Absolute path names are like HTML absolute names and 102start from the document root directory of the web server. 103<p> 104Relative names (relative to the invoking page) are <b>not</b> 105supported by this servlet. 106 107@author hursh jain 108*/ 109public 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 130File docroot; 131 132String d_charset = "text/html; charset=ISO-8859-1"; 133 134public 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/** 143Returns the specified gzip file. If the specified name 144does not end with ".gz", ".gz" will automatically 145be tacked on to the specified name (so files served by 146this method must always end with ".gz" 147*/ 148public void doGet(final HttpServletRequest req, final HttpServletResponse res) 149throws 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 166void sendFile(final String file, final HttpServletRequest req, final HttpServletResponse res) 167throws 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 178String js_content_type = "text/javascript; charset=utf-8"; 179 180void sendJSFile(final String file, final HttpServletRequest req, final HttpServletResponse res) 181throws 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 189void push(final ServletOutputStream out, final String file) 190throws 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 200File 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}